mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 20:56:46 +02:00
Compare commits
389 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 | |||
fd86f01fd0 | |||
e775c51a06 |
13
.gitignore
vendored
13
.gitignore
vendored
@ -9,19 +9,10 @@
|
|||||||
*.app
|
*.app
|
||||||
*.swp
|
*.swp
|
||||||
*.la
|
*.la
|
||||||
m4/*
|
|
||||||
!m4/pkg.m4
|
|
||||||
configure
|
|
||||||
configure_aux
|
|
||||||
Makefile.in
|
|
||||||
aclocal.m4
|
|
||||||
config.h*
|
|
||||||
config.log
|
|
||||||
config.status
|
|
||||||
stamp-h1
|
stamp-h1
|
||||||
autom4te.cache
|
|
||||||
.deps
|
.deps
|
||||||
.libs
|
.libs
|
||||||
*.orig
|
*.orig
|
||||||
build/toxic
|
build/toxic
|
||||||
Makefile
|
build/*.o
|
||||||
|
build/*.d
|
||||||
|
46
.travis.yml
46
.travis.yml
@ -4,27 +4,45 @@ compiler:
|
|||||||
- clang
|
- clang
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
# installing libsodium, needed for Core
|
# Installing yasm (needed for compiling vpx) and openal
|
||||||
- git clone git://github.com/jedisct1/libsodium.git
|
- sudo apt-get -yq install yasm libopenal-dev
|
||||||
|
# Installing libsodium, needed for toxcore
|
||||||
|
- git clone https://github.com/jedisct1/libsodium.git libsodium
|
||||||
- cd libsodium
|
- cd libsodium
|
||||||
- git checkout tags/0.4.2
|
- git checkout tags/0.4.2 > /dev/null
|
||||||
- ./autogen.sh
|
- ./autogen.sh > /dev/null
|
||||||
- ./configure && make -j3
|
- ./configure > /dev/null
|
||||||
- sudo make install
|
- make check -j2 || make check || exit 1 > /dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
- cd ..
|
- cd ..
|
||||||
# creating librarys' links and updating cache
|
# Installing libopus, needed for audio encoding/decoding
|
||||||
- sudo ldconfig
|
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
||||||
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
|
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
||||||
|
- cd opus-1.0.3
|
||||||
|
- ./configure > /dev/null
|
||||||
|
- make -j2 || make || exit 1 > /dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
|
- cd ..
|
||||||
|
# Installing vpx
|
||||||
|
- git clone http://git.chromium.org/webm/libvpx.git libvpx
|
||||||
|
- cd libvpx
|
||||||
|
- ./configure --enable-shared > /dev/null
|
||||||
|
- make -j2 || make || exit 1 > /dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
|
- cd ..
|
||||||
|
# Creating libraries links and updating cache
|
||||||
|
- sudo ldconfig > /dev/null
|
||||||
|
# Installing toxcore
|
||||||
|
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
||||||
- cd toxcore
|
- cd toxcore
|
||||||
- autoreconf -i
|
- autoreconf -i
|
||||||
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon
|
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
||||||
- make -j2
|
- make -j2 || make || exit 1
|
||||||
- sudo make install
|
- sudo make install
|
||||||
- cd ..
|
- cd ..
|
||||||
script:
|
script:
|
||||||
- autoreconf -i
|
- cd build
|
||||||
- ./configure
|
- make -j2 || make || exit 1
|
||||||
- make -j2
|
|
||||||
notifications:
|
notifications:
|
||||||
email: false
|
email: false
|
||||||
|
|
||||||
|
1
COPYING
1
COPYING
@ -672,3 +672,4 @@ may consider it more useful to permit linking proprietary applications with
|
|||||||
the library. If this is what you want to do, use the GNU Lesser General
|
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
|
Public License instead of this License. But first, please read
|
||||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
|
|
||||||
|
365
INSTALL
365
INSTALL
@ -1,365 +0,0 @@
|
|||||||
Installation Instructions
|
|
||||||
*************************
|
|
||||||
|
|
||||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
|
||||||
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
Copying and distribution of this file, with or without modification,
|
|
||||||
are permitted in any medium without royalty provided the copyright
|
|
||||||
notice and this notice are preserved. This file is offered as-is,
|
|
||||||
without warranty of any kind.
|
|
||||||
|
|
||||||
Basic Installation
|
|
||||||
==================
|
|
||||||
|
|
||||||
Briefly, the shell commands `./configure; make; make install' should
|
|
||||||
configure, build, and install this package. The following
|
|
||||||
more-detailed instructions are generic; see the `README' file for
|
|
||||||
instructions specific to this package. Some packages provide this
|
|
||||||
`INSTALL' file but do not implement all of the features documented
|
|
||||||
below. The lack of an optional feature in a given package is not
|
|
||||||
necessarily a bug. More recommendations for GNU packages can be found
|
|
||||||
in *note Makefile Conventions: (standards)Makefile Conventions.
|
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
|
||||||
various system-dependent variables used during compilation. It uses
|
|
||||||
those values to create a `Makefile' in each directory of the package.
|
|
||||||
It may also create one or more `.h' files containing system-dependent
|
|
||||||
definitions. Finally, it creates a shell script `config.status' that
|
|
||||||
you can run in the future to recreate the current configuration, and a
|
|
||||||
file `config.log' containing compiler output (useful mainly for
|
|
||||||
debugging `configure').
|
|
||||||
|
|
||||||
It can also use an optional file (typically called `config.cache'
|
|
||||||
and enabled with `--cache-file=config.cache' or simply `-C') that saves
|
|
||||||
the results of its tests to speed up reconfiguring. Caching is
|
|
||||||
disabled by default to prevent problems with accidental use of stale
|
|
||||||
cache files.
|
|
||||||
|
|
||||||
If you need to do unusual things to compile the package, please try
|
|
||||||
to figure out how `configure' could check whether to do them, and mail
|
|
||||||
diffs or instructions to the address given in the `README' so they can
|
|
||||||
be considered for the next release. If you are using the cache, and at
|
|
||||||
some point `config.cache' contains results you don't want to keep, you
|
|
||||||
may remove or edit it.
|
|
||||||
|
|
||||||
The file `configure.ac' (or `configure.in') is used to create
|
|
||||||
`configure' by a program called `autoconf'. You need `configure.ac' if
|
|
||||||
you want to change it or regenerate `configure' using a newer version
|
|
||||||
of `autoconf'.
|
|
||||||
|
|
||||||
The simplest way to compile this package is:
|
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type
|
|
||||||
`./configure' to configure the package for your system.
|
|
||||||
|
|
||||||
Running `configure' might take a while. While running, it prints
|
|
||||||
some messages telling which features it is checking for.
|
|
||||||
|
|
||||||
2. Type `make' to compile the package.
|
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with
|
|
||||||
the package, generally using the just-built uninstalled binaries.
|
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and
|
|
||||||
documentation. When installing into a prefix owned by root, it is
|
|
||||||
recommended that the package be configured and built as a regular
|
|
||||||
user, and only the `make install' phase executed with root
|
|
||||||
privileges.
|
|
||||||
|
|
||||||
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
|
||||||
this time using the binaries in their final installed location.
|
|
||||||
This target does not install anything. Running this target as a
|
|
||||||
regular user, particularly if the prior `make install' required
|
|
||||||
root privileges, verifies that the installation completed
|
|
||||||
correctly.
|
|
||||||
|
|
||||||
6. You can remove the program binaries and object files from the
|
|
||||||
source code directory by typing `make clean'. To also remove the
|
|
||||||
files that `configure' created (so you can compile the package for
|
|
||||||
a different kind of computer), type `make distclean'. There is
|
|
||||||
also a `make maintainer-clean' target, but that is intended mainly
|
|
||||||
for the package's developers. If you use it, you may have to get
|
|
||||||
all sorts of other programs in order to regenerate files that came
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
7. Often, you can also type `make uninstall' to remove the installed
|
|
||||||
files again. In practice, not all packages have tested that
|
|
||||||
uninstallation works correctly, even though it is required by the
|
|
||||||
GNU Coding Standards.
|
|
||||||
|
|
||||||
8. Some packages, particularly those that use Automake, provide `make
|
|
||||||
distcheck', which can by used by developers to test that all other
|
|
||||||
targets like `make install' and `make uninstall' work correctly.
|
|
||||||
This target is generally not run by end users.
|
|
||||||
|
|
||||||
Compilers and Options
|
|
||||||
=====================
|
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that
|
|
||||||
the `configure' script does not know about. Run `./configure --help'
|
|
||||||
for details on some of the pertinent environment variables.
|
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
|
||||||
by setting variables in the command line or in the environment. Here
|
|
||||||
is an example:
|
|
||||||
|
|
||||||
./configure CC=c99 CFLAGS=-g LIBS=-lposix
|
|
||||||
|
|
||||||
*Note Defining Variables::, for more details.
|
|
||||||
|
|
||||||
Compiling For Multiple Architectures
|
|
||||||
====================================
|
|
||||||
|
|
||||||
You can compile the package for more than one kind of computer at the
|
|
||||||
same time, by placing the object files for each architecture in their
|
|
||||||
own directory. To do this, you can use GNU `make'. `cd' to the
|
|
||||||
directory where you want the object files and executables to go and run
|
|
||||||
the `configure' script. `configure' automatically checks for the
|
|
||||||
source code in the directory that `configure' is in and in `..'. This
|
|
||||||
is known as a "VPATH" build.
|
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one
|
|
||||||
architecture at a time in the source code directory. After you have
|
|
||||||
installed the package for one architecture, use `make distclean' before
|
|
||||||
reconfiguring for another architecture.
|
|
||||||
|
|
||||||
On MacOS X 10.5 and later systems, you can create libraries and
|
|
||||||
executables that work on multiple system types--known as "fat" or
|
|
||||||
"universal" binaries--by specifying multiple `-arch' options to the
|
|
||||||
compiler but only a single `-arch' option to the preprocessor. Like
|
|
||||||
this:
|
|
||||||
|
|
||||||
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
|
||||||
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
|
||||||
CPP="gcc -E" CXXCPP="g++ -E"
|
|
||||||
|
|
||||||
This is not guaranteed to produce working output in all cases, you
|
|
||||||
may have to build one architecture at a time and combine the results
|
|
||||||
using the `lipo' tool if you have problems.
|
|
||||||
|
|
||||||
Installation Names
|
|
||||||
==================
|
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under
|
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
|
||||||
can specify an installation prefix other than `/usr/local' by giving
|
|
||||||
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
|
||||||
absolute file name.
|
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
|
||||||
architecture-specific files and architecture-independent files. If you
|
|
||||||
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
|
|
||||||
PREFIX as the prefix for installing programs and libraries.
|
|
||||||
Documentation and other data files still use the regular prefix.
|
|
||||||
|
|
||||||
In addition, if you use an unusual directory layout you can give
|
|
||||||
options like `--bindir=DIR' to specify different values for particular
|
|
||||||
kinds of files. Run `configure --help' for a list of the directories
|
|
||||||
you can set and what kinds of files go in them. In general, the
|
|
||||||
default for these options is expressed in terms of `${prefix}', so that
|
|
||||||
specifying just `--prefix' will affect all of the other directory
|
|
||||||
specifications that were not explicitly provided.
|
|
||||||
|
|
||||||
The most portable way to affect installation locations is to pass the
|
|
||||||
correct locations to `configure'; however, many packages provide one or
|
|
||||||
both of the following shortcuts of passing variable assignments to the
|
|
||||||
`make install' command line to change installation locations without
|
|
||||||
having to reconfigure or recompile.
|
|
||||||
|
|
||||||
The first method involves providing an override variable for each
|
|
||||||
affected directory. For example, `make install
|
|
||||||
prefix=/alternate/directory' will choose an alternate location for all
|
|
||||||
directory configuration variables that were expressed in terms of
|
|
||||||
`${prefix}'. Any directories that were specified during `configure',
|
|
||||||
but not in terms of `${prefix}', must each be overridden at install
|
|
||||||
time for the entire installation to be relocated. The approach of
|
|
||||||
makefile variable overrides for each directory variable is required by
|
|
||||||
the GNU Coding Standards, and ideally causes no recompilation.
|
|
||||||
However, some platforms have known limitations with the semantics of
|
|
||||||
shared libraries that end up requiring recompilation when using this
|
|
||||||
method, particularly noticeable in packages that use GNU Libtool.
|
|
||||||
|
|
||||||
The second method involves providing the `DESTDIR' variable. For
|
|
||||||
example, `make install DESTDIR=/alternate/directory' will prepend
|
|
||||||
`/alternate/directory' before all installation names. The approach of
|
|
||||||
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
|
||||||
does not work on platforms that have drive letters. On the other hand,
|
|
||||||
it does better at avoiding recompilation issues, and works well even
|
|
||||||
when some directory options were not specified in terms of `${prefix}'
|
|
||||||
at `configure' time.
|
|
||||||
|
|
||||||
Optional Features
|
|
||||||
=================
|
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed
|
|
||||||
with an extra prefix or suffix on their names by giving `configure' the
|
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to
|
|
||||||
`configure', where FEATURE indicates an optional part of the package.
|
|
||||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
|
||||||
is something like `gnu-as' or `x' (for the X Window System). The
|
|
||||||
`README' should mention any `--enable-' and `--with-' options that the
|
|
||||||
package recognizes.
|
|
||||||
|
|
||||||
For packages that use the X Window System, `configure' can usually
|
|
||||||
find the X include and library files automatically, but if it doesn't,
|
|
||||||
you can use the `configure' options `--x-includes=DIR' and
|
|
||||||
`--x-libraries=DIR' to specify their locations.
|
|
||||||
|
|
||||||
Some packages offer the ability to configure how verbose the
|
|
||||||
execution of `make' will be. For these packages, running `./configure
|
|
||||||
--enable-silent-rules' sets the default to minimal output, which can be
|
|
||||||
overridden with `make V=1'; while running `./configure
|
|
||||||
--disable-silent-rules' sets the default to verbose, which can be
|
|
||||||
overridden with `make V=0'.
|
|
||||||
|
|
||||||
Particular systems
|
|
||||||
==================
|
|
||||||
|
|
||||||
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
|
||||||
CC is not installed, it is recommended to use the following options in
|
|
||||||
order to use an ANSI C compiler:
|
|
||||||
|
|
||||||
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
|
||||||
|
|
||||||
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
|
||||||
|
|
||||||
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
|
||||||
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
|
||||||
a workaround. If GNU CC is not installed, it is therefore recommended
|
|
||||||
to try
|
|
||||||
|
|
||||||
./configure CC="cc"
|
|
||||||
|
|
||||||
and if that doesn't work, try
|
|
||||||
|
|
||||||
./configure CC="cc -nodtk"
|
|
||||||
|
|
||||||
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
|
||||||
directory contains several dysfunctional programs; working variants of
|
|
||||||
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
|
||||||
in your `PATH', put it _after_ `/usr/bin'.
|
|
||||||
|
|
||||||
On Haiku, software installed for all users goes in `/boot/common',
|
|
||||||
not `/usr/local'. It is recommended to use the following options:
|
|
||||||
|
|
||||||
./configure --prefix=/boot/common
|
|
||||||
|
|
||||||
Specifying the System Type
|
|
||||||
==========================
|
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out
|
|
||||||
automatically, but needs to determine by the type of machine the package
|
|
||||||
will run on. Usually, assuming the package is built to be run on the
|
|
||||||
_same_ architectures, `configure' can figure that out, but if it prints
|
|
||||||
a message saying it cannot guess the machine type, give it the
|
|
||||||
`--build=TYPE' option. TYPE can either be a short name for the system
|
|
||||||
type, such as `sun4', or a canonical name which has the form:
|
|
||||||
|
|
||||||
CPU-COMPANY-SYSTEM
|
|
||||||
|
|
||||||
where SYSTEM can have one of these forms:
|
|
||||||
|
|
||||||
OS
|
|
||||||
KERNEL-OS
|
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If
|
|
||||||
`config.sub' isn't included in this package, then this package doesn't
|
|
||||||
need to know the machine type.
|
|
||||||
|
|
||||||
If you are _building_ compiler tools for cross-compiling, you should
|
|
||||||
use the option `--target=TYPE' to select the type of system they will
|
|
||||||
produce code for.
|
|
||||||
|
|
||||||
If you want to _use_ a cross compiler, that generates code for a
|
|
||||||
platform different from the build platform, you should specify the
|
|
||||||
"host" platform (i.e., that on which the generated programs will
|
|
||||||
eventually be run) with `--host=TYPE'.
|
|
||||||
|
|
||||||
Sharing Defaults
|
|
||||||
================
|
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share,
|
|
||||||
you can create a site shell script called `config.site' that gives
|
|
||||||
default values for variables like `CC', `cache_file', and `prefix'.
|
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
|
||||||
`CONFIG_SITE' environment variable to the location of the site script.
|
|
||||||
A warning: not all `configure' scripts look for a site script.
|
|
||||||
|
|
||||||
Defining Variables
|
|
||||||
==================
|
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the
|
|
||||||
environment passed to `configure'. However, some packages may run
|
|
||||||
configure again during the build, and the customized values of these
|
|
||||||
variables may be lost. In order to avoid this problem, you should set
|
|
||||||
them in the `configure' command line, using `VAR=value'. For example:
|
|
||||||
|
|
||||||
./configure CC=/usr/local2/bin/gcc
|
|
||||||
|
|
||||||
causes the specified `gcc' to be used as the C compiler (unless it is
|
|
||||||
overridden in the site shell script).
|
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
|
||||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
|
||||||
|
|
||||||
`configure' Invocation
|
|
||||||
======================
|
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it
|
|
||||||
operates.
|
|
||||||
|
|
||||||
`--help'
|
|
||||||
`-h'
|
|
||||||
Print a summary of all of the options to `configure', and exit.
|
|
||||||
|
|
||||||
`--help=short'
|
|
||||||
`--help=recursive'
|
|
||||||
Print a summary of the options unique to this package's
|
|
||||||
`configure', and exit. The `short' variant lists options used
|
|
||||||
only in the top level, while the `recursive' variant lists options
|
|
||||||
also present in any nested packages.
|
|
||||||
|
|
||||||
`--version'
|
|
||||||
`-V'
|
|
||||||
Print the version of Autoconf used to generate the `configure'
|
|
||||||
script, and exit.
|
|
||||||
|
|
||||||
`--cache-file=FILE'
|
|
||||||
Enable the cache: use and save the results of the tests in FILE,
|
|
||||||
traditionally `config.cache'. FILE defaults to `/dev/null' to
|
|
||||||
disable caching.
|
|
||||||
|
|
||||||
`--config-cache'
|
|
||||||
`-C'
|
|
||||||
Alias for `--cache-file=config.cache'.
|
|
||||||
|
|
||||||
`--quiet'
|
|
||||||
`--silent'
|
|
||||||
`-q'
|
|
||||||
Do not print messages saying which checks are being made. To
|
|
||||||
suppress all normal output, redirect it to `/dev/null' (any error
|
|
||||||
messages will still be shown).
|
|
||||||
|
|
||||||
`--srcdir=DIR'
|
|
||||||
Look for the package's source code in directory DIR. Usually
|
|
||||||
`configure' can determine that directory automatically.
|
|
||||||
|
|
||||||
`--prefix=DIR'
|
|
||||||
Use DIR as the installation prefix. *note Installation Names::
|
|
||||||
for more details, including other options available for fine-tuning
|
|
||||||
the installation locations.
|
|
||||||
|
|
||||||
`--no-create'
|
|
||||||
`-n'
|
|
||||||
Run the configure checks, but stop before creating any output
|
|
||||||
files.
|
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run
|
|
||||||
`configure --help' for more details.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
SUBDIRS = build misc
|
|
||||||
|
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
|
50
README.md
50
README.md
@ -1,20 +1,48 @@
|
|||||||
## Toxic
|
# Toxic [](https://travis-ci.org/Tox/toxic)
|
||||||
|
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
|
||||||
|
|
||||||
Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png).
|
.
|
||||||
|
|
||||||
To compile, first generate the configure script by running the ```autoreconf -i``` command.
|
## Installation
|
||||||
|
|
||||||
Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.):
|
### Dependencies
|
||||||
|
##### Base
|
||||||
|
* [libtoxcore](https://github.com/irungentoo/toxcore)
|
||||||
|
* [ncurses](https://www.gnu.org/software/ncurses) (for Debian based systems, 'libncursesw5-dev')
|
||||||
|
|
||||||
|
##### Audio
|
||||||
|
* libtoxav (libtoxcore compiled with audio support)
|
||||||
|
* [openal](http://openal.org)
|
||||||
|
|
||||||
|
### Compiling
|
||||||
|
1. `cd build/`
|
||||||
|
2. `make PREFIX="/where/to/install"`
|
||||||
|
3. `sudo make install PREFIX="/where/to/install"`
|
||||||
|
|
||||||
|
### Compilation Notes
|
||||||
|
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
|
||||||
|
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
|
||||||
|
* Audio call support is automatically enabled if all dependencies are found
|
||||||
|
* If you want to build toxic without audio call support, you can use `make DISABLE_AV=1`
|
||||||
|
|
||||||
|
### Packaging
|
||||||
|
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
|
||||||
|
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
|
||||||
|
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
|
||||||
|
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
|
||||||
|
|
||||||
|
### Troubleshooting
|
||||||
|
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
|
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
```
|
```
|
||||||
If you dont already have them, you may need to install the ncurses libraries. For Debian based systems:
|
|
||||||
```
|
## Settings
|
||||||
sudo apt-get install libncurses5-dev libncursesw5-dev
|
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).
|
||||||
|
|
||||||
|
132
build/Makefile
Normal file
132
build/Makefile
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
TOXIC_VERSION = 0.4.5
|
||||||
|
REV = $(shell git rev-list HEAD --count)
|
||||||
|
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||||
|
|
||||||
|
CFG_DIR = ../cfg
|
||||||
|
SRC_DIR = ../src
|
||||||
|
MISC_DIR = ../misc
|
||||||
|
DOC_DIR = ../doc
|
||||||
|
PREFIX = /usr/local
|
||||||
|
BINDIR = $(PREFIX)/bin
|
||||||
|
DATADIR = $(PREFIX)/share/toxic
|
||||||
|
MANDIR = $(PREFIX)/man
|
||||||
|
DATAFILES = DHTnodes toxic.conf.example
|
||||||
|
MANFILES = toxic.1 toxic.conf.5
|
||||||
|
|
||||||
|
LIBS = libtoxcore ncursesw
|
||||||
|
|
||||||
|
CFLAGS = -std=gnu99 -pthread -Wall -g
|
||||||
|
CFLAGS += -DTOXICVER="\"$(VERSION)\"" -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
|
||||||
|
CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\""
|
||||||
|
CFLAGS += $(USER_CFLAGS)
|
||||||
|
LDFLAGS = $(USER_LDFLAGS)
|
||||||
|
|
||||||
|
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o
|
||||||
|
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o
|
||||||
|
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
|
||||||
|
|
||||||
|
# Variables for audio support
|
||||||
|
AUDIO_LIBS = libtoxav openal
|
||||||
|
AUDIO_CFLAGS = -D_SUPPORT_AUDIO
|
||||||
|
AUDIO_OBJ = device.o audio_call.o
|
||||||
|
|
||||||
|
# Check on wich system we are running
|
||||||
|
UNAME_S = $(shell uname -s)
|
||||||
|
ifeq ($(UNAME_S), Linux)
|
||||||
|
-include $(CFG_DIR)/Linux.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), FreeBSD)
|
||||||
|
-include $(CFG_DIR)/FreeBSD.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), Darwin)
|
||||||
|
-include $(CFG_DIR)/Darwin.mk
|
||||||
|
endif
|
||||||
|
ifeq ($(UNAME_S), Solaris)
|
||||||
|
-include $(CFG_DIR)/Solaris.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check on which platform we are running
|
||||||
|
UNAME_M = $(shell uname -m)
|
||||||
|
ifeq ($(UNAME_M), x86_64)
|
||||||
|
-include $(CFG_DIR)/x86_64.mk
|
||||||
|
endif
|
||||||
|
ifneq ($(filter %86, $(UNAME_M)),)
|
||||||
|
-include $(CFG_DIR)/x86.mk
|
||||||
|
endif
|
||||||
|
ifneq ($(filter arm%, $(UNAME_M)),)
|
||||||
|
-include $(CFG_DIR)/arm.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want/can build audio
|
||||||
|
ifneq ($(DISABLE_AV), 1)
|
||||||
|
CHECK_AUDIO_LIBS = $(shell pkg-config $(AUDIO_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||||
|
LIBS += $(AUDIO_LIBS)
|
||||||
|
CFLAGS += $(AUDIO_CFLAGS)
|
||||||
|
OBJ += $(AUDIO_OBJ)
|
||||||
|
else
|
||||||
|
ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||||
|
$(warning WARNING -- You need these libraries for audio support)
|
||||||
|
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build Toxic
|
||||||
|
CHECK_LIBS = $(shell pkg-config $(LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_LIBS), error)
|
||||||
|
CFLAGS += $(shell pkg-config --cflags $(LIBS))
|
||||||
|
LDFLAGS += $(shell pkg-config --libs $(LIBS))
|
||||||
|
else
|
||||||
|
ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning ERROR -- Cannot compile Toxic)
|
||||||
|
$(warning ERROR -- You need these libraries)
|
||||||
|
$(warning ERROR -- $(MISSING_LIBS))
|
||||||
|
$(error ERROR)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Targets
|
||||||
|
all: toxic
|
||||||
|
|
||||||
|
toxic: $(OBJ)
|
||||||
|
@echo " LD $@"
|
||||||
|
@$(CC) $(CFLAGS) -o toxic $(OBJ) $(LDFLAGS)
|
||||||
|
|
||||||
|
install: toxic
|
||||||
|
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
||||||
|
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
||||||
|
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
|
||||||
|
@echo "Installing toxic executable"
|
||||||
|
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
|
||||||
|
@echo "Installing data files"
|
||||||
|
@for f in $(DATAFILES) ; do \
|
||||||
|
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
|
||||||
|
done
|
||||||
|
@echo "Installing man pages"
|
||||||
|
@for f in $(MANFILES) ; do \
|
||||||
|
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||||
|
file=$$section/$$f ;\
|
||||||
|
mkdir -p $$section ;\
|
||||||
|
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||||
|
sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
|
||||||
|
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
||||||
|
gzip -f -9 $$file ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
%.o: $(SRC_DIR)/%.c
|
||||||
|
@echo " CC $@"
|
||||||
|
@$(CC) $(CFLAGS) -o $*.o -c $(SRC_DIR)/$*.c
|
||||||
|
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $*.d
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf *.d *.o toxic
|
||||||
|
|
||||||
|
-include $(CFG_DIR)/help.mk
|
||||||
|
|
||||||
|
-include $(OBJ:.o=.d)
|
||||||
|
|
||||||
|
.PHONY: clean all install
|
@ -1,45 +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 \
|
|
||||||
$(top_srcdir)/src/log.c \
|
|
||||||
$(top_srcdir)/src/log.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)
|
|
||||||
|
|
3
cfg/FreeBSD.mk
Normal file
3
cfg/FreeBSD.mk
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Specials options for freebsd systems
|
||||||
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
|
LDFLAGS += -lncursesw
|
3
cfg/Linux.mk
Normal file
3
cfg/Linux.mk
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Specials options for linux systems
|
||||||
|
CFLAGS +=
|
||||||
|
LDFLAGS += -ldl -lresolv
|
17
cfg/help.mk
Normal file
17
cfg/help.mk
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Help target
|
||||||
|
help:
|
||||||
|
@echo "-- Targets --"
|
||||||
|
@echo " all: Build toxic [DEFAULT]"
|
||||||
|
@echo " toxic: Build toxic"
|
||||||
|
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " clean: Remove built files"
|
||||||
|
@echo " help: This help"
|
||||||
|
@echo
|
||||||
|
@echo "-- Variables --"
|
||||||
|
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
|
||||||
|
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||||
|
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||||
|
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||||
|
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||||
|
|
||||||
|
.PHONY: help
|
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.7], [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
|
|
63
doc/toxic.1
Normal file
63
doc/toxic.1
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
.TH TOXIC 1 "June 2014" "Toxic v__VERSION__" "User Manual"
|
||||||
|
.SH NAME
|
||||||
|
Toxic \- CLI client for Tox
|
||||||
|
.SH SYNOPSYS
|
||||||
|
.B toxic [\-f
|
||||||
|
.I data\-file
|
||||||
|
.B ] [\-x] [\-4] [\-c
|
||||||
|
.I config\-file
|
||||||
|
.B ] [\-n
|
||||||
|
.I nodes\-file
|
||||||
|
.B ] [\-h]
|
||||||
|
.SH DESCRIPTION
|
||||||
|
Toxic is an ncurses-based instant messaging client for Tox which formerly
|
||||||
|
resided in the Tox core repository, and is now available as a standalone
|
||||||
|
application.
|
||||||
|
.SH OPTIONS
|
||||||
|
.IP "\-f, \-\-file data\-file"
|
||||||
|
Use specified
|
||||||
|
.I data\-file
|
||||||
|
instead of
|
||||||
|
.IR ~/.config/tox/data
|
||||||
|
.IP "\-x, \-\-nodata"
|
||||||
|
Ignore data file
|
||||||
|
.IP "\-4, \-\-ipv4"
|
||||||
|
Force IPv4 connection
|
||||||
|
.IP "\-d, \-\-default_locale
|
||||||
|
Use default locale
|
||||||
|
.IP "\-c, \-\-config config\-file"
|
||||||
|
Use specified
|
||||||
|
.I config\-file
|
||||||
|
instead of
|
||||||
|
.IR ~/.config/tox/toxic.conf
|
||||||
|
.IP "\-n, \-\-nodes nodes\-file"
|
||||||
|
Use specified
|
||||||
|
.I nodes\-file
|
||||||
|
for DHT bootstrap nodes, instead of
|
||||||
|
.IR __DATADIR__/DHTnodes
|
||||||
|
.IP "\-h, \-\-help"
|
||||||
|
Show help message
|
||||||
|
.SH FILES
|
||||||
|
.IP __DATADIR__/DHTnodes
|
||||||
|
Default list of DHT bootstrap nodes.
|
||||||
|
.IP ~/.config/tox/data
|
||||||
|
Savestate which contains your personal info (nickname, Tox ID,...) and
|
||||||
|
your contacts list.
|
||||||
|
.IP ~/.config/tox/toxic.conf
|
||||||
|
Configuration file. See
|
||||||
|
.BR toxic.conf (5)
|
||||||
|
for more details.
|
||||||
|
.IP __DATADIR__/toxic.conf.example
|
||||||
|
Configuration example.
|
||||||
|
.SH BUGS
|
||||||
|
Unicode characters with a width larger than 1 column may cause
|
||||||
|
strange behaviour. Expect more bugs and bad
|
||||||
|
behaviour: this software is in a pre\-alpha stage.
|
||||||
|
.SH AUTHORS
|
||||||
|
JFreegman <JFreegman@gmail.com>
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR toxic.conf (5)
|
||||||
|
.SH LINKS
|
||||||
|
Project page on github: https://github.com/Tox/toxic
|
||||||
|
.br
|
||||||
|
IRC channel on Freenode: chat.freenode.net#tox
|
111
doc/toxic.conf.5
Normal file
111
doc/toxic.conf.5
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
.TH TOXIC.CONF 5 "June 2014" "Toxic v__VERSION__" "User Manual"
|
||||||
|
.SH NAME
|
||||||
|
toxic.conf \- Configuration file for toxic(1)
|
||||||
|
.SH DESCRIPTION
|
||||||
|
The
|
||||||
|
.I toxic.conf
|
||||||
|
file is the main configuration file for
|
||||||
|
.BR toxic (1)
|
||||||
|
client.
|
||||||
|
.SH SYNTAX
|
||||||
|
.IB <KEY> : <VALUE> ;
|
||||||
|
.PP
|
||||||
|
Lines starting with "#" are comments and will be ignored.
|
||||||
|
.PP
|
||||||
|
Keys:
|
||||||
|
.PP
|
||||||
|
.B time
|
||||||
|
.RS
|
||||||
|
Select between 24 and 12 hour time.
|
||||||
|
.br
|
||||||
|
Values: 24, 12
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B timestamps
|
||||||
|
.RS
|
||||||
|
Enable or disable timestamps.
|
||||||
|
.br
|
||||||
|
Values: 1 to enable, 0 to disable
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B autolog
|
||||||
|
.RS
|
||||||
|
Enable or disable autologging.
|
||||||
|
.br
|
||||||
|
Values: 1 to enable, 0 to disable
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B alerts
|
||||||
|
.RS
|
||||||
|
Enable or disable terminal alerts on events.
|
||||||
|
.br
|
||||||
|
Values: 1 to enable, 0 to disable
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B history_size
|
||||||
|
.RS
|
||||||
|
Maximum lines for chat window history.
|
||||||
|
.br
|
||||||
|
Values: <INTEGER> (for example: 700)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B colour_theme
|
||||||
|
.RS
|
||||||
|
Select between toxic colour theme and native terminal colours.
|
||||||
|
.br
|
||||||
|
Values: 0 for toxic colours, 1 for terminal colours
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B audio_in_dev
|
||||||
|
.RS
|
||||||
|
Audio input device.
|
||||||
|
.br
|
||||||
|
Values: <INTEGER> (number correspond to "/lsdev in")
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B audio_out_dev
|
||||||
|
.RS
|
||||||
|
Audio output device.
|
||||||
|
.br
|
||||||
|
Values: <INTEGER> (number correspond to "/lsdev out")
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
|
.B download_path
|
||||||
|
.RS
|
||||||
|
Default path for downloads.
|
||||||
|
.br
|
||||||
|
Values: <STRING> (absolute path where to store downloaded files)
|
||||||
|
.RE
|
||||||
|
.SH EXAMPLES
|
||||||
|
Default settings from __DATADIR__/toxic.conf.exmaple:
|
||||||
|
.PP
|
||||||
|
time:24;
|
||||||
|
.br
|
||||||
|
timestamps:1;
|
||||||
|
.br
|
||||||
|
autolog:0;
|
||||||
|
.br
|
||||||
|
alerts:1;
|
||||||
|
.br
|
||||||
|
history_size:700;
|
||||||
|
.br
|
||||||
|
colour_theme:0;
|
||||||
|
.br
|
||||||
|
audio_in_dev:0;
|
||||||
|
.br
|
||||||
|
audio_out_dev:0;
|
||||||
|
.br
|
||||||
|
download_path:/home/USERNAME/Downloads/;
|
||||||
|
.SH FILES
|
||||||
|
.IP ~/.config/tox/toxic.conf
|
||||||
|
Main configuration file.
|
||||||
|
.IP __DATADIR__/toxic.conf.example
|
||||||
|
Configuration example.
|
||||||
|
.SH AUTHORS
|
||||||
|
JFreegman <JFreegman@gmail.com>
|
||||||
|
.SH SEE ALSO
|
||||||
|
.BR toxic (1)
|
||||||
|
.SH LINKS
|
||||||
|
Project page on github: https://github.com/Tox/toxic
|
||||||
|
.br
|
||||||
|
IRC channel on Freenode: chat.freenode.net#tox
|
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
|
|
8
misc/DHTnodes
Normal file
8
misc/DHTnodes
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
192.254.75.98 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
||||||
|
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
|
||||||
|
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||||
|
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||||
|
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
|
||||||
|
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||||
|
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
|
||||||
|
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
@ -1,2 +0,0 @@
|
|||||||
192.254.75.98 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
|
||||||
2607:5600:284::2 33445 FE3914F4616E227F29B2103450D6B55A836AD4BD23F97144E2C4ABE8D504FE1B
|
|
@ -1 +0,0 @@
|
|||||||
dist_pkgdata_DATA = DHTservers
|
|
26
misc/toxic.conf.example
Normal file
26
misc/toxic.conf.example
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# 24 or 12 hour time
|
||||||
|
time:24;
|
||||||
|
|
||||||
|
# 1 to enable timestamps, 0 to disable
|
||||||
|
timestamps:1;
|
||||||
|
|
||||||
|
# 1 to enable autologging, 0 to disable
|
||||||
|
autolog:0;
|
||||||
|
|
||||||
|
# 1 to disbale terminal alerts on messages, 0 to enable
|
||||||
|
alerts:1;
|
||||||
|
|
||||||
|
# maximum lines for chat window history
|
||||||
|
history_size:700;
|
||||||
|
|
||||||
|
# 1 to use native terminal colours, 0 to use toxic default colour theme
|
||||||
|
colour_theme:0;
|
||||||
|
|
||||||
|
# preferred audio input device; numbers correspond to /lsdev in
|
||||||
|
audio_in_dev:0;
|
||||||
|
|
||||||
|
# preferred audio output device; numbers correspond to /lsdev out
|
||||||
|
audio_out_dev:0;
|
||||||
|
|
||||||
|
# preferred path for downloads
|
||||||
|
download_path:/home/USERNAME/Downloads/;
|
737
src/audio_call.c
Normal file
737
src/audio_call.c
Normal file
@ -0,0 +1,737 @@
|
|||||||
|
/* audio_call.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "audio_call.h"
|
||||||
|
#include "device.h"
|
||||||
|
#include "chat_commands.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
|
#include <curses.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
#include <OpenAL/alc.h>
|
||||||
|
#else
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define _cbend pthread_exit(NULL)
|
||||||
|
|
||||||
|
#define MAX_CALLS 10
|
||||||
|
|
||||||
|
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
||||||
|
|
||||||
|
typedef struct _Call {
|
||||||
|
pthread_t ttid; /* Transmission thread id */
|
||||||
|
_Bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||||
|
uint32_t in_idx, out_idx;
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
} Call;
|
||||||
|
|
||||||
|
|
||||||
|
void set_call(Call* call, _Bool start)
|
||||||
|
{
|
||||||
|
call->in_idx = -1;
|
||||||
|
call->out_idx = -1;
|
||||||
|
|
||||||
|
if ( start ) {
|
||||||
|
call->ttas = _True;
|
||||||
|
pthread_mutex_init(&call->mutex, NULL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
call->ttid = 0;
|
||||||
|
pthread_mutex_destroy(&call->mutex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct _ASettings {
|
||||||
|
AudioError errors;
|
||||||
|
|
||||||
|
ToxAv *av;
|
||||||
|
|
||||||
|
ToxAvCodecSettings cs;
|
||||||
|
|
||||||
|
Call calls[MAX_CALLS];
|
||||||
|
} ASettins;
|
||||||
|
|
||||||
|
void callback_recv_invite ( int32_t call_index, void *arg );
|
||||||
|
void callback_recv_ringing ( int32_t call_index, void *arg );
|
||||||
|
void callback_recv_starting ( int32_t call_index, void *arg );
|
||||||
|
void callback_recv_ending ( int32_t call_index, void *arg );
|
||||||
|
void callback_recv_error ( int32_t call_index, void *arg );
|
||||||
|
void callback_call_started ( int32_t call_index, void *arg );
|
||||||
|
void callback_call_canceled ( int32_t call_index, void *arg );
|
||||||
|
void callback_call_rejected ( int32_t call_index, void *arg );
|
||||||
|
void callback_call_ended ( int32_t call_index, void *arg );
|
||||||
|
void callback_requ_timeout ( int32_t call_index, void *arg );
|
||||||
|
void callback_peer_timeout ( int32_t call_index, void *arg );
|
||||||
|
|
||||||
|
int stop_transmission(int call_index);
|
||||||
|
void write_device_callback(ToxAv* av, int32_t call_index, int16_t* data, int size);
|
||||||
|
|
||||||
|
static void print_err (ToxWindow *self, char *error_str)
|
||||||
|
{
|
||||||
|
line_info_add(self, NULL, NULL, NULL, error_str, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAv *init_audio(ToxWindow *self, Tox *tox)
|
||||||
|
{
|
||||||
|
ASettins.cs = av_DefaultSettings;
|
||||||
|
ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0;
|
||||||
|
|
||||||
|
ASettins.errors = ae_None;
|
||||||
|
|
||||||
|
memset(ASettins.calls, 0, sizeof(Call) * 10);
|
||||||
|
|
||||||
|
|
||||||
|
/* Streaming stuff from core */
|
||||||
|
|
||||||
|
ASettins.av = toxav_new(tox, MAX_CALLS);
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
ASettins.errors |= ae_StartingCoreAudio;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( init_devices(ASettins.av) == de_InternalError ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "Failed to init devices", SYS_MSG, 0, 0);
|
||||||
|
toxav_kill(ASettins.av);
|
||||||
|
return ASettins.av = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_call_started, av_OnStart, self);
|
||||||
|
toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, self);
|
||||||
|
toxav_register_callstate_callback(callback_call_rejected, av_OnReject, self);
|
||||||
|
toxav_register_callstate_callback(callback_call_ended, av_OnEnd, self);
|
||||||
|
toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, self);
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, self);
|
||||||
|
toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, self);
|
||||||
|
toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, self);
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_recv_error, av_OnError, self);
|
||||||
|
toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, self);
|
||||||
|
toxav_register_callstate_callback(callback_peer_timeout, av_OnPeerTimeout, self);
|
||||||
|
|
||||||
|
toxav_register_audio_recv_callback(ASettins.av, write_device_callback);
|
||||||
|
|
||||||
|
return ASettins.av;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate_audio()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_CALLS; i ++)
|
||||||
|
stop_transmission(i);
|
||||||
|
|
||||||
|
if ( ASettins.av )
|
||||||
|
toxav_kill(ASettins.av);
|
||||||
|
|
||||||
|
terminate_devices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_device_callback (const int16_t* captured, uint32_t size, void* data)
|
||||||
|
{
|
||||||
|
int32_t call_index = *((int32_t*)data); /* TODO: Or pass an array of call_idx's */
|
||||||
|
|
||||||
|
uint8_t encoded_payload[RTP_PAYLOAD_SIZE];
|
||||||
|
int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size);
|
||||||
|
if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
|
||||||
|
/*fprintf(stderr, "Could not encode audio packet\n");*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void write_device_callback(ToxAv* av, int32_t call_index, int16_t* data, int size)
|
||||||
|
{
|
||||||
|
if (ASettins.calls[call_index].ttas)
|
||||||
|
write_out(ASettins.calls[call_index].out_idx, data, size, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_transmission(ToxWindow *self)
|
||||||
|
{
|
||||||
|
if ( !ASettins.av || self->call_idx == -1 ) return -1;
|
||||||
|
|
||||||
|
/* Don't provide support for video */
|
||||||
|
if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, &ASettins.cs, 0) ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "Could not prepare transmission", SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !toxav_capability_supported(ASettins.av, self->call_idx, AudioDecoding) ||
|
||||||
|
!toxav_capability_supported(ASettins.av, self->call_idx, AudioEncoding) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
set_call(&ASettins.calls[self->call_idx], _True);
|
||||||
|
|
||||||
|
if ( open_primary_device(input, &ASettins.calls[self->call_idx].in_idx) != de_None )
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "Failed to open input device!", SYS_MSG, 0, 0);
|
||||||
|
|
||||||
|
if ( register_device_callback(self->call_idx, ASettins.calls[self->call_idx].in_idx,
|
||||||
|
read_device_callback, &self->call_idx, _True) != de_None)
|
||||||
|
/* Set VAD as true for all; TODO: Make it more dynamic */
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "Failed to register input handler!", SYS_MSG, 0, 0);
|
||||||
|
|
||||||
|
if ( open_primary_device(output, &ASettins.calls[self->call_idx].out_idx) != de_None ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "Failed to open output device!", SYS_MSG, 0, 0);
|
||||||
|
ASettins.calls[self->call_idx].has_output = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stop_transmission(int call_index)
|
||||||
|
{
|
||||||
|
if ( ASettins.calls[call_index].ttas ) {
|
||||||
|
toxav_kill_transmission(ASettins.av, call_index);
|
||||||
|
ASettins.calls[call_index].ttas = _False;
|
||||||
|
|
||||||
|
if ( ASettins.calls[call_index].in_idx != -1 )
|
||||||
|
close_device(input, ASettins.calls[call_index].in_idx);
|
||||||
|
|
||||||
|
if ( ASettins.calls[call_index].out_idx != -1 )
|
||||||
|
close_device(output, ASettins.calls[call_index].out_idx);
|
||||||
|
|
||||||
|
set_call(&ASettins.calls[call_index], _False);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of transmission
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0)
|
||||||
|
|
||||||
|
void callback_recv_invite ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onInvite);
|
||||||
|
}
|
||||||
|
void callback_recv_ringing ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onRinging);
|
||||||
|
}
|
||||||
|
void callback_recv_starting ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
ToxWindow* windows = arg;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
||||||
|
if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
|
||||||
|
windows[i].onStarting(&windows[i], ASettins.av, call_index);
|
||||||
|
if ( 0 != start_transmission(&windows[i]) ) {/* YEAH! */
|
||||||
|
line_info_add(&windows[i], NULL, NULL, NULL, "Error starting transmission!", SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_recv_ending ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onEnding);
|
||||||
|
stop_transmission(call_index);
|
||||||
|
}
|
||||||
|
void callback_recv_error ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onError);
|
||||||
|
stop_transmission(call_index);
|
||||||
|
}
|
||||||
|
void callback_call_started ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
ToxWindow* windows = arg;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
||||||
|
if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
|
||||||
|
windows[i].onStart(&windows[i], ASettins.av, call_index);
|
||||||
|
if ( 0 != start_transmission(&windows[i]) ) {/* YEAH! */
|
||||||
|
line_info_add(&windows[i], NULL, NULL, NULL, "Error starting transmission!", SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_call_canceled ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onCancel);
|
||||||
|
|
||||||
|
/* In case call is active */
|
||||||
|
stop_transmission(call_index);
|
||||||
|
}
|
||||||
|
void callback_call_rejected ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onReject);
|
||||||
|
}
|
||||||
|
void callback_call_ended ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onEnd);
|
||||||
|
stop_transmission(call_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback_requ_timeout ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onRequestTimeout);
|
||||||
|
}
|
||||||
|
void callback_peer_timeout ( int32_t call_index, void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(call_index, arg, onPeerTimeout);
|
||||||
|
stop_transmission(call_index);
|
||||||
|
/* Call is stopped manually since there might be some other
|
||||||
|
* actions that one can possibly take on timeout
|
||||||
|
*/
|
||||||
|
toxav_stop_call(ASettins.av, call_index);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commands from chat_commands.h
|
||||||
|
*/
|
||||||
|
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
error_str = "Invalid syntax!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
error_str = "Audio not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->stb->is_online) {
|
||||||
|
error_str = "Friend is offline.";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, TypeAudio, 30);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "Calling... idx: %d", self->call_idx);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
snprintf(msg, sizeof(msg), "%s", error_str);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
error_str = "Invalid syntax!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
error_str = "Audio not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAvError error = toxav_answer(ASettins.av, self->call_idx, TypeAudio);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
|
||||||
|
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback will print status... */
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
error_str = "Invalid syntax!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
error_str = "Audio not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
|
||||||
|
else if ( error == ErrorNoCall ) error_str = "No incoming call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback will print status... */
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
error_str = "Invalid syntax!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
error_str = "Audio not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAvError error = toxav_hangup(ASettins.av, self->call_idx);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
|
||||||
|
else if ( error == ErrorNoCall ) error_str = "No call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if (argc != 0) {
|
||||||
|
error_str = "Invalid syntax!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
error_str = "Audio not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAvError error = toxav_cancel(ASettins.av, self->call_idx, self->num,
|
||||||
|
"Only those who appreciate small things know the beauty that is life");
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorNoCall ) error_str = "No call!";
|
||||||
|
else if ( error == ErrorInvalidState ) error_str = "Cannot cancel in invalid state!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback will print status... */
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else error_str = "Only one argument allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_devices(self, type);
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This changes primary device only */
|
||||||
|
void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else if ( argc < 2 ) error_str = "Must have id!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
long int selection = strtol(argv[2], &end, 10);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( set_primary_device(type, selection) == de_InvalidSelection ) {
|
||||||
|
error_str="Invalid selection!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_ccur_device(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else if ( argc < 2 ) error_str = "Must have id!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
long int selection = strtol(argv[2], &end, 10);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( selection_valid(type, selection) == de_InvalidSelection ) {
|
||||||
|
error_str="Invalid selection!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If call is active, change device */
|
||||||
|
if ( self->call_idx > -1) {
|
||||||
|
Call* this_call = &ASettins.calls[self->call_idx];
|
||||||
|
if (this_call->ttas) {
|
||||||
|
if (type == output) {
|
||||||
|
pthread_mutex_lock(&this_call->mutex);
|
||||||
|
close_device(output, this_call->out_idx);
|
||||||
|
this_call->has_output = open_device(output, selection, &this_call->out_idx)
|
||||||
|
== de_None ? 1 : 0;
|
||||||
|
pthread_mutex_unlock(&this_call->mutex);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* TODO: check for failure */
|
||||||
|
close_device(input, this_call->in_idx);
|
||||||
|
open_device(input, selection, &this_call->in_idx);
|
||||||
|
/* Set VAD as true for all; TODO: Make it more dynamic */
|
||||||
|
register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, _True);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->device_selection[type] = selection;
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_mute(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
snprintf(msg, sizeof(msg), "Invalid type: %s", argv[1]);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* If call is active, use this_call values */
|
||||||
|
if ( self->call_idx > -1) {
|
||||||
|
Call* this_call = &ASettins.calls[self->call_idx];
|
||||||
|
|
||||||
|
pthread_mutex_lock(&this_call->mutex);
|
||||||
|
if (type == input) {
|
||||||
|
device_mute(type, this_call->in_idx);
|
||||||
|
self->chatwin->infobox.in_is_muted ^= 1;
|
||||||
|
} else {
|
||||||
|
device_mute(type, this_call->out_idx);
|
||||||
|
self->chatwin->infobox.out_is_muted ^= 1;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&this_call->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_sense(WINDOW * window, ToxWindow * self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Must have value!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
float value = strtof(argv[1], &end);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call must be active */
|
||||||
|
if ( self->call_idx > -1) {
|
||||||
|
device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value);
|
||||||
|
self->chatwin->infobox.vad_lvl = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void stop_current_call(ToxWindow* self)
|
||||||
|
{
|
||||||
|
ToxAvCallState callstate;
|
||||||
|
if ( ASettins.av != NULL && self->call_idx != -1 &&
|
||||||
|
( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistant) {
|
||||||
|
switch (callstate)
|
||||||
|
{
|
||||||
|
case av_CallActive:
|
||||||
|
case av_CallHold:
|
||||||
|
toxav_hangup(ASettins.av, self->call_idx);
|
||||||
|
break;
|
||||||
|
case av_CallInviting:
|
||||||
|
toxav_cancel(ASettins.av, self->call_idx, 0, "Not interested anymore");
|
||||||
|
break;
|
||||||
|
case av_CallStarting:
|
||||||
|
toxav_reject(ASettins.av, self->call_idx, "Not interested");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/audio_call.h
Normal file
47
src/audio_call.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/* audio_call.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _audio_h
|
||||||
|
#define _audio_h
|
||||||
|
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
|
#include "device.h"
|
||||||
|
|
||||||
|
#define VAD_THRESHOLD_DEFAULT 40.0
|
||||||
|
|
||||||
|
typedef enum _AudioError {
|
||||||
|
ae_None = 0,
|
||||||
|
ae_StartingCaptureDevice = 1 << 0,
|
||||||
|
ae_StartingOutputDevice = 1 << 1,
|
||||||
|
ae_StartingCoreAudio = 1 << 2
|
||||||
|
} AudioError;
|
||||||
|
|
||||||
|
/* You will have to pass pointer to first member of 'windows'
|
||||||
|
* declared in windows.c otherwise undefined behaviour will
|
||||||
|
*/
|
||||||
|
ToxAv *init_audio(ToxWindow *self, Tox *tox);
|
||||||
|
void terminate_audio();
|
||||||
|
|
||||||
|
void stop_current_call(ToxWindow *self);
|
||||||
|
|
||||||
|
#endif /* _audio_h */
|
900
src/chat.c
900
src/chat.c
File diff suppressed because it is too large
Load Diff
@ -23,9 +23,10 @@
|
|||||||
#ifndef CHAT_H_6489PZ13
|
#ifndef CHAT_H_6489PZ13
|
||||||
#define CHAT_H_6489PZ13
|
#define CHAT_H_6489PZ13
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
void kill_chat_window(ToxWindow *self);
|
void kill_chat_window(ToxWindow *self);
|
||||||
ToxWindow new_chat(Tox *m, int friendnum);
|
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||||
|
|
||||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
||||||
|
@ -20,17 +20,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "groupchat.h"
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
@ -39,80 +38,64 @@ extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
|||||||
extern FileSender file_senders[MAX_FILES];
|
extern FileSender file_senders[MAX_FILES];
|
||||||
extern uint8_t max_file_senders_index;
|
extern uint8_t max_file_senders_index;
|
||||||
|
|
||||||
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
|
||||||
{
|
|
||||||
if (argc == 1) {
|
|
||||||
if (!strcmp(argv[1], "global")) {
|
|
||||||
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
|
||||||
wprintw(window, "Chat commands:\n");
|
|
||||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
|
||||||
|
|
||||||
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
|
|
||||||
wprintw(window, " /join : Join a pending group chat\n");
|
|
||||||
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
|
|
||||||
wprintw(window, " /sendfile <filepath> : Send a file\n");
|
|
||||||
wprintw(window, " /savefile <n> : Receive a file\n");
|
|
||||||
wprintw(window, " /close : Close the current chat window\n");
|
|
||||||
wprintw(window, " /help : Print this message again\n");
|
|
||||||
wprintw(window, " /help global : Show a list of global commands\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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = atoi(argv[1]);
|
int groupnum = atoi(argv[1]);
|
||||||
|
|
||||||
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
||||||
wprintw(window, "Failed to invite friend.\n");
|
errmsg = "Failed to invite contact to group.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(window, "Invited friend to group chat %d.\n", groupnum);
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "Invited contact to Group %d.", groupnum);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
char *errmsg;
|
||||||
wattron(window, COLOR_PAIR(RED));
|
|
||||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wattron(window, COLOR_PAIR(RED));
|
errmsg = " * Warning: Too many windows are open.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *groupkey = friends[self->num].pending_groupchat;
|
char *groupkey = friends[self->num].groupchat_key;
|
||||||
|
|
||||||
if (groupkey[0] == '\0') {
|
if (!friends[self->num].groupchat_pending) {
|
||||||
wprintw(window, "No pending group chat invite.\n");
|
errmsg = "No pending group chat invite.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = tox_join_groupchat(m, self->num, groupkey);
|
int groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey);
|
||||||
|
|
||||||
if (groupnum == -1) {
|
if (groupnum == -1) {
|
||||||
wprintw(window, "Group chat instance failed to initialize.\n");
|
errmsg = "Group chat instance failed to initialize.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||||
wprintw(window, "Group chat window failed to initialize.\n");
|
errmsg = "Group chat window failed to initialize.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
tox_del_groupchat(m, groupnum);
|
tox_del_groupchat(m, groupnum);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -120,64 +103,88 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
|||||||
|
|
||||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t filenum = atoi(argv[1]);
|
uint8_t filenum = atoi(argv[1]);
|
||||||
|
|
||||||
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
if ((filenum == 0 && strcmp(argv[1], "0")) || filenum >= MAX_FILES) {
|
||||||
wprintw(window, "No pending file transfers with that number.\n");
|
errmsg = "No pending file transfers with that number.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!friends[self->num].file_receiver.pending[filenum]) {
|
if (!friends[self->num].file_receiver.pending[filenum]) {
|
||||||
wprintw(window, "No pending file transfers with that number.\n");
|
errmsg = "No pending file transfers with that number.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
char *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||||
|
|
||||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0)
|
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);
|
char msg[MAX_STR_SIZE];
|
||||||
else
|
snprintf(msg, sizeof(msg), "Saving file as: '%s' (%.1f%%)", filename, 0.0);
|
||||||
wprintw(window, "File transfer failed.\n");
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 1;
|
||||||
|
|
||||||
|
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
|
||||||
|
errmsg = "* Error writing to file.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
|
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errmsg = "File transfer failed.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
friends[self->num].file_receiver.pending[filenum] = false;
|
friends[self->num].file_receiver.pending[filenum] = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (max_file_senders_index >= (MAX_FILES-1)) {
|
char *errmsg;
|
||||||
wprintw(window,"Please wait for some of your outgoing file transfers to complete.\n");
|
|
||||||
|
if (max_file_senders_index >= (MAX_FILES - 1)) {
|
||||||
|
errmsg = "Please wait for some of your outgoing file transfers to complete.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *path = argv[1];
|
char *path = argv[1];
|
||||||
|
|
||||||
if (path[0] != '\"') {
|
if (path[0] != '\"') {
|
||||||
wprintw(window, "File path must be enclosed in quotes.\n");
|
errmsg = "File path must be enclosed in quotes.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path[strlen(++path)-1] = L'\0';
|
++path;
|
||||||
int path_len = strlen(path);
|
int path_len = strlen(path) - 1;
|
||||||
|
path[path_len] = '\0';
|
||||||
|
|
||||||
if (path_len > MAX_STR_SIZE) {
|
if (path_len > MAX_STR_SIZE) {
|
||||||
wprintw(window, "File path exceeds character limit.\n");
|
errmsg = "File path exceeds character limit.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *file_to_send = fopen(path, "r");
|
FILE *file_to_send = fopen(path, "r");
|
||||||
|
|
||||||
if (file_to_send == NULL) {
|
if (file_to_send == NULL) {
|
||||||
wprintw(window, "File '%s' not found.\n", path);
|
errmsg = "File not found.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,12 +192,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
uint64_t filesize = ftell(file_to_send);
|
uint64_t filesize = ftell(file_to_send);
|
||||||
fseek(file_to_send, 0, SEEK_SET);
|
fseek(file_to_send, 0, SEEK_SET);
|
||||||
|
|
||||||
uint8_t filename[MAX_STR_SIZE];
|
char filename[MAX_STR_SIZE];
|
||||||
get_file_name(path, filename);
|
get_file_name(filename, sizeof(filename), path);
|
||||||
int filenum = tox_new_file_sender(m, self->num, filesize, filename, strlen(filename) + 1);
|
int filenum = tox_new_file_sender(m, self->num, filesize, (const uint8_t *) filename, strlen(filename));
|
||||||
|
|
||||||
if (filenum == -1) {
|
if (filenum == -1) {
|
||||||
wprintw(window, "Error sending file.\n");
|
errmsg = "Error sending file.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,13 +210,16 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
file_senders[i].active = true;
|
file_senders[i].active = true;
|
||||||
file_senders[i].toxwin = self;
|
file_senders[i].toxwin = self;
|
||||||
file_senders[i].file = file_to_send;
|
file_senders[i].file = file_to_send;
|
||||||
file_senders[i].filenum = (uint8_t) filenum;
|
file_senders[i].filenum = filenum;
|
||||||
file_senders[i].friendnum = self->num;
|
file_senders[i].friendnum = self->num;
|
||||||
file_senders[i].timestamp = (uint64_t) time(NULL);
|
file_senders[i].timestamp = get_unix_time();
|
||||||
|
file_senders[i].size = filesize;
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||||
tox_file_data_size(m, self->num), file_to_send);
|
tox_file_data_size(m, self->num), file_to_send);
|
||||||
|
|
||||||
wprintw(window, "Sending file: '%s'\n", path);
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "Sending file: '%s'", path);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
|
||||||
if (i == max_file_senders_index)
|
if (i == max_file_senders_index)
|
||||||
++max_file_senders_index;
|
++max_file_senders_index;
|
||||||
|
@ -20,8 +20,26 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
#ifndef _chat_commands_h
|
||||||
|
#define _chat_commands_h
|
||||||
|
|
||||||
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
void cmd_groupinvite(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]);
|
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_reject(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#endif /* #define _chat_commands_h */
|
||||||
|
@ -20,24 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <shlobj.h>
|
|
||||||
#include <direct.h>
|
|
||||||
#else /* WIN32 */
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
#endif /* WIN32 */
|
|
||||||
|
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
|
|
||||||
@ -51,25 +41,6 @@
|
|||||||
char *get_user_config_dir(void)
|
char *get_user_config_dir(void)
|
||||||
{
|
{
|
||||||
char *user_config_dir;
|
char *user_config_dir;
|
||||||
#ifdef _WIN32
|
|
||||||
#warning Please fix configdir for Win32
|
|
||||||
return NULL;
|
|
||||||
#if 0
|
|
||||||
char appdata[MAX_PATH];
|
|
||||||
BOOL ok;
|
|
||||||
|
|
||||||
ok = SHGetSpecialFolderPathA(NULL, appdata, CSIDL_PROFILE, TRUE);
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
user_config_dir = strdup(appdata);
|
|
||||||
|
|
||||||
return user_config_dir;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else /* WIN32 */
|
|
||||||
|
|
||||||
#ifndef NSS_BUFLEN_PASSWD
|
#ifndef NSS_BUFLEN_PASSWD
|
||||||
#define NSS_BUFLEN_PASSWD 4096
|
#define NSS_BUFLEN_PASSWD 4096
|
||||||
@ -110,6 +81,7 @@ char *get_user_config_dir(void)
|
|||||||
# else /* __APPLE__ */
|
# else /* __APPLE__ */
|
||||||
|
|
||||||
const char *tmp;
|
const char *tmp;
|
||||||
|
|
||||||
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
||||||
len = strlen(home) + strlen("/.config") + 1;
|
len = strlen(home) + strlen("/.config") + 1;
|
||||||
user_config_dir = malloc(len);
|
user_config_dir = malloc(len);
|
||||||
@ -127,7 +99,6 @@ char *get_user_config_dir(void)
|
|||||||
|
|
||||||
return user_config_dir;
|
return user_config_dir;
|
||||||
#undef NSS_BUFLEN_PASSWD
|
#undef NSS_BUFLEN_PASSWD
|
||||||
#endif /* WIN32 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -135,26 +106,6 @@ char *get_user_config_dir(void)
|
|||||||
*/
|
*/
|
||||||
int create_user_config_dir(char *path)
|
int create_user_config_dir(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;
|
int mkdir_err;
|
||||||
|
|
||||||
mkdir_err = mkdir(path, 0700);
|
mkdir_err = mkdir(path, 0700);
|
||||||
@ -177,5 +128,4 @@ int create_user_config_dir(char *path)
|
|||||||
|
|
||||||
free(fullpath);
|
free(fullpath);
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef _win32
|
#ifndef _configdir_h
|
||||||
#define CONFIGDIR "\\tox\\"
|
#define _configdir_h
|
||||||
#else
|
|
||||||
#define CONFIGDIR "/tox/"
|
#define CONFIGDIR "/tox/"
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef S_ISDIR
|
#ifndef S_ISDIR
|
||||||
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
|
||||||
@ -33,3 +32,5 @@
|
|||||||
char *get_user_config_dir(void);
|
char *get_user_config_dir(void);
|
||||||
|
|
||||||
int create_user_config_dir(char *path);
|
int create_user_config_dir(char *path);
|
||||||
|
|
||||||
|
#endif /* #define _configdir_h */
|
||||||
|
432
src/device.c
Normal file
432
src/device.c
Normal file
@ -0,0 +1,432 @@
|
|||||||
|
/* 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_call.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <OpenAL/al.h>
|
||||||
|
#include <OpenAL/alc.h>
|
||||||
|
#else
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
|
#define openal_bufs 5
|
||||||
|
#define sample_rate 48000
|
||||||
|
#define inline__ inline __attribute__((always_inline))
|
||||||
|
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
||||||
|
|
||||||
|
typedef struct _Device {
|
||||||
|
ALCdevice *dhndl; /* Handle of device selected/opened */
|
||||||
|
ALCcontext *ctx; /* Device context */
|
||||||
|
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
||||||
|
void* cb_data; /* Data to be passed to callback */
|
||||||
|
int32_t call_idx; /* ToxAv call index */
|
||||||
|
|
||||||
|
uint32_t source, buffers[openal_bufs]; /* Playback source/buffers */
|
||||||
|
size_t ref_count;
|
||||||
|
int32_t selection;
|
||||||
|
_Bool enable_VAD;
|
||||||
|
_Bool muted;
|
||||||
|
float VAD_treshold; /* 40 is usually recommended value */
|
||||||
|
pthread_mutex_t mutex[1];
|
||||||
|
} Device;
|
||||||
|
|
||||||
|
const char *ddevice_names[2]; /* Default device */
|
||||||
|
const char *devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
||||||
|
static int size[2]; /* Size of above containers */
|
||||||
|
Device *running[2][MAX_DEVICES]; /* Running devices */
|
||||||
|
uint32_t primary_device[2]; /* Primary device */
|
||||||
|
|
||||||
|
static ToxAv* av = NULL;
|
||||||
|
|
||||||
|
/* q_mutex */
|
||||||
|
#define lock pthread_mutex_lock(&mutex)
|
||||||
|
#define unlock pthread_mutex_unlock(&mutex)
|
||||||
|
pthread_mutex_t mutex;
|
||||||
|
|
||||||
|
|
||||||
|
_Bool thread_running = _True,
|
||||||
|
thread_paused = _True; /* Thread control */
|
||||||
|
|
||||||
|
void* thread_poll(void*);
|
||||||
|
/* Meet devices */
|
||||||
|
DeviceError init_devices(ToxAv* av_)
|
||||||
|
{
|
||||||
|
const char *stringed_device_list;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size[input] = 0;
|
||||||
|
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
||||||
|
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
|
||||||
|
devices_names[input][size[input]] = stringed_device_list;
|
||||||
|
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size[output] = 0;
|
||||||
|
if ( (stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER)) ) {
|
||||||
|
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
||||||
|
devices_names[output][size[output]] = stringed_device_list;
|
||||||
|
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start poll thread
|
||||||
|
|
||||||
|
pthread_mutex_init(&mutex, NULL);
|
||||||
|
|
||||||
|
pthread_t thread_id;
|
||||||
|
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
|
||||||
|
return de_InternalError;
|
||||||
|
|
||||||
|
av = av_;
|
||||||
|
|
||||||
|
return (DeviceError) ae_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError terminate_devices()
|
||||||
|
{
|
||||||
|
/* Cleanup if needed */
|
||||||
|
thread_running = false;
|
||||||
|
usleep(20000);
|
||||||
|
|
||||||
|
pthread_mutex_destroy(&mutex);
|
||||||
|
|
||||||
|
return (DeviceError) ae_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
lock;
|
||||||
|
|
||||||
|
Device* device = running[type][device_idx];
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
unlock;
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->muted = !device->muted;
|
||||||
|
|
||||||
|
unlock;
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
lock;
|
||||||
|
|
||||||
|
Device* device = running[input][device_idx];
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
unlock;
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->VAD_treshold = value;
|
||||||
|
|
||||||
|
unlock;
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError set_primary_device(DeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||||
|
primary_device[type] = selection;
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx)
|
||||||
|
{
|
||||||
|
return open_device(type, primary_device[type], device_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: generate buffers separately
|
||||||
|
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx)
|
||||||
|
{
|
||||||
|
if (size[type] <= selection || selection < 0) return de_InvalidSelection;
|
||||||
|
|
||||||
|
lock;
|
||||||
|
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; i ++);
|
||||||
|
|
||||||
|
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
|
||||||
|
else *device_idx = i;
|
||||||
|
|
||||||
|
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));;
|
||||||
|
device->selection = selection;
|
||||||
|
|
||||||
|
for (i = 0; i < *device_idx; i ++) { /* Check if any previous has the same selection */
|
||||||
|
if ( running[type][i]->selection == selection ) {
|
||||||
|
device->dhndl = running[type][i]->dhndl;
|
||||||
|
if (type == output) {
|
||||||
|
device->ctx = running[type][i]->ctx;
|
||||||
|
memcpy(device->buffers, running[type][i]->buffers, sizeof(running[type][i]->buffers));
|
||||||
|
device->source = running[type][i]->source;
|
||||||
|
}
|
||||||
|
device->ref_count++;
|
||||||
|
pthread_mutex_init(device->mutex, NULL);
|
||||||
|
unlock;
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
|
||||||
|
av_DefaultSettings.audio_sample_rate, AL_FORMAT_MONO16, frame_size * 2);
|
||||||
|
device->VAD_treshold = VAD_THRESHOLD_DEFAULT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
device->dhndl = alcOpenDevice(devices_names[type][selection]);
|
||||||
|
if ( !device->dhndl ) {
|
||||||
|
free(device);
|
||||||
|
running[type][*device_idx] = NULL;
|
||||||
|
unlock;
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->ctx = alcCreateContext(device->dhndl, NULL);
|
||||||
|
alcMakeContextCurrent(device->ctx);
|
||||||
|
|
||||||
|
alGenBuffers(openal_bufs, device->buffers);
|
||||||
|
alGenSources((uint32_t)1, &device->source);
|
||||||
|
alSourcei(device->source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
|
uint16_t zeros[frame_size];
|
||||||
|
memset(zeros, 0, frame_size*2);
|
||||||
|
|
||||||
|
for ( i =0; i < openal_bufs; ++i) {
|
||||||
|
alBufferData(device->buffers[i], AL_FORMAT_MONO16, zeros, frame_size*2, sample_rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceQueueBuffers(device->source, openal_bufs, device->buffers);
|
||||||
|
alSourcePlay(device->source);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alcGetError(device->dhndl) != AL_NO_ERROR) {
|
||||||
|
free(device);
|
||||||
|
running[type][*device_idx] = NULL;
|
||||||
|
unlock;
|
||||||
|
return de_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
alcCaptureStart(device->dhndl);
|
||||||
|
thread_paused = _False;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_init(device->mutex, NULL);
|
||||||
|
unlock;
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError close_device(DeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
|
lock;
|
||||||
|
Device* device = running[type][device_idx];
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
unlock;
|
||||||
|
return de_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !(device->ref_count--) ) {
|
||||||
|
running[type][device_idx] = NULL;
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
DeviceError rc = de_None;
|
||||||
|
|
||||||
|
if (type == input) {
|
||||||
|
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
|
||||||
|
|
||||||
|
alDeleteSources(1, &device->source);
|
||||||
|
alDeleteBuffers(openal_bufs, device->buffers);
|
||||||
|
|
||||||
|
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
if ( device->ctx ) alcDestroyContext(device->ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD)
|
||||||
|
{
|
||||||
|
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||||
|
return de_InvalidSelection;
|
||||||
|
|
||||||
|
lock;
|
||||||
|
running[input][device_idx]->cb = callback;
|
||||||
|
running[input][device_idx]->cb_data = data;
|
||||||
|
running[input][device_idx]->enable_VAD = enable_VAD;
|
||||||
|
running[input][device_idx]->call_idx = call_idx;
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline__ DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels)
|
||||||
|
{
|
||||||
|
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||||
|
|
||||||
|
Device* device = running[output][device_idx];
|
||||||
|
|
||||||
|
if (!device || device->muted) return de_DeviceNotActive;
|
||||||
|
|
||||||
|
pthread_mutex_lock(device->mutex);
|
||||||
|
|
||||||
|
|
||||||
|
ALuint bufid;
|
||||||
|
ALint processed, queued;
|
||||||
|
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
|
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
|
||||||
|
|
||||||
|
if(processed) {
|
||||||
|
ALuint bufids[processed];
|
||||||
|
alSourceUnqueueBuffers(device->source, processed, bufids);
|
||||||
|
alDeleteBuffers(processed - 1, bufids + 1);
|
||||||
|
bufid = bufids[0];
|
||||||
|
}
|
||||||
|
else if(queued < 16) alGenBuffers(1, &bufid);
|
||||||
|
else {
|
||||||
|
pthread_mutex_unlock(device->mutex);
|
||||||
|
return de_Busy;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
alBufferData(bufid, AL_FORMAT_MONO16, data, lenght * 2 * channels, av_DefaultSettings.audio_sample_rate);
|
||||||
|
alSourceQueueBuffers(device->source, 1, &bufid);
|
||||||
|
|
||||||
|
ALint state;
|
||||||
|
alGetSourcei(device->source, AL_SOURCE_STATE, &state);
|
||||||
|
|
||||||
|
if(state != AL_PLAYING) alSourcePlay(device->source);
|
||||||
|
|
||||||
|
|
||||||
|
pthread_mutex_unlock(device->mutex);
|
||||||
|
return de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* NOTE: We only need to poll input devices for data.
|
||||||
|
*/
|
||||||
|
(void)arg;
|
||||||
|
uint32_t i;
|
||||||
|
int32_t sample = 0;
|
||||||
|
|
||||||
|
int f_size = frame_size;
|
||||||
|
|
||||||
|
while (thread_running)
|
||||||
|
{
|
||||||
|
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < size[input]; i ++)
|
||||||
|
{
|
||||||
|
lock;
|
||||||
|
if (running[input][i] != NULL)
|
||||||
|
{
|
||||||
|
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
|
||||||
|
|
||||||
|
if (sample < f_size) {
|
||||||
|
unlock;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Device* device = running[input][i];
|
||||||
|
|
||||||
|
int16_t frame[4096];
|
||||||
|
alcCaptureSamples(device->dhndl, frame, f_size);
|
||||||
|
|
||||||
|
if ( device->muted ||
|
||||||
|
(device->enable_VAD && !toxav_has_activity(av, device->call_idx, frame, f_size, device->VAD_treshold)))
|
||||||
|
{ unlock; continue; } /* Skip if no voice activity */
|
||||||
|
|
||||||
|
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
|
||||||
|
}
|
||||||
|
unlock;
|
||||||
|
}
|
||||||
|
usleep(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_devices(ToxWindow* self, DeviceType type)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for ( ; i < size[type]; i ++) {
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "%d: %s", i, devices_names[type][i]);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceError selection_valid(DeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* get_device_callback_data(uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return running[input][device_idx]->cb_data;
|
||||||
|
}
|
84
src/device.h
Normal file
84
src/device.h
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* device.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* You can have multiple sources (Input devices) but only one output device.
|
||||||
|
* Pass buffers to output device via write();
|
||||||
|
* Read from running input device(s) via select()/callback combo.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _device_h
|
||||||
|
#define _device_h
|
||||||
|
|
||||||
|
#define MAX_DEVICES 32
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#define _True 1
|
||||||
|
#define _False 0
|
||||||
|
|
||||||
|
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_AlError = -8,
|
||||||
|
} DeviceError;
|
||||||
|
|
||||||
|
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
|
||||||
|
|
||||||
|
|
||||||
|
DeviceError init_devices(ToxAv* av);
|
||||||
|
DeviceError terminate_devices();
|
||||||
|
|
||||||
|
/* Callback handles ready data from INPUT device */
|
||||||
|
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, _Bool enable_VAD);
|
||||||
|
void* get_device_callback_data(uint32_t device_idx);
|
||||||
|
|
||||||
|
/* toggle device mute */
|
||||||
|
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
|
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||||
|
|
||||||
|
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
||||||
|
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx);
|
||||||
|
/* Start device */
|
||||||
|
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx);
|
||||||
|
/* Stop device */
|
||||||
|
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
|
/* Write data to device */
|
||||||
|
DeviceError write_out(uint32_t device_idx, int16_t* data, uint32_t lenght, uint8_t channels);
|
||||||
|
|
||||||
|
void print_devices(ToxWindow* self, DeviceType type);
|
||||||
|
|
||||||
|
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||||
|
#endif /* _device_h */
|
316
src/dns.c
Normal file
316
src/dns.c
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
/* dns.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <arpa/nameser_compat.h>
|
||||||
|
#else
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
#endif /* ifdef __APPLE__ */
|
||||||
|
|
||||||
|
#include <tox/toxdns.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
|
#define MAX_DNS_REQST_SIZE 256
|
||||||
|
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
|
||||||
|
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
||||||
|
#define DNS3_KEY_SZ 32
|
||||||
|
|
||||||
|
extern struct _Winthread Winthread;
|
||||||
|
|
||||||
|
/* TODO: process keys from key file instead of hard-coding like a noob */
|
||||||
|
static struct dns3_server {
|
||||||
|
char *name;
|
||||||
|
char key[DNS3_KEY_SZ];
|
||||||
|
} dns3_servers[] = {
|
||||||
|
{
|
||||||
|
"utox.org",
|
||||||
|
{
|
||||||
|
0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B,
|
||||||
|
0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"toxme.se",
|
||||||
|
{
|
||||||
|
0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
|
||||||
|
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct _thread_data {
|
||||||
|
ToxWindow *self;
|
||||||
|
char id_bin[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
|
char addr[MAX_STR_SIZE];
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
uint8_t busy;
|
||||||
|
Tox *m;
|
||||||
|
} t_data;
|
||||||
|
|
||||||
|
static struct _dns_thread {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
} dns_thread;
|
||||||
|
|
||||||
|
|
||||||
|
static int dns_error(ToxWindow *self, char *errmsg)
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "User lookup failed: %s", errmsg);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kill_dns_thread(void *dns_obj)
|
||||||
|
{
|
||||||
|
if (dns_obj)
|
||||||
|
tox_dns3_kill(dns_obj);
|
||||||
|
|
||||||
|
memset(&t_data, 0, sizeof(struct _thread_data));
|
||||||
|
pthread_attr_destroy(&dns_thread.attr);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/
|
||||||
|
static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf)
|
||||||
|
{
|
||||||
|
uint8_t *ans_pt = answer + sizeof(HEADER);
|
||||||
|
uint8_t *ans_end = answer + ans_len;
|
||||||
|
char exp_ans[PACKETSZ];
|
||||||
|
|
||||||
|
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||||
|
|
||||||
|
if (len == -1)
|
||||||
|
return dns_error(self, "dn_expand failed.");
|
||||||
|
|
||||||
|
ans_pt += len;
|
||||||
|
|
||||||
|
if (ans_pt > ans_end - 4)
|
||||||
|
return dns_error(self, "DNS reply was too short.");
|
||||||
|
|
||||||
|
int type;
|
||||||
|
GETSHORT(type, ans_pt);
|
||||||
|
|
||||||
|
if (type != T_TXT)
|
||||||
|
return dns_error(self, "Broken DNS reply.");
|
||||||
|
|
||||||
|
|
||||||
|
ans_pt += INT16SZ; /* class */
|
||||||
|
uint32_t size = 0;
|
||||||
|
|
||||||
|
/* recurse through CNAME rr's */
|
||||||
|
do {
|
||||||
|
ans_pt += size;
|
||||||
|
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
||||||
|
|
||||||
|
if (len == -1)
|
||||||
|
return dns_error(self, "Second dn_expand failed.");
|
||||||
|
|
||||||
|
ans_pt += len;
|
||||||
|
|
||||||
|
if (ans_pt > ans_end - 10)
|
||||||
|
return dns_error(self, "DNS reply was too short.");
|
||||||
|
|
||||||
|
GETSHORT(type, ans_pt);
|
||||||
|
ans_pt += INT16SZ;
|
||||||
|
ans_pt += 4;
|
||||||
|
GETSHORT(size, ans_pt);
|
||||||
|
|
||||||
|
if (ans_pt + size < answer || ans_pt + size > ans_end)
|
||||||
|
return dns_error(self, "RR overflow.");
|
||||||
|
|
||||||
|
} while (type == T_CNAME);
|
||||||
|
|
||||||
|
if (type != T_TXT)
|
||||||
|
return dns_error(self, "DNS response failed.");
|
||||||
|
|
||||||
|
uint32_t txt_len = *ans_pt;
|
||||||
|
|
||||||
|
if (!size || txt_len >= size || !txt_len)
|
||||||
|
return dns_error(self, "No record found.");
|
||||||
|
|
||||||
|
ans_pt++;
|
||||||
|
ans_pt[txt_len] = '\0';
|
||||||
|
memcpy(buf, ans_pt, txt_len + 1);
|
||||||
|
|
||||||
|
return txt_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||||
|
and the domain in dombuf.
|
||||||
|
|
||||||
|
return length of username on success, -1 on failure */
|
||||||
|
static int parse_addr(char *addr, char *namebuf, char *dombuf)
|
||||||
|
{
|
||||||
|
char tmpaddr[MAX_STR_SIZE];
|
||||||
|
char *tmpname, *tmpdom;
|
||||||
|
|
||||||
|
strcpy(tmpaddr, addr);
|
||||||
|
tmpname = strtok(tmpaddr, "@");
|
||||||
|
tmpdom = strtok(NULL, "");
|
||||||
|
|
||||||
|
if (tmpname == NULL || tmpdom == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
str_to_lower(tmpdom);
|
||||||
|
strcpy(namebuf, tmpname);
|
||||||
|
strcpy(dombuf, tmpdom);
|
||||||
|
|
||||||
|
return strlen(namebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
|
||||||
|
void *dns3_lookup_thread(void *data)
|
||||||
|
{
|
||||||
|
ToxWindow *self = t_data.self;
|
||||||
|
|
||||||
|
char domain[MAX_STR_SIZE];
|
||||||
|
char name[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
int namelen = parse_addr(t_data.addr, name, domain);
|
||||||
|
|
||||||
|
if (namelen == -1) {
|
||||||
|
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
||||||
|
kill_dns_thread(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get domain name/pub key */
|
||||||
|
char *DNS_pubkey = NULL;
|
||||||
|
char *domname = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
|
||||||
|
if (strcmp(dns3_servers[i].name, domain) == 0) {
|
||||||
|
DNS_pubkey = dns3_servers[i].key;
|
||||||
|
domname = dns3_servers[i].name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (domname == NULL) {
|
||||||
|
dns_error(self, "Domain not found.");
|
||||||
|
kill_dns_thread(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
|
||||||
|
|
||||||
|
if (dns_obj == NULL) {
|
||||||
|
dns_error(self, "Core failed to create DNS object.");
|
||||||
|
kill_dns_thread(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
char string[MAX_DNS_REQST_SIZE];
|
||||||
|
uint32_t request_id;
|
||||||
|
|
||||||
|
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
|
||||||
|
(uint8_t *) name, namelen);
|
||||||
|
|
||||||
|
if (str_len == -1) {
|
||||||
|
dns_error(self, "Core failed to generate DNS3 string.");
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
string[str_len] = '\0';
|
||||||
|
|
||||||
|
u_char answer[PACKETSZ];
|
||||||
|
char d_string[MAX_DNS_REQST_SIZE];
|
||||||
|
|
||||||
|
/* format string and create dns query */
|
||||||
|
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
|
||||||
|
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
|
||||||
|
|
||||||
|
if (ans_len <= 0) {
|
||||||
|
dns_error(self, "DNS query failed.");
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
char ans_id[MAX_DNS_REQST_SIZE];
|
||||||
|
|
||||||
|
/* extract TXT from DNS response */
|
||||||
|
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
|
||||||
|
char encrypted_id[MAX_DNS_REQST_SIZE];
|
||||||
|
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
|
||||||
|
|
||||||
|
/* extract the encrypted ID from TXT response */
|
||||||
|
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
|
||||||
|
dns_error(self, "Bad DNS3 TXT response.");
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len);
|
||||||
|
|
||||||
|
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
|
||||||
|
strlen(encrypted_id), request_id) == -1) {
|
||||||
|
dns_error(self, "Core failed to decrypt DNS response.");
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
kill_dns_thread(dns_obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||||
|
void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg)
|
||||||
|
{
|
||||||
|
if (t_data.busy) {
|
||||||
|
char *err = "Please wait for previous user lookup to finish.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, err, SYS_MSG, 0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
|
||||||
|
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
|
||||||
|
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);
|
||||||
|
t_data.self = self;
|
||||||
|
t_data.m = m;
|
||||||
|
t_data.busy = 1;
|
||||||
|
|
||||||
|
if (pthread_attr_init(&dns_thread.attr) != 0)
|
||||||
|
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0)
|
||||||
|
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_ATTR);
|
||||||
|
|
||||||
|
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0)
|
||||||
|
exit_toxic_err("failed in dns3_lookup", FATALERR_THREAD_CREATE);
|
||||||
|
}
|
32
src/dns.h
Normal file
32
src/dns.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/* dns.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
|
||||||
|
Return 0 on success, -1 on failure. */
|
||||||
|
|
||||||
|
#ifndef _dns_h
|
||||||
|
#define _dns_h
|
||||||
|
|
||||||
|
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||||
|
void dns3_lookup(ToxWindow *self, Tox *m, char *id_bin, char *addr, char *msg);
|
||||||
|
|
||||||
|
#endif /* #define _dns_h */
|
@ -22,11 +22,15 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "chat_commands.h"
|
#include "chat_commands.h"
|
||||||
#include "global_commands.h"
|
#include "global_commands.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
struct cmd_func {
|
struct cmd_func {
|
||||||
const char *name;
|
const char *name;
|
||||||
@ -48,31 +52,47 @@ static struct cmd_func global_commands[] = {
|
|||||||
{ "/q", cmd_quit },
|
{ "/q", cmd_quit },
|
||||||
{ "/quit", cmd_quit },
|
{ "/quit", cmd_quit },
|
||||||
{ "/status", cmd_status },
|
{ "/status", cmd_status },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
{ "/lsdev", cmd_list_devices },
|
||||||
|
{ "/sdev", cmd_change_device },
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cmd_func chat_commands[] = {
|
static struct cmd_func chat_commands[] = {
|
||||||
{ "/help", cmd_chat_help },
|
|
||||||
{ "/invite", cmd_groupinvite },
|
{ "/invite", cmd_groupinvite },
|
||||||
{ "/join", cmd_join_group },
|
{ "/join", cmd_join_group },
|
||||||
{ "/savefile", cmd_savefile },
|
{ "/savefile", cmd_savefile },
|
||||||
{ "/sendfile", cmd_sendfile },
|
{ "/sendfile", cmd_sendfile },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
{ "/call", cmd_call },
|
||||||
|
{ "/cancel", cmd_cancel },
|
||||||
|
{ "/answer", cmd_answer },
|
||||||
|
{ "/reject", cmd_reject },
|
||||||
|
{ "/hangup", cmd_hangup },
|
||||||
|
{ "/sdev", cmd_ccur_device },
|
||||||
|
{ "/mute", cmd_mute },
|
||||||
|
{ "/sense", cmd_sense },
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Parses input command and puts args into arg array.
|
/* Parses input command and puts args into arg array.
|
||||||
Returns number of arguments on success, -1 on failure. */
|
Returns number of arguments on success, -1 on failure. */
|
||||||
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
|
static int parse_command(WINDOW *w, ToxWindow *self, char *cmd, char (*args)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
int num_args = 0;
|
int num_args = 0;
|
||||||
bool cmd_end = false; // flags when we get to the end of cmd
|
bool cmd_end = false; /* flags when we get to the end of cmd */
|
||||||
char *end; // points to the end of the current arg
|
char *end; /* points to the end of the current arg */
|
||||||
|
|
||||||
/* characters wrapped in double quotes count as one arg */
|
/* characters wrapped in double quotes count as one arg */
|
||||||
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
||||||
if (*cmd == '\"') {
|
if (*cmd == '\"') {
|
||||||
end = strchr(cmd+1, '\"');
|
end = strchr(cmd + 1, '\"');
|
||||||
|
|
||||||
if (end++ == NULL) { /* Increment past the end quote */
|
if (end++ == NULL) { /* Increment past the end quote */
|
||||||
wprintw(w, "Invalid argument. Did you forget a closing \"?\n");
|
char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +121,7 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
|
|||||||
|
|
||||||
for (i = 0; i < num_cmds; ++i) {
|
for (i = 0; i < num_cmds; ++i) {
|
||||||
if (strcmp(args[0], commands[i].name) == 0) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,13 +129,13 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
||||||
{
|
{
|
||||||
if (string_is_empty(cmd))
|
if (string_is_empty(cmd))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE] = {0};
|
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||||
int num_args = parse_command(w, cmd, args);
|
int num_args = parse_command(w, self, cmd, args);
|
||||||
|
|
||||||
if (num_args == -1)
|
if (num_args == -1)
|
||||||
return;
|
return;
|
||||||
@ -128,6 +148,7 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
|||||||
case CHAT_COMMAND_MODE:
|
case CHAT_COMMAND_MODE:
|
||||||
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GROUPCHAT_COMMAND_MODE:
|
case GROUPCHAT_COMMAND_MODE:
|
||||||
@ -137,5 +158,5 @@ void execute(WINDOW* w, ToxWindow *self, Tox *m, char *cmd, int mode)
|
|||||||
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
|
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
wprintw(w, "Invalid command.\n");
|
line_info_add(self, NULL, NULL, NULL, "Invalid command.", SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,21 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _execute_h
|
||||||
|
#define _execute_h
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define GLOBAL_NUM_COMMANDS 16
|
||||||
|
#define CHAT_NUM_COMMANDS 12
|
||||||
|
#else
|
||||||
#define GLOBAL_NUM_COMMANDS 14
|
#define GLOBAL_NUM_COMMANDS 14
|
||||||
#define CHAT_NUM_COMMANDS 5
|
#define CHAT_NUM_COMMANDS 4
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GLOBAL_COMMAND_MODE,
|
GLOBAL_COMMAND_MODE,
|
||||||
@ -31,3 +43,5 @@ enum {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode);
|
void execute(WINDOW *w, ToxWindow *self, Tox *m, char *cmd, int mode);
|
||||||
|
|
||||||
|
#endif /* #define _execute_h */
|
||||||
|
129
src/file_senders.c
Normal file
129
src/file_senders.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* file_senders.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "file_senders.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
|
FileSender file_senders[MAX_FILES];
|
||||||
|
uint8_t max_file_senders_index;
|
||||||
|
|
||||||
|
static void set_max_file_senders_index(void)
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = max_file_senders_index; j > 0; --j) {
|
||||||
|
if (file_senders[j - 1].active)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_file_senders_index = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void close_file_sender(ToxWindow *self, Tox *m, int i, char *msg, int CTRL, int filenum, int32_t friendnum)
|
||||||
|
{
|
||||||
|
if (self->chatwin != NULL) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_file_send_control(m, friendnum, 0, filenum, CTRL, 0, 0);
|
||||||
|
fclose(file_senders[i].file);
|
||||||
|
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||||
|
set_max_file_senders_index();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_all_file_senders(Tox *m)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
|
if (file_senders[i].active) {
|
||||||
|
fclose(file_senders[i].file);
|
||||||
|
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
|
||||||
|
TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||||
|
}
|
||||||
|
|
||||||
|
set_max_file_senders_index();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_file_senders(Tox *m)
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
|
if (!file_senders[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ToxWindow *self = file_senders[i].toxwin;
|
||||||
|
char *pathname = file_senders[i].pathname;
|
||||||
|
int filenum = file_senders[i].filenum;
|
||||||
|
int32_t friendnum = file_senders[i].friendnum;
|
||||||
|
FILE *fp = file_senders[i].file;
|
||||||
|
|
||||||
|
/* If file transfer has timed out kill transfer and send kill control */
|
||||||
|
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)) {
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", pathname);
|
||||||
|
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (tox_file_send_data(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
|
||||||
|
file_senders[i].piecelen) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint64_t curtime = get_unix_time();
|
||||||
|
file_senders[i].timestamp = curtime;
|
||||||
|
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||||
|
tox_file_data_size(m, friendnum), fp);
|
||||||
|
|
||||||
|
long double remain = (long double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
||||||
|
|
||||||
|
/* refresh line with percentage complete */
|
||||||
|
if ((self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1)) || !remain) {
|
||||||
|
file_senders[i].last_progress = curtime;
|
||||||
|
uint64_t size = file_senders[i].size;
|
||||||
|
long double pct_remain = remain ? (1 - (remain / size)) * 100 : 100;
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' accepted (%.1Lf%%)", pathname, pct_remain);
|
||||||
|
line_info_set(self, file_senders[i].line_id, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file_senders[i].piecelen == 0) {
|
||||||
|
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", pathname);
|
||||||
|
close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, friendnum);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
src/file_senders.h
Normal file
51
src/file_senders.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/* file_senders.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _filesenders_h
|
||||||
|
#define _filesenders_h
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
||||||
|
#define MAX_FILES 255
|
||||||
|
#define TIMEOUT_FILESENDER 120
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FILE *file;
|
||||||
|
ToxWindow *toxwin;
|
||||||
|
int32_t friendnum;
|
||||||
|
bool active;
|
||||||
|
int filenum;
|
||||||
|
char nextpiece[FILE_PIECE_SIZE];
|
||||||
|
uint16_t piecelen;
|
||||||
|
char pathname[MAX_STR_SIZE];
|
||||||
|
uint64_t timestamp;
|
||||||
|
uint64_t last_progress;
|
||||||
|
uint64_t size;
|
||||||
|
uint32_t line_id;
|
||||||
|
} FileSender;
|
||||||
|
|
||||||
|
void close_all_file_senders(Tox *m);
|
||||||
|
void do_file_senders(Tox *m);
|
||||||
|
|
||||||
|
#endif /* #define _filesenders_h */
|
377
src/friendlist.c
377
src/friendlist.c
@ -20,19 +20,24 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include "audio_call.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
@ -41,10 +46,19 @@ static int max_friends_index = 0; /* marks the index of the last friend in fr
|
|||||||
static int num_selected = 0;
|
static int num_selected = 0;
|
||||||
static int num_friends = 0;
|
static int num_friends = 0;
|
||||||
|
|
||||||
|
extern struct _Winthread Winthread;
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
ToxicFriend friends[MAX_FRIENDS_NUM];
|
ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
||||||
|
|
||||||
#define S_WEIGHT 100
|
static struct _pendingDel {
|
||||||
|
int num;
|
||||||
|
bool active;
|
||||||
|
WINDOW *popup;
|
||||||
|
} pendingdelete;
|
||||||
|
|
||||||
|
#define S_WEIGHT 100000
|
||||||
|
|
||||||
static int index_name_cmp(const void *n1, const void *n2)
|
static int index_name_cmp(const void *n1, const void *n2)
|
||||||
{
|
{
|
||||||
@ -58,7 +72,7 @@ static int index_name_cmp(const void *n1, const void *n2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(Tox *m)
|
void sort_friendlist_index(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
@ -71,52 +85,67 @@ void sort_friendlist_index(Tox *m)
|
|||||||
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
|
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
|
static void update_friend_last_online(int32_t num, uint64_t timestamp)
|
||||||
|
{
|
||||||
|
friends[num].last_online.last_on = timestamp;
|
||||||
|
friends[num].last_online.tm = *localtime((const time_t*)×tamp);
|
||||||
|
|
||||||
|
/* if the format changes make sure TIME_STR_SIZE is the correct size */
|
||||||
|
const char *t = user_settings->time == TIME_12 ? "%I:%M %p" : "%H:%M";
|
||||||
|
strftime(friends[num].last_online.hour_min_str, TIME_STR_SIZE, t,
|
||||||
|
&friends[num].last_online.tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, const char *str, uint16_t len)
|
||||||
{
|
{
|
||||||
if (num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_name(m, num, nick);
|
get_nick_truncate(m, nick, num);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
|
||||||
wprintw(prompt->window, "%s: %s\n", nick, str);
|
|
||||||
|
|
||||||
prep_prompt_win();
|
char timefrmt[TIME_STR_SIZE];
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
|
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
|
line_info_add(prompt, timefrmt, nick, NULL, str, IN_MSG, 0, 0);
|
||||||
|
|
||||||
|
char *msg = "* Warning: Too many windows are open.";
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
|
||||||
alert_window(prompt, WINDOW_ALERT_1, true);
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int num, uint8_t status)
|
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
|
||||||
{
|
{
|
||||||
if (num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
friends[num].online = status == 1 ? true : false;
|
friends[num].online = status;
|
||||||
sort_friendlist_index(m);
|
update_friend_last_online(num, get_unix_time());
|
||||||
|
store_data(m, DATA_FILE);
|
||||||
|
sort_friendlist_index();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int num, uint8_t *str, uint16_t len)
|
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, const char *nick, uint16_t len)
|
||||||
{
|
{
|
||||||
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
|
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
str[TOXIC_MAX_NAME_LENGTH] = '\0';
|
char tempname[TOX_MAX_NAME_LENGTH];
|
||||||
len = strlen(str) + 1;
|
strcpy(tempname, nick);
|
||||||
memcpy(friends[num].name, str, len);
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
tempname[len] = '\0';
|
||||||
|
snprintf(friends[num].name, sizeof(friends[num].name), "%s", tempname);
|
||||||
friends[num].namelength = len;
|
friends[num].namelength = len;
|
||||||
sort_friendlist_index(m);
|
sort_friendlist_index();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USERSTATUS status)
|
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
|
||||||
{
|
{
|
||||||
if (num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
@ -124,16 +153,16 @@ static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int num, TOX_USER
|
|||||||
friends[num].status = status;
|
friends[num].status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *str, uint16_t len)
|
static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, const char *status, uint16_t len)
|
||||||
{
|
{
|
||||||
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
|
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memcpy(friends[num].statusmsg, str, len);
|
snprintf(friends[num].statusmsg, sizeof(friends[num].statusmsg), "%s", status);
|
||||||
friends[num].statusmsg_len = len;
|
friends[num].statusmsg_len = strlen(friends[num].statusmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort)
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort)
|
||||||
{
|
{
|
||||||
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
||||||
return;
|
return;
|
||||||
@ -147,15 +176,19 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
|
|||||||
friends[i].chatwin = -1;
|
friends[i].chatwin = -1;
|
||||||
friends[i].online = false;
|
friends[i].online = false;
|
||||||
friends[i].status = TOX_USERSTATUS_NONE;
|
friends[i].status = TOX_USERSTATUS_NONE;
|
||||||
friends[i].namelength = tox_get_name(m, num, friends[i].name);
|
friends[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON;
|
||||||
tox_get_client_id(m, num, friends[i].pub_key);
|
tox_get_client_id(m, num, (uint8_t *) friends[i].pub_key);
|
||||||
|
update_friend_last_online(i, tox_get_last_online(m, i));
|
||||||
|
|
||||||
if (friends[i].namelength == -1 || friends[i].name[0] == '\0') {
|
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
||||||
strcpy(friends[i].name, (uint8_t *) UNKNOWN_NAME);
|
int len = get_nick_truncate(m, tempname, num);
|
||||||
friends[i].namelength = strlen(UNKNOWN_NAME) + 1;
|
|
||||||
|
if (len == -1 || tempname[0] == '\0') {
|
||||||
|
strcpy(friends[i].name, UNKNOWN_NAME);
|
||||||
|
friends[i].namelength = strlen(UNKNOWN_NAME);
|
||||||
} else { /* Enforce toxic's maximum name length */
|
} else { /* Enforce toxic's maximum name length */
|
||||||
friends[i].name[TOXIC_MAX_NAME_LENGTH] = '\0';
|
friends[i].namelength = len;
|
||||||
friends[i].namelength = strlen(friends[i].name) + 1;
|
snprintf(friends[i].name, sizeof(friends[i].name), "%s", tempname);
|
||||||
}
|
}
|
||||||
|
|
||||||
num_friends = tox_count_friendlist(m);
|
num_friends = tox_count_friendlist(m);
|
||||||
@ -164,56 +197,52 @@ static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort
|
|||||||
++max_friends_index;
|
++max_friends_index;
|
||||||
|
|
||||||
if (sort)
|
if (sort)
|
||||||
sort_friendlist_index(m);
|
sort_friendlist_index();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t filenum,
|
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
|
||||||
uint64_t filesize, uint8_t *filename, uint16_t filename_len)
|
uint64_t filesize, const char *filename, uint16_t filename_len)
|
||||||
{
|
{
|
||||||
if (num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_name(m, num, nick);
|
get_nick_truncate(m, nick, num);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
|
||||||
|
|
||||||
prep_prompt_win();
|
char msg[MAX_STR_SIZE];
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
snprintf(msg, sizeof(msg), "* File transfer from %s failed: too many windows are open.", nick);
|
||||||
wprintw(prompt->window, "* File transfer from %s failed: too many windows are open.\n", nick);
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
alert_window(prompt, WINDOW_ALERT_1, true);
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *group_pub_key)
|
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, const char *group_pub_key)
|
||||||
{
|
{
|
||||||
if (num >= max_friends_index)
|
if (num >= max_friends_index)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_name(m, num, nick);
|
get_nick_truncate(m, nick, num);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
|
||||||
|
|
||||||
prep_prompt_win();
|
char msg[MAX_STR_SIZE];
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
snprintf(msg, sizeof(msg), "* Group chat invite from %s failed: too many windows are open.", nick);
|
||||||
wprintw(prompt->window, "* Group chat invite from %s failed: too many windows are open.\n", nick);
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
alert_window(prompt, WINDOW_ALERT_1, true);
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
@ -230,7 +259,7 @@ static void select_friend(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
static void delete_friend(Tox *m, int32_t f_num)
|
||||||
{
|
{
|
||||||
tox_del_friend(m, f_num);
|
tox_del_friend(m, f_num);
|
||||||
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
||||||
@ -238,7 +267,7 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = max_friends_index; i > 0; --i) {
|
for (i = max_friends_index; i > 0; --i) {
|
||||||
if (friends[i-1].active)
|
if (friends[i - 1].active)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,40 +278,87 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
|||||||
if (num_friends && num_selected == num_friends)
|
if (num_friends && num_selected == num_friends)
|
||||||
--num_selected;
|
--num_selected;
|
||||||
|
|
||||||
sort_friendlist_index(m);
|
sort_friendlist_index();
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
/* activates delete friend popup */
|
||||||
|
static void del_friend_activate(ToxWindow *self, Tox *m, int32_t f_num)
|
||||||
|
{
|
||||||
|
pendingdelete.popup = newwin(3, 22 + TOXIC_MAX_NAME_LENGTH - 1, 8, 8);
|
||||||
|
pendingdelete.active = true;
|
||||||
|
pendingdelete.num = f_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deactivates delete friend popup and deletes friend if instructed */
|
||||||
|
static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key)
|
||||||
|
{
|
||||||
|
if (key == 'y')
|
||||||
|
delete_friend(m, pendingdelete.num);
|
||||||
|
|
||||||
|
delwin(pendingdelete.popup);
|
||||||
|
memset(&pendingdelete, 0, sizeof(pendingdelete));
|
||||||
|
clear();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_popup(void)
|
||||||
|
{
|
||||||
|
if (!pendingdelete.active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wattron(pendingdelete.popup, A_BOLD);
|
||||||
|
box(pendingdelete.popup, ACS_VLINE, ACS_HLINE);
|
||||||
|
wattroff(pendingdelete.popup, A_BOLD);
|
||||||
|
|
||||||
|
wmove(pendingdelete.popup, 1, 1);
|
||||||
|
wprintw(pendingdelete.popup, "Delete contact ");
|
||||||
|
wattron(pendingdelete.popup, A_BOLD);
|
||||||
|
wprintw(pendingdelete.popup, "%s", friends[pendingdelete.num].name);
|
||||||
|
wattroff(pendingdelete.popup, A_BOLD);
|
||||||
|
wprintw(pendingdelete.popup, "? y/n");
|
||||||
|
|
||||||
|
wrefresh(pendingdelete.popup);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||||
{
|
{
|
||||||
if (num_friends == 0)
|
if (num_friends == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int f = friendlist_index[num_selected];
|
int f = friendlist_index[num_selected];
|
||||||
|
|
||||||
|
/* lock screen and force decision on deletion popup */
|
||||||
|
if (pendingdelete.active) {
|
||||||
|
if (key == 'y' || key == 'n')
|
||||||
|
del_friend_deactivate(self, m, key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key != ltr) {
|
||||||
if (key == '\n') {
|
if (key == '\n') {
|
||||||
/* Jump to chat window if already open */
|
/* Jump to chat window if already open */
|
||||||
if (friends[f].chatwin != -1) {
|
if (friends[f].chatwin != -1) {
|
||||||
set_active_window(friends[f].chatwin);
|
set_active_window(friends[f].chatwin);
|
||||||
} else if (num_active_windows() < MAX_WINDOWS_NUM) {
|
} else if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
||||||
set_active_window(friends[f].chatwin);
|
set_active_window(friends[f].chatwin);
|
||||||
} else {
|
} else {
|
||||||
prep_prompt_win();
|
char *msg = "* Warning: Too many windows are open.";
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, RED);
|
||||||
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
|
|
||||||
wattron(prompt->window, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
alert_window(prompt, WINDOW_ALERT_1, true);
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
} else if (key == KEY_DC) {
|
} else if (key == KEY_DC) {
|
||||||
delete_friend(m, self, f, key);
|
del_friend_activate(self, m, f);
|
||||||
} else {
|
} else {
|
||||||
select_friend(self, m, key);
|
select_friend(self, m, key);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FLIST_OFST 4 /* Accounts for the lines at top */
|
#define FLIST_OFST 6 /* Accounts for space at top and bottom */
|
||||||
|
|
||||||
static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
@ -291,27 +367,37 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
bool fix_statuses = x2 != self->x; /* true if window x axis has changed */
|
uint64_t cur_time = get_unix_time();
|
||||||
|
struct tm cur_loc_tm = *localtime((const time_t*)&cur_time);
|
||||||
|
|
||||||
|
bool fix_statuses = x2 != self->x; /* true if window max x value has changed */
|
||||||
|
|
||||||
wattron(self->window, COLOR_PAIR(CYAN));
|
wattron(self->window, COLOR_PAIR(CYAN));
|
||||||
wprintw(self->window, " Open a chat window with the");
|
wprintw(self->window, " Open a chat window with the");
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " Enter ");
|
wprintw(self->window, " Enter ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "key. Delete a friend with the");
|
wprintw(self->window, "key. Delete a contact with the");
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " Delete ");
|
wprintw(self->window, " Delete ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "key.\n\n");
|
wprintw(self->window, "key.\n\n");
|
||||||
wattroff(self->window, COLOR_PAIR(CYAN));
|
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(self->window, " Friends: %d/%d \n\n", tox_get_num_online_friends(m), num_friends);
|
int nf = tox_get_num_online_friends(m);
|
||||||
wattroff(self->window, A_BOLD);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
|
wattron(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, " Online: ");
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "%d/%d \n\n", nf, num_friends);
|
||||||
|
|
||||||
|
if ((y2 - FLIST_OFST) <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
int selected_num = 0;
|
||||||
|
|
||||||
/* Determine which portion of friendlist to draw based on current position */
|
/* Determine which portion of friendlist to draw based on current position */
|
||||||
int page = num_selected / (y2 - FLIST_OFST);
|
int page = num_selected / (y2 - FLIST_OFST);
|
||||||
int start = (y2 - FLIST_OFST) * page;
|
int start = (y2 - FLIST_OFST) * page;
|
||||||
@ -328,90 +414,168 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " > ");
|
wprintw(self->window, " > ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
selected_num = f;
|
||||||
f_selected = true;
|
f_selected = true;
|
||||||
} else {
|
} else {
|
||||||
wprintw(self->window, " ");
|
wprintw(self->window, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (friends[f].online) {
|
if (friends[f].online) {
|
||||||
TOX_USERSTATUS status = friends[f].status;
|
uint8_t status = friends[f].status;
|
||||||
int colour = WHITE;
|
int colour = WHITE;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case TOX_USERSTATUS_NONE:
|
case TOX_USERSTATUS_NONE:
|
||||||
colour = GREEN;
|
colour = GREEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USERSTATUS_AWAY:
|
case TOX_USERSTATUS_AWAY:
|
||||||
colour = YELLOW;
|
colour = YELLOW;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USERSTATUS_BUSY:
|
case TOX_USERSTATUS_BUSY:
|
||||||
colour = RED;
|
colour = RED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOX_USERSTATUS_INVALID:
|
||||||
|
colour = MAGENTA;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(self->window, "[");
|
|
||||||
wattron(self->window, COLOR_PAIR(colour) | A_BOLD);
|
wattron(self->window, COLOR_PAIR(colour) | A_BOLD);
|
||||||
wprintw(self->window, "O");
|
wprintw(self->window, "O ");
|
||||||
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
|
||||||
wprintw(self->window, "]");
|
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
|
wattron(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
|
|
||||||
wprintw(self->window, "%s", friends[f].name);
|
wprintw(self->window, "%s", friends[f].name);
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
/* Reset friends[f].statusmsg on window resize */
|
/* Reset friends[f].statusmsg on window resize */
|
||||||
if (fix_statuses) {
|
if (fix_statuses) {
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {'\0'};
|
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||||
tox_get_status_message(m, friends[f].num, statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
tox_get_status_message(m, friends[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
|
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
|
||||||
friends[f].statusmsg_len = tox_get_status_message_size(m, f);
|
friends[f].statusmsg_len = strlen(friends[f].statusmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Truncate note if it doesn't fit on one line */
|
/* Truncate note if it doesn't fit on one line */
|
||||||
uint16_t maxlen = x2 - getcurx(self->window) - 4;
|
uint16_t maxlen = x2 - getcurx(self->window) - 2;
|
||||||
|
|
||||||
if (friends[f].statusmsg_len > maxlen) {
|
if (friends[f].statusmsg_len > maxlen) {
|
||||||
friends[f].statusmsg[maxlen-3] = '\0';
|
friends[f].statusmsg[maxlen - 3] = '\0';
|
||||||
strcat(friends[f].statusmsg, "...");
|
strcat(friends[f].statusmsg, "...");
|
||||||
friends[f].statusmsg[maxlen] = '\0';
|
friends[f].statusmsg[maxlen] = '\0';
|
||||||
friends[f].statusmsg_len = maxlen;
|
friends[f].statusmsg_len = maxlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(self->window, " (%s)\n", friends[f].statusmsg);
|
if (friends[f].statusmsg[0])
|
||||||
|
wprintw(self->window, " %s", friends[f].statusmsg);
|
||||||
|
|
||||||
|
wprintw(self->window, "\n");
|
||||||
} else {
|
} else {
|
||||||
wprintw(self->window, "[");
|
wprintw(self->window, "o ");
|
||||||
wattron(self->window, A_BOLD);
|
|
||||||
wprintw(self->window, "O");
|
|
||||||
wattroff(self->window, A_BOLD);
|
|
||||||
wprintw(self->window, "]");
|
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wprintw(self->window, "%s\n", friends[f].name);
|
wattron(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "%s", friends[f].name);
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
uint64_t last_seen = friends[f].last_online.last_on;
|
||||||
|
|
||||||
|
if (last_seen != 0) {
|
||||||
|
int day_dist = (cur_loc_tm.tm_yday - friends[f].last_online.tm.tm_yday) % 365;
|
||||||
|
const char *hourmin = friends[f].last_online.hour_min_str;
|
||||||
|
|
||||||
|
switch (day_dist) {
|
||||||
|
case 0:
|
||||||
|
wprintw(self->window, " Last seen: Today %s\n", hourmin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
wprintw(self->window, " Last seen: Yesterday %s\n", hourmin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
wprintw(self->window, " Last seen: %d days ago\n", day_dist);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wprintw(self->window, " Last seen: Never\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->x = x2;
|
self->x = x2;
|
||||||
|
|
||||||
|
if (num_friends) {
|
||||||
|
wmove(self->window, y2 - 1, 1);
|
||||||
|
|
||||||
|
wattron(self->window, A_BOLD);
|
||||||
|
wprintw(self->window, "ID: ");
|
||||||
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
|
||||||
|
wprintw(self->window, "%02X", friends[selected_num].pub_key[i] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
|
draw_popup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable_chatwin(int f_num)
|
void disable_chatwin(int32_t f_num)
|
||||||
{
|
{
|
||||||
friends[f_num].chatwin = -1;
|
friends[f_num].chatwin = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onInit(ToxWindow *self, Tox *m)
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
|
||||||
{
|
{
|
||||||
|
int id = toxav_get_peer_id(av, call_index, 0);
|
||||||
|
|
||||||
|
/*id++;*/
|
||||||
|
if ( id != ErrorInternal && id >= max_friends_index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tox *m = toxav_get_tox(av);
|
||||||
|
|
||||||
|
if (friends[id].chatwin == -1) {
|
||||||
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
|
if (toxav_get_call_state(av, call_index) == av_CallStarting) /* Only open windows when call is incoming */
|
||||||
|
friends[id].chatwin = add_window(m, new_chat(m, friends[id].num));
|
||||||
|
} else {
|
||||||
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
|
get_nick_truncate(m, nick, friends[id].num);
|
||||||
|
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "Audio action from: %s!", nick);
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
|
||||||
|
char *errmsg = "* Warning: Too many windows are open.";
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
|
|
||||||
|
alert_window(prompt, WINDOW_ALERT_0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
ToxWindow new_friendlist(void)
|
ToxWindow new_friendlist(void)
|
||||||
{
|
{
|
||||||
@ -419,20 +583,37 @@ ToxWindow new_friendlist(void)
|
|||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
ret.active = true;
|
ret.active = true;
|
||||||
|
ret.is_friendlist = true;
|
||||||
|
|
||||||
ret.onKey = &friendlist_onKey;
|
ret.onKey = &friendlist_onKey;
|
||||||
ret.onDraw = &friendlist_onDraw;
|
ret.onDraw = &friendlist_onDraw;
|
||||||
ret.onInit = &friendlist_onInit;
|
|
||||||
ret.onFriendAdded = &friendlist_onFriendAdded;
|
ret.onFriendAdded = &friendlist_onFriendAdded;
|
||||||
ret.onMessage = &friendlist_onMessage;
|
ret.onMessage = &friendlist_onMessage;
|
||||||
ret.onConnectionChange = &friendlist_onConnectionChange;
|
ret.onConnectionChange = &friendlist_onConnectionChange;
|
||||||
ret.onAction = &friendlist_onMessage; // Action has identical behaviour to message
|
ret.onAction = &friendlist_onMessage; /* Action has identical behaviour to message */
|
||||||
ret.onNickChange = &friendlist_onNickChange;
|
ret.onNickChange = &friendlist_onNickChange;
|
||||||
ret.onStatusChange = &friendlist_onStatusChange;
|
ret.onStatusChange = &friendlist_onStatusChange;
|
||||||
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
|
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
|
||||||
ret.onFileSendRequest = &friendlist_onFileSendRequest;
|
ret.onFileSendRequest = &friendlist_onFileSendRequest;
|
||||||
ret.onGroupInvite = &friendlist_onGroupInvite;
|
ret.onGroupInvite = &friendlist_onGroupInvite;
|
||||||
|
|
||||||
strcpy(ret.name, "friends");
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
ret.onInvite = &friendlist_onAv;
|
||||||
|
ret.onRinging = &friendlist_onAv;
|
||||||
|
ret.onStarting = &friendlist_onAv;
|
||||||
|
ret.onEnding = &friendlist_onAv;
|
||||||
|
ret.onError = &friendlist_onAv;
|
||||||
|
ret.onStart = &friendlist_onAv;
|
||||||
|
ret.onCancel = &friendlist_onAv;
|
||||||
|
ret.onReject = &friendlist_onAv;
|
||||||
|
ret.onEnd = &friendlist_onAv;
|
||||||
|
ret.onRequestTimeout = &friendlist_onAv;
|
||||||
|
ret.onPeerTimeout = &friendlist_onAv;
|
||||||
|
|
||||||
|
ret.call_idx = -1;
|
||||||
|
ret.device_selection[0] = ret.device_selection[1] = -1;
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
strcpy(ret.name, "contacts");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -23,30 +23,53 @@
|
|||||||
#ifndef FRIENDLIST_H_53I41IM
|
#ifndef FRIENDLIST_H_53I41IM
|
||||||
#define FRIENDLIST_H_53I41IM
|
#define FRIENDLIST_H_53I41IM
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "file_senders.h"
|
||||||
|
|
||||||
|
struct FileReceiver {
|
||||||
|
char filenames[MAX_FILES][MAX_STR_SIZE];
|
||||||
|
FILE *files[MAX_FILES];
|
||||||
|
bool pending[MAX_FILES];
|
||||||
|
uint64_t size[MAX_FILES];
|
||||||
|
uint64_t last_progress[MAX_FILES];
|
||||||
|
uint32_t line_id[MAX_FILES];
|
||||||
|
};
|
||||||
|
|
||||||
|
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" */
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
char name[TOXIC_MAX_NAME_LENGTH];
|
||||||
uint16_t namelength;
|
int namelength;
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||||
uint16_t statusmsg_len;
|
uint16_t statusmsg_len;
|
||||||
uint8_t pending_groupchat[TOX_CLIENT_ID_SIZE];
|
char groupchat_key[TOX_CLIENT_ID_SIZE];
|
||||||
uint8_t pub_key[TOX_CLIENT_ID_SIZE];
|
bool groupchat_pending;
|
||||||
int num;
|
char pub_key[TOX_CLIENT_ID_SIZE];
|
||||||
|
int32_t num;
|
||||||
int chatwin;
|
int chatwin;
|
||||||
bool active;
|
bool active;
|
||||||
bool online;
|
bool online;
|
||||||
bool is_typing;
|
uint8_t is_typing;
|
||||||
bool logging_on; /* saves preference for friend irrespective of chat windows */
|
bool logging_on; /* saves preference for friend irrespective of chat windows */
|
||||||
TOX_USERSTATUS status;
|
uint8_t status;
|
||||||
|
struct LastOnline last_online;
|
||||||
struct FileReceiver file_receiver;
|
struct FileReceiver file_receiver;
|
||||||
} ToxicFriend;
|
} ToxicFriend;
|
||||||
|
|
||||||
ToxWindow new_friendlist(void);
|
ToxWindow new_friendlist(void);
|
||||||
void disable_chatwin(int f_num);
|
void disable_chatwin(int32_t f_num);
|
||||||
int get_friendnum(uint8_t *name);
|
int get_friendnum(uint8_t *name);
|
||||||
|
|
||||||
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(Tox *m);
|
void sort_friendlist_index(void);
|
||||||
|
|
||||||
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
#endif /* end of include guard: FRIENDLIST_H_53I41IM */
|
||||||
|
@ -20,52 +20,60 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "dns.h"
|
||||||
|
#include "groupchat.h"
|
||||||
|
#include "prompt.h"
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
|
|
||||||
extern uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
extern char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||||
extern uint8_t num_frnd_requests;
|
extern uint8_t num_frnd_requests;
|
||||||
|
|
||||||
/* command functions */
|
/* command functions */
|
||||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
/* check arguments */
|
char *msg;
|
||||||
|
|
||||||
if (argc != 1) {
|
if (argc != 1) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
msg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int req = atoi(argv[1]);
|
int req = atoi(argv[1]);
|
||||||
|
|
||||||
if ((req == 0 && strcmp(argv[1], "0"))|| req >= MAX_FRIENDS_NUM) {
|
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
|
||||||
wprintw(window, "No pending friend request with that number.\n");
|
msg = "No pending friend request with that number.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strlen(pending_frnd_requests[req])) {
|
if (!strlen(pending_frnd_requests[req])) {
|
||||||
wprintw(window, "No pending friend request with that number.\n");
|
msg = "No pending friend request with that number.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int friendnum = tox_add_friend_norequest(m, pending_frnd_requests[req]);
|
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) pending_frnd_requests[req]);
|
||||||
|
|
||||||
if (friendnum == -1)
|
if (friendnum == -1)
|
||||||
wprintw(window, "Failed to add friend.\n");
|
msg = "Failed to add friend.";
|
||||||
else {
|
else {
|
||||||
wprintw(window, "Friend request accepted.\n");
|
msg = "Friend request accepted.";
|
||||||
on_friendadded(m, friendnum, true);
|
on_friendadded(m, friendnum, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,48 +82,97 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = num_frnd_requests; i > 0; --i) {
|
for (i = num_frnd_requests; i > 0; --i) {
|
||||||
if (!strlen(pending_frnd_requests[i-1]))
|
if (!strlen(pending_frnd_requests[i - 1]))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
num_frnd_requests = i;
|
num_frnd_requests = i;
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg)
|
||||||
|
{
|
||||||
|
char *errmsg;
|
||||||
|
int32_t f_num = tox_add_friend(m, (uint8_t *) id_bin, (uint8_t *) msg, (uint16_t) strlen(msg));
|
||||||
|
|
||||||
|
switch (f_num) {
|
||||||
|
case TOX_FAERR_TOOLONG:
|
||||||
|
errmsg = "Message is too long.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_NOMESSAGE:
|
||||||
|
errmsg = "Please add a message to your request.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_OWNKEY:
|
||||||
|
errmsg = "That appears to be your own ID.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_ALREADYSENT:
|
||||||
|
errmsg = "Friend request has already been sent.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_UNKNOWN:
|
||||||
|
errmsg = "Undefined error when adding friend.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_BADCHECKSUM:
|
||||||
|
errmsg = "Bad checksum in address.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FAERR_SETNEWNOSPAM:
|
||||||
|
errmsg = "Nospam was different.";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errmsg = "Friend request sent.";
|
||||||
|
on_friendadded(m, f_num, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *id = argv[1];
|
char *id = argv[1];
|
||||||
uint8_t msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
uint8_t *temp = argv[2];
|
char *temp = argv[2];
|
||||||
|
|
||||||
if (temp[0] != '\"') {
|
if (temp[0] != '\"') {
|
||||||
wprintw(window, "Message must be enclosed in quotes.\n");
|
errmsg = "Message must be enclosed in quotes.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp[strlen(++temp)-1] = L'\0';
|
++temp;
|
||||||
|
temp[strlen(temp) - 1] = '\0';
|
||||||
snprintf(msg, sizeof(msg), "%s", temp);
|
snprintf(msg, sizeof(msg), "%s", temp);
|
||||||
} else {
|
} else {
|
||||||
uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
char selfname[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfname);
|
||||||
|
selfname[n_len] = '\0';
|
||||||
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
snprintf(msg, sizeof(msg), "Hello, my name is %s. Care to Tox?", selfname);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(id) != 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
char id_bin[TOX_FRIEND_ADDRESS_SIZE] = {0};
|
||||||
wprintw(window, "Invalid ID length.\n");
|
uint16_t id_len = (uint16_t) strlen(id);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* try to add tox ID */
|
||||||
|
if (id_len == 2 * TOX_FRIEND_ADDRESS_SIZE) {
|
||||||
size_t i;
|
size_t i;
|
||||||
char xx[3];
|
char xx[3];
|
||||||
uint32_t x;
|
uint32_t x;
|
||||||
uint8_t id_bin[TOX_FRIEND_ADDRESS_SIZE];
|
|
||||||
|
|
||||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; ++i) {
|
||||||
xx[0] = id[2 * i];
|
xx[0] = id[2 * i];
|
||||||
@ -123,175 +180,137 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
xx[2] = '\0';
|
xx[2] = '\0';
|
||||||
|
|
||||||
if (sscanf(xx, "%02x", &x) != 1) {
|
if (sscanf(xx, "%02x", &x) != 1) {
|
||||||
wprintw(window, "Invalid ID.\n");
|
errmsg = "Invalid ID.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
id_bin[i] = x;
|
id_bin[i] = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < TOX_FRIEND_ADDRESS_SIZE; i++) {
|
cmd_add_helper(self, m, id_bin, msg);
|
||||||
id[i] = toupper(id[i]);
|
} else { /* assume id is a username@domain address and do DNS lookup */
|
||||||
}
|
dns3_lookup(self, m, id_bin, id, msg);
|
||||||
|
|
||||||
int f_num = tox_add_friend(m, id_bin, msg, strlen(msg) + 1);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
line_info_clear(self->chatwin->hst);
|
||||||
wclear(window);
|
wclear(window);
|
||||||
wprintw(window, "\n\n");
|
endwin();
|
||||||
|
refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
/* check arguments */
|
/* check arguments */
|
||||||
if (argc != 3) {
|
if (argc != 3) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tox_IP_Port dht;
|
const char *ip = argv[1];
|
||||||
char *ip = argv[1];
|
const char *port = argv[2];
|
||||||
char *port = argv[2];
|
const char *key = argv[3];
|
||||||
char *key = argv[3];
|
|
||||||
|
|
||||||
if (atoi(port) == 0) {
|
if (atoi(port) == 0) {
|
||||||
wprintw(window, "Invalid syntax.\n");
|
errmsg = "Invalid syntax.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *binary_string = hex_string_to_bin(key);
|
char *binary_string = hex_string_to_bin(key);
|
||||||
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT,
|
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, htons(atoi(port)), (uint8_t *) binary_string);
|
||||||
htons(atoi(port)), binary_string);
|
|
||||||
free(binary_string);
|
free(binary_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
char *errmsg;
|
||||||
wattron(window, COLOR_PAIR(RED));
|
|
||||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wattron(window, COLOR_PAIR(RED));
|
errmsg = " * Warning: Too many windows are open.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = tox_add_groupchat(m);
|
int groupnum = tox_add_groupchat(m);
|
||||||
|
|
||||||
if (groupnum == -1) {
|
if (groupnum == -1) {
|
||||||
wprintw(window, "Group chat instance failed to initialize.\n");
|
errmsg = "Group chat instance failed to initialize.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
if (init_groupchat_win(prompt, m, groupnum) == -1) {
|
||||||
wprintw(window, "Group chat window failed to initialize.\n");
|
errmsg = "Group chat window failed to initialize.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
tox_del_groupchat(m, groupnum);
|
tox_del_groupchat(m, groupnum);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(window, "Group chat created as %d.\n", groupnum);
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "Group chat created as %d.", groupnum);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *msg;
|
||||||
|
struct chatlog *log = self->chatwin->log;
|
||||||
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
bool on;
|
if (log->log_on)
|
||||||
|
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
|
||||||
if (self->is_chat || self->is_groupchat)
|
else
|
||||||
on = self->chatwin->log->log_on;
|
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
||||||
else if (self->is_prompt)
|
|
||||||
on = self->promptbuf->log->log_on;
|
|
||||||
|
|
||||||
if (on) {
|
|
||||||
wprintw(window, "Logging for this window is ");
|
|
||||||
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
|
|
||||||
wprintw(window, "[on]");
|
|
||||||
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
|
|
||||||
wprintw(window, ". Type \"/log off\" to disable.\n");
|
|
||||||
} else {
|
|
||||||
wprintw(window, "Logging for this window is ");
|
|
||||||
wattron(window, COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
wprintw(window, "[off]");
|
|
||||||
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
wprintw(window, ". Type \"/log on\" to enable.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *swch = argv[1];
|
char *swch = argv[1];
|
||||||
|
|
||||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||||
|
|
||||||
if (self->is_chat) {
|
if (self->is_chat) {
|
||||||
friends[self->num].logging_on = true;
|
friends[self->num].logging_on = true;
|
||||||
log_enable(self->name, friends[self->num].pub_key, self->chatwin->log);
|
log_enable(self->name, friends[self->num].pub_key, log);
|
||||||
} else if (self->is_prompt) {
|
} else if (self->is_prompt) {
|
||||||
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
|
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
tox_get_address(m, myid);
|
tox_get_address(m, (uint8_t *) myid);
|
||||||
log_enable(self->name, &myid, self->promptbuf->log);
|
log_enable(self->name, myid, log);
|
||||||
} else if (self->is_groupchat) {
|
} else if (self->is_groupchat) {
|
||||||
log_enable(self->name, NULL, self->chatwin->log);
|
log_enable(self->name, NULL, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(window, "Logging ");
|
msg = "Logging enabled";
|
||||||
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
wprintw(window, "[on]\n");
|
|
||||||
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
|
|
||||||
return;
|
return;
|
||||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||||
if (self->is_chat) {
|
if (self->is_chat)
|
||||||
friends[self->num].logging_on = false;
|
friends[self->num].logging_on = false;
|
||||||
log_disable(self->chatwin->log);
|
|
||||||
} else if (self->is_prompt) {
|
|
||||||
log_disable(self->promptbuf->log);
|
|
||||||
} else if (self->is_groupchat) {
|
|
||||||
log_disable(self->chatwin->log);
|
|
||||||
}
|
|
||||||
|
|
||||||
wprintw(window, "Logging ");
|
log_disable(log);
|
||||||
wattron(window, COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
wprintw(window, "[off]\n");
|
msg = "Logging disabled";
|
||||||
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n");
|
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
|
||||||
uint8_t address[TOX_FRIEND_ADDRESS_SIZE];
|
char address[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
tox_get_address(m, address);
|
tox_get_address(m, (uint8_t *) address);
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
@ -301,128 +320,112 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
strcat(id, xx);
|
strcat(id, xx);
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(window, "%s\n", id);
|
line_info_add(self, NULL, NULL, NULL, id, SYS_MSG, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
/* check arguments */
|
/* check arguments */
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
wprintw(window, "Invalid name.\n");
|
errmsg = "Invalid name.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *nick = argv[1];
|
char *nick = argv[1];
|
||||||
int len = strlen(nick);
|
int len = strlen(nick);
|
||||||
|
|
||||||
if (nick[0] == '\"') {
|
if (nick[0] == '\"') {
|
||||||
++nick;
|
++nick;
|
||||||
len -= 2;
|
len -= 2;
|
||||||
nick[len] = L'\0';
|
nick[len] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!valid_nick(nick)) {
|
if (!valid_nick(nick)) {
|
||||||
wprintw(window, "Invalid name.\n");
|
errmsg = "Invalid name.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > TOXIC_MAX_NAME_LENGTH) {
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = L'\0';
|
nick[len] = '\0';
|
||||||
len = TOXIC_MAX_NAME_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_set_name(m, nick, len+1);
|
tox_set_name(m, (uint8_t *) nick, (uint16_t) len);
|
||||||
prompt_update_nick(prompt, nick, len+1);
|
prompt_update_nick(prompt, nick);
|
||||||
|
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
if (argc < 1) {
|
if (argc < 1) {
|
||||||
wprintw(window, "Wrong number of arguments.\n");
|
errmsg = "Wrong number of arguments.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *msg = argv[1];
|
char *msg = argv[1];
|
||||||
|
|
||||||
if (msg[0] != '\"') {
|
if (msg[0] != '\"') {
|
||||||
wprintw(window, "Note must be enclosed in quotes.\n");
|
errmsg = "Note must be enclosed in quotes.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
msg[strlen(++msg)-1] = L'\0';
|
++msg;
|
||||||
uint16_t len = strlen(msg) + 1;
|
int len = strlen(msg) - 1;
|
||||||
tox_set_status_message(m, msg, len);
|
msg[len] = '\0';
|
||||||
|
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
||||||
prompt_update_statusmessage(prompt, msg, len);
|
prompt_update_statusmessage(prompt, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
wclear(window);
|
help_init_menu(self);
|
||||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
|
||||||
wprintw(window, "\n\nGlobal commands:\n");
|
|
||||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
|
||||||
|
|
||||||
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
|
||||||
wprintw(window, " /accept <n> : Accept friend request\n");
|
|
||||||
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
|
|
||||||
wprintw(window, " /status <type> <msg> : Set status with optional note\n");
|
|
||||||
wprintw(window, " /note <msg> : Set a personal note\n");
|
|
||||||
wprintw(window, " /nick <nick> : Set your nickname\n");
|
|
||||||
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
|
|
||||||
wprintw(window, " /groupchat : Create a group chat\n");
|
|
||||||
wprintw(window, " /myid : Print your ID\n");
|
|
||||||
wprintw(window, " /help : Print this message again\n");
|
|
||||||
wprintw(window, " /clear : Clear the window\n");
|
|
||||||
wprintw(window, " /quit or /exit : Exit Toxic\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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
exit_toxic(m);
|
exit_toxic_success(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
uint8_t *msg = NULL;
|
char *msg = NULL;
|
||||||
|
char *errmsg;
|
||||||
|
|
||||||
if (argc >= 2) {
|
if (argc >= 2) {
|
||||||
msg = argv[2];
|
msg = argv[2];
|
||||||
|
|
||||||
if (msg[0] != '\"') {
|
if (msg[0] != '\"') {
|
||||||
wprintw(window, "Note must be enclosed in quotes.\n");
|
errmsg = "Note must be enclosed in quotes.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (argc != 1) {
|
} else if (argc != 1) {
|
||||||
wprintw(window, "Wrong number of arguments.\n");
|
errmsg = "Wrong number of arguments.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *status = argv[1];
|
char *status = argv[1];
|
||||||
int len = strlen(status);
|
str_to_lower(status);
|
||||||
char l_status[len+1];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i <= len; ++i)
|
|
||||||
l_status[i] = tolower(status[i]);
|
|
||||||
|
|
||||||
TOX_USERSTATUS status_kind;
|
TOX_USERSTATUS status_kind;
|
||||||
|
|
||||||
if (!strcmp(l_status, "online"))
|
if (!strcmp(status, "online"))
|
||||||
status_kind = TOX_USERSTATUS_NONE;
|
status_kind = TOX_USERSTATUS_NONE;
|
||||||
else if (!strcmp(l_status, "away"))
|
else if (!strcmp(status, "away"))
|
||||||
status_kind = TOX_USERSTATUS_AWAY;
|
status_kind = TOX_USERSTATUS_AWAY;
|
||||||
else if (!strcmp(l_status, "busy"))
|
else if (!strcmp(status, "busy"))
|
||||||
status_kind = TOX_USERSTATUS_BUSY;
|
status_kind = TOX_USERSTATUS_BUSY;
|
||||||
else {
|
else {
|
||||||
wprintw(window, "Invalid status. Valid statuses are: online, busy and away.\n");
|
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,9 +433,10 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
prompt_update_status(prompt, status_kind);
|
prompt_update_status(prompt, status_kind);
|
||||||
|
|
||||||
if (msg != NULL) {
|
if (msg != NULL) {
|
||||||
msg[strlen(++msg)-1] = L'\0'; /* remove opening and closing quotes */
|
++msg;
|
||||||
uint16_t len = strlen(msg) + 1;
|
int len = strlen(msg) - 1;
|
||||||
tox_set_status_message(m, msg, len);
|
msg[len] = '\0'; /* remove opening and closing quotes */
|
||||||
prompt_update_statusmessage(prompt, msg, len);
|
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
||||||
|
prompt_update_statusmessage(prompt, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _global_commands_h
|
||||||
|
#define _global_commands_h
|
||||||
|
|
||||||
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
@ -32,3 +38,12 @@ void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
|
|||||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_quit(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_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
|
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#endif /* #define _global_commands_h */
|
||||||
|
497
src/groupchat.c
497
src/groupchat.c
@ -20,33 +20,43 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifndef _GNU_SOURCE
|
||||||
#include "config.h"
|
#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern int store_data(Tox *m, char *path);
|
|
||||||
|
|
||||||
static GroupChat groupchats[MAX_WINDOWS_NUM];
|
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
||||||
static int max_groupchat_index = 0;
|
static int max_groupchat_index = 0;
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
/* temporary until group chats have unique commands */
|
/* temporary until group chats have unique commands */
|
||||||
extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
extern const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
||||||
{
|
{
|
||||||
|
if (groupnum > MAX_GROUPCHAT_NUM)
|
||||||
|
return -1;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i <= max_groupchat_index; ++i) {
|
for (i = 0; i <= max_groupchat_index; ++i) {
|
||||||
@ -56,9 +66,11 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
|||||||
groupchats[i].num_peers = 0;
|
groupchats[i].num_peers = 0;
|
||||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||||
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||||
|
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
|
||||||
|
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
||||||
|
|
||||||
/* temp fix */
|
|
||||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||||
|
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
||||||
|
|
||||||
set_active_window(groupchats[i].chatwin);
|
set_active_window(groupchats[i].chatwin);
|
||||||
|
|
||||||
@ -77,25 +89,31 @@ void kill_groupchat_window(ToxWindow *self)
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
log_disable(ctx->log);
|
log_disable(ctx->log);
|
||||||
|
line_info_cleanup(ctx->hst);
|
||||||
delwin(ctx->linewin);
|
delwin(ctx->linewin);
|
||||||
del_window(self);
|
delwin(ctx->history);
|
||||||
|
delwin(ctx->sidebar);
|
||||||
free(ctx->log);
|
free(ctx->log);
|
||||||
|
free(ctx->hst);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
|
free(self->help);
|
||||||
|
del_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||||
{
|
{
|
||||||
set_active_window(0);
|
|
||||||
tox_del_groupchat(m, groupnum);
|
tox_del_groupchat(m, groupnum);
|
||||||
|
|
||||||
free(groupchats[groupnum].peer_names);
|
free(groupchats[groupnum].peer_names);
|
||||||
free(groupchats[groupnum].oldpeer_names);
|
free(groupchats[groupnum].oldpeer_names);
|
||||||
|
free(groupchats[groupnum].peer_name_lengths);
|
||||||
|
free(groupchats[groupnum].oldpeer_name_lengths);
|
||||||
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = max_groupchat_index; i > 0; --i) {
|
for (i = max_groupchat_index; i > 0; --i) {
|
||||||
if (groupchats[i-1].active)
|
if (groupchats[i - 1].active)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,49 +121,30 @@ static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
|||||||
kill_groupchat_window(self);
|
kill_groupchat_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, " /log <on> or <off> : Enable/disable logging\n");
|
|
||||||
wprintw(ctx->history, " /close : Close the current group chat\n");
|
|
||||||
wprintw(ctx->history, " /help : Print this message again\n");
|
|
||||||
wprintw(ctx->history, " /help global : Show a list of global commands\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,
|
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||||
uint8_t *msg, uint16_t len)
|
const char *msg, uint16_t len)
|
||||||
{
|
{
|
||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_group_peername(m, groupnum, peernum, nick);
|
int n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
|
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1); /* enforce client max name length */
|
||||||
|
nick[n_len] = '\0';
|
||||||
|
|
||||||
/* check if message contains own name and alert appropriately */
|
/* check if message contains own name and alert appropriately */
|
||||||
int alert_type = WINDOW_ALERT_1;
|
int alert_type = WINDOW_ALERT_1;
|
||||||
bool beep = false;
|
bool beep = false;
|
||||||
|
|
||||||
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
|
uint16_t sn_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
||||||
|
selfnick[sn_len] = '\0';
|
||||||
|
|
||||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||||
|
|
||||||
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH);
|
bool nick_match = strcasestr(msg, selfnick) && strncmp(selfnick, nick, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
|
||||||
if (nick_match) {
|
if (nick_match) {
|
||||||
alert_type = WINDOW_ALERT_0;
|
alert_type = WINDOW_ALERT_0;
|
||||||
@ -155,23 +154,14 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
|||||||
|
|
||||||
alert_window(self, alert_type, beep);
|
alert_window(self, alert_type, beep);
|
||||||
|
|
||||||
print_time(ctx->history);
|
char timefrmt[TIME_STR_SIZE];
|
||||||
wattron(ctx->history, COLOR_PAIR(nick_clr));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
wprintw(ctx->history, "%s: ", nick);
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(nick_clr));
|
|
||||||
|
|
||||||
if (msg[0] == '>') {
|
line_info_add(self, timefrmt, nick, NULL, msg, IN_MSG, 0, nick_clr);
|
||||||
wattron(ctx->history, COLOR_PAIR(GREEN));
|
write_to_log(msg, nick, ctx->log, false);
|
||||||
wprintw(ctx->history, "%s\n", msg);
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
|
||||||
} else {
|
|
||||||
wprintw(ctx->history, "%s\n", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, ctx->log, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
|
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
|
||||||
uint16_t len)
|
uint16_t len)
|
||||||
{
|
{
|
||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
@ -183,8 +173,9 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
|||||||
int alert_type = WINDOW_ALERT_1;
|
int alert_type = WINDOW_ALERT_1;
|
||||||
bool beep = false;
|
bool beep = false;
|
||||||
|
|
||||||
uint8_t selfnick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_self_name(m, selfnick, TOX_MAX_NAME_LENGTH);
|
uint16_t n_len = tox_get_self_name(m, (uint8_t *) selfnick);
|
||||||
|
selfnick[n_len] = '\0';
|
||||||
|
|
||||||
bool nick_match = strcasestr(action, selfnick);
|
bool nick_match = strcasestr(action, selfnick);
|
||||||
|
|
||||||
@ -195,48 +186,60 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
|||||||
|
|
||||||
alert_window(self, alert_type, beep);
|
alert_window(self, alert_type, beep);
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_group_peername(m, groupnum, peernum, nick);
|
n_len = tox_group_peername(m, groupnum, peernum, (uint8_t *) nick);
|
||||||
nick[TOXIC_MAX_NAME_LENGTH] = '\0'; /* enforce client max name length */
|
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
nick[n_len] = '\0';
|
||||||
|
|
||||||
print_time(ctx->history);
|
char timefrmt[TIME_STR_SIZE];
|
||||||
wattron(ctx->history, COLOR_PAIR(YELLOW));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
wprintw(ctx->history, "* %s %s\n", nick, action);
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
|
||||||
|
|
||||||
add_to_log_buf(action, nick, ctx->log, true);
|
line_info_add(self, timefrmt, nick, NULL, action, ACTION, 0, 0);
|
||||||
|
write_to_log(action, nick, ctx->log, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Puts two copies of peerlist in chat instance */
|
/* Puts two copies of peerlist/lengths in chat instance */
|
||||||
static void copy_peernames(int gnum, int npeers, uint8_t tmp_peerlist[][TOX_MAX_NAME_LENGTH])
|
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
|
||||||
{
|
{
|
||||||
/* Assumes these are initiated in init_groupchat_win */
|
/* Assumes these are initiated in init_groupchat_win */
|
||||||
free(groupchats[gnum].peer_names);
|
free(groupchats[gnum].peer_names);
|
||||||
free(groupchats[gnum].oldpeer_names);
|
free(groupchats[gnum].oldpeer_names);
|
||||||
|
free(groupchats[gnum].peer_name_lengths);
|
||||||
|
free(groupchats[gnum].oldpeer_name_lengths);
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
int N = TOX_MAX_NAME_LENGTH;
|
||||||
|
|
||||||
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
|
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
|
||||||
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
|
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
|
||||||
|
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
||||||
|
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
||||||
|
|
||||||
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL) {
|
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|
||||||
endwin();
|
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16_t unknown_len = (uint16_t) strlen(UNKNOWN_NAME);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < npeers; ++i) {
|
for (i = 0; i < npeers; ++i) {
|
||||||
if (string_is_empty(tmp_peerlist[i])) {
|
if (string_is_empty((char *) peerlist[i])) {
|
||||||
memcpy(&groupchats[gnum].peer_names[i*N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||||
|
groupchats[gnum].peer_name_lengths[i] = unknown_len;
|
||||||
} else {
|
} else {
|
||||||
memcpy(&groupchats[gnum].peer_names[i*N], tmp_peerlist[i], N);
|
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], N);
|
||||||
groupchats[gnum].peer_names[i*N+TOXIC_MAX_NAME_LENGTH] = '\0';
|
uint16_t n_len = lengths[i];
|
||||||
|
|
||||||
|
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
|
||||||
|
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
||||||
|
groupchats[gnum].peer_name_lengths[i] = n_len;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N*npeers);
|
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
||||||
|
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths,
|
||||||
|
sizeof(uint16_t) * npeers);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||||
@ -245,220 +248,134 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
if (self->num != groupnum)
|
if (self->num != groupnum)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (groupnum > max_groupchat_index)
|
||||||
|
return;
|
||||||
|
|
||||||
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
||||||
int num_peers = groupchats[groupnum].num_peers;
|
int num_peers = groupchats[groupnum].num_peers;
|
||||||
|
|
||||||
|
if (peernum > num_peers)
|
||||||
|
return;
|
||||||
|
|
||||||
/* get old peer name before updating name list */
|
/* get old peer name before updating name list */
|
||||||
uint8_t oldpeername[TOX_MAX_NAME_LENGTH] = {0};
|
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
|
||||||
|
|
||||||
if (change != TOX_CHAT_CHANGE_PEER_ADD)
|
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
|
||||||
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum*TOX_MAX_NAME_LENGTH],
|
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
|
||||||
sizeof(oldpeername));
|
sizeof(oldpeername));
|
||||||
|
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
|
||||||
|
oldpeername[old_n_len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
/* Update name lists */
|
/* Update name/len lists */
|
||||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
||||||
tox_group_get_names(m, groupnum, tmp_peerlist, num_peers);
|
uint16_t tmp_peerlens[num_peers];
|
||||||
copy_peernames(groupnum, num_peers, tmp_peerlist);
|
tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||||
|
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||||
|
|
||||||
/* get current peername then sort namelist */
|
/* get current peername then sort namelist */
|
||||||
uint8_t peername[TOX_MAX_NAME_LENGTH] = {0};
|
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
||||||
memcpy(peername, &groupchats[groupnum].peer_names[peernum*TOX_MAX_NAME_LENGTH], sizeof(peername));
|
|
||||||
|
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
|
||||||
|
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
|
||||||
|
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
|
||||||
|
peername[n_len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
print_time(ctx->history);
|
|
||||||
|
|
||||||
uint8_t *event;
|
char *event;
|
||||||
|
char timefrmt[TIME_STR_SIZE];
|
||||||
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
switch (change) {
|
switch (change) {
|
||||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
case TOX_CHAT_CHANGE_PEER_ADD:
|
||||||
event = "has joined the room";
|
event = "has joined the room";
|
||||||
|
line_info_add(self, timefrmt, (char *) peername, NULL, event, CONNECTION, 0, GREEN);
|
||||||
wattron(ctx->history, COLOR_PAIR(GREEN));
|
write_to_log(event, (char *) peername, ctx->log, true);
|
||||||
wattron(ctx->history, A_BOLD);
|
|
||||||
wprintw(ctx->history, "* %s", peername);
|
|
||||||
wattroff(ctx->history, A_BOLD);
|
|
||||||
wprintw(ctx->history, " %s\n", event);
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
|
||||||
|
|
||||||
add_to_log_buf(event, peername, ctx->log, true);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
case TOX_CHAT_CHANGE_PEER_DEL:
|
||||||
event = "has left the room";
|
event = "has left the room";
|
||||||
|
line_info_add(self, timefrmt, (char *) oldpeername, NULL, event, CONNECTION, 0, 0);
|
||||||
wattron(ctx->history, A_BOLD);
|
|
||||||
wprintw(ctx->history, "* %s", oldpeername);
|
|
||||||
wattroff(ctx->history, A_BOLD);
|
|
||||||
wprintw(ctx->history, " %s\n", event);
|
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos > 0)
|
if (groupchats[self->num].side_pos > 0)
|
||||||
--groupchats[self->num].side_pos;
|
--groupchats[self->num].side_pos;
|
||||||
|
|
||||||
add_to_log_buf(event, oldpeername, ctx->log, true);
|
write_to_log(event, (char *) oldpeername, ctx->log, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||||
wattron(ctx->history, COLOR_PAIR(MAGENTA));
|
event = " is now known as ";
|
||||||
wattron(ctx->history, A_BOLD);
|
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, event, NAME_CHANGE, 0, 0);
|
||||||
wprintw(ctx->history, "* %s", oldpeername);
|
|
||||||
wattroff(ctx->history, A_BOLD);
|
|
||||||
|
|
||||||
wprintw(ctx->history, " is now known as ");
|
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
||||||
|
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
|
||||||
wattron(ctx->history, A_BOLD);
|
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
|
||||||
wprintw(ctx->history, "%s\n", peername);
|
|
||||||
wattroff(ctx->history, A_BOLD);
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
|
|
||||||
|
|
||||||
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
|
|
||||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
|
|
||||||
add_to_log_buf(tmp_event, oldpeername, ctx->log, true);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
alert_window(self, WINDOW_ALERT_2, false);
|
alert_window(self, WINDOW_ALERT_2, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||||
|
{
|
||||||
if (action == NULL) {
|
if (action == NULL) {
|
||||||
wprintw(ctx->history, "Invalid syntax.\n");
|
wprintw(ctx->history, "Invalid syntax.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
|
||||||
// tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
char *errmsg = " * Failed to send action.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
// 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)
|
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
int x, y, y2, x2;
|
int x, y, y2, x2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
int cur_len = 0;
|
|
||||||
|
|
||||||
if (key == 0x107 || key == 0x8 || key == 0x7f) { /* BACKSPACE key: Remove character behind pos */
|
if (x2 <= 0)
|
||||||
if (ctx->pos > 0) {
|
return;
|
||||||
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos - 1]));
|
|
||||||
del_char_buf_bck(ctx->line, &ctx->pos, &ctx->len);
|
|
||||||
|
|
||||||
if (x == 0)
|
if (self->help->active) {
|
||||||
wmove(self->window, y-1, x2 - cur_len);
|
help_onKey(self, key);
|
||||||
else
|
return;
|
||||||
wmove(self->window, y, x - cur_len);
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
|
if (ltr) { /* char is printable */
|
||||||
if (ctx->pos != ctx->len)
|
input_new_char(self, key, x, y, x2, y2);
|
||||||
del_char_buf_frnt(ctx->line, &ctx->pos, &ctx->len);
|
return;
|
||||||
else
|
|
||||||
beep();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
|
if (line_info_onKey(self, key))
|
||||||
if (ctx->pos > 0) {
|
return;
|
||||||
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 (input_handle(self, key, x, y, x2, y2))
|
||||||
if (ctx->pos != ctx->len)
|
return;
|
||||||
kill_buf(ctx->line, &ctx->pos, &ctx->len);
|
|
||||||
else
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
|
if (key == '\t') { /* TAB key: auto-completes peer name or command */
|
||||||
if (ctx->pos > 0) {
|
|
||||||
ctx->pos = 0;
|
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
|
|
||||||
if (ctx->pos != ctx->len) {
|
|
||||||
ctx->pos = ctx->len;
|
|
||||||
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_LEFT) {
|
|
||||||
if (ctx->pos > 0) {
|
|
||||||
--ctx->pos;
|
|
||||||
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
|
|
||||||
|
|
||||||
if (x == 0)
|
|
||||||
wmove(self->window, y-1, x2 - cur_len);
|
|
||||||
else
|
|
||||||
wmove(self->window, y, x - cur_len);
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_RIGHT) {
|
|
||||||
if (ctx->pos < ctx->len) {
|
|
||||||
cur_len = MAX(1, wcwidth(ctx->line[ctx->pos]));
|
|
||||||
++ctx->pos;
|
|
||||||
|
|
||||||
if (x == x2-1)
|
|
||||||
wmove(self->window, y+1, 0);
|
|
||||||
else
|
|
||||||
wmove(self->window, y, x + cur_len);
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_UP) { /* fetches previous item in history */
|
|
||||||
fetch_hist_item(ctx->line, &ctx->pos, &ctx->len, ctx->ln_history, ctx->hst_tot,
|
|
||||||
&ctx->hst_pos, 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) {
|
if (ctx->len > 0) {
|
||||||
int diff;
|
int diff;
|
||||||
|
|
||||||
if ((ctx->line[0] != '/') || (ctx->line[1] == 'm' && ctx->line[2] == 'e'))
|
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,
|
diff = complete_line(ctx, groupchats[self->num].peer_names,
|
||||||
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
|
groupchats[self->num].num_peers, TOX_MAX_NAME_LENGTH);
|
||||||
else
|
else
|
||||||
diff = complete_line(ctx->line, &ctx->pos, &ctx->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
|
diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||||
MAX_CMDNAME_SIZE);
|
|
||||||
|
|
||||||
if (diff != -1) {
|
if (diff != -1) {
|
||||||
if (x + diff > x2 - 1) {
|
if (x + diff > x2 - 1) {
|
||||||
int ofst = (x + diff - 1) - (x2 - 1);
|
wmove(self->window, y, x + diff);
|
||||||
wmove(self->window, y+1, ofst);
|
ctx->start += diff;
|
||||||
} else {
|
} else {
|
||||||
wmove(self->window, y, x+diff);
|
wmove(self->window, y, x + diff);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
beep();
|
beep();
|
||||||
@ -466,104 +383,67 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
} else {
|
} else {
|
||||||
beep();
|
beep();
|
||||||
}
|
}
|
||||||
}
|
} else if (key == T_KEY_C_RB) { /* Scroll peerlist up and down one position */
|
||||||
|
|
||||||
/* Scroll peerlist up and down one position if list overflows window */
|
|
||||||
else if (key == KEY_NPAGE) {
|
|
||||||
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||||
|
|
||||||
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
||||||
++groupchats[self->num].side_pos;
|
++groupchats[self->num].side_pos;
|
||||||
}
|
} else if (key == T_KEY_C_LB) {
|
||||||
|
|
||||||
else if (key == KEY_PPAGE) {
|
|
||||||
if (groupchats[self->num].side_pos > 0)
|
if (groupchats[self->num].side_pos > 0)
|
||||||
--groupchats[self->num].side_pos;
|
--groupchats[self->num].side_pos;
|
||||||
}
|
} else if (key == '\n') {
|
||||||
|
rm_trailing_spaces_buf(ctx);
|
||||||
|
|
||||||
else
|
char line[MAX_STR_SIZE];
|
||||||
#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)
|
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||||
memset(&line, 0, sizeof(line));
|
memset(&line, 0, sizeof(line));
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
|
||||||
wclrtobot(self->window);
|
|
||||||
|
|
||||||
if (!string_is_empty(line))
|
if (!string_is_empty(line))
|
||||||
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
|
add_line_to_hist(ctx);
|
||||||
|
|
||||||
if (line[0] == '/') {
|
if (line[0] == '/') {
|
||||||
if (strcmp(line, "/close") == 0) {
|
if (strcmp(line, "/close") == 0) {
|
||||||
close_groupchat(self, m, self->num);
|
close_groupchat(self, m, self->num);
|
||||||
return;
|
return;
|
||||||
} else if (strcmp(line, "/help") == 0) {
|
|
||||||
if (strcmp(line, "help global") == 0)
|
|
||||||
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
|
|
||||||
else
|
|
||||||
print_groupchat_help(ctx);
|
|
||||||
|
|
||||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
||||||
send_group_action(self, ctx, m, line + strlen("/me "));
|
send_group_action(self, ctx, m, line + strlen("/me "));
|
||||||
} else {
|
} else {
|
||||||
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
||||||
}
|
}
|
||||||
} else if (!string_is_empty(line)) {
|
} else if (!string_is_empty(line)) {
|
||||||
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
|
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
char *errmsg = " * Failed to send message.";
|
||||||
wprintw(ctx->history, " * Failed to send message.\n");
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, RED);
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_buf(ctx->line, &ctx->pos, &ctx->len);
|
wclear(ctx->linewin);
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
reset_buf(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
curs_set(1);
|
|
||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
line_info_print(self);
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
if (ctx->len > 0) {
|
curs_set(1);
|
||||||
uint8_t line[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
if (ctx->len > 0)
|
||||||
reset_buf(ctx->line, &ctx->pos, &ctx->len);
|
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
|
||||||
} else {
|
|
||||||
mvwprintw(ctx->linewin, 1, 0, "%s", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wclear(ctx->sidebar);
|
wclear(ctx->sidebar);
|
||||||
mvwhline(ctx->linewin, 0, 0, ACS_HLINE, x2);
|
|
||||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2-CHATBOX_HEIGHT);
|
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||||
mvwaddch(ctx->sidebar, y2-CHATBOX_HEIGHT, 0, ACS_BTEE);
|
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||||
|
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||||
|
|
||||||
int num_peers = groupchats[self->num].num_peers;
|
int num_peers = groupchats[self->num].num_peers;
|
||||||
|
|
||||||
@ -573,50 +453,62 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattroff(ctx->sidebar, A_BOLD);
|
wattroff(ctx->sidebar, A_BOLD);
|
||||||
|
|
||||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH-1);
|
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||||
|
|
||||||
int N = TOX_MAX_NAME_LENGTH;
|
int N = TOX_MAX_NAME_LENGTH;
|
||||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||||
wmove(ctx->sidebar, i+2, 1);
|
wmove(ctx->sidebar, i + 2, 1);
|
||||||
int peer = i + groupchats[self->num].side_pos;
|
int peer = i + groupchats[self->num].side_pos;
|
||||||
|
|
||||||
/* truncate nick to fit in side panel without modifying list */
|
/* truncate nick to fit in side panel without modifying list */
|
||||||
uint8_t tmpnck[TOX_MAX_NAME_LENGTH];
|
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer*N], SIDEBAR_WIDTH-2);
|
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * N], SIDEBAR_WIDTH - 2);
|
||||||
tmpnck[SIDEBAR_WIDTH-2] = '\0';
|
tmpnck[SIDEBAR_WIDTH - 2] = '\0';
|
||||||
|
|
||||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int y, x;
|
||||||
|
getyx(self->window, y, x);
|
||||||
|
(void) x;
|
||||||
|
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
||||||
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
|
wrefresh(self->window);
|
||||||
|
|
||||||
|
if (self->help->active)
|
||||||
|
help_onDraw(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
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);
|
|
||||||
|
|
||||||
ctx->log = malloc(sizeof(struct chatlog));
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
|
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
|
|
||||||
if (ctx->log == NULL) {
|
ctx->hst = calloc(1, sizeof(struct history));
|
||||||
endwin();
|
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(ctx->log, 0, sizeof(struct chatlog));
|
if (ctx->log == NULL || ctx->hst == NULL)
|
||||||
|
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
|
if (user_settings->autolog == AUTOLOG_ON)
|
||||||
|
log_enable(self->name, NULL, ctx->log);
|
||||||
|
|
||||||
print_groupchat_help(ctx);
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||||
|
|
||||||
wmove(self->window, y-CURS_Y_OFFSET, 0);
|
scrollok(ctx->history, 0);
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum)
|
ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||||
@ -634,17 +526,16 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
|||||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||||
ret.onGroupAction = &groupchat_onGroupAction;
|
ret.onGroupAction = &groupchat_onGroupAction;
|
||||||
|
|
||||||
snprintf(ret.name, sizeof(ret.name), "Room #%d", groupnum);
|
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
||||||
|
|
||||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||||
|
Help *help = calloc(1, sizeof(Help));
|
||||||
|
|
||||||
|
if (chatwin == NULL || help == NULL)
|
||||||
|
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
|
||||||
|
|
||||||
if (chatwin != NULL)
|
|
||||||
ret.chatwin = chatwin;
|
ret.chatwin = chatwin;
|
||||||
else {
|
ret.help = help;
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "calloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ret.num = groupnum;
|
ret.num = groupnum;
|
||||||
|
|
||||||
|
@ -20,8 +20,15 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _groupchat_h
|
||||||
|
#define _groupchat_h
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
#define SIDEBAR_WIDTH 16
|
#define SIDEBAR_WIDTH 16
|
||||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||||
|
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int chatwin;
|
int chatwin;
|
||||||
@ -30,8 +37,12 @@ typedef struct {
|
|||||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||||
uint8_t *peer_names;
|
uint8_t *peer_names;
|
||||||
uint8_t *oldpeer_names;
|
uint8_t *oldpeer_names;
|
||||||
|
uint16_t *peer_name_lengths;
|
||||||
|
uint16_t *oldpeer_name_lengths;
|
||||||
} GroupChat;
|
} GroupChat;
|
||||||
|
|
||||||
void kill_groupchat_window(ToxWindow *self);
|
void kill_groupchat_window(ToxWindow *self);
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
|
||||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
ToxWindow new_group_chat(Tox *m, int groupnum);
|
||||||
|
|
||||||
|
#endif /* #define _groupchat_h */
|
||||||
|
282
src/help.c
Normal file
282
src/help.c
Normal file
@ -0,0 +1,282 @@
|
|||||||
|
/* help.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "help.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
|
#define HELP_MENU_HEIGHT 7
|
||||||
|
#define HELP_MENU_WIDTH 26
|
||||||
|
|
||||||
|
void help_init_menu(ToxWindow *self)
|
||||||
|
{
|
||||||
|
if (self->help->win)
|
||||||
|
delwin(self->help->win);
|
||||||
|
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
|
||||||
|
self->help->active = true;
|
||||||
|
self->help->type = HELP_MENU;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_exit(ToxWindow *self)
|
||||||
|
{
|
||||||
|
delwin(self->help->win);
|
||||||
|
memset(self->help, 0, sizeof(Help));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_init_window(ToxWindow *self, int height, int width)
|
||||||
|
{
|
||||||
|
if (self->help->win)
|
||||||
|
delwin(self->help->win);
|
||||||
|
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(stdscr, y2, x2);
|
||||||
|
|
||||||
|
height = MIN(height, y2);
|
||||||
|
width = MIN(width, x2);
|
||||||
|
|
||||||
|
self->help->win = newwin(height, width, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_menu(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, " Help Menu\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " G");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "lobal commands\n");
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " C");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "hat commands\n");
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " K");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "ey bindings\n");
|
||||||
|
|
||||||
|
wprintw(win, " E");
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "x");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "it menu\n");
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_bottom_menu(WINDOW *win)
|
||||||
|
{
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(win, y2, x2);
|
||||||
|
(void) x2;
|
||||||
|
|
||||||
|
wmove(win, y2 - 2, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, " M");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "ain menu |");
|
||||||
|
|
||||||
|
wprintw(win, " E");
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "x");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "it");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_global(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Global Commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " /add <id> <msg> : Add contact with optional message\n");
|
||||||
|
wprintw(win, " /accept <n> : Accept friend request\n");
|
||||||
|
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||||
|
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
||||||
|
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||||
|
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||||
|
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||||
|
wprintw(win, " /groupchat : Create a group chat\n");
|
||||||
|
wprintw(win, " /myid : Print your ID\n");
|
||||||
|
wprintw(win, " /clear : Clear window history\n");
|
||||||
|
wprintw(win, " /close : Close the current chat window\n");
|
||||||
|
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Audio:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, " /lsdev <type> : List devices where type: in|out\n");
|
||||||
|
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_chat(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Chat Commands:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
||||||
|
wprintw(win, " /join : Join a pending group chat\n");
|
||||||
|
wprintw(win, " /sendfile <path> : Send a file\n");
|
||||||
|
wprintw(win, " /savefile <n> : Receive a file\n");
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Audio:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, " /call : Audio call\n");
|
||||||
|
wprintw(win, " /cancel : Cancel call\n");
|
||||||
|
wprintw(win, " /answer : Answer incomming call\n");
|
||||||
|
wprintw(win, " /reject : Reject incoming call\n");
|
||||||
|
wprintw(win, " /hangup : Hangup active call\n");
|
||||||
|
wprintw(win, " /sdev <type> <id> : Change active device\n");
|
||||||
|
wprintw(win, " /mute <type> : Mute active device if in call\n");
|
||||||
|
wprintw(win, " /sense <n> : VAD sensitivity treshold\n");
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void help_draw_keys(ToxWindow *self)
|
||||||
|
{
|
||||||
|
WINDOW *win = self->help->win;
|
||||||
|
|
||||||
|
wmove(win, 1, 1);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
wprintw(win, "Key bindings:\n");
|
||||||
|
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
wprintw(win, " Ctrl+O and Ctrl+P : Navigate through the tabs\n");
|
||||||
|
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
||||||
|
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
|
||||||
|
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
|
||||||
|
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
|
||||||
|
|
||||||
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
|
wrefresh(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void help_onKey(ToxWindow *self, wint_t key)
|
||||||
|
{
|
||||||
|
switch(key) {
|
||||||
|
case 'x':
|
||||||
|
case T_KEY_ESC:
|
||||||
|
help_exit(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
help_init_window(self, 19, 80);
|
||||||
|
#else
|
||||||
|
help_init_window(self, 9, 80);
|
||||||
|
#endif
|
||||||
|
self->help->type = HELP_CHAT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
help_init_window(self, 21, 80);
|
||||||
|
#else
|
||||||
|
help_init_window(self, 17, 80);
|
||||||
|
#endif
|
||||||
|
self->help->type = HELP_GLOBAL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'k':
|
||||||
|
help_init_window(self, 10, 80);
|
||||||
|
self->help->type = HELP_KEYS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
help_init_menu(self);
|
||||||
|
self->help->type = HELP_MENU;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void help_onDraw(ToxWindow *self)
|
||||||
|
{
|
||||||
|
curs_set(0);
|
||||||
|
|
||||||
|
switch(self->help->type) {
|
||||||
|
case HELP_MENU:
|
||||||
|
help_draw_menu(self);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case HELP_CHAT:
|
||||||
|
help_draw_chat(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HELP_GLOBAL:
|
||||||
|
help_draw_global(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HELP_KEYS:
|
||||||
|
help_draw_keys(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HELP_GROUP:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
41
src/help.h
Normal file
41
src/help.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* help.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _help_h
|
||||||
|
#define _help_h
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
HELP_MENU,
|
||||||
|
HELP_GLOBAL,
|
||||||
|
HELP_CHAT,
|
||||||
|
HELP_GROUP,
|
||||||
|
HELP_KEYS,
|
||||||
|
} HELP_TYPES;
|
||||||
|
|
||||||
|
void help_onDraw(ToxWindow *self);
|
||||||
|
void help_init_menu(ToxWindow *self);
|
||||||
|
void help_onKey(ToxWindow *self, wint_t key);
|
||||||
|
|
||||||
|
#endif /* #define _help_h */
|
248
src/input.c
Normal file
248
src/input.c
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/* input.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE /* needed for wcwidth() */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "toxic_strings.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
|
/* add a char to input field and buffer */
|
||||||
|
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
int cur_len = wcwidth(key);
|
||||||
|
|
||||||
|
/* this is the only place we need to do this check */
|
||||||
|
if (cur_len == -1) {
|
||||||
|
beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add_char_to_buf(ctx, key) == -1) {
|
||||||
|
beep();
|
||||||
|
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) {
|
||||||
|
beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
||||||
|
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||||
|
|
||||||
|
if (ctx->start && (x >= mx_x - cur_len))
|
||||||
|
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||||
|
else if (ctx->start && (ctx->pos == ctx->len))
|
||||||
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
else if (ctx->start)
|
||||||
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deletes entire line before cursor from input field and buffer */
|
||||||
|
static void input_discard(ToxWindow *self)
|
||||||
|
{
|
||||||
|
if (discard_buf(self->chatwin) == -1)
|
||||||
|
beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deletes entire line after cursor from input field and buffer */
|
||||||
|
static void input_kill(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (kill_buf(ctx) == -1)
|
||||||
|
beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (yank_buf(ctx) == -1) {
|
||||||
|
beep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int yank_cols = MAX(0, wcswidth(ctx->yank, ctx->yank_len));
|
||||||
|
|
||||||
|
if (x + yank_cols >= mx_x) {
|
||||||
|
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
||||||
|
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
|
||||||
|
ctx->start += s_len + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves cursor/line position to end of line in input field and buffer */
|
||||||
|
static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
ctx->pos = ctx->len;
|
||||||
|
|
||||||
|
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
||||||
|
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves cursor/line position to start of line in input field and buffer */
|
||||||
|
static void input_mv_home(ToxWindow *self)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ctx->pos = 0;
|
||||||
|
ctx->start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves cursor/line position left in input field and buffer */
|
||||||
|
static void input_mv_left(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
||||||
|
|
||||||
|
--ctx->pos;
|
||||||
|
|
||||||
|
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||||
|
|
||||||
|
if (ctx->start && (x >= mx_x - cur_len))
|
||||||
|
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||||
|
else if (ctx->start && (ctx->pos == ctx->len))
|
||||||
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
else if (ctx->start)
|
||||||
|
ctx->start = MAX(0, ctx->start - cur_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves cursor/line position right in input field and buffer */
|
||||||
|
static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx->pos >= ctx->len)
|
||||||
|
return;
|
||||||
|
|
||||||
|
++ctx->pos;
|
||||||
|
|
||||||
|
int cur_len = wcwidth(ctx->line[ctx->pos - 1]);
|
||||||
|
|
||||||
|
if (x + cur_len >= mx_x) {
|
||||||
|
int s_len = wcwidth(ctx->line[ctx->start]);
|
||||||
|
ctx->start += 1 + MAX(0, cur_len - s_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* puts a line history item in input field and buffer */
|
||||||
|
static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
fetch_hist_item(ctx, key);
|
||||||
|
ctx->start = mx_x * (ctx->len / mx_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||||
|
return true if key matches a function, false otherwise */
|
||||||
|
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||||
|
{
|
||||||
|
bool match = true;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 0x7f:
|
||||||
|
case KEY_BACKSPACE:
|
||||||
|
input_backspace(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_DC:
|
||||||
|
input_delete(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_KEY_DISCARD:
|
||||||
|
input_discard(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_KEY_KILL:
|
||||||
|
input_kill(self->chatwin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_KEY_C_Y:
|
||||||
|
input_yank(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_HOME:
|
||||||
|
case T_KEY_C_A:
|
||||||
|
input_mv_home(self);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_END:
|
||||||
|
case T_KEY_C_E:
|
||||||
|
input_mv_end(self, y, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_LEFT:
|
||||||
|
input_mv_left(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_RIGHT:
|
||||||
|
input_mv_right(self, x, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_UP:
|
||||||
|
case KEY_DOWN:
|
||||||
|
input_history(self, key, mx_x);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
33
src/input.h
Normal file
33
src/input.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* input.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _input_h
|
||||||
|
#define _input_h
|
||||||
|
|
||||||
|
/* add a char to input field and buffer for given chatcontext */
|
||||||
|
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||||
|
|
||||||
|
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||||
|
return true if key matches a function, false otherwise */
|
||||||
|
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||||
|
|
||||||
|
#endif /* #define _input_h */
|
486
src/line_info.c
Normal file
486
src/line_info.c
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
/* line_info.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "groupchat.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
void line_info_init(struct history *hst)
|
||||||
|
{
|
||||||
|
hst->line_root = calloc(1, sizeof(struct line_info));
|
||||||
|
|
||||||
|
if (hst->line_root == NULL)
|
||||||
|
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
hst->line_start = hst->line_root;
|
||||||
|
hst->line_end = hst->line_start;
|
||||||
|
hst->queue_sz = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resets line_start (page end) */
|
||||||
|
static void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||||
|
{
|
||||||
|
struct line_info *line = hst->line_end;
|
||||||
|
|
||||||
|
if (line->prev == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
int side_offst = self->is_groupchat ? SIDEBAR_WIDTH : 0;
|
||||||
|
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
||||||
|
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
||||||
|
|
||||||
|
int curlines = 0;
|
||||||
|
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||||
|
|
||||||
|
do {
|
||||||
|
curlines += 1 + nxtlines;
|
||||||
|
line = line->prev;
|
||||||
|
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||||
|
} while (line->prev && curlines + nxtlines < max_y);
|
||||||
|
|
||||||
|
hst->line_start = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_info_cleanup(struct history *hst)
|
||||||
|
{
|
||||||
|
struct line_info *tmp1 = hst->line_root;
|
||||||
|
|
||||||
|
while (tmp1) {
|
||||||
|
struct line_info *tmp2 = tmp1->next;
|
||||||
|
free(tmp1);
|
||||||
|
tmp1 = tmp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < hst->queue_sz; ++i) {
|
||||||
|
if (hst->queue[i])
|
||||||
|
free(hst->queue[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* moves root forward and frees previous root */
|
||||||
|
static void line_info_root_fwd(struct history *hst)
|
||||||
|
{
|
||||||
|
struct line_info *tmp = hst->line_root->next;
|
||||||
|
tmp->prev = NULL;
|
||||||
|
|
||||||
|
if (hst->line_start->prev == NULL) { /* if line_start is root move it forward as well */
|
||||||
|
hst->line_start = hst->line_start->next;
|
||||||
|
hst->line_start->prev = NULL;
|
||||||
|
++hst->start_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(hst->line_root);
|
||||||
|
hst->line_root = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adds a line_info line to queue */
|
||||||
|
static void line_info_add_queue(struct history *hst, struct line_info *line)
|
||||||
|
{
|
||||||
|
if (hst->queue_sz >= MAX_QUEUE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hst->queue[hst->queue_sz++] = line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns ptr to queue item 0 and removes it from queue */
|
||||||
|
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||||
|
{
|
||||||
|
if (hst->queue_sz <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
struct line_info *ret = hst->queue[0];
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < hst->queue_sz; ++i)
|
||||||
|
hst->queue[i] = hst->queue[i + 1];
|
||||||
|
|
||||||
|
--hst->queue_sz;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* creates new line_info line and puts it in the queue */
|
||||||
|
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg, uint8_t type,
|
||||||
|
uint8_t bold, uint8_t colour)
|
||||||
|
{
|
||||||
|
struct history *hst = self->chatwin->hst;
|
||||||
|
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
||||||
|
|
||||||
|
if (new_line == NULL)
|
||||||
|
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
int len = 1; /* there will always be a newline */
|
||||||
|
|
||||||
|
/* for type-specific formatting in print function */
|
||||||
|
switch (type) {
|
||||||
|
case ACTION:
|
||||||
|
case CONNECTION:
|
||||||
|
len += 3;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYS_MSG:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROMPT:
|
||||||
|
++len;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
len += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg) {
|
||||||
|
snprintf(new_line->msg, sizeof(new_line->msg), "%s", msg);
|
||||||
|
len += strlen(new_line->msg);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; msg[i]; ++i) {
|
||||||
|
if (msg[i] == '\n')
|
||||||
|
++new_line->newlines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tmstmp) {
|
||||||
|
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
|
||||||
|
len += strlen(new_line->timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name1) {
|
||||||
|
snprintf(new_line->name1, sizeof(new_line->name1), "%s", name1);
|
||||||
|
len += strlen(new_line->name1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name2) {
|
||||||
|
snprintf(new_line->name2, sizeof(new_line->name2), "%s", name2);
|
||||||
|
len += strlen(new_line->name2);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_line->len = len;
|
||||||
|
new_line->type = type;
|
||||||
|
new_line->bold = bold;
|
||||||
|
new_line->colour = colour;
|
||||||
|
|
||||||
|
line_info_add_queue(hst, new_line);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
||||||
|
static void line_info_check_queue(ToxWindow *self)
|
||||||
|
{
|
||||||
|
struct history *hst = self->chatwin->hst;
|
||||||
|
struct line_info *line = line_info_ret_queue(hst);
|
||||||
|
|
||||||
|
if (line == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (hst->start_id > user_settings->history_size)
|
||||||
|
line_info_root_fwd(hst);
|
||||||
|
|
||||||
|
line->id = hst->line_end->id + 1;
|
||||||
|
line->prev = hst->line_end;
|
||||||
|
hst->line_end->next = line;
|
||||||
|
hst->line_end = line;
|
||||||
|
|
||||||
|
int y, y2, x, x2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
getyx(self->chatwin->history, y, x);
|
||||||
|
(void) x;
|
||||||
|
|
||||||
|
if (x2 <= SIDEBAR_WIDTH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int offst = self->is_groupchat ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||||
|
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
||||||
|
int max_y = y2 - CHATBOX_HEIGHT;
|
||||||
|
|
||||||
|
/* move line_start forward proportionate to the number of new lines */
|
||||||
|
if (y + lines - 1 >= max_y) {
|
||||||
|
while (lines > 0 && hst->line_start->next) {
|
||||||
|
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
|
||||||
|
hst->line_start = hst->line_start->next;
|
||||||
|
++hst->start_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_info_print(ToxWindow *self)
|
||||||
|
{
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if (ctx == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct history *hst = ctx->hst;
|
||||||
|
|
||||||
|
/* Only allow one new item to be added to chat window per call to this function */
|
||||||
|
line_info_check_queue(self);
|
||||||
|
|
||||||
|
WINDOW *win = ctx->history;
|
||||||
|
wclear(win);
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (x2 <= SIDEBAR_WIDTH)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self->is_groupchat)
|
||||||
|
wmove(win, 0, 0);
|
||||||
|
else
|
||||||
|
wmove(win, 2, 0);
|
||||||
|
|
||||||
|
struct line_info *line = hst->line_start->next;
|
||||||
|
int numlines = 0;
|
||||||
|
|
||||||
|
while (line && numlines++ <= y2) {
|
||||||
|
uint8_t type = line->type;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case OUT_MSG:
|
||||||
|
case IN_MSG:
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s", line->timestamp);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
int nameclr = GREEN;
|
||||||
|
|
||||||
|
if (line->colour)
|
||||||
|
nameclr = line->colour;
|
||||||
|
else if (type == IN_MSG)
|
||||||
|
nameclr = CYAN;
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(nameclr));
|
||||||
|
wprintw(win, "%s: ", line->name1);
|
||||||
|
wattroff(win, COLOR_PAIR(nameclr));
|
||||||
|
|
||||||
|
if (line->msg[0] == '>')
|
||||||
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
wprintw(win, "%s\n", line->msg);
|
||||||
|
|
||||||
|
if (line->msg[0] == '>')
|
||||||
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACTION:
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s", line->timestamp);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(YELLOW));
|
||||||
|
wprintw(win, "* %s %s\n", line->name1, line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SYS_MSG:
|
||||||
|
if (line->timestamp[0]) {
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s", line->timestamp);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line->bold)
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
|
||||||
|
if (line->colour)
|
||||||
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
|
wprintw(win, "%s\n", line->msg);
|
||||||
|
|
||||||
|
if (line->bold)
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
if (line->colour)
|
||||||
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PROMPT:
|
||||||
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
wprintw(win, "$ ");
|
||||||
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
|
if (line->msg[0])
|
||||||
|
wprintw(win, "%s", line->msg);
|
||||||
|
|
||||||
|
wprintw(win, "\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONNECTION:
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s", line->timestamp);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(line->colour));
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "* %s ", line->name1);
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wprintw(win, "%s\n", line->msg);
|
||||||
|
wattroff(win, COLOR_PAIR(line->colour));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAME_CHANGE:
|
||||||
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
|
wprintw(win, "%s", line->timestamp);
|
||||||
|
wattroff(win, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
|
wattron(win, COLOR_PAIR(MAGENTA));
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "* %s", line->name1);
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, "%s", line->msg);
|
||||||
|
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "%s\n", line->name2);
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wattroff(win, COLOR_PAIR(MAGENTA));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = line->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* keep calling until queue is empty */
|
||||||
|
if (hst->queue_sz > 0)
|
||||||
|
line_info_print(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||||
|
{
|
||||||
|
struct line_info *line = self->chatwin->hst->line_end;
|
||||||
|
|
||||||
|
while (line) {
|
||||||
|
if (line->id == id) {
|
||||||
|
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line = line->prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* static void line_info_goto_root(struct history *hst)
|
||||||
|
{
|
||||||
|
hst->line_start = hst->line_root;
|
||||||
|
} */
|
||||||
|
|
||||||
|
static void line_info_scroll_up(struct history *hst)
|
||||||
|
{
|
||||||
|
if (hst->line_start->prev)
|
||||||
|
hst->line_start = hst->line_start->prev;
|
||||||
|
else beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line_info_scroll_down(struct history *hst)
|
||||||
|
{
|
||||||
|
if (hst->line_start->next)
|
||||||
|
hst->line_start = hst->line_start->next;
|
||||||
|
else beep();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||||
|
{
|
||||||
|
int x2, y2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
(void) x2;
|
||||||
|
int jump_dist = y2 / 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < jump_dist && hst->line_start->prev; ++i)
|
||||||
|
hst->line_start = hst->line_start->prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
||||||
|
{
|
||||||
|
int x2, y2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
(void) x2;
|
||||||
|
int jump_dist = y2 / 2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < jump_dist && hst->line_start->next; ++i)
|
||||||
|
hst->line_start = hst->line_start->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||||
|
{
|
||||||
|
struct history *hst = self->chatwin->hst;
|
||||||
|
bool match = true;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
/* TODO: Find good key bindings for all this stuff */
|
||||||
|
case T_KEY_C_F:
|
||||||
|
line_info_page_up(self, hst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case T_KEY_C_V:
|
||||||
|
line_info_page_down(self, hst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_PPAGE:
|
||||||
|
line_info_scroll_up(hst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KEY_NPAGE:
|
||||||
|
line_info_scroll_down(hst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* case ?:
|
||||||
|
line_info_goto_root(hst);
|
||||||
|
break; */
|
||||||
|
|
||||||
|
case T_KEY_C_H:
|
||||||
|
line_info_reset_start(self, hst);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
void line_info_clear(struct history *hst)
|
||||||
|
{
|
||||||
|
hst->line_start = hst->line_end;
|
||||||
|
hst->start_id = hst->line_start->id;
|
||||||
|
}
|
89
src/line_info.h
Normal file
89
src/line_info.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* line_info.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _line_info_h
|
||||||
|
#define _line_info_h
|
||||||
|
|
||||||
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
|
#define MAX_HISTORY 10000
|
||||||
|
#define MIN_HISTORY 40
|
||||||
|
#define MAX_QUEUE 128
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SYS_MSG,
|
||||||
|
IN_MSG,
|
||||||
|
OUT_MSG,
|
||||||
|
PROMPT,
|
||||||
|
ACTION,
|
||||||
|
CONNECTION,
|
||||||
|
NAME_CHANGE,
|
||||||
|
} LINE_TYPE;
|
||||||
|
|
||||||
|
struct line_info {
|
||||||
|
char timestamp[TIME_STR_SIZE];
|
||||||
|
char name1[TOXIC_MAX_NAME_LENGTH];
|
||||||
|
char name2[TOXIC_MAX_NAME_LENGTH];
|
||||||
|
char msg[TOX_MAX_MESSAGE_LENGTH];
|
||||||
|
uint8_t type;
|
||||||
|
uint8_t bold;
|
||||||
|
uint8_t colour;
|
||||||
|
uint32_t id;
|
||||||
|
uint16_t len; /* combined len of all strings */
|
||||||
|
uint8_t newlines;
|
||||||
|
|
||||||
|
struct line_info *prev;
|
||||||
|
struct line_info *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Linked list containing chat history lines */
|
||||||
|
struct history {
|
||||||
|
struct line_info *line_root;
|
||||||
|
struct line_info *line_start; /* the first line we want to start printing at */
|
||||||
|
struct line_info *line_end;
|
||||||
|
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||||
|
|
||||||
|
struct line_info *queue[MAX_QUEUE];
|
||||||
|
int queue_sz;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* creates new line_info line and puts it in the queue */
|
||||||
|
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, const char *msg,
|
||||||
|
uint8_t type, uint8_t bold, uint8_t colour);
|
||||||
|
|
||||||
|
/* Prints a section of history starting at line_start */
|
||||||
|
void line_info_print(ToxWindow *self);
|
||||||
|
|
||||||
|
/* frees all history lines */
|
||||||
|
void line_info_cleanup(struct history *hst);
|
||||||
|
|
||||||
|
/* clears the screen (does not delete anything) */
|
||||||
|
void line_info_clear(struct history *hst);
|
||||||
|
|
||||||
|
/* puts msg in specified line_info msg buffer */
|
||||||
|
void line_info_set(ToxWindow *self, uint32_t id, char *msg);
|
||||||
|
|
||||||
|
void line_info_init(struct history *hst);
|
||||||
|
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
|
||||||
|
|
||||||
|
#endif /* #define _line_info_h */
|
97
src/log.c
97
src/log.c
@ -25,114 +25,107 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
|
extern struct user_settings *user_settings;
|
||||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
|
||||||
|
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||||
|
void init_logging_session(char *name, char *key, struct chatlog *log)
|
||||||
{
|
{
|
||||||
if (!log->log_on)
|
if (!log->log_on)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!valid_nick(name))
|
||||||
|
name = UNKNOWN_NAME;
|
||||||
|
|
||||||
char *user_config_dir = get_user_config_dir();
|
char *user_config_dir = get_user_config_dir();
|
||||||
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
|
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
|
||||||
|
|
||||||
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
||||||
uint8_t ident[32];
|
char ident[32];
|
||||||
|
|
||||||
if (key != NULL) {
|
if (key != NULL) {
|
||||||
path_len += (KEY_IDENT_DIGITS * 2 + 5);
|
path_len += (KEY_IDENT_DIGITS * 2 + 5);
|
||||||
|
|
||||||
sprintf(&ident[0], "%02X", key[0] & 0xff);
|
sprintf(&ident[0], "%02X", key[0] & 0xff);
|
||||||
sprintf(&ident[2], "%02X", key[2] & 0xff);
|
sprintf(&ident[2], "%02X", key[1] & 0xff);
|
||||||
ident[KEY_IDENT_DIGITS*2+1] = '\0';
|
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
|
||||||
} else {
|
} else {
|
||||||
struct tm *tminfo = get_time();
|
strftime(ident, sizeof(ident), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||||
snprintf(ident, sizeof(ident),
|
|
||||||
"%04d-%02d-%02d[%02d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday,
|
|
||||||
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
|
|
||||||
path_len += strlen(ident) + 1;
|
path_len += strlen(ident) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_len > MAX_STR_SIZE) {
|
if (path_len > MAX_STR_SIZE) {
|
||||||
log->log_on = false;
|
log->log_on = false;
|
||||||
|
free(user_config_dir);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(log->log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
char log_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
||||||
user_config_dir, CONFIGDIR, name, ident);
|
user_config_dir, CONFIGDIR, name, ident);
|
||||||
|
|
||||||
FILE *logfile = fopen(log->log_path, "a");
|
|
||||||
|
|
||||||
if (logfile == NULL) {
|
|
||||||
log->log_on = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(logfile, "\n*** NEW SESSION ***\n\n");
|
|
||||||
|
|
||||||
fclose(logfile);
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
}
|
|
||||||
|
|
||||||
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
|
log->file = fopen(log_path, "a");
|
||||||
This is triggered when the log buffer is full, but may be forced. */
|
|
||||||
void write_to_log(struct chatlog *log)
|
|
||||||
{
|
|
||||||
if (!log->log_on)
|
|
||||||
return;
|
|
||||||
|
|
||||||
FILE *logfile = fopen(log->log_path, "a");
|
if (log->file == NULL) {
|
||||||
|
|
||||||
if (logfile == NULL) {
|
|
||||||
log->log_on = false;
|
log->log_on = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
||||||
|
|
||||||
for (i = 0; i < log->pos; ++i)
|
|
||||||
fprintf(logfile, "%s", log->log_buf[i]);
|
|
||||||
|
|
||||||
log->pos = 0;
|
|
||||||
fclose(logfile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
|
void write_to_log(const char *msg, char *name, struct chatlog *log, bool event)
|
||||||
If event is true, formats line as an event, e.g. * name has gone offline */
|
|
||||||
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
|
|
||||||
{
|
{
|
||||||
if (!log->log_on)
|
if (!log->log_on)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
if (log->file == NULL) {
|
||||||
|
log->log_on = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
|
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
|
||||||
else
|
else
|
||||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||||
|
|
||||||
struct tm *tminfo = get_time();
|
const char *t = user_settings->time == TIME_12 ? "%Y/%m/%d [%I:%M:%S %p]" : "%Y/%m/%d [%H:%M:%S]";
|
||||||
snprintf(log->log_buf[log->pos], MAX_LOG_LINE_SIZE, "%04d/%02d/%02d [%02d:%02d:%02d] %s %s\n",
|
char s[MAX_STR_SIZE];
|
||||||
tminfo->tm_year + 1900, tminfo->tm_mon + 1, tminfo->tm_mday,
|
strftime(s, MAX_STR_SIZE, t, get_time());
|
||||||
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec, name_frmt, msg);
|
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
||||||
|
|
||||||
if (++(log->pos) >= MAX_LOG_BUF_LINES)
|
uint64_t curtime = get_unix_time();
|
||||||
write_to_log(log);
|
|
||||||
|
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
||||||
|
fflush(log->file);
|
||||||
|
log->lastwrite = curtime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
|
void log_enable(char *name, char *key, struct chatlog *log)
|
||||||
{
|
{
|
||||||
log->log_on = true;
|
log->log_on = true;
|
||||||
|
|
||||||
if (!log->log_path[0])
|
if (log->file == NULL)
|
||||||
init_logging_session(name, key, log);
|
init_logging_session(name, key, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_disable(struct chatlog *log)
|
void log_disable(struct chatlog *log)
|
||||||
{
|
{
|
||||||
if (log->log_on) {
|
|
||||||
write_to_log(log);
|
|
||||||
log->log_on = false;
|
log->log_on = false;
|
||||||
|
|
||||||
|
if (log->file != NULL) {
|
||||||
|
fclose(log->file);
|
||||||
|
log->file = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
30
src/log.h
30
src/log.h
@ -20,16 +20,28 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
|
#ifndef _log_h
|
||||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
|
#define _log_h
|
||||||
|
|
||||||
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
|
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||||
If event is true, formats line as an event, e.g. * name has gone offline */
|
|
||||||
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
|
|
||||||
|
|
||||||
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
|
struct chatlog {
|
||||||
This is triggered automatically when the log buffer is full, but may be forced. */
|
FILE *file;
|
||||||
void write_to_log(struct chatlog *log);
|
uint64_t lastwrite;
|
||||||
|
int pos;
|
||||||
|
bool log_on; /* specific to current chat window */
|
||||||
|
};
|
||||||
|
|
||||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
|
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||||
|
void init_logging_session(char *name, char *key, struct chatlog *log);
|
||||||
|
|
||||||
|
/* formats/writes line to log file */
|
||||||
|
void write_to_log(const char *msg, char *name, struct chatlog *log, bool event);
|
||||||
|
|
||||||
|
/* enables logging for specified log and creates/fetches file if necessary */
|
||||||
|
void log_enable(char *name, char *key, struct chatlog *log);
|
||||||
|
|
||||||
|
/* disables logging for specified log and closes file */
|
||||||
void log_disable(struct chatlog *log);
|
void log_disable(struct chatlog *log);
|
||||||
|
|
||||||
|
#endif /* #define _log_h */
|
||||||
|
596
src/main.c
596
src/main.c
@ -1,596 +0,0 @@
|
|||||||
/* main.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SIGWINCH
|
|
||||||
#define SIGWINCH 28
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <curses.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <locale.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <direct.h>
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <ws2tcpip.h>
|
|
||||||
#else
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <tox/tox.h>
|
|
||||||
|
|
||||||
#include "configdir.h"
|
|
||||||
#include "toxic_windows.h"
|
|
||||||
#include "friendlist.h"
|
|
||||||
#include "prompt.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
|
|
||||||
#ifndef PACKAGE_DATADIR
|
|
||||||
#define PACKAGE_DATADIR "."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Export for use in Callbacks */
|
|
||||||
char *DATA_FILE = NULL;
|
|
||||||
char *SRVLIST_FILE = NULL;
|
|
||||||
|
|
||||||
ToxWindow *prompt = NULL;
|
|
||||||
|
|
||||||
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
|
|
||||||
|
|
||||||
FileSender file_senders[MAX_FILES];
|
|
||||||
uint8_t max_file_senders_index;
|
|
||||||
|
|
||||||
void on_window_resize(int sig)
|
|
||||||
{
|
|
||||||
endwin();
|
|
||||||
refresh();
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void init_term(void)
|
|
||||||
{
|
|
||||||
/* Setup terminal */
|
|
||||||
signal(SIGWINCH, on_window_resize);
|
|
||||||
#if HAVE_WIDECHAR
|
|
||||||
if (setlocale(LC_ALL, "") == NULL) {
|
|
||||||
fprintf(stderr, "Could not set your locale, plese check your locale settings or"
|
|
||||||
"disable wide char support\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
initscr();
|
|
||||||
cbreak();
|
|
||||||
keypad(stdscr, 1);
|
|
||||||
noecho();
|
|
||||||
timeout(100);
|
|
||||||
|
|
||||||
if (has_colors()) {
|
|
||||||
start_color();
|
|
||||||
init_pair(0, COLOR_WHITE, COLOR_BLACK);
|
|
||||||
init_pair(1, COLOR_GREEN, COLOR_BLACK);
|
|
||||||
init_pair(2, COLOR_CYAN, COLOR_BLACK);
|
|
||||||
init_pair(3, COLOR_RED, COLOR_BLACK);
|
|
||||||
init_pair(4, COLOR_BLUE, COLOR_BLACK);
|
|
||||||
init_pair(5, COLOR_YELLOW, COLOR_BLACK);
|
|
||||||
init_pair(6, COLOR_MAGENTA, COLOR_BLACK);
|
|
||||||
init_pair(7, COLOR_BLACK, COLOR_BLACK);
|
|
||||||
init_pair(8, COLOR_BLACK, COLOR_WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
static Tox *init_tox(int ipv4)
|
|
||||||
{
|
|
||||||
/* Init core */
|
|
||||||
int ipv6 = !ipv4;
|
|
||||||
Tox *m = tox_new(ipv6);
|
|
||||||
|
|
||||||
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
|
|
||||||
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
|
|
||||||
m = tox_new(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* Callbacks */
|
|
||||||
tox_callback_connection_status(m, on_connectionchange, NULL);
|
|
||||||
tox_callback_typing_change(m, on_typing_change, NULL);
|
|
||||||
tox_callback_friend_request(m, on_request, NULL);
|
|
||||||
tox_callback_friend_message(m, on_message, NULL);
|
|
||||||
tox_callback_name_change(m, on_nickchange, NULL);
|
|
||||||
tox_callback_user_status(m, on_statuschange, NULL);
|
|
||||||
tox_callback_status_message(m, on_statusmessagechange, NULL);
|
|
||||||
tox_callback_friend_action(m, on_action, NULL);
|
|
||||||
tox_callback_group_invite(m, on_groupinvite, NULL);
|
|
||||||
tox_callback_group_message(m, on_groupmessage, NULL);
|
|
||||||
tox_callback_group_action(m, on_groupaction, NULL);
|
|
||||||
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
|
|
||||||
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
|
||||||
tox_callback_file_control(m, on_file_control, NULL);
|
|
||||||
tox_callback_file_data(m, on_file_data, NULL);
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
tox_set_name(m, (uint8_t *) "Cool guy", sizeof("Cool guy"));
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
|
|
||||||
#elif defined(__APPLE__)
|
|
||||||
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
|
|
||||||
#else
|
|
||||||
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
|
||||||
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
|
||||||
#define MAXSERVERS 50
|
|
||||||
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
|
||||||
|
|
||||||
static int linecnt = 0;
|
|
||||||
static char servers[MAXSERVERS][SERVERLEN];
|
|
||||||
static uint16_t ports[MAXSERVERS];
|
|
||||||
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
|
|
||||||
|
|
||||||
static int serverlist_load(const char *filename)
|
|
||||||
{
|
|
||||||
FILE *fp = NULL;
|
|
||||||
|
|
||||||
if (!filename)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
fp = fopen(filename, "r");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
char line[MAXLINE];
|
|
||||||
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
|
|
||||||
if (strlen(line) > MINLINE) {
|
|
||||||
char *name = strtok(line, " ");
|
|
||||||
char *port = strtok(NULL, " ");
|
|
||||||
char *key_ascii = strtok(NULL, " ");
|
|
||||||
/* invalid line */
|
|
||||||
if (name == NULL || port == NULL || key_ascii == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
strncpy(servers[linecnt], name, SERVERLEN);
|
|
||||||
servers[linecnt][SERVERLEN - 1] = 0;
|
|
||||||
ports[linecnt] = htons(atoi(port));
|
|
||||||
|
|
||||||
uint8_t *key_binary = hex_string_to_bin(key_ascii);
|
|
||||||
memcpy(keys[linecnt], key_binary, TOX_CLIENT_ID_SIZE);
|
|
||||||
free(key_binary);
|
|
||||||
|
|
||||||
linecnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (linecnt < 1) {
|
|
||||||
fclose(fp);
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int init_connection_helper(Tox *m, int line)
|
|
||||||
{
|
|
||||||
return tox_bootstrap_from_address(m, servers[line], TOX_ENABLE_IPV6_DEFAULT,
|
|
||||||
ports[line], keys[line]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connects to a random DHT server listed in the DHTservers file
|
|
||||||
*
|
|
||||||
* return codes:
|
|
||||||
* 1: failed to open server file
|
|
||||||
* 2: no line of sufficient length in server file
|
|
||||||
* 3: failed to resolve name to IP
|
|
||||||
* 4: serverlist file contains no acceptable line
|
|
||||||
*/
|
|
||||||
static bool srvlist_loaded = false;
|
|
||||||
|
|
||||||
#define NUM_INIT_NODES 5
|
|
||||||
|
|
||||||
int init_connection(Tox *m)
|
|
||||||
{
|
|
||||||
if (linecnt > 0) /* already loaded serverlist */
|
|
||||||
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
|
|
||||||
|
|
||||||
/* only once:
|
|
||||||
* - load the serverlist
|
|
||||||
* - connect to "everyone" inside
|
|
||||||
*/
|
|
||||||
if (!srvlist_loaded) {
|
|
||||||
srvlist_loaded = true;
|
|
||||||
int res = serverlist_load(SRVLIST_FILE);
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
/* Fallback on the provided DHTServers in /usr/share or /usr/local/share,
|
|
||||||
so new starts of toxic will connect to the DHT. */
|
|
||||||
res = serverlist_load(PACKAGE_DATADIR "/DHTservers");
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!linecnt)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
res = 3;
|
|
||||||
int i;
|
|
||||||
int n = MIN(NUM_INIT_NODES, linecnt);
|
|
||||||
|
|
||||||
for(i = 0; i < n; ++i)
|
|
||||||
if (init_connection_helper(m, rand() % linecnt))
|
|
||||||
res = 0;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* empty serverlist file */
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_connection(Tox *m, ToxWindow *prompt)
|
|
||||||
{
|
|
||||||
static int conn_try = 0;
|
|
||||||
static int conn_err = 0;
|
|
||||||
static bool dht_on = false;
|
|
||||||
|
|
||||||
bool is_connected = tox_isconnected(m);
|
|
||||||
|
|
||||||
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
|
|
||||||
prep_prompt_win();
|
|
||||||
if (!conn_err) {
|
|
||||||
wprintw(prompt->window, "Establishing connection...\n");
|
|
||||||
if ((conn_err = init_connection(m)))
|
|
||||||
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
|
||||||
}
|
|
||||||
} else if (!dht_on && is_connected) {
|
|
||||||
dht_on = true;
|
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
|
||||||
|
|
||||||
prep_prompt_win();
|
|
||||||
wprintw(prompt->window, "\nDHT connected.\n");
|
|
||||||
} else if (dht_on && !is_connected) {
|
|
||||||
dht_on = false;
|
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
|
||||||
|
|
||||||
prep_prompt_win();
|
|
||||||
wprintw(prompt->window, "\nDHT disconnected. Attempting to reconnect.\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Store Messenger to given location
|
|
||||||
* Return 0 stored successfully
|
|
||||||
* Return 1 file path is NULL
|
|
||||||
* Return 2 malloc failed
|
|
||||||
* Return 3 opening path failed
|
|
||||||
* Return 4 fwrite failed
|
|
||||||
*/
|
|
||||||
int store_data(Tox *m, char *path)
|
|
||||||
{
|
|
||||||
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (path == NULL)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
FILE *fd;
|
|
||||||
size_t len;
|
|
||||||
uint8_t *buf;
|
|
||||||
|
|
||||||
len = tox_size(m);
|
|
||||||
buf = malloc(len);
|
|
||||||
|
|
||||||
if (buf == NULL)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
tox_save(m, buf);
|
|
||||||
|
|
||||||
fd = fopen(path, "wb");
|
|
||||||
|
|
||||||
if (fd == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fwrite(buf, len, 1, fd) != 1) {
|
|
||||||
free(buf);
|
|
||||||
fclose(fd);
|
|
||||||
return 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
fclose(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_data(Tox *m, char *path)
|
|
||||||
{
|
|
||||||
if (f_loadfromfile == 0) /*If file loading/saving is disabled*/
|
|
||||||
return;
|
|
||||||
|
|
||||||
FILE *fd;
|
|
||||||
size_t len;
|
|
||||||
uint8_t *buf;
|
|
||||||
|
|
||||||
if ((fd = fopen(path, "rb")) != NULL) {
|
|
||||||
fseek(fd, 0, SEEK_END);
|
|
||||||
len = ftell(fd);
|
|
||||||
fseek(fd, 0, SEEK_SET);
|
|
||||||
|
|
||||||
buf = malloc(len);
|
|
||||||
|
|
||||||
if (buf == NULL) {
|
|
||||||
fclose(fd);
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fread(buf, len, 1, fd) != 1) {
|
|
||||||
free(buf);
|
|
||||||
fclose(fd);
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "fread() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_load(m, buf, len);
|
|
||||||
|
|
||||||
uint32_t i = 0;
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
|
||||||
|
|
||||||
while (tox_get_name(m, i, name) != -1)
|
|
||||||
on_friendadded(m, i++, false);
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
fclose(fd);
|
|
||||||
} else {
|
|
||||||
int st;
|
|
||||||
|
|
||||||
if ((st = store_data(m, path)) != 0) {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "Store messenger failed with return code: %d\n", st);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_file_sender(int i)
|
|
||||||
{
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
|
||||||
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = max_file_senders_index; j > 0; --j) {
|
|
||||||
if (file_senders[j-1].active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_file_senders_index = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (!file_senders[i].active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint8_t *pathname = file_senders[i].pathname;
|
|
||||||
uint8_t filenum = file_senders[i].filenum;
|
|
||||||
int friendnum = file_senders[i].friendnum;
|
|
||||||
FILE *fp = file_senders[i].file;
|
|
||||||
uint64_t current_time = (uint64_t) time(NULL);
|
|
||||||
|
|
||||||
/* If file transfer has timed out kill transfer and send kill control */
|
|
||||||
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
|
||||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
|
||||||
|
|
||||||
if (ctx != NULL) {
|
|
||||||
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
|
||||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
close_file_sender(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
|
||||||
file_senders[i].piecelen) == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
file_senders[i].timestamp = current_time;
|
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
|
||||||
tox_file_data_size(m, friendnum), fp);
|
|
||||||
|
|
||||||
if (file_senders[i].piecelen == 0) {
|
|
||||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
|
||||||
|
|
||||||
if (ctx != NULL) {
|
|
||||||
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
|
||||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
|
||||||
close_file_sender(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void exit_toxic(Tox *m)
|
|
||||||
{
|
|
||||||
store_data(m, DATA_FILE);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (file_senders[i].active)
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
}
|
|
||||||
|
|
||||||
kill_all_windows();
|
|
||||||
free(DATA_FILE);
|
|
||||||
free(SRVLIST_FILE);
|
|
||||||
free(prompt->stb);
|
|
||||||
log_disable(prompt->promptbuf->log);
|
|
||||||
free(prompt->promptbuf->log);
|
|
||||||
free(prompt->promptbuf);
|
|
||||||
tox_kill(m);
|
|
||||||
endwin();
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_toxic(Tox *m, ToxWindow *prompt)
|
|
||||||
{
|
|
||||||
do_connection(m, prompt);
|
|
||||||
draw_active_window(m);
|
|
||||||
do_file_senders(m);
|
|
||||||
|
|
||||||
/* main tox-core loop */
|
|
||||||
tox_do(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
char *user_config_dir = get_user_config_dir();
|
|
||||||
int config_err = 0;
|
|
||||||
|
|
||||||
f_loadfromfile = 1;
|
|
||||||
int f_flag = 0;
|
|
||||||
int i = 0;
|
|
||||||
int f_use_ipv4 = 0;
|
|
||||||
|
|
||||||
/* Make sure all written files are read/writeable only by the current user. */
|
|
||||||
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
|
||||||
|
|
||||||
for (i = 0; i < argc; ++i) {
|
|
||||||
if (argv[i] == NULL)
|
|
||||||
break;
|
|
||||||
else if (argv[i][0] == '-') {
|
|
||||||
if (argv[i][1] == 'f') {
|
|
||||||
if (argv[i + 1] != NULL)
|
|
||||||
DATA_FILE = strdup(argv[i + 1]);
|
|
||||||
else
|
|
||||||
f_flag = -1;
|
|
||||||
} else if (argv[i][1] == 'n') {
|
|
||||||
f_loadfromfile = 0;
|
|
||||||
} else if (argv[i][1] == '4') {
|
|
||||||
f_use_ipv4 = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config_err = create_user_config_dir(user_config_dir);
|
|
||||||
if (DATA_FILE == NULL ) {
|
|
||||||
if (config_err) {
|
|
||||||
DATA_FILE = strdup("data");
|
|
||||||
} else {
|
|
||||||
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
|
|
||||||
if (DATA_FILE != NULL) {
|
|
||||||
strcpy(DATA_FILE, user_config_dir);
|
|
||||||
strcat(DATA_FILE, CONFIGDIR);
|
|
||||||
strcat(DATA_FILE, "data");
|
|
||||||
} else {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
|
|
||||||
if (SRVLIST_FILE != NULL) {
|
|
||||||
strcpy(SRVLIST_FILE, user_config_dir);
|
|
||||||
strcat(SRVLIST_FILE, CONFIGDIR);
|
|
||||||
strcat(SRVLIST_FILE, "DHTservers");
|
|
||||||
} else {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(user_config_dir);
|
|
||||||
|
|
||||||
init_term();
|
|
||||||
Tox *m = init_tox(f_use_ipv4);
|
|
||||||
|
|
||||||
if (m == NULL) {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "Failed to initialize network. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt = init_windows(m);
|
|
||||||
|
|
||||||
if (f_loadfromfile)
|
|
||||||
load_data(m, DATA_FILE);
|
|
||||||
|
|
||||||
if (f_flag == -1) {
|
|
||||||
attron(COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
wprintw(prompt->window, "You passed '-f' without giving an argument.\n"
|
|
||||||
"defaulting to 'data' for a keyfile...\n");
|
|
||||||
attroff(COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_err) {
|
|
||||||
attron(COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
wprintw(prompt->window, "Unable to determine configuration directory.\n"
|
|
||||||
"defaulting to 'data' for a keyfile...\n");
|
|
||||||
attroff(COLOR_PAIR(RED) | A_BOLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt_init_statusbar(prompt, m);
|
|
||||||
sort_friendlist_index(m);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
do_toxic(m, prompt);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
222
src/misc_tools.c
222
src/misc_tools.c
@ -21,63 +21,100 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
// XXX: FIX
|
static uint64_t current_unix_time;
|
||||||
unsigned char *hex_string_to_bin(char hex_string[])
|
|
||||||
|
void update_unix_time(void)
|
||||||
{
|
{
|
||||||
size_t len = strlen(hex_string);
|
current_unix_time = (uint64_t) time(NULL);
|
||||||
unsigned char *val = malloc(len);
|
}
|
||||||
|
|
||||||
if (val == NULL) {
|
uint64_t get_unix_time(void)
|
||||||
endwin();
|
{
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
return current_unix_time;
|
||||||
exit(EXIT_FAILURE);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
char *pos = hex_string;
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
size_t i;
|
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
||||||
|
{
|
||||||
for (i = 0; i < len; ++i, pos += 2)
|
return timestamp + timeout <= curtime;
|
||||||
sscanf(pos, "%2hhx", &val[i]);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the current local time */
|
/* Get the current local time */
|
||||||
struct tm *get_time(void)
|
struct tm *get_time(void)
|
||||||
{
|
{
|
||||||
struct tm *timeinfo;
|
struct tm *timeinfo;
|
||||||
time_t now;
|
uint64_t t = get_unix_time();
|
||||||
time(&now);
|
timeinfo = localtime((const time_t*) &t);
|
||||||
timeinfo = localtime(&now);
|
|
||||||
return timeinfo;
|
return timeinfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Prints the time to given window */
|
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||||
void print_time(WINDOW *window)
|
void get_time_str(char *buf, int bufsize)
|
||||||
{
|
{
|
||||||
struct tm *timeinfo = get_time();
|
if (user_settings->timestamps == TIMESTAMPS_OFF) {
|
||||||
|
buf[0] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wattron(window, COLOR_PAIR(BLUE));
|
const char *t = user_settings->time == TIME_12 ? "[%-I:%M:%S] " : "[%H:%M:%S] ";
|
||||||
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
strftime(buf, bufsize, t, get_time());
|
||||||
wattroff(window,COLOR_PAIR(BLUE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if the string is empty, false otherwise */
|
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||||
bool string_is_empty(char *string)
|
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
|
||||||
|
{
|
||||||
|
if (!secs)
|
||||||
|
return;
|
||||||
|
|
||||||
|
long int seconds = secs % 60;
|
||||||
|
long int minutes = (secs % 3600) / 60;
|
||||||
|
long int hours = secs / 3600;
|
||||||
|
|
||||||
|
if (!minutes && !hours)
|
||||||
|
snprintf(buf, bufsize, "%.2ld", seconds);
|
||||||
|
else if (!hours)
|
||||||
|
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
|
||||||
|
else
|
||||||
|
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *hex_string_to_bin(const char *hex_string)
|
||||||
|
{
|
||||||
|
size_t len = strlen(hex_string);
|
||||||
|
char *val = malloc(len);
|
||||||
|
|
||||||
|
if (val == NULL)
|
||||||
|
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; ++i, hex_string += 2)
|
||||||
|
sscanf(hex_string, "%2hhx", &val[i]);
|
||||||
|
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
|
int string_is_empty(char *string)
|
||||||
{
|
{
|
||||||
return string[0] == '\0';
|
return string[0] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
/* convert a multibyte string to a wide character string and puts in buf. */
|
||||||
int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
|
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
|
||||||
{
|
{
|
||||||
size_t len = mbstowcs(NULL, string, 0) + 1;
|
size_t len = mbstowcs(NULL, string, 0) + 1;
|
||||||
|
|
||||||
@ -90,9 +127,8 @@ int mbs_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* converts wide character string into a multibyte string.
|
/* converts wide character string into a multibyte string and puts in buf. */
|
||||||
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
|
||||||
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
|
||||||
{
|
{
|
||||||
size_t len = wcstombs(NULL, string, 0) + 1;
|
size_t len = wcstombs(NULL, string, 0) + 1;
|
||||||
|
|
||||||
@ -105,59 +141,6 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert wide characters to multibyte string: string returned must be free'd */
|
|
||||||
uint8_t *wcs_to_mbs(wchar_t *string)
|
|
||||||
{
|
|
||||||
uint8_t *ret = NULL;
|
|
||||||
size_t len = wcstombs(NULL, string, 0);
|
|
||||||
|
|
||||||
if (len != (size_t) -1) {
|
|
||||||
ret = malloc(++len);
|
|
||||||
|
|
||||||
if (ret != NULL) {
|
|
||||||
if (wcstombs(ret, string, len) == (size_t) -1)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = malloc(2);
|
|
||||||
|
|
||||||
if (ret != NULL) {
|
|
||||||
ret[0] = ' ';
|
|
||||||
ret[1] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == NULL) {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* convert a wide char to multibyte string */
|
|
||||||
char *wc_to_char(wchar_t ch)
|
|
||||||
{
|
|
||||||
static char ret[MB_LEN_MAX + 1];
|
|
||||||
int len = wctomb(ret, ch);
|
|
||||||
|
|
||||||
if (len == -1) {
|
|
||||||
ret[0] = ' ';
|
|
||||||
ret[1] = '\0';
|
|
||||||
} else {
|
|
||||||
ret[len] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns true if connection has timed out, false otherwise */
|
|
||||||
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
|
||||||
{
|
|
||||||
return timestamp + timeout <= curtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
void alert_window(ToxWindow *self, int type, bool is_beep)
|
void alert_window(ToxWindow *self, int type, bool is_beep)
|
||||||
{
|
{
|
||||||
@ -165,9 +148,11 @@ void alert_window(ToxWindow *self, int type, bool is_beep)
|
|||||||
case WINDOW_ALERT_0:
|
case WINDOW_ALERT_0:
|
||||||
self->alert0 = true;
|
self->alert0 = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WINDOW_ALERT_1:
|
case WINDOW_ALERT_1:
|
||||||
self->alert1 = true;
|
self->alert1 = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WINDOW_ALERT_2:
|
case WINDOW_ALERT_2:
|
||||||
self->alert2 = true;
|
self->alert2 = true;
|
||||||
break;
|
break;
|
||||||
@ -175,7 +160,7 @@ void alert_window(ToxWindow *self, int type, bool is_beep)
|
|||||||
|
|
||||||
StatusBar *stb = prompt->stb;
|
StatusBar *stb = prompt->stb;
|
||||||
|
|
||||||
if (is_beep && stb->status != TOX_USERSTATUS_BUSY)
|
if (is_beep && stb->status != TOX_USERSTATUS_BUSY && user_settings->alerts == ALERTS_ENABLED)
|
||||||
beep();
|
beep();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,52 +170,67 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
|
|||||||
return strcasecmp((const char *) nick1, (const char *) nick2);
|
return strcasecmp((const char *) nick1, (const char *) nick2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if nick is valid. A valid toxic nick:
|
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
|
- must not contain a forward slash (for logfile naming purposes)
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces */
|
||||||
bool valid_nick(uint8_t *nick)
|
int valid_nick(char *nick)
|
||||||
{
|
{
|
||||||
if (!nick[0] || nick[0] == ' ')
|
if (!nick[0] || nick[0] == ' ')
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; nick[i]; ++i) {
|
for (i = 0; nick[i]; ++i) {
|
||||||
if (nick[i] == ' ' && nick[i+1] == ' ')
|
if (nick[i] == ' ' && nick[i + 1] == ' ')
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
|
if (nick[i] == '/')
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return 1;
|
||||||
}
|
|
||||||
|
|
||||||
/* Moves cursor to the end of the line in given window */
|
|
||||||
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x)
|
|
||||||
{
|
|
||||||
int end_y = (len / max_x) + (max_y - CURS_Y_OFFSET);
|
|
||||||
int end_x = len % max_x;
|
|
||||||
wmove(w, end_y, end_x);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* gets base file name from path or original file name if no path is supplied */
|
/* gets base file name from path or original file name if no path is supplied */
|
||||||
void get_file_name(uint8_t *pathname, uint8_t *namebuf)
|
void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
||||||
{
|
{
|
||||||
int idx = strlen(pathname) - 1;
|
int idx = strlen(pathname) - 1;
|
||||||
|
|
||||||
while (idx >= 0 && pathname[idx] == '/')
|
char tmpname[MAX_STR_SIZE];
|
||||||
pathname[idx--] = '\0';
|
snprintf(tmpname, sizeof(tmpname), "%s", pathname);
|
||||||
|
|
||||||
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths
|
while (idx >= 0 && pathname[idx] == '/')
|
||||||
|
tmpname[idx--] = '\0';
|
||||||
|
|
||||||
|
char *filename = strrchr(tmpname, '/');
|
||||||
|
|
||||||
if (filename != NULL) {
|
if (filename != NULL) {
|
||||||
if (!strlen(++filename))
|
if (!strlen(++filename))
|
||||||
filename = pathname;
|
filename = tmpname;
|
||||||
} else {
|
} else {
|
||||||
filename = strrchr(pathname, '\\'); // Try windows style paths
|
filename = tmpname;
|
||||||
|
|
||||||
if (filename == NULL)
|
|
||||||
filename = pathname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(namebuf, MAX_STR_SIZE, "%s", filename);
|
snprintf(namebuf, bufsize, "%s", filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* converts str to all lowercase */
|
||||||
|
void str_to_lower(char *str)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; str[i]; ++i)
|
||||||
|
str[i] = tolower(str[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||||
|
Returns nick len on success, -1 on failure */
|
||||||
|
int get_nick_truncate(Tox *m, char *buf, int friendnum)
|
||||||
|
{
|
||||||
|
int len = tox_get_name(m, friendnum, (uint8_t *) buf);
|
||||||
|
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||||
|
buf[len] = '\0';
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -19,37 +19,52 @@
|
|||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
#ifndef _misc_tools_h
|
||||||
|
#define _misc_tools_h
|
||||||
|
|
||||||
|
#include "windows.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
|
#ifndef MIN
|
||||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||||
|
#endif
|
||||||
|
|
||||||
/* convert a hex string to binary */
|
/* convert a hex string to binary */
|
||||||
unsigned char *hex_string_to_bin(char hex_string[]);
|
char *hex_string_to_bin(const char *hex_string);
|
||||||
|
|
||||||
|
/* get the current unix time */
|
||||||
|
uint64_t get_unix_time(void);
|
||||||
|
|
||||||
|
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||||
|
void get_time_str(char *buf, int bufsize);
|
||||||
|
|
||||||
|
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||||
|
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
|
||||||
|
|
||||||
/* get the current local time */
|
/* get the current local time */
|
||||||
struct tm *get_time(void);
|
struct tm *get_time(void);
|
||||||
|
|
||||||
/* Prints the time to given window */
|
/* updates current unix time (should be run once per do_toxic loop) */
|
||||||
void print_time(WINDOW *window);
|
void update_unix_time(void);
|
||||||
|
|
||||||
/* Returns true if the string is empty, false otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
bool string_is_empty(char *string);
|
int string_is_empty(char *string);
|
||||||
|
|
||||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||||
int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n);
|
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||||
|
|
||||||
/* converts wide character string into a multibyte string.
|
/* converts wide character string into a multibyte string and puts in buf. */
|
||||||
Same thing as wcs_to_mbs() but caller must provide its own buffer */
|
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
|
||||||
int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n);
|
|
||||||
|
|
||||||
/* convert wide characters to multibyte string: string returned must be free'd */
|
/* convert a multibyte string to a wide character string and puts in buf) */
|
||||||
uint8_t *wcs_to_mbs(wchar_t *string);
|
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||||
|
|
||||||
/* convert a wide char to multibyte char */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
char *wc_to_char(wchar_t ch);
|
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||||
|
|
||||||
/* Returns true if connection has timed out, false otherwise */
|
|
||||||
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||||
@ -57,14 +72,21 @@ void alert_window(ToxWindow *self, int type, bool is_beep);
|
|||||||
/* case-insensitive string compare function for use with qsort */
|
/* case-insensitive string compare function for use with qsort */
|
||||||
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
|
int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2);
|
||||||
|
|
||||||
/* Returns true if nick is valid. A valid toxic nick:
|
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
|
- must not contain a forward slash (for logfile naming purposes)
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces */
|
||||||
bool valid_nick(uint8_t *nick);
|
int valid_nick(char *nick);
|
||||||
|
|
||||||
/* Moves the cursor to the end of the line in given window */
|
|
||||||
void mv_curs_end(WINDOW *w, size_t len, int max_y, int max_x);
|
|
||||||
|
|
||||||
/* gets base file name from path or original file name if no path is supplied */
|
/* gets base file name from path or original file name if no path is supplied */
|
||||||
void get_file_name(uint8_t *pathname, uint8_t *namebuf);
|
void get_file_name(char *namebuf, int bufsize, const char *pathname);
|
||||||
|
|
||||||
|
/* converts str to all lowercase */
|
||||||
|
void str_to_lower(char *str);
|
||||||
|
|
||||||
|
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
|
||||||
|
Returns nick len on success, -1 on failure */
|
||||||
|
int get_nick_truncate(Tox *m, char *buf, int friendnum);
|
||||||
|
|
||||||
|
#endif /* #define _misc_tools_h */
|
||||||
|
502
src/prompt.c
502
src/prompt.c
@ -20,25 +20,35 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifndef _GNU_SOURCE
|
||||||
#include "config.h"
|
#define _GNU_SOURCE /* needed for wcswidth() */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
|
#include "log.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "input.h"
|
||||||
|
#include "help.h"
|
||||||
|
|
||||||
uint8_t pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE] = {0};
|
char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||||
uint8_t num_frnd_requests = 0;
|
uint16_t num_frnd_requests = 0;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
struct _Winthread Winthread;
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
/* Array of global command names used for tab completion. */
|
/* Array of global command names used for tab completion. */
|
||||||
const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
{ "/accept" },
|
{ "/accept" },
|
||||||
{ "/add" },
|
{ "/add" },
|
||||||
{ "/clear" },
|
{ "/clear" },
|
||||||
@ -54,45 +64,54 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/note" },
|
{ "/note" },
|
||||||
{ "/quit" },
|
{ "/quit" },
|
||||||
{ "/status" },
|
{ "/status" },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
{ "/lsdev" },
|
||||||
|
{ "/sdev" },
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* prevents input string from eating system messages: call this prior to printing a prompt message
|
void kill_prompt_window(ToxWindow *self)
|
||||||
TODO: This is only a partial fix */
|
|
||||||
void prep_prompt_win(void)
|
|
||||||
{
|
{
|
||||||
PromptBuf *prt = prompt->promptbuf;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
StatusBar *statusbar = self->stb;
|
||||||
|
|
||||||
if (prt->len <= 0)
|
log_disable(ctx->log);
|
||||||
return;
|
line_info_cleanup(ctx->hst);
|
||||||
|
|
||||||
wprintw(prompt->window, "\n");
|
delwin(ctx->linewin);
|
||||||
|
delwin(ctx->history);
|
||||||
|
delwin(statusbar->topline);
|
||||||
|
|
||||||
if (!prt->at_bottom) {
|
free(ctx->log);
|
||||||
wmove(prompt->window, prt->orig_y - 1, X_OFST);
|
free(ctx->hst);
|
||||||
++prt->orig_y;
|
free(ctx);
|
||||||
} else {
|
free(self->help);
|
||||||
wmove(prompt->window, prt->orig_y - 2, X_OFST);
|
free(statusbar);
|
||||||
}
|
|
||||||
|
del_window(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updates own nick in prompt statusbar */
|
/* Updates own nick in prompt statusbar */
|
||||||
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len)
|
void prompt_update_nick(ToxWindow *prompt, char *nick)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
||||||
statusbar->nick_len = len;
|
statusbar->nick_len = strlen(statusbar->nick);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updates own statusmessage in prompt statusbar */
|
/* Updates own statusmessage in prompt statusbar */
|
||||||
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len)
|
void prompt_update_statusmessage(ToxWindow *prompt, char *statusmsg)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
statusbar->statusmsg_len = len;
|
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Updates own status in prompt statusbar */
|
/* Updates own status in prompt statusbar */
|
||||||
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status)
|
void prompt_update_status(ToxWindow *prompt, uint8_t status)
|
||||||
{
|
{
|
||||||
StatusBar *statusbar = prompt->stb;
|
StatusBar *statusbar = prompt->stb;
|
||||||
statusbar->status = status;
|
statusbar->status = status;
|
||||||
@ -107,7 +126,7 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
|||||||
|
|
||||||
/* Adds friend request to pending friend requests.
|
/* Adds friend request to pending friend requests.
|
||||||
Returns request number on success, -1 if queue is full or other error. */
|
Returns request number on success, -1 if queue is full or other error. */
|
||||||
static int add_friend_request(uint8_t *public_key)
|
static int add_friend_request(const char *public_key)
|
||||||
{
|
{
|
||||||
if (num_frnd_requests >= MAX_FRIENDS_NUM)
|
if (num_frnd_requests >= MAX_FRIENDS_NUM)
|
||||||
return -1;
|
return -1;
|
||||||
@ -128,357 +147,289 @@ static int add_friend_request(uint8_t *public_key)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
|
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||||
{
|
{
|
||||||
PromptBuf *prt = self->promptbuf;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
int x, y, y2, x2;
|
int x, y, y2, x2;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
/* BACKSPACE key: Remove one character from line */
|
if (x2 <= 0)
|
||||||
if (key == 0x107 || key == 0x8 || key == 0x7f) {
|
|
||||||
if (prt->pos > 0) {
|
|
||||||
del_char_buf_bck(prt->line, &prt->pos, &prt->len);
|
|
||||||
wmove(self->window, y, x-1); /* not necessary but fixes a display glitch */
|
|
||||||
prt->scroll = false;
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_DC) { /* DEL key: Remove character at pos */
|
|
||||||
if (prt->pos != prt->len) {
|
|
||||||
del_char_buf_frnt(prt->line, &prt->pos, &prt->len);
|
|
||||||
prt->scroll = false;
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == T_KEY_DISCARD) { /* CTRL-U: Delete entire line behind pos */
|
|
||||||
if (prt->pos > 0) {
|
|
||||||
wmove(self->window, prt->orig_y, X_OFST);
|
|
||||||
wclrtobot(self->window);
|
|
||||||
discard_buf(prt->line, &prt->pos, &prt->len);
|
|
||||||
} else {
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == T_KEY_KILL) { /* CTRL-K: Delete entire line in front of pos */
|
|
||||||
if (prt->len != prt->pos)
|
|
||||||
kill_buf(prt->line, &prt->pos, &prt->len);
|
|
||||||
else
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
|
|
||||||
if (prt->pos != 0)
|
|
||||||
prt->pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
|
|
||||||
if (prt->pos != prt->len)
|
|
||||||
prt->pos = prt->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_LEFT) {
|
|
||||||
if (prt->pos > 0)
|
|
||||||
--prt->pos;
|
|
||||||
else
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_RIGHT) {
|
|
||||||
if (prt->pos < prt->len)
|
|
||||||
++prt->pos;
|
|
||||||
else
|
|
||||||
beep();
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (key == KEY_UP) { /* fetches previous item in history */
|
|
||||||
wmove(self->window, prt->orig_y, X_OFST);
|
|
||||||
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
|
|
||||||
&prt->hst_pos, LN_HIST_MV_UP);
|
|
||||||
|
|
||||||
/* adjust line y origin appropriately when window scrolls down */
|
|
||||||
if (prt->at_bottom && prt->len >= x2 - X_OFST) {
|
|
||||||
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
|
|
||||||
int p_ofst = px2 != x2 ? 0 : X_OFST;
|
|
||||||
|
|
||||||
if (px2 <= 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
|
/* ignore non-menu related input if active */
|
||||||
|
if (self->help->active) {
|
||||||
if (k >= y2) {
|
help_onKey(self, key);
|
||||||
wprintw(self->window, "\n");
|
return;
|
||||||
--prt->orig_y;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (key == KEY_DOWN) { /* fetches next item in history */
|
if (ltr) { /* char is printable */
|
||||||
wmove(self->window, prt->orig_y, X_OFST);
|
input_new_char(self, key, x, y, x2, y2);
|
||||||
fetch_hist_item(prt->line, &prt->pos, &prt->len, prt->ln_history, prt->hst_tot,
|
return;
|
||||||
&prt->hst_pos, LN_HIST_MV_DWN);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (key == '\t') { /* TAB key: completes command */
|
if (line_info_onKey(self, key))
|
||||||
if (prt->len > 1 && prt->line[0] == '/') {
|
return;
|
||||||
if (complete_line(prt->line, &prt->pos, &prt->len, glob_cmd_list, AC_NUM_GLOB_COMMANDS,
|
|
||||||
MAX_CMDNAME_SIZE) == -1)
|
input_handle(self, key, x, y, x2, y2);
|
||||||
beep();
|
|
||||||
|
if (key == '\t') { /* TAB key: auto-completes command */
|
||||||
|
if (ctx->len > 1 && ctx->line[0] == '/') {
|
||||||
|
int diff = complete_line(ctx, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||||
|
|
||||||
|
if (diff != -1) {
|
||||||
|
if (x + diff > x2 - 1) {
|
||||||
|
wmove(self->window, y, x + diff);
|
||||||
|
ctx->start += diff;
|
||||||
|
} else {
|
||||||
|
wmove(self->window, y, x + diff);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
beep();
|
beep();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
beep();
|
||||||
}
|
}
|
||||||
|
} else if (key == '\n') {
|
||||||
|
rm_trailing_spaces_buf(ctx);
|
||||||
|
|
||||||
else
|
char line[MAX_STR_SIZE] = {0};
|
||||||
#if HAVE_WIDECHAR
|
|
||||||
if (iswprint(key))
|
|
||||||
#else
|
|
||||||
if (isprint(key))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (prt->len < (MAX_STR_SIZE-1)) {
|
|
||||||
add_char_to_buf(prt->line, &prt->pos, &prt->len, key);
|
|
||||||
prt->scroll = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* RETURN key: execute command */
|
|
||||||
else if (key == '\n') {
|
|
||||||
wprintw(self->window, "\n");
|
|
||||||
uint8_t line[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
|
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||||
memset(&line, 0, sizeof(line));
|
memset(&line, 0, sizeof(line));
|
||||||
|
|
||||||
if (!string_is_empty(line))
|
if (!string_is_empty(line))
|
||||||
add_line_to_hist(prt->line, prt->len, prt->ln_history, &prt->hst_tot, &prt->hst_pos);
|
add_line_to_hist(ctx);
|
||||||
|
|
||||||
execute(self->window, self, m, line, GLOBAL_COMMAND_MODE);
|
line_info_add(self, NULL, NULL, NULL, line, PROMPT, 0, 0);
|
||||||
reset_buf(prt->line, &prt->pos, &prt->len);
|
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||||
|
|
||||||
|
wclear(ctx->linewin);
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
reset_buf(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
PromptBuf *prt = self->promptbuf;
|
int x2, y2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
line_info_print(self);
|
||||||
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
int x, y, x2, y2;
|
|
||||||
getyx(self->window, y, x);
|
|
||||||
getmaxyx(self->window, y2, x2);
|
|
||||||
wclrtobot(self->window);
|
|
||||||
|
|
||||||
/* if len is >= screen width offset max x by X_OFST to account for prompt char */
|
if (ctx->len > 0)
|
||||||
int px2 = prt->len >= x2 ? x2 : x2 - X_OFST;
|
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
|
|
||||||
if (px2 <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* len offset to account for prompt char (0 if len is < width of screen) */
|
|
||||||
int p_ofst = px2 != x2 ? 0 : X_OFST;
|
|
||||||
|
|
||||||
if (prt->len > 0) {
|
|
||||||
uint8_t line[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
if (wcs_to_mbs_buf(line, prt->line, MAX_STR_SIZE) == -1)
|
|
||||||
reset_buf(prt->line, &prt->pos, &prt->len);
|
|
||||||
else
|
|
||||||
mvwprintw(self->window, prt->orig_y, X_OFST, line);
|
|
||||||
|
|
||||||
int k = prt->orig_y + ((prt->len + p_ofst) / px2);
|
|
||||||
|
|
||||||
prt->at_bottom = k == y2 - 1;
|
|
||||||
bool botm = k == y2;
|
|
||||||
bool edge = (prt->len + p_ofst) % px2 == 0;
|
|
||||||
|
|
||||||
/* move point of line origin up when input scrolls screen down */
|
|
||||||
if (prt->scroll && edge && botm) {
|
|
||||||
--prt->orig_y;
|
|
||||||
prt->scroll = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { /* Mark point of origin for new line */
|
|
||||||
prt->orig_y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
wattron(self->window, COLOR_PAIR(GREEN));
|
|
||||||
mvwprintw(self->window, prt->orig_y, 0, "$ ");
|
|
||||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
|
||||||
|
|
||||||
StatusBar *statusbar = self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
werase(statusbar->topline);
|
|
||||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||||
wmove(statusbar->topline, 0, 0);
|
wmove(statusbar->topline, 0, 0);
|
||||||
|
|
||||||
if (statusbar->is_online) {
|
if (statusbar->is_online) {
|
||||||
int colour = WHITE;
|
int colour = WHITE;
|
||||||
char *status_text = "Unknown";
|
const char *status_text = "Unknown";
|
||||||
|
|
||||||
switch (statusbar->status) {
|
switch (statusbar->status) {
|
||||||
case TOX_USERSTATUS_NONE:
|
case TOX_USERSTATUS_NONE:
|
||||||
status_text = "Online";
|
status_text = "Online";
|
||||||
colour = GREEN;
|
colour = GREEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USERSTATUS_AWAY:
|
case TOX_USERSTATUS_AWAY:
|
||||||
status_text = "Away";
|
status_text = "Away";
|
||||||
colour = YELLOW;
|
colour = YELLOW;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_USERSTATUS_BUSY:
|
case TOX_USERSTATUS_BUSY:
|
||||||
status_text = "Busy";
|
status_text = "Busy";
|
||||||
colour = RED;
|
colour = RED;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TOX_USERSTATUS_INVALID:
|
||||||
|
status_text = "ERROR";
|
||||||
|
colour = MAGENTA;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
|
||||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
|
||||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
wprintw(statusbar->topline, "[%s]", status_text);
|
wprintw(statusbar->topline, " [%s]", status_text);
|
||||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
|
|
||||||
|
wattron(statusbar->topline, A_BOLD);
|
||||||
|
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||||
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
} else {
|
} else {
|
||||||
|
wprintw(statusbar->topline, " [Offline]");
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wattron(statusbar->topline, A_BOLD);
|
||||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
wprintw(statusbar->topline, "[Offline]");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
if (statusbar->statusmsg[0])
|
||||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
|
||||||
|
|
||||||
/* put cursor back in correct spot */
|
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||||
int y_m = prt->orig_y + ((prt->pos + p_ofst) / px2);
|
|
||||||
int x_m = (prt->pos + X_OFST) % x2;
|
int y, x;
|
||||||
wmove(self->window, y_m, x_m);
|
getyx(self->window, y, x);
|
||||||
|
(void) x;
|
||||||
|
|
||||||
|
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
||||||
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
|
wrefresh(self->window);
|
||||||
|
|
||||||
|
if (self->help->active)
|
||||||
|
help_onDraw(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int32_t friendnum , uint8_t status)
|
||||||
{
|
|
||||||
scrollok(self->window, true);
|
|
||||||
PromptBuf *prt = self->promptbuf;
|
|
||||||
|
|
||||||
prt->log = malloc(sizeof(struct chatlog));
|
|
||||||
|
|
||||||
if (prt->log == NULL) {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(prt->log, 0, sizeof(struct chatlog));
|
|
||||||
|
|
||||||
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
|
|
||||||
wclrtoeol(self->window);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , uint8_t status)
|
|
||||||
{
|
{
|
||||||
if (friendnum < 0)
|
if (friendnum < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PromptBuf *prt = self->promptbuf;
|
ChatContext *ctx = self->chatwin;
|
||||||
prep_prompt_win();
|
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
||||||
|
get_nick_truncate(m, nick, friendnum);
|
||||||
if (tox_get_name(m, friendnum, nick) == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!nick[0])
|
if (!nick[0])
|
||||||
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
||||||
|
|
||||||
wprintw(self->window, "\n");
|
char timefrmt[TIME_STR_SIZE];
|
||||||
print_time(self->window);
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
char *msg;
|
||||||
uint8_t *msg;
|
|
||||||
|
|
||||||
if (status == 1) {
|
if (status == 1) {
|
||||||
msg = "has come online\n";
|
msg = "has come online";
|
||||||
wattron(self->window, COLOR_PAIR(GREEN));
|
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, GREEN);
|
||||||
wattron(self->window, A_BOLD);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
wprintw(self->window, "* %s ", nick);
|
|
||||||
wattroff(self->window, A_BOLD);
|
|
||||||
wprintw(self->window, "%s", msg);
|
|
||||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, prt->log, true);
|
|
||||||
alert_window(self, WINDOW_ALERT_2, false);
|
alert_window(self, WINDOW_ALERT_2, false);
|
||||||
} else {
|
} else {
|
||||||
msg = "has gone offline\n";
|
msg = "has gone offline";
|
||||||
wattron(self->window, COLOR_PAIR(RED));
|
line_info_add(self, timefrmt, nick, NULL, msg, CONNECTION, 0, RED);
|
||||||
wattron(self->window, A_BOLD);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
wprintw(self->window, "* %s ", nick);
|
|
||||||
wattroff(self->window, A_BOLD);
|
|
||||||
wprintw(self->window, "%s", msg);
|
|
||||||
wattroff(self->window, COLOR_PAIR(RED));
|
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, prt->log, true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
|
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data,
|
||||||
|
uint16_t length)
|
||||||
{
|
{
|
||||||
// make sure message data is null-terminated
|
ChatContext *ctx = self->chatwin;
|
||||||
data[length - 1] = 0;
|
|
||||||
PromptBuf *prt = self->promptbuf;
|
|
||||||
prep_prompt_win();
|
|
||||||
|
|
||||||
wprintw(self->window, "\n");
|
char timefrmt[TIME_STR_SIZE];
|
||||||
print_time(self->window);
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
uint8_t msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
|
snprintf(msg, sizeof(msg), "Friend request with the message '%s'", data);
|
||||||
wprintw(self->window, "%s", msg);
|
line_info_add(self, timefrmt, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
add_to_log_buf(msg, "", prt->log, true);
|
write_to_log(msg, "", ctx->log, true);
|
||||||
|
|
||||||
int n = add_friend_request(key);
|
int n = add_friend_request(key);
|
||||||
|
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
|
char *errmsg = "Friend request queue is full. Discarding request.";
|
||||||
wprintw(self->window, "%s", errmsg);
|
line_info_add(self, NULL, NULL, NULL, errmsg, SYS_MSG, 0, 0);
|
||||||
add_to_log_buf(errmsg, "", prt->log, true);
|
write_to_log(errmsg, "", ctx->log, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wprintw(self->window, "Type \"/accept %d\" to accept it.\n", n);
|
snprintf(msg, sizeof(msg), "Type \"/accept %d\" to accept it.", n);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
alert_window(self, WINDOW_ALERT_1, true);
|
alert_window(self, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
||||||
{
|
{
|
||||||
int x, y;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y, x);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
(void) y2;
|
||||||
|
|
||||||
/* Init statusbar info */
|
/* Init statusbar info */
|
||||||
StatusBar *statusbar = self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
statusbar->status = TOX_USERSTATUS_NONE;
|
statusbar->status = TOX_USERSTATUS_NONE;
|
||||||
statusbar->is_online = false;
|
statusbar->is_online = false;
|
||||||
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
tox_get_self_name(m, nick, TOX_MAX_NAME_LENGTH);
|
char statusmsg[MAX_STR_SIZE];
|
||||||
snprintf(statusbar->nick, sizeof(statusbar->nick), "%s", nick);
|
|
||||||
|
|
||||||
/* temporary until statusmessage saving works */
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
uint8_t ver[strlen(TOXICVER) + 1];
|
uint16_t n_len = tox_get_self_name(m, (uint8_t *) nick);
|
||||||
uint8_t statusmsg[MAX_STR_SIZE];
|
uint16_t s_len = tox_get_self_status_message(m, (uint8_t *) statusmsg, MAX_STR_SIZE);
|
||||||
|
uint8_t status = tox_get_self_user_status(m);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
nick[n_len] = '\0';
|
||||||
|
statusmsg[s_len] = '\0';
|
||||||
|
|
||||||
|
/* load prev status message or show toxic version if it has never been set */
|
||||||
|
char ver[strlen(TOXICVER) + 1];
|
||||||
strcpy(ver, TOXICVER);
|
strcpy(ver, TOXICVER);
|
||||||
uint8_t *toxic_ver = strtok(ver, "_");
|
const char *toxic_ver = strtok(ver, "_");
|
||||||
|
|
||||||
if (toxic_ver != NULL)
|
if ( (!strcmp("Online", statusmsg) || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) {
|
||||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
||||||
else
|
s_len = strlen(statusmsg);
|
||||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic hacker edition");
|
statusmsg[s_len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
m_set_statusmessage(m, statusmsg, strlen(statusmsg) + 1);
|
prompt_update_statusmessage(prompt, statusmsg);
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
prompt_update_status(prompt, status);
|
||||||
|
prompt_update_nick(prompt, nick);
|
||||||
|
|
||||||
/* Init statusbar subwindow */
|
/* Init statusbar subwindow */
|
||||||
statusbar->topline = subwin(self->window, 2, x, 0, 0);
|
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_welcome_msg(ToxWindow *self)
|
||||||
|
{
|
||||||
|
line_info_add(self, NULL, NULL, NULL, " _____ _____ _____ ____ ", SYS_MSG, 1, BLUE);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, " |_ _/ _ \\ \\/ /_ _/ ___|", SYS_MSG, 1, BLUE);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, " | || | | \\ / | | | ", SYS_MSG, 1, BLUE);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, " | || |_| / \\ | | |___ ", SYS_MSG, 1, BLUE);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, " |_| \\___/_/\\_\\___\\____|", SYS_MSG, 1, BLUE);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0);
|
||||||
|
|
||||||
|
char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging client.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||||
|
msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
|
||||||
|
line_info_add(self, NULL, NULL, NULL, msg, SYS_MSG, 1, CYAN);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, "", SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||||
|
{
|
||||||
|
curs_set(1);
|
||||||
|
int y2, x2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||||
|
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
|
|
||||||
|
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||||
|
ctx->hst = calloc(1, sizeof(struct history));
|
||||||
|
|
||||||
|
if (ctx->log == NULL || ctx->hst == NULL)
|
||||||
|
exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
line_info_init(ctx->hst);
|
||||||
|
|
||||||
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
|
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||||
|
tox_get_address(m, (uint8_t *) myid);
|
||||||
|
log_enable(self->name, myid, ctx->log);
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollok(ctx->history, 0);
|
||||||
|
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||||
|
|
||||||
|
print_welcome_msg(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow new_prompt(void)
|
ToxWindow new_prompt(void)
|
||||||
@ -495,19 +446,18 @@ ToxWindow new_prompt(void)
|
|||||||
ret.onConnectionChange = &prompt_onConnectionChange;
|
ret.onConnectionChange = &prompt_onConnectionChange;
|
||||||
ret.onFriendRequest = &prompt_onFriendRequest;
|
ret.onFriendRequest = &prompt_onFriendRequest;
|
||||||
|
|
||||||
strcpy(ret.name, "prompt");
|
strcpy(ret.name, "home");
|
||||||
|
|
||||||
PromptBuf *promptbuf = calloc(1, sizeof(PromptBuf));
|
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||||
|
Help *help = calloc(1, sizeof(Help));
|
||||||
|
|
||||||
if (stb != NULL && promptbuf != NULL) {
|
if (stb == NULL || chatwin == NULL || help == NULL)
|
||||||
ret.promptbuf = promptbuf;
|
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
ret.chatwin = chatwin;
|
||||||
ret.stb = stb;
|
ret.stb = stb;
|
||||||
} else {
|
ret.help = help;
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "calloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
14
src/prompt.h
14
src/prompt.h
@ -23,16 +23,22 @@
|
|||||||
#ifndef PROMPT_H_UZYGWFFL
|
#ifndef PROMPT_H_UZYGWFFL
|
||||||
#define PROMPT_H_UZYGWFFL
|
#define PROMPT_H_UZYGWFFL
|
||||||
|
|
||||||
#define X_OFST 2 /* offset to account for prompt char */
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define AC_NUM_GLOB_COMMANDS 17
|
||||||
|
#else
|
||||||
#define AC_NUM_GLOB_COMMANDS 15
|
#define AC_NUM_GLOB_COMMANDS 15
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
ToxWindow new_prompt(void);
|
ToxWindow new_prompt(void);
|
||||||
void prep_prompt_win(void);
|
void prep_prompt_win(void);
|
||||||
void prompt_init_statusbar(ToxWindow *self, Tox *m);
|
void prompt_init_statusbar(ToxWindow *self, Tox *m);
|
||||||
void prompt_update_nick(ToxWindow *prompt, uint8_t *nick, uint16_t len);
|
void prompt_update_nick(ToxWindow *prompt, char *nick);
|
||||||
void prompt_update_statusmessage(ToxWindow *prompt, uint8_t *statusmsg, uint16_t len);
|
void prompt_update_statusmessage(ToxWindow *prompt, char *statusmsg);
|
||||||
void prompt_update_status(ToxWindow *prompt, TOX_USERSTATUS status);
|
void prompt_update_status(ToxWindow *prompt, uint8_t status);
|
||||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
|
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
|
||||||
|
void kill_prompt_window(ToxWindow *self);
|
||||||
|
|
||||||
#endif /* end of include guard: PROMPT_H_UZYGWFFL */
|
#endif /* end of include guard: PROMPT_H_UZYGWFFL */
|
||||||
|
228
src/settings.c
Normal file
228
src/settings.c
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
/* settings.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "configdir.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include "device.h"
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
|
static void uset_autolog(struct user_settings *s, const char *val);
|
||||||
|
static void uset_time(struct user_settings *s, const char *val);
|
||||||
|
static void uset_timestamps(struct user_settings *s, const char *val);
|
||||||
|
static void uset_alerts(struct user_settings *s, const char *val);
|
||||||
|
static void uset_colours(struct user_settings *s, const char *val);
|
||||||
|
static void uset_hst_size(struct user_settings *s, const char *val);
|
||||||
|
static void uset_dwnld_path(struct user_settings *s, const char *val);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
static void uset_ain_dev(struct user_settings *s, const char *val);
|
||||||
|
static void uset_aout_dev(struct user_settings *s, const char *val);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct {
|
||||||
|
const char *key;
|
||||||
|
void (*func)(struct user_settings *s, const char *val);
|
||||||
|
} user_settings_list[] = {
|
||||||
|
{ "autolog", uset_autolog },
|
||||||
|
{ "time", uset_time },
|
||||||
|
{ "timestamps", uset_timestamps },
|
||||||
|
{ "alerts", uset_alerts },
|
||||||
|
{ "colour_theme", uset_colours },
|
||||||
|
{ "history_size", uset_hst_size },
|
||||||
|
{ "download_path", uset_dwnld_path },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
{ "audio_in_dev", uset_ain_dev },
|
||||||
|
{ "audio_out_dev", uset_aout_dev },
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static void uset_autolog(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* default off if invalid value */
|
||||||
|
s->autolog = n == AUTOLOG_ON ? AUTOLOG_ON : AUTOLOG_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_time(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* default to 24 hour time if invalid value */
|
||||||
|
s->time = n == TIME_12 ? TIME_12 : TIME_24;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_timestamps(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* default on if invalid value */
|
||||||
|
s->timestamps = n == TIMESTAMPS_OFF ? TIMESTAMPS_OFF : TIMESTAMPS_ON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_alerts(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* alerts default on if invalid value */
|
||||||
|
s->alerts = n == ALERTS_DISABLED ? ALERTS_DISABLED : ALERTS_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_colours(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* use default toxic colours if invalid value */
|
||||||
|
s->colour_theme = n == NATIVE_COLS ? NATIVE_COLS : DFLT_COLS;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
static void uset_ain_dev(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
if (n < 0 || n > MAX_DEVICES)
|
||||||
|
n = (long int) 0;
|
||||||
|
|
||||||
|
s->audio_in_dev = (long int) n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_aout_dev(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
if (n < 0 || n > MAX_DEVICES)
|
||||||
|
n = (long int) 0;
|
||||||
|
|
||||||
|
s->audio_out_dev = (long int) n;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
static void uset_hst_size(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
int n = atoi(val);
|
||||||
|
|
||||||
|
/* if val is out of range use default history size */
|
||||||
|
s->history_size = (n > MAX_HISTORY || n < MIN_HISTORY) ? DFLT_HST_SIZE : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uset_dwnld_path(struct user_settings *s, const char *val)
|
||||||
|
{
|
||||||
|
memset(s->download_path, 0, sizeof(s->download_path));
|
||||||
|
|
||||||
|
if (val == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int len = strlen(val);
|
||||||
|
|
||||||
|
if (len >= sizeof(s->download_path) - 2) /* leave room for null and '/' */
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE *fp = fopen(val, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
strcpy(s->download_path, val);
|
||||||
|
|
||||||
|
if (val[len - 1] != '/')
|
||||||
|
strcat(s->download_path, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_default_settings(struct user_settings *s)
|
||||||
|
{
|
||||||
|
/* see settings_values enum in settings.h for defaults */
|
||||||
|
uset_autolog(s, "0");
|
||||||
|
uset_time(s, "24");
|
||||||
|
uset_timestamps(s, "1");
|
||||||
|
uset_alerts(s, "1");
|
||||||
|
uset_colours(s, "0");
|
||||||
|
uset_hst_size(s, "700");
|
||||||
|
uset_dwnld_path(s, NULL);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
uset_ain_dev(s, "0");
|
||||||
|
uset_aout_dev(s, "0");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int settings_load(struct user_settings *s, char *path)
|
||||||
|
{
|
||||||
|
char *user_config_dir = get_user_config_dir();
|
||||||
|
FILE *fp = NULL;
|
||||||
|
char dflt_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
fp = fopen(path, "r");
|
||||||
|
} else {
|
||||||
|
snprintf(dflt_path, sizeof(dflt_path), "%s%stoxic.conf", user_config_dir, CONFIGDIR);
|
||||||
|
fp = fopen(dflt_path, "r");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(user_config_dir);
|
||||||
|
|
||||||
|
set_default_settings(s);
|
||||||
|
|
||||||
|
if (fp == NULL && !path) {
|
||||||
|
if ((fp = fopen(dflt_path, "w")) == NULL)
|
||||||
|
return -1;
|
||||||
|
} else if (fp == NULL && path) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char line[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
if (line[0] == '#' || !line[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const char *key = strtok(line, ":");
|
||||||
|
const char *val = strtok(NULL, ";");
|
||||||
|
|
||||||
|
if (key == NULL || val == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_SETTINGS; ++i) {
|
||||||
|
if (strcmp(user_settings_list[i].key, key) == 0) {
|
||||||
|
(user_settings_list[i].func)(s, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
71
src/settings.h
Normal file
71
src/settings.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* settings.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _settings_h
|
||||||
|
#define _settings_h
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define NUM_SETTINGS 9
|
||||||
|
#else
|
||||||
|
#define NUM_SETTINGS 7
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
/* holds user setting values */
|
||||||
|
struct user_settings {
|
||||||
|
int autolog; /* boolean */
|
||||||
|
int alerts; /* boolean */
|
||||||
|
int time; /* 12 or 24 */
|
||||||
|
int timestamps; /* boolean */
|
||||||
|
int colour_theme; /* boolean (0 for default toxic colours) */
|
||||||
|
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
|
||||||
|
char download_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
long int audio_in_dev;
|
||||||
|
long int audio_out_dev;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AUTOLOG_OFF = 0,
|
||||||
|
AUTOLOG_ON = 1,
|
||||||
|
|
||||||
|
TIME_24 = 24,
|
||||||
|
TIME_12 = 12,
|
||||||
|
|
||||||
|
TIMESTAMPS_OFF = 0,
|
||||||
|
TIMESTAMPS_ON = 1,
|
||||||
|
|
||||||
|
ALERTS_DISABLED = 0,
|
||||||
|
ALERTS_ENABLED = 1,
|
||||||
|
|
||||||
|
NATIVE_COLS = 1,
|
||||||
|
DFLT_COLS = 0,
|
||||||
|
|
||||||
|
DFLT_HST_SIZE = 700,
|
||||||
|
} settings_values;
|
||||||
|
|
||||||
|
int settings_load(struct user_settings *s, char *path);
|
||||||
|
|
||||||
|
#endif /* #define _settings_h */
|
663
src/toxic.c
Normal file
663
src/toxic.c
Normal file
@ -0,0 +1,663 @@
|
|||||||
|
/* main.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <curses.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#include "configdir.h"
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "prompt.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "file_senders.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include "audio_call.h"
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#ifndef PACKAGE_DATADIR
|
||||||
|
#define PACKAGE_DATADIR "."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
ToxAv *av;
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
/* Export for use in Callbacks */
|
||||||
|
char *DATA_FILE = NULL;
|
||||||
|
ToxWindow *prompt = NULL;
|
||||||
|
|
||||||
|
#define AUTOSAVE_FREQ 60
|
||||||
|
|
||||||
|
struct arg_opts {
|
||||||
|
int ignore_data_file;
|
||||||
|
int use_ipv4;
|
||||||
|
int default_locale;
|
||||||
|
char config_path[MAX_STR_SIZE];
|
||||||
|
char nodes_path[MAX_STR_SIZE];
|
||||||
|
} arg_opts;
|
||||||
|
|
||||||
|
struct _Winthread Winthread;
|
||||||
|
struct user_settings *user_settings = NULL;
|
||||||
|
|
||||||
|
static void catch_SIGINT(int sig)
|
||||||
|
{
|
||||||
|
Winthread.sig_exit_toxic = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void flag_window_resize(int sig)
|
||||||
|
{
|
||||||
|
Winthread.flag_resize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_toxic_success(Tox *m)
|
||||||
|
{
|
||||||
|
store_data(m, DATA_FILE);
|
||||||
|
close_all_file_senders(m);
|
||||||
|
kill_all_windows();
|
||||||
|
|
||||||
|
free(DATA_FILE);
|
||||||
|
free(user_settings);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
terminate_audio();
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
tox_kill(m);
|
||||||
|
endwin();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_toxic_err(const char *errmsg, int errcode)
|
||||||
|
{
|
||||||
|
if (errmsg == NULL)
|
||||||
|
errmsg = "No error message";
|
||||||
|
|
||||||
|
endwin();
|
||||||
|
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void init_term(void)
|
||||||
|
{
|
||||||
|
signal(SIGWINCH, flag_window_resize);
|
||||||
|
|
||||||
|
#if HAVE_WIDECHAR
|
||||||
|
|
||||||
|
if (!arg_opts.default_locale) {
|
||||||
|
if (setlocale(LC_ALL, "") == NULL)
|
||||||
|
exit_toxic_err("Could not set your locale, please check your locale settings or "
|
||||||
|
"disable unicode support with the -d flag.", FATALERR_LOCALE_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
initscr();
|
||||||
|
cbreak();
|
||||||
|
keypad(stdscr, 1);
|
||||||
|
noecho();
|
||||||
|
timeout(100);
|
||||||
|
|
||||||
|
if (has_colors()) {
|
||||||
|
short bg_color = COLOR_BLACK;
|
||||||
|
start_color();
|
||||||
|
|
||||||
|
if (user_settings->colour_theme == NATIVE_COLS) {
|
||||||
|
if (assume_default_colors(-1, -1) == OK)
|
||||||
|
bg_color = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
init_pair(0, COLOR_WHITE, COLOR_BLACK);
|
||||||
|
init_pair(1, COLOR_GREEN, bg_color);
|
||||||
|
init_pair(2, COLOR_CYAN, bg_color);
|
||||||
|
init_pair(3, COLOR_RED, bg_color);
|
||||||
|
init_pair(4, COLOR_BLUE, bg_color);
|
||||||
|
init_pair(5, COLOR_YELLOW, bg_color);
|
||||||
|
init_pair(6, COLOR_MAGENTA, bg_color);
|
||||||
|
init_pair(7, COLOR_BLACK, COLOR_BLACK);
|
||||||
|
init_pair(8, COLOR_BLACK, COLOR_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Tox *init_tox(int ipv4)
|
||||||
|
{
|
||||||
|
/* Init core */
|
||||||
|
int ipv6 = !ipv4;
|
||||||
|
Tox *m = tox_new(ipv6);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TOX_ENABLE_IPV6_DEFAULT is always 1.
|
||||||
|
* Checking it is redundant, this *should* be doing ipv4 fallback
|
||||||
|
*/
|
||||||
|
if (ipv6 && m == NULL) {
|
||||||
|
fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n");
|
||||||
|
m = tox_new(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ipv4)
|
||||||
|
fprintf(stderr, "Forcing IPv4 connection\n");
|
||||||
|
|
||||||
|
if (m == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Callbacks */
|
||||||
|
tox_callback_connection_status(m, on_connectionchange, NULL);
|
||||||
|
tox_callback_typing_change(m, on_typing_change, NULL);
|
||||||
|
tox_callback_friend_request(m, on_request, NULL);
|
||||||
|
tox_callback_friend_message(m, on_message, NULL);
|
||||||
|
tox_callback_name_change(m, on_nickchange, NULL);
|
||||||
|
tox_callback_user_status(m, on_statuschange, NULL);
|
||||||
|
tox_callback_status_message(m, on_statusmessagechange, NULL);
|
||||||
|
tox_callback_friend_action(m, on_action, NULL);
|
||||||
|
tox_callback_group_invite(m, on_groupinvite, NULL);
|
||||||
|
tox_callback_group_message(m, on_groupmessage, NULL);
|
||||||
|
tox_callback_group_action(m, on_groupaction, NULL);
|
||||||
|
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
|
||||||
|
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
||||||
|
tox_callback_file_control(m, on_file_control, NULL);
|
||||||
|
tox_callback_file_data(m, on_file_data, NULL);
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
tox_set_name(m, (uint8_t *) "Nerd", strlen("Nerd"));
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
tox_set_name(m, (uint8_t *) "Hipster", strlen("Hipster")); /* This used to users of other Unixes are hipsters */
|
||||||
|
#else
|
||||||
|
tox_set_name(m, (uint8_t *) "Registered Minix user #4", strlen("Registered Minix user #4"));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
||||||
|
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||||
|
#define MAXNODES 50
|
||||||
|
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
||||||
|
|
||||||
|
static struct _toxNodes {
|
||||||
|
int lines;
|
||||||
|
char nodes[MAXNODES][NODELEN];
|
||||||
|
uint16_t ports[MAXNODES];
|
||||||
|
char keys[MAXNODES][TOX_CLIENT_ID_SIZE];
|
||||||
|
} toxNodes;
|
||||||
|
|
||||||
|
static int nodelist_load(const char *filename)
|
||||||
|
{
|
||||||
|
if (!filename)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
char line[MAXLINE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
||||||
|
if (strlen(line) > MINLINE) {
|
||||||
|
const char *name = strtok(line, " ");
|
||||||
|
const char *port = strtok(NULL, " ");
|
||||||
|
const char *key_ascii = strtok(NULL, " ");
|
||||||
|
|
||||||
|
/* invalid line */
|
||||||
|
if (name == NULL || port == NULL || key_ascii == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
||||||
|
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
||||||
|
toxNodes.ports[toxNodes.lines] = htons(atoi(port));
|
||||||
|
|
||||||
|
char *key_binary = hex_string_to_bin(key_ascii);
|
||||||
|
memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_CLIENT_ID_SIZE);
|
||||||
|
free(key_binary);
|
||||||
|
|
||||||
|
toxNodes.lines++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toxNodes.lines < 1) {
|
||||||
|
fclose(fp);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_connection_helper(Tox *m, int line)
|
||||||
|
{
|
||||||
|
return tox_bootstrap_from_address(m, toxNodes.nodes[line], TOX_ENABLE_IPV6_DEFAULT,
|
||||||
|
toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Connects to a random DHT node listed in the DHTnodes file
|
||||||
|
*
|
||||||
|
* return codes:
|
||||||
|
* 1: failed to open node file
|
||||||
|
* 2: no line of sufficient length in node file
|
||||||
|
* 3: failed to resolve name to IP
|
||||||
|
* 4: nodelist file contains no acceptable line
|
||||||
|
*/
|
||||||
|
static bool srvlist_loaded = false;
|
||||||
|
|
||||||
|
#define NUM_INIT_NODES 5
|
||||||
|
|
||||||
|
int init_connection(Tox *m)
|
||||||
|
{
|
||||||
|
if (toxNodes.lines > 0) /* already loaded nodelist */
|
||||||
|
return init_connection_helper(m, rand() % toxNodes.lines) ? 0 : 3;
|
||||||
|
|
||||||
|
/* only once:
|
||||||
|
* - load the nodelist
|
||||||
|
* - connect to "everyone" inside
|
||||||
|
*/
|
||||||
|
if (!srvlist_loaded) {
|
||||||
|
srvlist_loaded = true;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (!arg_opts.nodes_path[0])
|
||||||
|
res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
|
||||||
|
else
|
||||||
|
res = nodelist_load(arg_opts.nodes_path);
|
||||||
|
|
||||||
|
if (toxNodes.lines < 1)
|
||||||
|
return res;
|
||||||
|
|
||||||
|
res = 3;
|
||||||
|
int i;
|
||||||
|
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i) {
|
||||||
|
if (init_connection_helper(m, rand() % toxNodes.lines))
|
||||||
|
res = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* empty nodelist file */
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define TRY_CONNECT 10 /* Seconds between connection attempts when DHT is not connected */
|
||||||
|
|
||||||
|
static void do_connection(Tox *m, ToxWindow *prompt)
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE] = {0};
|
||||||
|
|
||||||
|
static int conn_err = 0;
|
||||||
|
static bool was_connected = false;
|
||||||
|
static uint64_t last_conn_try = 0;
|
||||||
|
uint64_t curtime = get_unix_time();
|
||||||
|
bool is_connected = tox_isconnected(m);
|
||||||
|
|
||||||
|
if (was_connected && is_connected)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!was_connected && is_connected) {
|
||||||
|
was_connected = true;
|
||||||
|
prompt_update_connectionstatus(prompt, was_connected);
|
||||||
|
snprintf(msg, sizeof(msg), "DHT connected.");
|
||||||
|
} else if (was_connected && !is_connected) {
|
||||||
|
was_connected = false;
|
||||||
|
prompt_update_connectionstatus(prompt, was_connected);
|
||||||
|
snprintf(msg, sizeof(msg), "DHT disconnected. Attempting to reconnect.");
|
||||||
|
} else if (!was_connected && !is_connected && timed_out(last_conn_try, curtime, TRY_CONNECT)) {
|
||||||
|
/* if autoconnect has already failed there's no point in trying again */
|
||||||
|
if (conn_err == 0) {
|
||||||
|
last_conn_try = curtime;
|
||||||
|
|
||||||
|
if ((conn_err = init_connection(m)) != 0)
|
||||||
|
snprintf(msg, sizeof(msg), "Auto-connect failed with error code %d", conn_err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg[0])
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_friendlist(Tox *m)
|
||||||
|
{
|
||||||
|
uint32_t i;
|
||||||
|
uint32_t numfriends = tox_count_friendlist(m);
|
||||||
|
|
||||||
|
for (i = 0; i < numfriends; ++i)
|
||||||
|
friendlist_onFriendAdded(NULL, m, i, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Store Messenger to given location
|
||||||
|
* Return 0 stored successfully
|
||||||
|
* Return 1 file path is NULL
|
||||||
|
* Return 2 malloc failed
|
||||||
|
* Return 3 opening path failed
|
||||||
|
* Return 4 fwrite failed
|
||||||
|
*/
|
||||||
|
int store_data(Tox *m, char *path)
|
||||||
|
{
|
||||||
|
if (arg_opts.ignore_data_file)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (path == NULL)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
FILE *fd;
|
||||||
|
int len;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
len = tox_size(m);
|
||||||
|
buf = malloc(len);
|
||||||
|
|
||||||
|
if (buf == NULL)
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
tox_save(m, (uint8_t *) buf);
|
||||||
|
|
||||||
|
fd = fopen(path, "wb");
|
||||||
|
|
||||||
|
if (fd == NULL) {
|
||||||
|
free(buf);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(buf, len, 1, fd) != 1) {
|
||||||
|
free(buf);
|
||||||
|
fclose(fd);
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
fclose(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void load_data(Tox *m, char *path)
|
||||||
|
{
|
||||||
|
if (arg_opts.ignore_data_file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FILE *fd;
|
||||||
|
int len;
|
||||||
|
char *buf;
|
||||||
|
|
||||||
|
if ((fd = fopen(path, "rb")) != NULL) {
|
||||||
|
fseek(fd, 0, SEEK_END);
|
||||||
|
len = ftell(fd);
|
||||||
|
fseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
buf = malloc(len);
|
||||||
|
|
||||||
|
if (buf == NULL) {
|
||||||
|
fclose(fd);
|
||||||
|
exit_toxic_err("failed in load_data", FATALERR_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fread(buf, len, 1, fd) != 1) {
|
||||||
|
free(buf);
|
||||||
|
fclose(fd);
|
||||||
|
exit_toxic_err("failed in load_data", FATALERR_FREAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_load(m, (uint8_t *) buf, len);
|
||||||
|
load_friendlist(m);
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
fclose(fd);
|
||||||
|
} else {
|
||||||
|
if (store_data(m, path) != 0)
|
||||||
|
exit_toxic_err("failed in load_data", FATALERR_STORE_DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_toxic(Tox *m, ToxWindow *prompt)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
do_connection(m, prompt);
|
||||||
|
do_file_senders(m);
|
||||||
|
tox_do(m); /* main tox-core loop */
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define INACTIVE_WIN_REFRESH_RATE 10
|
||||||
|
|
||||||
|
void *thread_winref(void *data)
|
||||||
|
{
|
||||||
|
Tox *m = (Tox *) data;
|
||||||
|
uint8_t draw_count = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
draw_active_window(m);
|
||||||
|
draw_count++;
|
||||||
|
|
||||||
|
if (Winthread.flag_resize) {
|
||||||
|
on_window_resize();
|
||||||
|
Winthread.flag_resize = false;
|
||||||
|
} else if (draw_count >= INACTIVE_WIN_REFRESH_RATE) {
|
||||||
|
refresh_inactive_windows();
|
||||||
|
draw_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Winthread.sig_exit_toxic) {
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
exit_toxic_success(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_usage(void)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "usage: toxic [OPTION] [FILE ...]\n");
|
||||||
|
fprintf(stderr, " -f, --file Use specified data file\n");
|
||||||
|
fprintf(stderr, " -x, --nodata Ignore data file\n");
|
||||||
|
fprintf(stderr, " -4, --ipv4 Force IPv4 connection\n");
|
||||||
|
fprintf(stderr, " -d, --default_locale Use default locale\n");
|
||||||
|
fprintf(stderr, " -c, --config Use specified config file\n");
|
||||||
|
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
|
||||||
|
fprintf(stderr, " -h, --help Show this message and exit\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_default_opts(void)
|
||||||
|
{
|
||||||
|
arg_opts.use_ipv4 = 0;
|
||||||
|
arg_opts.ignore_data_file = 0;
|
||||||
|
arg_opts.default_locale = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_args(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
set_default_opts();
|
||||||
|
|
||||||
|
static struct option long_opts[] = {
|
||||||
|
{"file", required_argument, 0, 'f'},
|
||||||
|
{"nodata", no_argument, 0, 'x'},
|
||||||
|
{"ipv4", no_argument, 0, '4'},
|
||||||
|
{"default_locale", no_argument, 0, 'd'},
|
||||||
|
{"config", required_argument, 0, 'c'},
|
||||||
|
{"nodes", required_argument, 0, 'n'},
|
||||||
|
{"help", no_argument, 0, 'h'},
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *opts_str = "4xdf:c:n:h";
|
||||||
|
int opt, indexptr;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'f':
|
||||||
|
DATA_FILE = strdup(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
arg_opts.ignore_data_file = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '4':
|
||||||
|
arg_opts.use_ipv4 = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'n':
|
||||||
|
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
arg_opts.default_locale = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
default:
|
||||||
|
print_usage();
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
char *user_config_dir = get_user_config_dir();
|
||||||
|
int config_err = 0;
|
||||||
|
|
||||||
|
parse_args(argc, argv);
|
||||||
|
|
||||||
|
/* Make sure all written files are read/writeable only by the current user. */
|
||||||
|
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
|
||||||
|
signal(SIGINT, catch_SIGINT);
|
||||||
|
|
||||||
|
config_err = create_user_config_dir(user_config_dir);
|
||||||
|
|
||||||
|
if (DATA_FILE == NULL ) {
|
||||||
|
if (config_err) {
|
||||||
|
DATA_FILE = strdup("data");
|
||||||
|
} else {
|
||||||
|
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("data") + 1);
|
||||||
|
|
||||||
|
if (DATA_FILE == NULL)
|
||||||
|
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
strcpy(DATA_FILE, user_config_dir);
|
||||||
|
strcat(DATA_FILE, CONFIGDIR);
|
||||||
|
strcat(DATA_FILE, "data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(user_config_dir);
|
||||||
|
|
||||||
|
/* init user_settings struct and load settings from conf file */
|
||||||
|
user_settings = malloc(sizeof(struct user_settings));
|
||||||
|
|
||||||
|
if (user_settings == NULL)
|
||||||
|
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||||
|
|
||||||
|
memset(user_settings, 0, sizeof(struct user_settings));
|
||||||
|
|
||||||
|
char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||||
|
int settings_err = settings_load(user_settings, p);
|
||||||
|
|
||||||
|
Tox *m = init_tox(arg_opts.use_ipv4);
|
||||||
|
init_term();
|
||||||
|
|
||||||
|
if (m == NULL)
|
||||||
|
exit_toxic_err("failed in main", FATALERR_NETWORKINIT);
|
||||||
|
|
||||||
|
if (!arg_opts.ignore_data_file)
|
||||||
|
load_data(m, DATA_FILE);
|
||||||
|
|
||||||
|
prompt = init_windows(m);
|
||||||
|
|
||||||
|
/* thread for ncurses stuff */
|
||||||
|
if (pthread_mutex_init(&Winthread.lock, NULL) != 0)
|
||||||
|
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
||||||
|
|
||||||
|
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0)
|
||||||
|
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||||
|
|
||||||
|
char *msg;
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
av = init_audio(prompt, m);
|
||||||
|
|
||||||
|
|
||||||
|
set_primary_device(input, user_settings->audio_in_dev);
|
||||||
|
set_primary_device(output, user_settings->audio_out_dev);
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
if (config_err) {
|
||||||
|
msg = "Unable to determine configuration directory. Defaulting to 'data' for a keyfile...";
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings_err == -1) {
|
||||||
|
msg = "Failed to load user settings";
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, msg, SYS_MSG, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_friendlist_index();
|
||||||
|
prompt_init_statusbar(prompt, m);
|
||||||
|
|
||||||
|
uint64_t last_save = get_unix_time();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
update_unix_time();
|
||||||
|
do_toxic(m, prompt);
|
||||||
|
uint64_t cur_time = get_unix_time();
|
||||||
|
|
||||||
|
if (timed_out(last_save, cur_time, AUTOSAVE_FREQ)) {
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
store_data(m, DATA_FILE);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
last_save = cur_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(40000);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
108
src/toxic.h
Normal file
108
src/toxic.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/* toxic.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _toxic_h
|
||||||
|
#define _toxic_h
|
||||||
|
|
||||||
|
#ifndef TOXICVER
|
||||||
|
#define TOXICVER "NOVER_" /* Use the -D flag to set this */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SIGWINCH
|
||||||
|
#define SIGWINCH 28
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SIGINT
|
||||||
|
#define SIGINT 2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <curses.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#define UNKNOWN_NAME "Anonymous"
|
||||||
|
|
||||||
|
#define MAX_FRIENDS_NUM 500
|
||||||
|
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH
|
||||||
|
#define MAX_CMDNAME_SIZE 64
|
||||||
|
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
||||||
|
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
|
||||||
|
#define TIME_STR_SIZE 16
|
||||||
|
|
||||||
|
/* ASCII key codes */
|
||||||
|
#define T_KEY_ESC 0X1B /* esc key */
|
||||||
|
#define T_KEY_KILL 0x0B /* ctrl-k */
|
||||||
|
#define T_KEY_DISCARD 0x15 /* ctrl-u */
|
||||||
|
#define T_KEY_NEXT 0x10 /* ctrl-p */
|
||||||
|
#define T_KEY_PREV 0x0F /* ctrl-o */
|
||||||
|
#define T_KEY_C_E 0x05 /* ctrl-e */
|
||||||
|
#define T_KEY_C_A 0x01 /* ctrl-a */
|
||||||
|
#define T_KEY_C_RB 0x1D /* ctrl-] */
|
||||||
|
#define T_KEY_C_LB 0x1B /* ctrl-[ */
|
||||||
|
#define T_KEY_C_V 0x16 /* ctrl-v */
|
||||||
|
#define T_KEY_C_F 0x06 /* ctrl-f */
|
||||||
|
#define T_KEY_C_H 0x08 /* ctrl-h */
|
||||||
|
#define T_KEY_C_Y 0x19 /* ctrl-y */
|
||||||
|
|
||||||
|
typedef enum _FATAL_ERRS {
|
||||||
|
FATALERR_MEMORY = -1, /* malloc() or calloc() failed */
|
||||||
|
FATALERR_FREAD = -2, /* fread() failed on critical read */
|
||||||
|
FATALERR_THREAD_CREATE = -3, /* thread creation failed */
|
||||||
|
FATALERR_MUTEX_INIT = -4, /* mutex init failed */
|
||||||
|
FATALERR_THREAD_ATTR = -5, /* thread attr object init failed */
|
||||||
|
FATALERR_LOCALE_SET = -6, /* system locale not set */
|
||||||
|
FATALERR_STORE_DATA = -7, /* store_data failed in critical section */
|
||||||
|
FATALERR_NETWORKINIT = -8, /* Tox network failed to init */
|
||||||
|
FATALERR_INFLOOP = -9, /* infinite loop detected */
|
||||||
|
FATALERR_WININIT = -10, /* window init failed */
|
||||||
|
} FATAL_ERRS;
|
||||||
|
|
||||||
|
/* Fixes text color problem on some terminals.
|
||||||
|
Uncomment if necessary */
|
||||||
|
/* #define URXVT_FIX */
|
||||||
|
|
||||||
|
void exit_toxic_success(Tox *m);
|
||||||
|
void exit_toxic_err(const char *errmsg, int errcode);
|
||||||
|
|
||||||
|
int store_data(Tox *m, char *path);
|
||||||
|
|
||||||
|
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata);
|
||||||
|
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
|
||||||
|
void on_message(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
|
||||||
|
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
|
||||||
|
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
|
||||||
|
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
|
||||||
|
void on_statusmessagechange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata);
|
||||||
|
void on_friendadded(Tox *m, int32_t friendnumber, bool sort);
|
||||||
|
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
|
||||||
|
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
|
||||||
|
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata);
|
||||||
|
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
|
||||||
|
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize, const uint8_t *pathname,
|
||||||
|
uint16_t pathname_length, void *userdata);
|
||||||
|
void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type,
|
||||||
|
const uint8_t *data, uint16_t length, void *userdata);
|
||||||
|
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
|
||||||
|
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
|
||||||
|
|
||||||
|
#endif /* #define _toxic_h */
|
@ -22,179 +22,224 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
|
|
||||||
/* Adds char to buffer at pos */
|
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
|
||||||
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch)
|
int add_char_to_buf(ChatContext *ctx, wint_t ch)
|
||||||
{
|
{
|
||||||
if (*pos < 0 || *len >= MAX_STR_SIZE)
|
if (ctx->len >= MAX_STR_SIZE - 1)
|
||||||
return;
|
return -1;
|
||||||
|
|
||||||
/* move all chars including null in front of pos one space forward and insert char in pos */
|
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||||
int i;
|
ctx->line[ctx->pos++] = ch;
|
||||||
|
ctx->line[++ctx->len] = L'\0';
|
||||||
|
|
||||||
for (i = *len; i >= *pos && i >= 0; --i)
|
return 0;
|
||||||
buf[i+1] = buf[i];
|
|
||||||
|
|
||||||
buf[(*pos)++] = ch;
|
|
||||||
++(*len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deletes the character before pos */
|
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
|
||||||
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len)
|
int del_char_buf_bck(ChatContext *ctx)
|
||||||
{
|
{
|
||||||
if (*pos <= 0)
|
if (ctx->pos <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||||
|
--ctx->pos;
|
||||||
|
ctx->line[--ctx->len] = L'\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
|
||||||
|
int del_char_buf_frnt(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->pos >= ctx->len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
|
||||||
|
ctx->line[--ctx->len] = L'\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
|
||||||
|
Return 0 on success, -1 if noting to discard. */
|
||||||
|
int discard_buf(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->pos <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ctx->yank_len = ctx->pos;
|
||||||
|
wmemcpy(ctx->yank, ctx->line, ctx->yank_len);
|
||||||
|
ctx->yank[ctx->yank_len] = L'\0';
|
||||||
|
|
||||||
|
wmemmove(ctx->line, &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||||
|
ctx->len -= ctx->pos;
|
||||||
|
ctx->pos = 0;
|
||||||
|
ctx->start = 0;
|
||||||
|
ctx->line[ctx->len] = L'\0';
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deletes the line from pos to len and puts killed portion in yank buffer.
|
||||||
|
Return 0 on success, -1 if nothing to kill. */
|
||||||
|
int kill_buf(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->len <= ctx->pos)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ctx->yank_len = ctx->len - ctx->pos;
|
||||||
|
wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len);
|
||||||
|
ctx->yank[ctx->yank_len] = L'\0';
|
||||||
|
|
||||||
|
ctx->line[ctx->pos] = L'\0';
|
||||||
|
ctx->len = ctx->pos;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inserts string in ctx->yank into line at pos.
|
||||||
|
Return 0 on success, -1 if yank buffer is empty or too long */
|
||||||
|
int yank_buf(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (!ctx->yank[0])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE - 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos);
|
||||||
|
wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len);
|
||||||
|
|
||||||
|
ctx->pos += ctx->yank_len;
|
||||||
|
ctx->len += ctx->yank_len;
|
||||||
|
ctx->line[ctx->len] = L'\0';
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nulls line and sets pos, len and start to 0 */
|
||||||
|
void reset_buf(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
ctx->line[0] = L'\0';
|
||||||
|
ctx->pos = 0;
|
||||||
|
ctx->len = 0;
|
||||||
|
ctx->start = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Removes trailing spaces from line. */
|
||||||
|
void rm_trailing_spaces_buf(ChatContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->len <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ctx->line[ctx->len - 1] != ' ')
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* similar to add_char_to_buf but deletes a char */
|
for (i = ctx->len - 1; i >= 0; --i) {
|
||||||
for (i = *pos-1; i <= *len; ++i)
|
if (ctx->line[i] != ' ')
|
||||||
buf[i] = buf[i+1];
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
--(*pos);
|
ctx->len = i + 1;
|
||||||
--(*len);
|
ctx->pos = MIN(ctx->pos, ctx->len);
|
||||||
}
|
ctx->line[ctx->len] = L'\0';
|
||||||
|
|
||||||
/* Deletes the character at pos */
|
|
||||||
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len)
|
|
||||||
{
|
|
||||||
if (*pos < 0 || *pos >= *len)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = *pos; i < *len; ++i)
|
|
||||||
buf[i] = buf[i+1];
|
|
||||||
|
|
||||||
--(*len);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deletes the line from beginning to pos */
|
|
||||||
void discard_buf(wchar_t *buf, size_t *pos, size_t *len)
|
|
||||||
{
|
|
||||||
if (*pos <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
int c = 0;
|
|
||||||
|
|
||||||
for (i = *pos; i <= *len; ++i)
|
|
||||||
buf[c++] = buf[i];
|
|
||||||
|
|
||||||
*pos = 0;
|
|
||||||
*len = c - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Deletes the line from pos to len */
|
|
||||||
void kill_buf(wchar_t *buf, size_t *pos, size_t *len)
|
|
||||||
{
|
|
||||||
if (*len == *pos)
|
|
||||||
return;
|
|
||||||
|
|
||||||
buf[*pos] = L'\0';
|
|
||||||
*len = *pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* nulls buf and sets pos and len to 0 */
|
|
||||||
void reset_buf(wchar_t *buf, size_t *pos, size_t *len)
|
|
||||||
{
|
|
||||||
buf[0] = L'\0';
|
|
||||||
*pos = 0;
|
|
||||||
*len = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HIST_PURGE MAX_LINE_HIST / 4
|
#define HIST_PURGE MAX_LINE_HIST / 4
|
||||||
|
|
||||||
/* shifts hist items back and makes room for HIST_PURGE new entries */
|
/* shifts hist items back and makes room for HIST_PURGE new entries */
|
||||||
static void shift_hist_back(wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot)
|
static void shift_hist_back(ChatContext *ctx)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int n = MAX_LINE_HIST - HIST_PURGE;
|
int n = MAX_LINE_HIST - HIST_PURGE;
|
||||||
|
|
||||||
for (i = 0; i < n; ++i)
|
for (i = 0; i < n; ++i)
|
||||||
wmemcpy(hst[i], hst[i+HIST_PURGE], MAX_STR_SIZE);
|
wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE);
|
||||||
|
|
||||||
*hst_tot = n;
|
ctx->hst_tot = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
|
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
|
||||||
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
|
void add_line_to_hist(ChatContext *ctx)
|
||||||
int *hst_pos)
|
|
||||||
{
|
{
|
||||||
if (len > MAX_STR_SIZE)
|
if (ctx->len > MAX_STR_SIZE)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (*hst_tot >= MAX_LINE_HIST)
|
if (ctx->hst_tot >= MAX_LINE_HIST)
|
||||||
shift_hist_back(hst, hst_tot);
|
shift_hist_back(ctx);
|
||||||
|
|
||||||
++(*hst_tot);
|
++ctx->hst_tot;
|
||||||
*hst_pos = *hst_tot;
|
ctx->hst_pos = ctx->hst_tot;
|
||||||
|
|
||||||
wmemcpy(hst[*hst_tot-1], buf, len + 1);
|
wmemcpy(ctx->ln_history[ctx->hst_tot - 1], ctx->line, ctx->len + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
|
||||||
hst_pos is decremented or incremented depending on key_dir.
|
hst_pos is decremented or incremented depending on key_dir.
|
||||||
|
|
||||||
resets buffer if at end of history */
|
resets line if at end of history */
|
||||||
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
|
void fetch_hist_item(ChatContext *ctx, int key_dir)
|
||||||
int hst_tot, int *hst_pos, int key_dir)
|
|
||||||
{
|
{
|
||||||
if (key_dir == LN_HIST_MV_UP) {
|
if (key_dir == KEY_UP) {
|
||||||
if (--(*hst_pos) < 0) {
|
if (--ctx->hst_pos < 0) {
|
||||||
*hst_pos = 0;
|
ctx->hst_pos = 0;
|
||||||
beep();
|
beep();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (++(*hst_pos) >= hst_tot) {
|
if (++ctx->hst_pos >= ctx->hst_tot) {
|
||||||
*hst_pos = hst_tot;
|
ctx->hst_pos = ctx->hst_tot;
|
||||||
reset_buf(buf, pos, len);
|
reset_buf(ctx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const wchar_t *hst_line = hst[*hst_pos];
|
const wchar_t *hst_line = ctx->ln_history[ctx->hst_pos];
|
||||||
size_t h_len = wcslen(hst_line);
|
size_t h_len = wcslen(hst_line);
|
||||||
|
|
||||||
wmemcpy(buf, hst_line, h_len + 1);
|
wmemcpy(ctx->line, hst_line, h_len + 1);
|
||||||
|
ctx->pos = h_len;
|
||||||
*pos = h_len;
|
ctx->len = h_len;
|
||||||
*len = h_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||||
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||||
with "Hello john".
|
with "Hello john".
|
||||||
|
|
||||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||||
in the list, and size is the size of each item in the list.
|
in the list, and size is the size of each item in the list.
|
||||||
|
|
||||||
Returns the difference between the old len and new len of buf on success, -1 if error */
|
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||||
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size)
|
int complete_line(ChatContext *ctx, const void *list, int n_items, int size)
|
||||||
{
|
{
|
||||||
if (*pos <= 0 || *len <= 0 || *len >= MAX_STR_SIZE)
|
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const uint8_t *L = (uint8_t *) list;
|
const char *L = (char *) list;
|
||||||
|
|
||||||
|
char ubuf[MAX_STR_SIZE];
|
||||||
|
|
||||||
uint8_t ubuf[MAX_STR_SIZE];
|
|
||||||
/* work with multibyte string copy of buf for simplicity */
|
/* work with multibyte string copy of buf for simplicity */
|
||||||
if (wcs_to_mbs_buf(ubuf, buf, MAX_STR_SIZE) == -1)
|
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* isolate substring from space behind pos to pos */
|
/* isolate substring from space behind pos to pos */
|
||||||
uint8_t tmp[MAX_STR_SIZE];
|
char tmp[MAX_STR_SIZE];
|
||||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||||
tmp[*pos] = '\0';
|
tmp[ctx->pos] = '\0';
|
||||||
uint8_t *sub = strrchr(tmp, ' ');
|
char *sub = strrchr(tmp, ' ');
|
||||||
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
|
int n_endchrs = 1; /* 1 = append space to end of match, 2 = append ": " */
|
||||||
|
|
||||||
if (!sub++) {
|
if (!sub++) {
|
||||||
sub = tmp;
|
sub = tmp;
|
||||||
|
|
||||||
if (sub[0] != '/') /* make sure it's not a command */
|
if (sub[0] != '/') /* make sure it's not a command */
|
||||||
n_endchrs = 2;
|
n_endchrs = 2;
|
||||||
}
|
}
|
||||||
@ -203,14 +248,15 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int s_len = strlen(sub);
|
int s_len = strlen(sub);
|
||||||
const uint8_t *match;
|
const char *match;
|
||||||
bool is_match = false;
|
bool is_match = false;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* look for a match in list */
|
/* look for a match in list */
|
||||||
for (i = 0; i < n_items; ++i) {
|
for (i = 0; i < n_items; ++i) {
|
||||||
match = &L[i*size];
|
match = &L[i * size];
|
||||||
if (is_match = strncasecmp(match, sub, s_len) == 0)
|
|
||||||
|
if ((is_match = strncasecmp(match, sub, s_len) == 0))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,19 +264,19 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
|
|||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* put match in correct spot in buf and append endchars (space or ": ") */
|
/* put match in correct spot in buf and append endchars (space or ": ") */
|
||||||
const uint8_t *endchrs = n_endchrs == 1 ? " " : ": ";
|
const char *endchrs = n_endchrs == 1 ? " " : ": ";
|
||||||
int m_len = strlen(match);
|
int m_len = strlen(match);
|
||||||
int strt = (int) *pos - s_len;
|
int strt = ctx->pos - s_len;
|
||||||
int diff = m_len - s_len + n_endchrs;
|
int diff = m_len - s_len + n_endchrs;
|
||||||
|
|
||||||
if (*len + diff > MAX_STR_SIZE)
|
if (ctx->len + diff > MAX_STR_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
uint8_t tmpend[MAX_STR_SIZE];
|
char tmpend[MAX_STR_SIZE];
|
||||||
strcpy(tmpend, &ubuf[*pos]);
|
strcpy(tmpend, &ubuf[ctx->pos]);
|
||||||
strcpy(&ubuf[strt], match);
|
strcpy(&ubuf[strt], match);
|
||||||
strcpy(&ubuf[strt+m_len], endchrs);
|
strcpy(&ubuf[strt + m_len], endchrs);
|
||||||
strcpy(&ubuf[strt+m_len+n_endchrs], tmpend);
|
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
||||||
|
|
||||||
/* convert to widechar and copy back to original buf */
|
/* convert to widechar and copy back to original buf */
|
||||||
wchar_t newbuf[MAX_STR_SIZE];
|
wchar_t newbuf[MAX_STR_SIZE];
|
||||||
@ -238,10 +284,10 @@ int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int
|
|||||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
wcscpy(buf, newbuf);
|
wcscpy(ctx->line, newbuf);
|
||||||
|
|
||||||
*len += (size_t) diff;
|
ctx->len += diff;
|
||||||
*pos += (size_t) diff;
|
ctx->pos += diff;
|
||||||
|
|
||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
@ -20,44 +20,55 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Adds char to buffer at pos */
|
#ifndef _toxic_strings_h
|
||||||
void add_char_to_buf(wchar_t *buf, size_t *pos, size_t *len, wint_t ch);
|
#define _toxic_strings_h
|
||||||
|
|
||||||
/* Deletes the character before pos */
|
#include "windows.h"
|
||||||
void del_char_buf_bck(wchar_t *buf, size_t *pos, size_t *len);
|
|
||||||
|
|
||||||
/* Deletes the character at pos */
|
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
|
||||||
void del_char_buf_frnt(wchar_t *buf, size_t *pos, size_t *len);
|
int add_char_to_buf(ChatContext *ctx, wint_t ch);
|
||||||
|
|
||||||
/* Deletes the line from beginning to pos */
|
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
|
||||||
void discard_buf(wchar_t *buf, size_t *pos, size_t *len);
|
int del_char_buf_bck(ChatContext *ctx);
|
||||||
|
|
||||||
/* Deletes the line from pos to len */
|
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
|
||||||
void kill_buf(wchar_t *buf, size_t *pos, size_t *len);
|
int del_char_buf_frnt(ChatContext *ctx);
|
||||||
|
|
||||||
/* nulls buf and sets pos and len to 0 */
|
/* Deletes the line from beginning to pos and puts discarded portion in yank buffer.
|
||||||
void reset_buf(wchar_t *buf, size_t *pos, size_t *len);
|
Return 0 on success, -1 if noting to discard */
|
||||||
|
int discard_buf(ChatContext *ctx);
|
||||||
|
|
||||||
/* looks for the first instance in list that begins with the last entered word in buf according to pos,
|
/* Deletes the line from pos to len and puts killed portion in yank buffer.
|
||||||
then fills buf with the complete word. e.g. "Hello jo" would complete the buffer
|
Return 0 on success, -1 if nothing to kill. */
|
||||||
|
int kill_buf(ChatContext *ctx);
|
||||||
|
|
||||||
|
/* nulls line and sets pos, len and start to 0 */
|
||||||
|
void reset_buf(ChatContext *ctx);
|
||||||
|
|
||||||
|
/* Inserts string in ctx->yank into line at pos.
|
||||||
|
Return 0 on success, -1 if yank buffer is empty or too long */
|
||||||
|
int yank_buf(ChatContext *ctx);
|
||||||
|
|
||||||
|
/* Removes trailing spaces from line. */
|
||||||
|
void rm_trailing_spaces_buf(ChatContext *ctx);
|
||||||
|
|
||||||
|
/* looks for the first instance in list that begins with the last entered word in line according to pos,
|
||||||
|
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||||
with "Hello john".
|
with "Hello john".
|
||||||
|
|
||||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||||
in the list, and size is the size of each item in the list.
|
in the list, and size is the size of each item in the list.
|
||||||
|
|
||||||
Returns the difference between the old len and new len of buf on success, -1 if error */
|
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||||
int complete_line(wchar_t *buf, size_t *pos, size_t *len, const void *list, int n_items, int size);
|
int complete_line(ChatContext *ctx, const void *list, int n_items, int size);
|
||||||
|
|
||||||
enum {
|
|
||||||
LN_HIST_MV_UP,
|
|
||||||
LN_HIST_MV_DWN,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
|
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to last history item. */
|
||||||
void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZE], int *hst_tot,
|
void add_line_to_hist(ChatContext *ctx);
|
||||||
int *hst_pos);
|
|
||||||
|
|
||||||
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
|
/* copies history item at hst_pos to line. Sets pos and len to the len of the history item.
|
||||||
hst_pos is decremented or incremented depending on key_dir. */
|
hst_pos is decremented or incremented depending on key_dir.
|
||||||
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
|
|
||||||
int hst_tot, int *hst_pos, int key_dir);
|
resets line if at end of history */
|
||||||
|
void fetch_hist_item(ChatContext *ctx, int key_dir);
|
||||||
|
|
||||||
|
#endif /* #define _toxic_strings_h */
|
||||||
|
@ -1,241 +0,0 @@
|
|||||||
/* toxic_windows.h
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _windows_h
|
|
||||||
#define _windows_h
|
|
||||||
|
|
||||||
#ifndef TOXICVER
|
|
||||||
#define TOXICVER "NOVER_" /* Use the -D flag to set this */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <curses.h>
|
|
||||||
#include <wctype.h>
|
|
||||||
#include <wchar.h>
|
|
||||||
|
|
||||||
#include <tox/tox.h>
|
|
||||||
|
|
||||||
#define UNKNOWN_NAME "Unknown"
|
|
||||||
|
|
||||||
#define MAX_WINDOWS_NUM 32
|
|
||||||
#define MAX_FRIENDS_NUM 100
|
|
||||||
#define MAX_STR_SIZE 256
|
|
||||||
#define MAX_CMDNAME_SIZE 64
|
|
||||||
#define KEY_SIZE_BYTES 32
|
|
||||||
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
|
||||||
#define N_DEFAULT_WINS 2 /* number of permanent default windows */
|
|
||||||
#define CURS_Y_OFFSET 3 /* y-axis cursor offset for chat contexts */
|
|
||||||
#define CHATBOX_HEIGHT 4
|
|
||||||
#define KEY_IDENT_DIGITS 2 /* number of hex digits to display for the pub-key based identifier */
|
|
||||||
|
|
||||||
#define EXIT_SUCCESS 0
|
|
||||||
#define EXIT_FAILURE 1
|
|
||||||
|
|
||||||
/* ASCII key codes */
|
|
||||||
#define T_KEY_KILL 0xB /* ctrl-k */
|
|
||||||
#define T_KEY_DISCARD 0x15 /* ctrl-u */
|
|
||||||
#define T_KEY_NEXT 0x10 /* ctrl-p */
|
|
||||||
#define T_KEY_PREV 0x0F /* ctrl-o */
|
|
||||||
#define T_KEY_C_E 0x05 /* ctrl-e */
|
|
||||||
#define T_KEY_C_A 0x01 /* ctrl-a */
|
|
||||||
|
|
||||||
/* Curses foreground colours (background is black) */
|
|
||||||
enum {
|
|
||||||
WHITE,
|
|
||||||
GREEN,
|
|
||||||
CYAN,
|
|
||||||
RED,
|
|
||||||
BLUE,
|
|
||||||
YELLOW,
|
|
||||||
MAGENTA,
|
|
||||||
BLACK,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* tab alert types: lower types take priority */
|
|
||||||
enum {
|
|
||||||
WINDOW_ALERT_0,
|
|
||||||
WINDOW_ALERT_1,
|
|
||||||
WINDOW_ALERT_2,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Fixes text color problem on some terminals.
|
|
||||||
Uncomment if necessary */
|
|
||||||
//#define URXVT_FIX
|
|
||||||
|
|
||||||
typedef struct ToxWindow ToxWindow;
|
|
||||||
typedef struct StatusBar StatusBar;
|
|
||||||
typedef struct PromptBuf PromptBuf;
|
|
||||||
typedef struct ChatContext ChatContext;
|
|
||||||
|
|
||||||
struct ToxWindow {
|
|
||||||
void(*onKey)(ToxWindow *, Tox *, wint_t);
|
|
||||||
void(*onDraw)(ToxWindow *, Tox *);
|
|
||||||
void(*onInit)(ToxWindow *, Tox *);
|
|
||||||
void(*onFriendRequest)(ToxWindow *, uint8_t *, uint8_t *, uint16_t);
|
|
||||||
void(*onFriendAdded)(ToxWindow *, Tox *, int, bool);
|
|
||||||
void(*onConnectionChange)(ToxWindow *, Tox *, int, uint8_t);
|
|
||||||
void(*onMessage)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
|
||||||
void(*onNickChange)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
|
||||||
void(*onStatusChange)(ToxWindow *, Tox *, int, TOX_USERSTATUS);
|
|
||||||
void(*onStatusMessageChange)(ToxWindow *, int, uint8_t *, uint16_t);
|
|
||||||
void(*onAction)(ToxWindow *, Tox *, int, uint8_t *, uint16_t);
|
|
||||||
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
|
|
||||||
void(*onGroupAction)(ToxWindow *, Tox *, int, int, uint8_t *, uint16_t);
|
|
||||||
void(*onGroupInvite)(ToxWindow *, Tox *, int, uint8_t *);
|
|
||||||
void(*onGroupNamelistChange)(ToxWindow *, Tox*, int, int, uint8_t);
|
|
||||||
void(*onFileSendRequest)(ToxWindow *, Tox *, int, uint8_t, uint64_t, uint8_t *, uint16_t);
|
|
||||||
void(*onFileControl)(ToxWindow *, Tox *, int, uint8_t, uint8_t, uint8_t, uint8_t *, uint16_t);
|
|
||||||
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
|
|
||||||
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
|
|
||||||
|
|
||||||
char name[TOX_MAX_NAME_LENGTH];
|
|
||||||
int num;
|
|
||||||
bool active;
|
|
||||||
int x;
|
|
||||||
|
|
||||||
/* window type identifiers */
|
|
||||||
bool is_chat;
|
|
||||||
bool is_groupchat;
|
|
||||||
bool is_prompt;
|
|
||||||
|
|
||||||
bool alert0;
|
|
||||||
bool alert1;
|
|
||||||
bool alert2;
|
|
||||||
|
|
||||||
ChatContext *chatwin;
|
|
||||||
PromptBuf *promptbuf;
|
|
||||||
StatusBar *stb;
|
|
||||||
|
|
||||||
WINDOW *window;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* statusbar info holder */
|
|
||||||
struct StatusBar {
|
|
||||||
WINDOW *topline;
|
|
||||||
uint8_t statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
|
||||||
uint16_t statusmsg_len;
|
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH];
|
|
||||||
uint16_t nick_len;
|
|
||||||
TOX_USERSTATUS status;
|
|
||||||
bool is_online;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_LOG_BUF_LINES 10 /* write log_buf contents to log file after this many lines */
|
|
||||||
#define MAX_LOG_LINE_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* extra room for timestamp */
|
|
||||||
|
|
||||||
struct chatlog {
|
|
||||||
uint8_t log_path[MAX_STR_SIZE];
|
|
||||||
uint8_t log_buf[MAX_LOG_BUF_LINES][MAX_LOG_LINE_SIZE];
|
|
||||||
int pos;
|
|
||||||
bool log_on; /* specific to current chat window */
|
|
||||||
};
|
|
||||||
|
|
||||||
#define MAX_LINE_HIST 128
|
|
||||||
|
|
||||||
/* chat and groupchat window/buffer holder */
|
|
||||||
struct ChatContext {
|
|
||||||
wchar_t line[MAX_STR_SIZE];
|
|
||||||
size_t pos;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
|
|
||||||
int hst_pos;
|
|
||||||
int hst_tot;
|
|
||||||
|
|
||||||
bool self_is_typing;
|
|
||||||
|
|
||||||
struct chatlog *log;
|
|
||||||
|
|
||||||
WINDOW *history;
|
|
||||||
WINDOW *linewin;
|
|
||||||
WINDOW *sidebar;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* prompt window/buffer holder */
|
|
||||||
struct PromptBuf {
|
|
||||||
wchar_t line[MAX_STR_SIZE];
|
|
||||||
size_t pos;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
bool at_bottom; /* true if line end is at bottom of window */
|
|
||||||
int orig_y; /* y axis point of line origin */
|
|
||||||
bool scroll; /* used for prompt window hack to determine when to scroll down */
|
|
||||||
|
|
||||||
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE];
|
|
||||||
int hst_pos;
|
|
||||||
int hst_tot;
|
|
||||||
|
|
||||||
struct chatlog *log;
|
|
||||||
WINDOW *linewin;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Start file transfer code */
|
|
||||||
|
|
||||||
#define MAX_FILES 256
|
|
||||||
#define FILE_PIECE_SIZE 1024
|
|
||||||
#define TIMEOUT_FILESENDER 300
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FILE *file;
|
|
||||||
ToxWindow *toxwin;
|
|
||||||
int friendnum;
|
|
||||||
bool active;
|
|
||||||
uint8_t filenum;
|
|
||||||
uint8_t nextpiece[FILE_PIECE_SIZE];
|
|
||||||
uint16_t piecelen;
|
|
||||||
uint8_t pathname[MAX_STR_SIZE];
|
|
||||||
uint64_t timestamp;
|
|
||||||
} FileSender;
|
|
||||||
|
|
||||||
struct FileReceiver {
|
|
||||||
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
|
||||||
bool pending[MAX_FILES];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* End file transfer code */
|
|
||||||
|
|
||||||
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata);
|
|
||||||
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata);
|
|
||||||
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
|
||||||
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
|
||||||
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
|
||||||
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata);
|
|
||||||
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata);
|
|
||||||
void on_friendadded(Tox *m, int friendnumber, bool sort);
|
|
||||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length, void *userdata);
|
|
||||||
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length, void *userdata);
|
|
||||||
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata);
|
|
||||||
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
|
|
||||||
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize, uint8_t *pathname, uint16_t pathname_length, void *userdata);
|
|
||||||
void on_file_control(Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber, uint8_t control_type, uint8_t *data, uint16_t length, void *userdata);
|
|
||||||
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length, void *userdata);
|
|
||||||
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata);
|
|
||||||
|
|
||||||
ToxWindow *init_windows(Tox *m);
|
|
||||||
void draw_active_window(Tox *m);
|
|
||||||
int add_window(Tox *m, ToxWindow w);
|
|
||||||
void del_window(ToxWindow *w);
|
|
||||||
void set_active_window(int ch);
|
|
||||||
int num_active_windows(void);
|
|
||||||
|
|
||||||
/* closes all chat and groupchat windows (should only be called on shutdown) */
|
|
||||||
void kill_all_windows(void);
|
|
||||||
#endif
|
|
253
src/windows.c
253
src/windows.c
@ -20,37 +20,40 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
|
||||||
#include "config.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "toxic_windows.h"
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
|
#include "chat.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
|
extern struct _Winthread Winthread;
|
||||||
static ToxWindow windows[MAX_WINDOWS_NUM];
|
static ToxWindow windows[MAX_WINDOWS_NUM];
|
||||||
static ToxWindow *active_window;
|
static ToxWindow *active_window;
|
||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
|
static int num_active_windows;
|
||||||
|
|
||||||
/* CALLBACKS START */
|
/* CALLBACKS START */
|
||||||
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
|
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFriendRequest != NULL)
|
if (windows[i].onFriendRequest != NULL)
|
||||||
windows[i].onFriendRequest(&windows[i], public_key, data, length);
|
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, (const char *) data, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdata)
|
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -60,7 +63,7 @@ void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
|
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -70,27 +73,27 @@ void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
|
void on_message(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onMessage != NULL)
|
if (windows[i].onMessage != NULL)
|
||||||
windows[i].onMessage(&windows[i], m, friendnumber, string, length);
|
windows[i].onMessage(&windows[i], m, friendnumber, (const char *) string, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
|
void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onAction != NULL)
|
if (windows[i].onAction != NULL)
|
||||||
windows[i].onAction(&windows[i], m, friendnumber, string, length);
|
windows[i].onAction(&windows[i], m, friendnumber, (const char *) string, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
|
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
|
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
|
||||||
return;
|
return;
|
||||||
@ -99,24 +102,23 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v
|
|||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onNickChange != NULL)
|
if (windows[i].onNickChange != NULL)
|
||||||
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
|
windows[i].onNickChange(&windows[i], m, friendnumber, (const char *) string, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store_data(m, DATA_FILE))
|
store_data(m, DATA_FILE);
|
||||||
wprintw(prompt->window, "\nCould not store Tox data\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void *userdata)
|
void on_statusmessagechange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onStatusMessageChange != NULL)
|
if (windows[i].onStatusMessageChange != NULL)
|
||||||
windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length);
|
windows[i].onStatusMessageChange(&windows[i], friendnumber, (const char *) string, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *userdata)
|
void on_statuschange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -126,7 +128,7 @@ void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *user
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_friendadded(Tox *m, int friendnumber, bool sort)
|
void on_friendadded(Tox *m, int32_t friendnumber, bool sort)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -135,39 +137,38 @@ void on_friendadded(Tox *m, int friendnumber, bool sort)
|
|||||||
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
|
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (store_data(m, DATA_FILE))
|
store_data(m, DATA_FILE);
|
||||||
wprintw(prompt->window, "\nCould not store Tox data\n");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message, uint16_t length,
|
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onGroupMessage != NULL)
|
if (windows[i].onGroupMessage != NULL)
|
||||||
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length);
|
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, (const char *) message, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, uint16_t length,
|
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onGroupAction != NULL)
|
if (windows[i].onGroupAction != NULL)
|
||||||
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length);
|
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, (const char *) action, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *userdata)
|
void on_groupinvite(Tox *m, int32_t friendnumber, const uint8_t *group_pub_key, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onGroupInvite != NULL)
|
if (windows[i].onGroupInvite != NULL)
|
||||||
windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key);
|
windows[i].onGroupInvite(&windows[i], m, friendnumber, (const char *) group_pub_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,38 +182,38 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t filesize,
|
void on_file_sendrequest(Tox *m, int32_t friendnumber, uint8_t filenumber, uint64_t filesize,
|
||||||
uint8_t *filename, uint16_t filename_length, void *userdata)
|
const uint8_t *filename, uint16_t filename_length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFileSendRequest != NULL)
|
if (windows[i].onFileSendRequest != NULL)
|
||||||
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
|
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
|
||||||
filename, filename_length);
|
(const char *) filename, filename_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t filenumber,
|
void on_file_control (Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t filenumber,
|
||||||
uint8_t control_type, uint8_t *data, uint16_t length, void *userdata)
|
uint8_t control_type, const uint8_t *data, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFileControl != NULL)
|
if (windows[i].onFileControl != NULL)
|
||||||
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
|
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
|
||||||
control_type, data, length);
|
control_type, (const char *) data, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, uint16_t length,
|
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFileData != NULL)
|
if (windows[i].onFileData != NULL)
|
||||||
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length);
|
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, (const char *) data, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,29 +234,30 @@ int add_window(Tox *m, ToxWindow w)
|
|||||||
|
|
||||||
if (w.window == NULL)
|
if (w.window == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
#ifdef URXVT_FIX
|
#ifdef URXVT_FIX
|
||||||
/* Fixes text color problem on some terminals. */
|
/* Fixes text color problem on some terminals. */
|
||||||
wbkgd(w.window, COLOR_PAIR(6));
|
wbkgd(w.window, COLOR_PAIR(6));
|
||||||
#endif
|
#endif
|
||||||
windows[i] = w;
|
windows[i] = w;
|
||||||
|
|
||||||
|
if (w.onInit)
|
||||||
w.onInit(&w, m);
|
w.onInit(&w, m);
|
||||||
|
|
||||||
|
++num_active_windows;
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Deletes window w and cleans up */
|
void set_active_window(int index)
|
||||||
void del_window(ToxWindow *w)
|
|
||||||
{
|
{
|
||||||
active_window = windows; // Go to prompt screen
|
if (index < 0 || index >= MAX_WINDOWS_NUM)
|
||||||
|
return;
|
||||||
|
|
||||||
delwin(w->window);
|
active_window = windows + index;
|
||||||
memset(w, 0, sizeof(ToxWindow));
|
|
||||||
|
|
||||||
clear();
|
|
||||||
refresh();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shows next window when tab or back-tab is pressed */
|
/* Shows next window when tab or back-tab is pressed */
|
||||||
@ -274,31 +276,30 @@ void set_next_window(int ch)
|
|||||||
if (active_window->window)
|
if (active_window->window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (active_window == inf) { // infinite loop check
|
if (active_window == inf) /* infinite loop check */
|
||||||
endwin();
|
exit_toxic_err("failed in set_next_window", FATALERR_INFLOOP);
|
||||||
fprintf(stderr, "set_next_window() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_active_window(int index)
|
/* Deletes window w and cleans up */
|
||||||
|
void del_window(ToxWindow *w)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= MAX_WINDOWS_NUM)
|
set_active_window(0); /* Go to prompt screen */
|
||||||
return;
|
|
||||||
|
|
||||||
active_window = windows + index;
|
delwin(w->window);
|
||||||
|
memset(w, 0, sizeof(ToxWindow));
|
||||||
|
|
||||||
|
clear();
|
||||||
|
refresh();
|
||||||
|
--num_active_windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxWindow *init_windows(Tox *m)
|
ToxWindow *init_windows(Tox *m)
|
||||||
{
|
{
|
||||||
int n_prompt = add_window(m, new_prompt());
|
int n_prompt = add_window(m, new_prompt());
|
||||||
|
|
||||||
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1) {
|
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1)
|
||||||
endwin();
|
exit_toxic_err("failed in init_windows", FATALERR_WININIT);
|
||||||
fprintf(stderr, "add_window() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt = &windows[n_prompt];
|
prompt = &windows[n_prompt];
|
||||||
active_window = prompt;
|
active_window = prompt;
|
||||||
@ -306,6 +307,65 @@ ToxWindow *init_windows(Tox *m)
|
|||||||
return prompt;
|
return prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_window_resize(void)
|
||||||
|
{
|
||||||
|
endwin();
|
||||||
|
refresh();
|
||||||
|
clear();
|
||||||
|
|
||||||
|
/* equivalent to LINES and COLS */
|
||||||
|
int x2, y2;
|
||||||
|
getmaxyx(stdscr, y2, x2);
|
||||||
|
y2 -= 2;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (!windows[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ToxWindow *w = &windows[i];
|
||||||
|
|
||||||
|
if (windows[i].is_friendlist) {
|
||||||
|
delwin(w->window);
|
||||||
|
w->window = newwin(y2, x2, 0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (w->help->active)
|
||||||
|
wclear(w->help->win);
|
||||||
|
|
||||||
|
if (w->is_groupchat)
|
||||||
|
delwin(w->chatwin->sidebar);
|
||||||
|
else
|
||||||
|
delwin(w->stb->topline);
|
||||||
|
|
||||||
|
delwin(w->chatwin->linewin);
|
||||||
|
delwin(w->chatwin->history);
|
||||||
|
delwin(w->window);
|
||||||
|
|
||||||
|
w->window = newwin(y2, x2, 0, 0);
|
||||||
|
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
|
|
||||||
|
if (w->is_groupchat) {
|
||||||
|
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
|
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||||
|
} else {
|
||||||
|
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||||
|
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
if (w->chatwin->infobox.active) {
|
||||||
|
delwin(w->chatwin->infobox.win);
|
||||||
|
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
|
||||||
|
}
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
scrollok(w->chatwin->history, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void draw_window_tab(ToxWindow toxwin)
|
static void draw_window_tab(ToxWindow toxwin)
|
||||||
{
|
{
|
||||||
/* alert0 takes priority */
|
/* alert0 takes priority */
|
||||||
@ -342,26 +402,29 @@ static void draw_bar(void)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].active) {
|
if (!windows[i].active)
|
||||||
if (windows + i == active_window) {
|
continue;
|
||||||
|
|
||||||
|
if (windows + i == active_window)
|
||||||
|
|
||||||
#ifdef URXVT_FIX
|
#ifdef URXVT_FIX
|
||||||
attron(A_BOLD | COLOR_PAIR(GREEN));
|
attron(A_BOLD | COLOR_PAIR(GREEN));
|
||||||
} else {
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
attron(A_BOLD);
|
attron(A_BOLD);
|
||||||
}
|
|
||||||
|
|
||||||
draw_window_tab(windows[i]);
|
draw_window_tab(windows[i]);
|
||||||
|
|
||||||
if (windows + i == active_window) {
|
if (windows + i == active_window)
|
||||||
|
|
||||||
#ifdef URXVT_FIX
|
#ifdef URXVT_FIX
|
||||||
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
||||||
} else {
|
else
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
attroff(A_BOLD);
|
attroff(A_BOLD);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
}
|
}
|
||||||
@ -378,42 +441,64 @@ void draw_active_window(Tox *m)
|
|||||||
draw_bar();
|
draw_bar();
|
||||||
|
|
||||||
touchwin(a->window);
|
touchwin(a->window);
|
||||||
#ifndef WIN32
|
|
||||||
wresize(a->window, LINES - 2, COLS);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
a->onDraw(a, m);
|
a->onDraw(a, m);
|
||||||
wrefresh(a->window);
|
|
||||||
|
|
||||||
/* Handle input */
|
/* Handle input */
|
||||||
|
bool ltr;
|
||||||
#ifdef HAVE_WIDECHAR
|
#ifdef HAVE_WIDECHAR
|
||||||
wget_wch(stdscr, &ch);
|
int status = wget_wch(stdscr, &ch);
|
||||||
|
|
||||||
|
if (status == ERR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (status == OK)
|
||||||
|
ltr = iswprint(ch);
|
||||||
|
else /* if (status == KEY_CODE_YES) */
|
||||||
|
ltr = false;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
ch = getch();
|
ch = getch();
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ch == T_KEY_NEXT || ch == T_KEY_PREV)
|
if (ch == ERR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* TODO verify if this works */
|
||||||
|
ltr = isprint(ch);
|
||||||
|
#endif /* HAVE_WIDECHAR */
|
||||||
|
|
||||||
|
if (!ltr && (ch == T_KEY_NEXT || ch == T_KEY_PREV)) {
|
||||||
set_next_window((int) ch);
|
set_next_window((int) ch);
|
||||||
else if (ch != ERR)
|
} else {
|
||||||
a->onKey(a, m, ch);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
a->onKey(a, m, ch, ltr);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_active_windows(void)
|
/* refresh inactive windows to prevent scrolling bugs.
|
||||||
|
call at least once per second */
|
||||||
|
void refresh_inactive_windows(void)
|
||||||
{
|
{
|
||||||
int count = 0;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].active)
|
ToxWindow *a = &windows[i];
|
||||||
++count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
if (a->active && a != active_window && !a->is_friendlist)
|
||||||
|
line_info_print(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_num_active_windows(void)
|
||||||
|
{
|
||||||
|
return num_active_windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
||||||
void kill_all_windows(void)
|
void kill_all_windows(void)
|
||||||
{
|
{
|
||||||
|
kill_prompt_window(prompt);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
222
src/windows.h
Normal file
222
src/windows.h
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
/* windows.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _windows_h
|
||||||
|
#define _windows_h
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <wctype.h>
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#include <tox/tox.h>
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
|
||||||
|
#define MAX_WINDOWS_NUM 32
|
||||||
|
#define MAX_WINDOW_NAME_LENGTH 16
|
||||||
|
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
|
||||||
|
#define CHATBOX_HEIGHT 2
|
||||||
|
|
||||||
|
/* Curses foreground colours (background is black) */
|
||||||
|
enum {
|
||||||
|
WHITE,
|
||||||
|
GREEN,
|
||||||
|
CYAN,
|
||||||
|
RED,
|
||||||
|
BLUE,
|
||||||
|
YELLOW,
|
||||||
|
MAGENTA,
|
||||||
|
BLACK,
|
||||||
|
} C_COLOURS;
|
||||||
|
|
||||||
|
/* tab alert types: lower types take priority */
|
||||||
|
enum {
|
||||||
|
WINDOW_ALERT_0,
|
||||||
|
WINDOW_ALERT_1,
|
||||||
|
WINDOW_ALERT_2,
|
||||||
|
} WINDOW_ALERTS;
|
||||||
|
|
||||||
|
/* Fixes text color problem on some terminals.
|
||||||
|
Uncomment if necessary */
|
||||||
|
/* #define URXVT_FIX */
|
||||||
|
|
||||||
|
struct _Winthread {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_mutex_t lock;
|
||||||
|
bool sig_exit_toxic;
|
||||||
|
bool flag_resize;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct ToxWindow ToxWindow;
|
||||||
|
typedef struct StatusBar StatusBar;
|
||||||
|
typedef struct PromptBuf PromptBuf;
|
||||||
|
typedef struct ChatContext ChatContext;
|
||||||
|
typedef struct Help Help;
|
||||||
|
|
||||||
|
struct ToxWindow {
|
||||||
|
void(*onKey)(ToxWindow *, Tox *, wint_t, bool);
|
||||||
|
void(*onDraw)(ToxWindow *, Tox *);
|
||||||
|
void(*onInit)(ToxWindow *, Tox *);
|
||||||
|
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, uint16_t);
|
||||||
|
void(*onFriendAdded)(ToxWindow *, Tox *, int32_t, bool);
|
||||||
|
void(*onConnectionChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
||||||
|
void(*onMessage)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
|
||||||
|
void(*onNickChange)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
|
||||||
|
void(*onStatusChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
||||||
|
void(*onStatusMessageChange)(ToxWindow *, int32_t, const char *, uint16_t);
|
||||||
|
void(*onAction)(ToxWindow *, Tox *, int32_t, const char *, uint16_t);
|
||||||
|
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
|
||||||
|
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
|
||||||
|
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, const char *);
|
||||||
|
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
|
||||||
|
void(*onFileSendRequest)(ToxWindow *, Tox *, int32_t, uint8_t, uint64_t, const char *, uint16_t);
|
||||||
|
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
|
||||||
|
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||||
|
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
void(*onInvite)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onRinging)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onStarting)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onEnding)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onError)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onStart)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onCancel)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onReject)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onEnd)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
|
||||||
|
void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
|
||||||
|
|
||||||
|
int call_idx; /* If in a call will have this index set, otherwise it's -1.
|
||||||
|
* Don't modify outside av callbacks. */
|
||||||
|
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
char name[TOXIC_MAX_NAME_LENGTH];
|
||||||
|
int32_t num; /* corresponds to friendnumber in chat windows */
|
||||||
|
bool active;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
bool is_chat;
|
||||||
|
bool is_groupchat;
|
||||||
|
bool is_prompt;
|
||||||
|
bool is_friendlist;
|
||||||
|
|
||||||
|
bool alert0;
|
||||||
|
bool alert1;
|
||||||
|
bool alert2;
|
||||||
|
|
||||||
|
ChatContext *chatwin;
|
||||||
|
StatusBar *stb;
|
||||||
|
Help *help;
|
||||||
|
|
||||||
|
WINDOW *window;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* statusbar info holder */
|
||||||
|
struct StatusBar {
|
||||||
|
WINDOW *topline;
|
||||||
|
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||||
|
uint16_t statusmsg_len;
|
||||||
|
char nick[TOXIC_MAX_NAME_LENGTH];
|
||||||
|
int nick_len;
|
||||||
|
uint8_t status;
|
||||||
|
bool is_online;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
|
||||||
|
#define INFOBOX_HEIGHT 7
|
||||||
|
#define INFOBOX_WIDTH 21
|
||||||
|
/* holds display info for audio calls */
|
||||||
|
struct infobox {
|
||||||
|
float vad_lvl;
|
||||||
|
bool in_is_muted;
|
||||||
|
bool out_is_muted;
|
||||||
|
bool hide;
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
uint64_t lastupdate;
|
||||||
|
uint64_t starttime;
|
||||||
|
char timestr[TIME_STR_SIZE];
|
||||||
|
|
||||||
|
WINDOW *win;
|
||||||
|
};
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#define MAX_LINE_HIST 128
|
||||||
|
|
||||||
|
/* chat and groupchat window/buffer holder */
|
||||||
|
struct ChatContext {
|
||||||
|
wchar_t line[MAX_STR_SIZE];
|
||||||
|
int pos;
|
||||||
|
int len;
|
||||||
|
int start; /* the position to start printing line at */
|
||||||
|
|
||||||
|
wchar_t ln_history[MAX_LINE_HIST][MAX_STR_SIZE]; /* history for input lines/commands */
|
||||||
|
int hst_pos;
|
||||||
|
int hst_tot;
|
||||||
|
|
||||||
|
wchar_t yank[MAX_STR_SIZE]; /* contains last killed/discarded line */
|
||||||
|
int yank_len;
|
||||||
|
|
||||||
|
struct history *hst;
|
||||||
|
struct chatlog *log;
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
struct infobox infobox;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint8_t self_is_typing;
|
||||||
|
|
||||||
|
WINDOW *history;
|
||||||
|
WINDOW *linewin;
|
||||||
|
WINDOW *sidebar;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Help {
|
||||||
|
WINDOW *win;
|
||||||
|
int type;
|
||||||
|
bool active;
|
||||||
|
};
|
||||||
|
|
||||||
|
ToxWindow *init_windows(Tox *m);
|
||||||
|
void draw_active_window(Tox *m);
|
||||||
|
int add_window(Tox *m, ToxWindow w);
|
||||||
|
void del_window(ToxWindow *w);
|
||||||
|
void set_active_window(int ch);
|
||||||
|
int get_num_active_windows(void);
|
||||||
|
void kill_all_windows(void); /* should only be called on shutdown */
|
||||||
|
void on_window_resize(void);
|
||||||
|
|
||||||
|
/* refresh inactive windows to prevent scrolling bugs.
|
||||||
|
call at least once per second */
|
||||||
|
void refresh_inactive_windows(void);
|
||||||
|
|
||||||
|
#endif /* #define _windows_h */
|
Reference in New Issue
Block a user