mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 20:56:46 +02:00
Compare commits
158 Commits
Author | SHA1 | Date | |
---|---|---|---|
74c1eef1d1 | |||
65c07a57db | |||
ab99c1ac73 | |||
05f5f16af3 | |||
d16be574f3 | |||
7e0b8b4870 | |||
39c4b7ecdd | |||
c5d9aca3e1 | |||
fa0e645a79 | |||
14a8bdb874 | |||
93a73cbef2 | |||
6aab9a79d8 | |||
dfff777283 | |||
a95fc7824c | |||
f707dce2da | |||
0d07d14b13 | |||
6cc1525daa | |||
49f5efaab0 | |||
a5e5e98afc | |||
4ab99c73a0 | |||
e02cf1bb7d | |||
9751cfc407 | |||
36963a5b38 | |||
7cf9c37aef | |||
2b4b8c0289 | |||
368a1465ec | |||
fea317ee24 | |||
8584feab80 | |||
f7e48d294e | |||
928f25bd89 | |||
941ac1d951 | |||
7af9327b37 | |||
6b97df2615 | |||
cea5f1fe04 | |||
abfdbfe468 | |||
462cfca175 | |||
db410cb01e | |||
a920f3edfe | |||
2c3921a9fb | |||
f295352495 | |||
ffcc804efe | |||
69be1bc398 | |||
b4464eda4d | |||
28dd43608d | |||
11701d22a1 | |||
4e2db756be | |||
19cfe3d393 | |||
c546df3917 | |||
ed0a4fb3b8 | |||
271ca08eb2 | |||
0e79b8a076 | |||
1606d01158 | |||
c8a9ac21f3 | |||
e91aaf6c73 | |||
619fdc1098 | |||
b7e613de32 | |||
929fca3de1 | |||
b67792f9f2 | |||
96162bf254 | |||
8a66c3fa4c | |||
2cdcbc07a7 | |||
6e0d19b01d | |||
ad04fa4dcd | |||
c2c612b85a | |||
d359ba6a54 | |||
54e2fe8d6f | |||
53353825e2 | |||
fcdc8e8b67 | |||
9b6efb65de | |||
c8ea02376e | |||
2369b5e9e2 | |||
8f28f1d748 | |||
a33e5f4bec | |||
e0a35a6569 | |||
9863dfc2ae | |||
c755247434 | |||
879b2b236e | |||
c6b9a288b6 | |||
e9e5b5af8d | |||
d175ff2480 | |||
750258adef | |||
60b4d62657 | |||
ea78dca756 | |||
3cb412632b | |||
8301ab1bc2 | |||
b6e90d2ebb | |||
06c268417f | |||
1458a6bbc5 | |||
737d29864b | |||
0a2ad23c15 | |||
a455c80a1f | |||
43bda5f7d9 | |||
f2121fae74 | |||
3241551cfb | |||
6e90072fb8 | |||
ca1fca5aa5 | |||
ef1068b6aa | |||
72982cee97 | |||
9a4eaa8693 | |||
64e7553fb0 | |||
dd8df1df76 | |||
c8d102b02d | |||
a3fa7fd524 | |||
b2ed8c0ead | |||
bbdf4c96b9 | |||
5496890b34 | |||
fd85d8f87b | |||
37e7b4c3d3 | |||
92d76c7f99 | |||
2a787c1097 | |||
327259c4c8 | |||
f173f4275e | |||
48eaf8a14f | |||
083611f18e | |||
48ffae68a9 | |||
c39f8909cd | |||
32e541bd1c | |||
f559bdabfe | |||
0047ba0e9f | |||
ecefc19b23 | |||
e83b397494 | |||
688ea927f8 | |||
904f58c0e8 | |||
035420e5c7 | |||
444d8e7a74 | |||
84a0276668 | |||
312d0c3f42 | |||
d8eca8393c | |||
374b78c763 | |||
409e4ddd96 | |||
1beb35025b | |||
51a1c660b4 | |||
85d3c18ba6 | |||
d0a7ca17d2 | |||
36640224af | |||
231078b6b9 | |||
414f58d896 | |||
4d73f8b241 | |||
82e76a3b5b | |||
0bc610e18d | |||
02e6d2db3c | |||
5a2c341259 | |||
1a7eaeddba | |||
f656d0a722 | |||
09c1ad4566 | |||
8b9e34db75 | |||
dd9186e834 | |||
5ff1517b28 | |||
bbb639c5aa | |||
860db2f612 | |||
523f205646 | |||
e998c8a866 | |||
eaea68c33e | |||
4780cfeafc | |||
bdb0951c84 | |||
e3130c92c0 | |||
12c880ab51 | |||
522aabd4e4 |
52
.travis.yml
52
.travis.yml
@ -1,52 +0,0 @@
|
|||||||
language: c
|
|
||||||
compiler:
|
|
||||||
- gcc
|
|
||||||
- clang
|
|
||||||
|
|
||||||
before_script:
|
|
||||||
# Installing yasm (needed for compiling vpx) and openal
|
|
||||||
- sudo apt-get -yq install yasm libopenal-dev libconfig-dev libalut-dev libnotify-dev clang llvm-dev
|
|
||||||
# Installing libsodium, needed for toxcore
|
|
||||||
- git clone https://github.com/jedisct1/libsodium.git libsodium
|
|
||||||
- cd libsodium
|
|
||||||
- git checkout tags/1.0.2 > /dev/null
|
|
||||||
- ./autogen.sh > /dev/null
|
|
||||||
- ./configure > /dev/null
|
|
||||||
- make check -j2 || make check || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Installing libopus, needed for audio encoding/decoding
|
|
||||||
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz
|
|
||||||
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
|
||||||
- cd opus-1.0.3
|
|
||||||
- ./configure > /dev/null
|
|
||||||
- make -j2 || make || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Installing vpx
|
|
||||||
- git clone http://git.chromium.org/webm/libvpx.git libvpx
|
|
||||||
- cd libvpx
|
|
||||||
- ./configure --enable-shared > /dev/null
|
|
||||||
- make -j2 || make || exit 1 > /dev/null
|
|
||||||
- sudo make install > /dev/null
|
|
||||||
- cd ..
|
|
||||||
# Creating libraries links and updating cache
|
|
||||||
- sudo ldconfig > /dev/null
|
|
||||||
# Installing toxcore
|
|
||||||
- git clone https://github.com/irungentoo/toxcore.git toxcore
|
|
||||||
- cd toxcore
|
|
||||||
- autoreconf -i
|
|
||||||
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
|
||||||
- make -j2 || make || exit 1
|
|
||||||
- sudo make install
|
|
||||||
- cd ..
|
|
||||||
script:
|
|
||||||
- make -j2 || make || exit 1
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
|
|
||||||
irc:
|
|
||||||
channels:
|
|
||||||
- "chat.freenode.net#tox-dev"
|
|
||||||
on_success: always
|
|
||||||
on_failure: always
|
|
24
INSTALL.md
24
INSTALL.md
@ -9,17 +9,19 @@
|
|||||||
|
|
||||||
<a name="deps" />
|
<a name="deps" />
|
||||||
## Dependencies
|
## Dependencies
|
||||||
| Name | Needed by | Debian package |
|
| Name | Needed by | Debian package |
|
||||||
|------------------------------------------------------|----------------------------|------------------|
|
|------------------------------------------------------|----------------------------|---------------------|
|
||||||
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
|
||||||
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
||||||
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
||||||
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
||||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
|
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
|
||||||
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
|
||||||
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
|
||||||
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||||
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||||
|
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||||
|
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||||
<sup>1</sup>: see [Documentation](#docs)
|
<sup>1</sup>: see [Documentation](#docs)
|
||||||
|
|
||||||
<a name="deps_osx" />
|
<a name="deps_osx" />
|
||||||
|
15
Makefile
15
Makefile
@ -3,18 +3,18 @@ CFG_DIR = $(BASE_DIR)/cfg
|
|||||||
|
|
||||||
-include $(CFG_DIR)/global_vars.mk
|
-include $(CFG_DIR)/global_vars.mk
|
||||||
|
|
||||||
LIBS = libtoxcore ncursesw libconfig
|
LIBS = libtoxcore ncursesw libconfig libqrencode
|
||||||
|
|
||||||
CFLAGS = -std=gnu99 -pthread -Wall -g
|
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
|
||||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||||
CFLAGS += $(USER_CFLAGS)
|
CFLAGS += $(USER_CFLAGS)
|
||||||
LDFLAGS = $(USER_LDFLAGS)
|
LDFLAGS = $(USER_LDFLAGS)
|
||||||
|
|
||||||
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_transfers.o notify.o
|
OBJ = chat.o chat_commands.o configdir.o execute.o file_transfers.o notify.o
|
||||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
||||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
|
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
|
||||||
OBJ += group_commands.o term_mplex.o
|
OBJ += group_commands.o term_mplex.o avatars.o name_lookup.o qr_code.o
|
||||||
|
|
||||||
# Check on wich system we are running
|
# Check on wich system we are running
|
||||||
UNAME_S = $(shell uname -s)
|
UNAME_S = $(shell uname -s)
|
||||||
@ -24,6 +24,9 @@ endif
|
|||||||
ifeq ($(UNAME_S), FreeBSD)
|
ifeq ($(UNAME_S), FreeBSD)
|
||||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(UNAME_S), DragonFly)
|
||||||
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
|
endif
|
||||||
ifeq ($(UNAME_S), OpenBSD)
|
ifeq ($(UNAME_S), OpenBSD)
|
||||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||||
endif
|
endif
|
||||||
@ -59,6 +62,10 @@ $(BUILD_DIR)/toxic: $(OBJ)
|
|||||||
@echo " LD $(@:$(BUILD_DIR)/%=%)"
|
@echo " LD $(@:$(BUILD_DIR)/%=%)"
|
||||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
|
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/toxic $(OBJ) $(LDFLAGS)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/osx_video.o: $(SRC_DIR)/$(OSX_VIDEO)
|
||||||
|
@echo " CC $(@:$(BUILD_DIR)/)osx_video.o"
|
||||||
|
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/osx_video.o -c $(SRC_DIR)/$(OSX_VIDEO)
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||||
@if [ ! -e $(BUILD_DIR) ]; then \
|
@if [ ! -e $(BUILD_DIR) ]; then \
|
||||||
mkdir -p $(BUILD_DIR) ;\
|
mkdir -p $(BUILD_DIR) ;\
|
||||||
|
24
README.md
24
README.md
@ -1,26 +1,16 @@
|
|||||||
# Toxic [](https://travis-ci.org/Tox/toxic)
|
<a href="https://scan.coverity.com/projects/toxic-tox">
|
||||||
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.
|
<img alt="Coverity Scan Build Status"
|
||||||
|
src="https://scan.coverity.com/projects/4975/badge.svg"/>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
|
||||||
|
|
||||||
[](https://i.imgur.com/san99Z2.png)
|
[](https://i.imgur.com/san99Z2.png)
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
[Use our repositories](https://wiki.tox.im/Binaries#Linux)<br />
|
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
|
||||||
[Compile it yourself](/INSTALL.md)
|
[Compile it yourself](/INSTALL.md)
|
||||||
|
|
||||||
## Downloads
|
|
||||||
If you don't like installation methods listed above, you can still download precompiled binaries from [jenkins](https://jenkins.libtoxcore.so):
|
|
||||||
* [Linux 32 bit](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz) [](https://jenkins.libtoxcore.so/job/toxic_linux_i386/lastSuccessfulBuild/artifact/toxic_linux_i386.tar.xz)
|
|
||||||
* [Linux 64 bit](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz) [](https://jenkins.libtoxcore.so/job/toxic_linux_amd64/lastSuccessfulBuild/artifact/toxic_linux_amd64.tar.xz)
|
|
||||||
* [~~Linux ARMv6~~](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz) **CURRENTLY DISABLED** [](https://jenkins.libtoxcore.so/job/toxic_linux_armv6/lastSuccessfulBuild/artifact/toxic_linux_armv6.tar.xz)
|
|
||||||
|
|
||||||
#### DEBs packages
|
|
||||||
* [toxic-i386.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.deb)
|
|
||||||
* [toxic-x86_64.deb](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.deb)
|
|
||||||
|
|
||||||
#### RPMs packages
|
|
||||||
* [toxic-i386.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-i386.rpm)
|
|
||||||
* [toxic-x86_64.rpm](https://jenkins.libtoxcore.so/job/toxic-linux-pkg/lastSuccessfulBuild/artifact/toxic-x86_64.rpm)
|
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
|
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).
|
You can view our example config file [here](misc/toxic.conf.example).
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
# Variables for audio call support
|
# Variables for audio call support
|
||||||
AUDIO_LIBS = libtoxav openal
|
AUDIO_LIBS = libtoxav openal
|
||||||
AUDIO_CFLAGS = -DAUDIO
|
AUDIO_CFLAGS = -DAUDIO
|
||||||
ifneq (, $(findstring device.o, $(OBJ)))
|
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||||
AUDIO_OBJ = audio_call.o
|
AUDIO_OBJ = audio_call.o
|
||||||
else
|
else
|
||||||
AUDIO_OBJ = audio_call.o device.o
|
AUDIO_OBJ = audio_call.o audio_device.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Check if we can build audio support
|
# Check if we can build audio support
|
||||||
CHECK_AUDIO_LIBS = $(shell pkg-config --exists $(AUDIO_LIBS) || echo -n "error")
|
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
|
||||||
ifneq ($(CHECK_AUDIO_LIBS), error)
|
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||||
LIBS += $(AUDIO_LIBS)
|
LIBS += $(AUDIO_LIBS)
|
||||||
CFLAGS += $(AUDIO_CFLAGS)
|
CFLAGS += $(AUDIO_CFLAGS)
|
||||||
OBJ += $(AUDIO_OBJ)
|
OBJ += $(AUDIO_OBJ)
|
||||||
else ifneq ($(MAKECMDGOALS), clean)
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
$(warning WARNING -- Toxic will be compiled without audio support)
|
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||||
$(warning WARNING -- You need these libraries for audio support)
|
$(warning WARNING -- You need these libraries for audio support)
|
||||||
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
@ -9,7 +9,17 @@ endif
|
|||||||
# Check if we want build audio support
|
# Check if we want build audio support
|
||||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
ifneq ($(AUDIO), disabled)
|
ifneq ($(AUDIO), disabled)
|
||||||
-include $(CHECKS_DIR)/av.mk
|
-include $(CHECKS_DIR)/audio.mk
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we want build video support
|
||||||
|
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||||
|
ifneq ($(X11), disabled)
|
||||||
|
ifneq ($(AUDIO), disabled)
|
||||||
|
ifneq ($(VIDEO), disabled)
|
||||||
|
-include $(CHECKS_DIR)/video.mk
|
||||||
|
endif
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Check if we want build sound notifications support
|
# Check if we want build sound notifications support
|
||||||
@ -25,12 +35,12 @@ ifneq ($(DESK_NOTIFY), disabled)
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
# Check if we can build Toxic
|
# Check if we can build Toxic
|
||||||
CHECK_LIBS = $(shell pkg-config --exists $(LIBS) || echo -n "error")
|
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||||
ifneq ($(CHECK_LIBS), error)
|
ifneq ($(CHECK_LIBS), error)
|
||||||
CFLAGS += $(shell pkg-config --cflags $(LIBS))
|
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
|
||||||
LDFLAGS += $(shell pkg-config --libs $(LIBS))
|
LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
|
||||||
else ifneq ($(MAKECMDGOALS), clean)
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
$(warning ERROR -- Cannot compile Toxic)
|
$(warning ERROR -- Cannot compile Toxic)
|
||||||
$(warning ERROR -- You need these libraries)
|
$(warning ERROR -- You need these libraries)
|
||||||
$(warning ERROR -- $(MISSING_LIBS))
|
$(warning ERROR -- $(MISSING_LIBS))
|
||||||
|
@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify
|
|||||||
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
|
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
|
||||||
|
|
||||||
# Check if we can build desktop notifications support
|
# Check if we can build desktop notifications support
|
||||||
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||||
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
|
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
|
||||||
LIBS += $(DESK_NOTIFY_LIBS)
|
LIBS += $(DESK_NOTIFY_LIBS)
|
||||||
CFLAGS += $(DESK_NOTIFY_CFLAGS)
|
CFLAGS += $(DESK_NOTIFY_CFLAGS)
|
||||||
else ifneq ($(MAKECMDGOALS), clean)
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
|
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
|
||||||
$(warning WARNING -- You need these libraries for desktop notifications support)
|
$(warning WARNING -- You need these libraries for desktop notifications support)
|
||||||
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
|
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
# Variables for sound notifications support
|
# Variables for sound notifications support
|
||||||
SND_NOTIFY_LIBS = openal freealut
|
SND_NOTIFY_LIBS = openal freealut
|
||||||
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
|
SND_NOTIFY_CFLAGS = -DSOUND_NOTIFY
|
||||||
ifneq (, $(findstring device.o, $(OBJ)))
|
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||||
SND_NOTIFY_OBJ =
|
SND_NOTIFY_OBJ =
|
||||||
else
|
else
|
||||||
SND_NOTIFY_OBJ = device.o
|
SND_NOTIFY_OBJ = audio_device.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Check if we can build sound notifications support
|
# Check if we can build sound notifications support
|
||||||
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(SND_NOTIFY_LIBS) || echo -n "error")
|
CHECK_SND_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(SND_NOTIFY_LIBS) || echo -n "error")
|
||||||
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
|
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
|
||||||
LIBS += $(SND_NOTIFY_LIBS)
|
LIBS += $(SND_NOTIFY_LIBS)
|
||||||
CFLAGS += $(SND_NOTIFY_CFLAGS)
|
CFLAGS += $(SND_NOTIFY_CFLAGS)
|
||||||
OBJ += $(SND_NOTIFY_OBJ)
|
OBJ += $(SND_NOTIFY_OBJ)
|
||||||
else ifneq ($(MAKECMDGOALS), clean)
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
$(warning WARNING -- Toxic will be compiled without sound notifications support)
|
$(warning WARNING -- Toxic will be compiled without sound notifications support)
|
||||||
$(warning WARNING -- You need these libraries for sound notifications support)
|
$(warning WARNING -- You need these libraries for sound notifications support)
|
||||||
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
|
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
|
||||||
|
21
cfg/checks/video.mk
Normal file
21
cfg/checks/video.mk
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Variables for video call support
|
||||||
|
VIDEO_LIBS = libtoxav vpx x11
|
||||||
|
VIDEO_CFLAGS = -DVIDEO
|
||||||
|
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||||
|
VIDEO_OBJ = video_call.o
|
||||||
|
else
|
||||||
|
VIDEO_OBJ = video_call.o video_device.o
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Check if we can build video support
|
||||||
|
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
|
||||||
|
ifneq ($(CHECK_VIDEO_LIBS), error)
|
||||||
|
LIBS += $(VIDEO_LIBS)
|
||||||
|
CFLAGS += $(VIDEO_CFLAGS)
|
||||||
|
OBJ += $(VIDEO_OBJ)
|
||||||
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
|
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
|
$(warning WARNING -- Toxic will be compiled without video support)
|
||||||
|
$(warning WARNING -- You will need these libraries for video support)
|
||||||
|
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
|
||||||
|
endif
|
@ -4,13 +4,13 @@ X11_CFLAGS = -DX11
|
|||||||
X11_OBJ = xtra.o
|
X11_OBJ = xtra.o
|
||||||
|
|
||||||
# Check if we can build X11 support
|
# Check if we can build X11 support
|
||||||
CHECK_X11_LIBS = $(shell pkg-config --exists $(X11_LIBS) || echo -n "error")
|
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
||||||
ifneq ($(CHECK_X11_LIBS), error)
|
ifneq ($(CHECK_X11_LIBS), error)
|
||||||
LIBS += $(X11_LIBS)
|
LIBS += $(X11_LIBS)
|
||||||
CFLAGS += $(X11_CFLAGS)
|
CFLAGS += $(X11_CFLAGS)
|
||||||
OBJ += $(X11_OBJ)
|
OBJ += $(X11_OBJ)
|
||||||
else ifneq ($(MAKECMDGOALS), clean)
|
else ifneq ($(MAKECMDGOALS), clean)
|
||||||
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||||
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support))
|
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking and drag&drop support))
|
||||||
$(warning WARNING -- You need these libraries for x11 support)
|
$(warning WARNING -- You need these libraries for x11 support)
|
||||||
$(warning WARNING -- $(MISSING_X11_LIBS))
|
$(warning WARNING -- $(MISSING_X11_LIBS))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Version
|
# Version
|
||||||
TOXIC_VERSION = 0.6.0
|
TOXIC_VERSION = 0.7.0
|
||||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||||
ifneq (, $(findstring error, $(REV)))
|
ifneq (, $(findstring error, $(REV)))
|
||||||
VERSION = $(TOXIC_VERSION)
|
VERSION = $(TOXIC_VERSION)
|
||||||
@ -16,7 +16,7 @@ MISC_DIR = $(BASE_DIR)/misc
|
|||||||
|
|
||||||
# Project files
|
# Project files
|
||||||
MANFILES = toxic.1 toxic.conf.5
|
MANFILES = toxic.1 toxic.conf.5
|
||||||
DATAFILES = DHTnodes DNSservers toxic.conf.example
|
DATAFILES = DHTnodes nameservers toxic.conf.example
|
||||||
DESKFILE = toxic.desktop
|
DESKFILE = toxic.desktop
|
||||||
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
|
SNDFILES = ToxicContactOnline.wav ToxicContactOffline.wav ToxicError.wav
|
||||||
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
||||||
@ -28,3 +28,6 @@ BINDIR = $(PREFIX)/bin
|
|||||||
DATADIR = $(PREFIX)/share/toxic
|
DATADIR = $(PREFIX)/share/toxic
|
||||||
MANDIR = $(PREFIX)/share/man
|
MANDIR = $(PREFIX)/share/man
|
||||||
APPDIR = $(PREFIX)/share/applications
|
APPDIR = $(PREFIX)/share/applications
|
||||||
|
|
||||||
|
# Platform tools
|
||||||
|
PKG_CONFIG = pkg-config
|
||||||
|
@ -6,5 +6,13 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o
|
|||||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
|
|
||||||
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
|
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
|
||||||
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -ltoxdns -lresolv -lconfig -ltoxencryptsave -g
|
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g
|
||||||
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
||||||
|
|
||||||
|
OSX_LIBRARIES = -lobjc -lresolv
|
||||||
|
OSX_FRAMEWORKS = -framework Foundation -framework CoreFoundation -framework AVFoundation \
|
||||||
|
-framework QuartzCore -framework CoreMedia
|
||||||
|
OSX_VIDEO = osx_video.m
|
||||||
|
|
||||||
|
LDFLAGS += $(OSX_LIBRARIES) $(OSX_FRAMEWORKS)
|
||||||
|
OBJ += osx_video.o
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Specials options for freebsd systems
|
# Specials options for freebsd systems
|
||||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||||
LDFLAGS += -lncursesw
|
LDFLAGS += -lncursesw -lcurl
|
||||||
MANDIR = $(PREFIX)/man
|
MANDIR = $(PREFIX)/man
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Specials options for linux systems
|
# Specials options for linux systems
|
||||||
CFLAGS +=
|
CFLAGS +=
|
||||||
LDFLAGS += -ldl -lresolv -lrt
|
LDFLAGS += -ldl -lrt -lcurl
|
||||||
MANDIR = $(PREFIX)/share/man
|
MANDIR = $(PREFIX)/share/man
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
# Help target
|
# Help target
|
||||||
help:
|
help:
|
||||||
@echo "-- Targets --"
|
@echo "-- Targets --"
|
||||||
@echo " all: Build toxic and documentation [DEFAULT]"
|
@echo " all: Build toxic and documentation [DEFAULT]"
|
||||||
@echo " toxic: Build toxic"
|
@echo " toxic: Build toxic"
|
||||||
@echo " doc: Build documentation"
|
@echo " doc: Build documentation"
|
||||||
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||||
@echo " clean: Remove built files"
|
@echo " uninstall: Remove toxic from PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||||
@echo " help: This help"
|
@echo " clean: Remove built files"
|
||||||
|
@echo " help: This help"
|
||||||
@echo
|
@echo
|
||||||
@echo "-- Variables --"
|
@echo "-- Variables --"
|
||||||
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
|
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
|
||||||
|
@ -13,7 +13,8 @@ install: $(BUILD_DIR)/toxic
|
|||||||
@for f in $(DATAFILES) ; do \
|
@for f in $(DATAFILES) ; do \
|
||||||
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
file=$(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||||
|
mv temp_file $$file ;\
|
||||||
done
|
done
|
||||||
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
@mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
||||||
@for f in $(SNDFILES) ; do \
|
@for f in $(SNDFILES) ; do \
|
||||||
@ -30,8 +31,10 @@ install: $(BUILD_DIR)/toxic
|
|||||||
file=$$section/$$f ;\
|
file=$$section/$$f ;\
|
||||||
mkdir -p $$section ;\
|
mkdir -p $$section ;\
|
||||||
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||||
sed -i'' -e 's:__VERSION__:'$(VERSION)':g' $$file ;\
|
sed -e 's:__VERSION__:'$(VERSION)':g' $$file > temp_file && \
|
||||||
sed -i'' -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file ;\
|
mv temp_file $$file ;\
|
||||||
|
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||||
|
mv temp_file $$file ;\
|
||||||
gzip -f -9 $$file ;\
|
gzip -f -9 $$file ;\
|
||||||
done
|
done
|
||||||
|
|
||||||
|
24
cfg/targets/uninstall.mk
Normal file
24
cfg/targets/uninstall.mk
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Uninstall target
|
||||||
|
uninstall:
|
||||||
|
@echo "Removing toxic executable"
|
||||||
|
@rm -f $(abspath $(DESTDIR)/$(BINDIR)/toxic)
|
||||||
|
|
||||||
|
@echo "Removing desktop file"
|
||||||
|
@rm -f $(abspath $(DESTDIR)/$(APPDIR)/$(DESKFILE))
|
||||||
|
|
||||||
|
@echo "Removing data files"
|
||||||
|
@for f in $(DATAFILES) ; do \
|
||||||
|
rm -f $(abspath $(DESTDIR)/$(DATADIR)/$$f) ;\
|
||||||
|
done
|
||||||
|
@for f in $(SNDFILES) ; do \
|
||||||
|
rm -f $(abspath $(DESTDIR)/$(DATADIR)/sounds/$$f) ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
@echo "Removing man pages"
|
||||||
|
@for f in $(MANFILES) ; do \
|
||||||
|
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||||
|
file=$$section/$$f ;\
|
||||||
|
rm -f $$file $$file.gz ;\
|
||||||
|
done
|
||||||
|
|
||||||
|
.PHONY: uninstall
|
13
doc/toxic.1
13
doc/toxic.1
@ -2,12 +2,12 @@
|
|||||||
.\" Title: toxic
|
.\" Title: toxic
|
||||||
.\" Author: [see the "AUTHORS" section]
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||||
.\" Date: 2014-12-27
|
.\" Date: 2015-07-08
|
||||||
.\" Manual: Toxic Manual
|
.\" Manual: Toxic Manual
|
||||||
.\" Source: toxic __VERSION__
|
.\" Source: toxic __VERSION__
|
||||||
.\" Language: English
|
.\" Language: English
|
||||||
.\"
|
.\"
|
||||||
.TH "TOXIC" "1" "2014\-12\-27" "toxic __VERSION__" "Toxic Manual"
|
.TH "TOXIC" "1" "2015\-07\-08" "toxic __VERSION__" "Toxic Manual"
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
.\" * Define some portability stuff
|
.\" * Define some portability stuff
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
@ -101,9 +101,9 @@ Use a SOCKS5 proxy: Requires [IP] [port]
|
|||||||
Use a HTTP proxy: Requires [IP] [port]
|
Use a HTTP proxy: Requires [IP] [port]
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-r, \-\-dnslist
|
\-r, \-\-namelist
|
||||||
.RS 4
|
.RS 4
|
||||||
Use specified DNSservers file
|
Use specified nameservers list
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\-t, \-\-force\-tcp
|
\-t, \-\-force\-tcp
|
||||||
@ -111,6 +111,11 @@ Use specified DNSservers file
|
|||||||
Force TCP connection (use this with proxies)
|
Force TCP connection (use this with proxies)
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
|
\-T, \-\-tcp\-relay
|
||||||
|
.RS 4
|
||||||
|
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||||
|
.RE
|
||||||
|
.PP
|
||||||
\-u, \-\-unencrypt\-data
|
\-u, \-\-unencrypt\-data
|
||||||
.RS 4
|
.RS 4
|
||||||
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
|
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
|
||||||
|
@ -53,12 +53,15 @@ OPTIONS
|
|||||||
-P, --HTTP-proxy::
|
-P, --HTTP-proxy::
|
||||||
Use a HTTP proxy: Requires [IP] [port]
|
Use a HTTP proxy: Requires [IP] [port]
|
||||||
|
|
||||||
-r, --dnslist::
|
-r, --namelist::
|
||||||
Use specified DNSservers file
|
Use specified nameservers list
|
||||||
|
|
||||||
-t, --force-tcp::
|
-t, --force-tcp::
|
||||||
Force TCP connection (use this with proxies)
|
Force TCP connection (use this with proxies)
|
||||||
|
|
||||||
|
-T, --tcp-relay::
|
||||||
|
Act as a TCP relay server for the network (Note: this uses significantly more bandwidth)
|
||||||
|
|
||||||
-u, --unencrypt-data::
|
-u, --unencrypt-data::
|
||||||
Unencrypt a data file. A warning will appear if this option is used
|
Unencrypt a data file. A warning will appear if this option is used
|
||||||
with a data file that is already unencrypted.
|
with a data file that is already unencrypted.
|
||||||
|
@ -2,12 +2,12 @@
|
|||||||
.\" Title: toxic.conf
|
.\" Title: toxic.conf
|
||||||
.\" Author: [see the "AUTHORS" section]
|
.\" Author: [see the "AUTHORS" section]
|
||||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||||
.\" Date: 2015-02-26
|
.\" Date: 2015-03-28
|
||||||
.\" Manual: Toxic Manual
|
.\" Manual: Toxic Manual
|
||||||
.\" Source: toxic __VERSION__
|
.\" Source: toxic __VERSION__
|
||||||
.\" Language: English
|
.\" Language: English
|
||||||
.\"
|
.\"
|
||||||
.TH "TOXIC\&.CONF" "5" "2015\-02\-26" "toxic __VERSION__" "Toxic Manual"
|
.TH "TOXIC\&.CONF" "5" "2015\-03\-28" "toxic __VERSION__" "Toxic Manual"
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
.\" * Define some portability stuff
|
.\" * Define some portability stuff
|
||||||
.\" -----------------------------------------------------------------
|
.\" -----------------------------------------------------------------
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
192.254.75.102 33445 951C88B7E75C867418ACDB5D273821372BB5BD652740BCDF623A4FA293E75D2F
|
|
||||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||||
178.62.125.224 33445 10B20C49ACBD968D7C80F2E8438F92EA51F189F4E70CFBBB2C2C8C799E97F03E
|
|
||||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||||
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
195.154.119.113 33445 E398A69646B8CEACA9F0B84F553726C1C49270558C57DF5F3C368F05A7D71354
|
||||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||||
104.219.184.206 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C
|
|
||||||
76.191.23.96 33445 93574A3FAB7D612FEA29FD8D67D3DD10DFD07A075A5D62E8AF3DD9F5D0932E11
|
|
||||||
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
|
46.38.239.179 33445 F5A1A38EFB6BD3C2C8AF8B10D85F0F89E931704D349F1D0720C3C4059AF2440A
|
||||||
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
|
178.62.250.138 33445 788236D34978D1D5BD822F0A5BEBD2C53C64CC31CD3149350EE27D4D9A2F9B6B
|
||||||
78.225.128.39 33445 7A2306BFBA665E5480AE59B31E116BE9C04DCEFE04D9FE25082316FA34B4DA0C
|
|
||||||
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
|
130.133.110.14 33445 461FA3776EF0FA655F1A05477DF1B3B614F7D6B124F7DB1DD4FE3C08B03B640F
|
||||||
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
|
104.167.101.29 33445 5918AC3C06955962A75AD7DF4F80A5D7C34F7DB9E1498D2E0495DE35B3FE8A57
|
||||||
195.154.109.148 33445 391C96CB67AE893D4782B8E4495EB9D89CF1031F48460C06075AA8CE76D50A21
|
|
||||||
192.3.173.88 33445 3E1FFDEB667BFF549F619EC6737834762124F50A89C8D0DBF1DDF64A2DD6CD1B
|
|
||||||
205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702
|
205.185.116.116 33445 A179B09749AC826FF01F37A9613F6B57118AE014D4196A0E1105A98F93A54702
|
||||||
198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F
|
198.98.51.198 33445 1D5A5F2F5D6233058BF0259B09622FB40B482E4FA0931EB8FD3AB8E7BF7DAF6F
|
||||||
80.232.246.79 33445 0B8DCEAA7BDDC44BB11173F987CAE3566A2D7057D8DD3CC642BD472B9391002A
|
80.232.246.79 33445 A7A060D553B017D9D8F038E265C7AFB6C70BAAC55070197F9C007432D0038E0F
|
||||||
|
108.61.165.198 33445 8E7D0B859922EF569298B4D261A8CCB5FEA14FB91ED412A7603A585A25698832
|
||||||
|
212.71.252.109 33445 C4CEB8C7AC607C6B374E2E782B3C00EA3A63B80D4910B8649CCACDD19F260819
|
||||||
|
194.249.212.109 33445 3CEE1F054081E7A011234883BC4FC39F661A55B73637A5AC293DDF1251D9432B
|
||||||
|
185.25.116.107 33445 DA4E4ED4B697F2E9B000EEFE3A34B554ACD3F45F5C96EAEA2516DD7FF9AF7B43
|
||||||
|
192.99.168.140 33445 6A4D0607A296838434A6A7DDF99F50EF9D60A2C510BBF31FE538A25CB6B4652F
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
|
|
||||||
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13
|
|
2
misc/nameservers
Normal file
2
misc/nameservers
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25
|
||||||
|
|
@ -66,7 +66,7 @@ tox = {
|
|||||||
// Path for downloaded files
|
// Path for downloaded files
|
||||||
// download_path="/home/USERNAME/Downloads/";
|
// download_path="/home/USERNAME/Downloads/";
|
||||||
|
|
||||||
// Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
|
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
|
||||||
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
|
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
|
||||||
|
|
||||||
// Path for chatlogs
|
// Path for chatlogs
|
||||||
|
581
src/audio_call.c
581
src/audio_call.c
@ -23,11 +23,17 @@
|
|||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
#include "chat_commands.h"
|
#include "chat_commands.h"
|
||||||
#include "global_commands.h"
|
#include "global_commands.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "chat.h"
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
#include "video_call.h"
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <curses.h>
|
#include <curses.h>
|
||||||
@ -49,174 +55,165 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
#define cbend pthread_exit(NULL)
|
#define cbend pthread_exit(NULL)
|
||||||
|
|
||||||
#define MAX_CALLS 10
|
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000)
|
||||||
|
|
||||||
#define frame_size (av_DefaultSettings.audio_sample_rate * av_DefaultSettings.audio_frame_duration / 1000)
|
|
||||||
|
|
||||||
static int set_call(Call* call, bool start)
|
static int set_call(Call* call, bool start)
|
||||||
{
|
{
|
||||||
call->in_idx = -1;
|
call->in_idx = -1;
|
||||||
call->out_idx = -1;
|
call->out_idx = -1;
|
||||||
|
#ifdef VIDEO
|
||||||
|
call->vin_idx = -1;
|
||||||
|
call->vout_idx = -1;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
if ( start ) {
|
if ( start ) {
|
||||||
call->ttas = true;
|
call->ttas = true;
|
||||||
|
|
||||||
if (pthread_mutex_init(&call->mutex, NULL) != 0)
|
if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
call->ttid = 0;
|
call->ttid = 0;
|
||||||
if (pthread_mutex_destroy(&call->mutex) != 0)
|
if ( pthread_mutex_destroy(&call->mutex) != 0 )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ASettings {
|
void call_cb ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data );
|
||||||
AudioError errors;
|
void callstate_cb ( ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data );
|
||||||
|
void receive_audio_frame_cb ( ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count,
|
||||||
|
uint8_t channels, uint32_t sampling_rate, void *user_data );
|
||||||
|
void receive_video_frame_cb ( ToxAV *av, uint32_t friend_number,
|
||||||
|
uint16_t width, uint16_t height,
|
||||||
|
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *a,
|
||||||
|
int32_t ystride, int32_t ustride, int32_t vstride, int32_t astride, void *user_data );
|
||||||
|
|
||||||
ToxAv *av;
|
void callback_recv_invite ( Tox *m, uint32_t friend_number );
|
||||||
|
void callback_recv_ringing ( uint32_t friend_number );
|
||||||
|
void callback_recv_starting ( uint32_t friend_number );
|
||||||
|
void callback_recv_ending ( uint32_t friend_number );
|
||||||
|
void callback_call_started ( uint32_t friend_number );
|
||||||
|
void callback_call_canceled ( uint32_t friend_number );
|
||||||
|
void callback_call_rejected ( uint32_t friend_number );
|
||||||
|
void callback_call_ended ( uint32_t friend_number );
|
||||||
|
|
||||||
ToxAvCSettings cs;
|
void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t size );
|
||||||
|
|
||||||
Call calls[MAX_CALLS];
|
|
||||||
} ASettins;
|
|
||||||
|
|
||||||
void callback_recv_invite ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_recv_ringing ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_recv_starting ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_recv_ending ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_call_started ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_call_canceled ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_call_rejected ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_call_ended ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_requ_timeout ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_peer_timeout ( void* av, int32_t call_index, void *arg );
|
|
||||||
void callback_media_change ( void* av, int32_t call_index, void *arg );
|
|
||||||
|
|
||||||
void write_device_callback( void* agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg );
|
|
||||||
|
|
||||||
static void print_err (ToxWindow *self, const char *error_str)
|
static void print_err (ToxWindow *self, const char *error_str)
|
||||||
{
|
{
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxAv *init_audio(ToxWindow *self, Tox *tox)
|
ToxAV *init_audio(ToxWindow *self, Tox *tox)
|
||||||
{
|
{
|
||||||
ASettins.cs = av_DefaultSettings;
|
TOXAV_ERR_NEW error;
|
||||||
ASettins.cs.max_video_height = ASettins.cs.max_video_width = 0;
|
CallControl.audio_errors = ae_None;
|
||||||
|
CallControl.prompt = self;
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
ASettins.errors = ae_None;
|
CallControl.av = toxav_new(tox, &error);
|
||||||
|
|
||||||
memset(ASettins.calls, 0, sizeof(ASettins.calls));
|
CallControl.audio_enabled = true;
|
||||||
|
CallControl.audio_bit_rate = 48;
|
||||||
|
CallControl.audio_sample_rate = 48000;
|
||||||
|
CallControl.audio_frame_duration = 10;
|
||||||
|
CallControl.audio_channels = 1;
|
||||||
|
|
||||||
|
#ifndef VIDEO
|
||||||
|
CallControl.video_enabled = false;
|
||||||
|
CallControl.video_bit_rate = 0;
|
||||||
|
CallControl.video_frame_duration = 0;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
/* Streaming stuff from core */
|
memset(CallControl.calls, 0, sizeof(CallControl.calls));
|
||||||
|
|
||||||
ASettins.av = toxav_new(tox, MAX_CALLS);
|
if ( !CallControl.av ) {
|
||||||
|
CallControl.audio_errors |= ae_StartingCoreAudio;
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV");
|
||||||
|
|
||||||
if ( !ASettins.av ) {
|
|
||||||
ASettins.errors |= ae_StartingCoreAudio;
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( init_devices(ASettins.av) == de_InternalError ) {
|
if ( init_devices(CallControl.av) == de_InternalError ) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
|
||||||
toxav_kill(ASettins.av);
|
toxav_kill(CallControl.av);
|
||||||
return ASettins.av = NULL;
|
|
||||||
|
return CallControl.av = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_call_started, av_OnStart, self);
|
toxav_callback_call(CallControl.av, call_cb, tox);
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_call_canceled, av_OnCancel, self);
|
toxav_callback_call_state(CallControl.av, callstate_cb, NULL);
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_call_rejected, av_OnReject, self);
|
toxav_callback_audio_receive_frame(CallControl.av, receive_audio_frame_cb, NULL);
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_call_ended, av_OnEnd, self);
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_recv_invite, av_OnInvite, self);
|
|
||||||
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_recv_ringing, av_OnRinging, self);
|
return CallControl.av;
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_recv_starting, av_OnStart, self);
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_recv_ending, av_OnEnd, self);
|
|
||||||
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_requ_timeout, av_OnRequestTimeout, self);
|
|
||||||
toxav_register_callstate_callback(ASettins.av, callback_peer_timeout, av_OnPeerTimeout, self);
|
|
||||||
//toxav_register_callstate_callback(ASettins.av, callback_media_change, av_OnMediaChange, self);
|
|
||||||
|
|
||||||
toxav_register_audio_callback(ASettins.av, write_device_callback, NULL);
|
|
||||||
|
|
||||||
return ASettins.av;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void terminate_audio()
|
void terminate_audio()
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < MAX_CALLS; ++i)
|
for (i = 0; i < MAX_CALLS; ++i)
|
||||||
stop_transmission(&ASettins.calls[i], i);
|
stop_transmission(&CallControl.calls[i], i);
|
||||||
|
|
||||||
if ( ASettins.av )
|
if ( CallControl.av )
|
||||||
toxav_kill(ASettins.av);
|
toxav_kill(CallControl.av);
|
||||||
|
|
||||||
terminate_devices();
|
terminate_devices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_device_callback (const int16_t* captured, uint32_t size, void* data)
|
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 */
|
TOXAV_ERR_SEND_FRAME error;
|
||||||
|
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
|
||||||
|
int64_t sample_count = ((int64_t) CallControl.audio_sample_rate) * \
|
||||||
|
((int64_t) CallControl.audio_frame_duration) / 1000;
|
||||||
|
|
||||||
uint8_t encoded_payload[RTP_PAYLOAD_SIZE];
|
if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
|
||||||
int32_t payload_size = toxav_prepare_audio_frame(ASettins.av, call_index, encoded_payload, RTP_PAYLOAD_SIZE, captured, size);
|
captured, sample_count,
|
||||||
if ( payload_size <= 0 || toxav_send_audio(ASettins.av, call_index, encoded_payload, payload_size) < 0 ) {
|
CallControl.audio_channels,
|
||||||
/*fprintf(stderr, "Could not encode audio packet\n");*/
|
CallControl.audio_sample_rate, &error) == false )
|
||||||
}
|
{}
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_device_callback(void *agent, int32_t call_index, const int16_t* PCM, uint16_t size, void* arg)
|
void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t size)
|
||||||
{
|
{
|
||||||
(void)arg;
|
if ( CallControl.calls[friend_number].ttas )
|
||||||
(void)agent;
|
write_out(CallControl.calls[friend_number].out_idx, PCM, size, CallControl.audio_channels);
|
||||||
|
|
||||||
if (call_index >= 0 && ASettins.calls[call_index].ttas) {
|
|
||||||
ToxAvCSettings csettings = ASettins.cs;
|
|
||||||
toxav_get_peer_csettings(ASettins.av, call_index, 0, &csettings);
|
|
||||||
write_out(ASettins.calls[call_index].out_idx, PCM, size, csettings.audio_channels);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int start_transmission(ToxWindow *self, Call *call)
|
int start_transmission(ToxWindow *self, Call *call)
|
||||||
{
|
{
|
||||||
if ( !self || !ASettins.av || self->call_idx == -1 ) {
|
if ( !self || !CallControl.av ) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't provide support for video */
|
|
||||||
if ( 0 != toxav_prepare_transmission(ASettins.av, self->call_idx, 0) ) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Could not prepare transmission");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !toxav_capability_supported(ASettins.av, self->call_idx, av_AudioDecoding) ||
|
|
||||||
!toxav_capability_supported(ASettins.av, self->call_idx, av_AudioEncoding) )
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if (set_call(call, true) == -1)
|
if (set_call(call, true) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ToxAvCSettings csettings;
|
DeviceError error = open_primary_device(input, &call->in_idx,
|
||||||
toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
|
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
|
||||||
|
|
||||||
if ( open_primary_device(input, &call->in_idx,
|
if ( error != de_None ) {
|
||||||
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None )
|
if ( error == de_FailedStart)
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input device!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start input device");
|
||||||
|
|
||||||
if ( register_device_callback(self->call_idx, call->in_idx,
|
if ( error == de_InternalError )
|
||||||
read_device_callback, &self->call_idx, true) != de_None)
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening input device");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( register_device_callback(self->num, call->in_idx,
|
||||||
|
read_device_callback, &self->num, true) != de_None)
|
||||||
/* Set VAD as true for all; TODO: Make it more dynamic */
|
/* Set VAD as true for all; TODO: Make it more dynamic */
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
|
||||||
|
|
||||||
if ( open_primary_device(output, &call->out_idx,
|
if ( open_primary_device(output, &call->out_idx,
|
||||||
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels) != de_None ) {
|
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
|
||||||
call->has_output = 0;
|
call->has_output = 0;
|
||||||
}
|
}
|
||||||
@ -224,22 +221,31 @@ int start_transmission(ToxWindow *self, Call *call)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stop_transmission(Call *call, int32_t call_index)
|
int stop_transmission(Call *call, uint32_t friend_number)
|
||||||
{
|
{
|
||||||
if ( call->ttas ) {
|
if ( call->ttas ) {
|
||||||
toxav_kill_transmission(ASettins.av, call_index);
|
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
|
||||||
call->ttas = false;
|
|
||||||
|
|
||||||
if ( call->in_idx != -1 )
|
if ( CallControl.call_state != TOXAV_FRIEND_CALL_STATE_FINISHED )
|
||||||
close_device(input, call->in_idx);
|
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_CANCEL, &error);
|
||||||
|
|
||||||
if ( call->out_idx != -1 )
|
if ( error == TOXAV_ERR_CALL_CONTROL_OK ) {
|
||||||
close_device(output, call->out_idx);
|
call->ttas = false;
|
||||||
|
|
||||||
|
if ( call->in_idx != -1 )
|
||||||
|
close_device(input, call->in_idx);
|
||||||
|
|
||||||
|
if ( call->out_idx != -1 )
|
||||||
|
close_device(output, call->out_idx);
|
||||||
|
|
||||||
|
if ( set_call(call, false) == -1 )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
|
||||||
if (set_call(call, false) == -1)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -255,85 +261,187 @@ int stop_transmission(Call *call, int32_t call_index)
|
|||||||
/*
|
/*
|
||||||
* Callbacks
|
* Callbacks
|
||||||
*/
|
*/
|
||||||
|
void call_cb(ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data)
|
||||||
#define CB_BODY(call_idx, Arg, onFunc) do { ToxWindow* windows = (Arg); int i;\
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) if (windows[i].onFunc != NULL) windows[i].onFunc(&windows[i], ASettins.av, call_idx); } while (0)
|
|
||||||
|
|
||||||
void callback_recv_invite ( void* av, int32_t call_index, void* arg )
|
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onInvite);
|
Tox *m = (Tox *) user_data;
|
||||||
|
CallControl.pending_call = true;
|
||||||
|
callback_recv_invite(m, friend_number);
|
||||||
}
|
}
|
||||||
void callback_recv_ringing ( void* av, int32_t call_index, void* arg )
|
|
||||||
|
void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data)
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onRinging);
|
CallControl.call_state = state;
|
||||||
}
|
|
||||||
void callback_recv_starting ( void* av, int32_t call_index, void* arg )
|
switch ( state ) {
|
||||||
{
|
case ( TOXAV_FRIEND_CALL_STATE_ERROR ):
|
||||||
ToxWindow* windows = arg;
|
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!");
|
||||||
int i;
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
#ifdef VIDEO
|
||||||
if (windows[i].onStarting != NULL && windows[i].call_idx == call_index) {
|
callback_video_end(friend_number);
|
||||||
windows[i].onStarting(&windows[i], ASettins.av, call_index);
|
#endif /* VIDEO */
|
||||||
if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index])) {/* YEAH! */
|
|
||||||
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
|
stop_transmission(&CallControl.calls[friend_number], friend_number);
|
||||||
|
callback_call_ended(friend_number);
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case ( TOXAV_FRIEND_CALL_STATE_FINISHED ):
|
||||||
|
if ( CallControl.pending_call )
|
||||||
|
callback_call_rejected(friend_number);
|
||||||
|
else
|
||||||
|
callback_call_ended(friend_number);
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
callback_recv_video_end(friend_number);
|
||||||
|
callback_video_end(friend_number);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
stop_transmission(&CallControl.calls[friend_number], friend_number);
|
||||||
|
|
||||||
|
/* Reset stored call state after finishing */
|
||||||
|
CallControl.call_state = 0;
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if ( CallControl.pending_call ) {
|
||||||
|
/* Start answered call */
|
||||||
|
callback_call_started(friend_number);
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
#ifdef VIDEO
|
||||||
|
/* Handle receiving client video call states */
|
||||||
|
if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V )
|
||||||
|
callback_recv_video_starting(friend_number);
|
||||||
|
else if ( state & ~TOXAV_FRIEND_CALL_STATE_SENDING_V )
|
||||||
|
callback_recv_video_end(friend_number);
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||||
|
int16_t const *pcm, size_t sample_count,
|
||||||
|
uint8_t channels, uint32_t sampling_rate, void *user_data)
|
||||||
|
{
|
||||||
|
write_device_callback(friend_number, pcm, frame_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||||
|
uint32_t video_bit_rate, void *user_data)
|
||||||
|
{
|
||||||
|
CallControl.audio_bit_rate = audio_bit_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback_recv_invite(Tox *m, uint32_t friend_number)
|
||||||
|
{
|
||||||
|
if (friend_number >= Friends.max_idx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Friends.list[friend_number].chatwin == -1) {
|
||||||
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxWindow *windows = CallControl.prompt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (windows[i].onInvite != NULL && windows[i].num == friend_number) {
|
||||||
|
windows[i].onInvite(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_recv_ringing(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
ToxWindow *windows = CallControl.prompt;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (windows[i].onRinging != NULL && windows[i].num == friend_number) {
|
||||||
|
windows[i].onRinging(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_recv_starting(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
ToxWindow* windows = CallControl.prompt;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) {
|
||||||
|
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
|
||||||
|
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void callback_recv_ending ( void* av, int32_t call_index, void* arg )
|
void callback_recv_ending(uint32_t friend_number)
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onEnding);
|
ToxWindow *windows = CallControl.prompt;
|
||||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
int i;
|
||||||
}
|
|
||||||
|
|
||||||
void callback_call_started ( void* av, int32_t call_index, void* arg )
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if (windows[i].onEnding != NULL && windows[i].num == friend_number) {
|
||||||
|
windows[i].onEnding(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_call_started(uint32_t friend_number)
|
||||||
{
|
{
|
||||||
ToxWindow* windows = arg;
|
ToxWindow* windows = CallControl.prompt;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
|
||||||
if (windows[i].onStart != NULL && windows[i].call_idx == call_index) {
|
if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
|
||||||
windows[i].onStart(&windows[i], ASettins.av, call_index);
|
windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
if ( 0 != start_transmission(&windows[i], &ASettins.calls[call_index]) ) {/* YEAH! */
|
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */
|
||||||
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
|
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void callback_call_canceled ( void* av, int32_t call_index, void* arg )
|
void callback_call_canceled(uint32_t friend_number)
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onCancel);
|
ToxWindow *windows = CallControl.prompt;
|
||||||
|
int i;
|
||||||
|
|
||||||
/* In case call is active */
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
if (windows[i].onCancel != NULL && windows[i].num == friend_number) {
|
||||||
|
windows[i].onCancel(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void callback_call_rejected ( void* av, int32_t call_index, void* arg )
|
void callback_call_rejected(uint32_t friend_number)
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onReject);
|
ToxWindow *windows = CallControl.prompt;
|
||||||
}
|
int i;
|
||||||
void callback_call_ended ( void* av, int32_t call_index, void* arg )
|
|
||||||
{
|
|
||||||
CB_BODY(call_index, arg, onEnd);
|
|
||||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
void callback_requ_timeout ( void* av, int32_t call_index, void* arg )
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
{
|
if (windows[i].onReject != NULL && windows[i].num == friend_number) {
|
||||||
CB_BODY(call_index, arg, onRequestTimeout);
|
windows[i].onReject(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
|
void callback_call_ended(uint32_t friend_number)
|
||||||
{
|
{
|
||||||
CB_BODY(call_index, arg, onPeerTimeout);
|
ToxWindow *windows = CallControl.prompt;
|
||||||
stop_transmission(&ASettins.calls[call_index], call_index);
|
int i;
|
||||||
/* Call is stopped manually since there might be some other
|
|
||||||
* actions that one can possibly take on timeout
|
|
||||||
*/
|
|
||||||
toxav_stop_call(ASettins.av, call_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// void callback_media_change(void* av, int32_t call_index, void* arg)
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
// {
|
if (windows[i].onEnd != NULL && windows[i].num == friend_number) {
|
||||||
/*... TODO cancel all media change requests */
|
windows[i].onEnd(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* End of Callbacks
|
* End of Callbacks
|
||||||
@ -345,33 +453,42 @@ void callback_peer_timeout ( void* av, int32_t call_index, void* arg )
|
|||||||
*/
|
*/
|
||||||
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
TOXAV_ERR_CALL error;
|
||||||
const char *error_str;
|
const char *error_str;
|
||||||
|
|
||||||
if (argc != 0) {
|
if ( argc != 0 ) {
|
||||||
error_str = "Unknown arguments.";
|
error_str = "Unknown arguments.";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !ASettins.av ) {
|
if ( !CallControl.av ) {
|
||||||
error_str = "Audio not supported!";
|
error_str = "ToxAV not supported!";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!self->stb->connection) {
|
if ( !self->stb->connection ) {
|
||||||
error_str = "Friend is offline.";
|
error_str = "Friend is offline.";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxAvError error = toxav_call(ASettins.av, &self->call_idx, self->num, &ASettins.cs, 30);
|
if ( CallControl.pending_call ) {
|
||||||
|
error_str = "Already a pending call!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
if ( error != av_ErrorNone ) {
|
toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
|
||||||
if ( error == av_ErrorAlreadyInCallWithPeer ) error_str = "Already in a call!";
|
if ( error != TOXAV_ERR_CALL_OK ) {
|
||||||
|
if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!";
|
||||||
|
else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue";
|
||||||
|
else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_FOUND ) error_str = "Friend number invalid";
|
||||||
|
else if ( error == TOXAV_ERR_CALL_FRIEND_NOT_CONNECTED ) error_str = "Friend is valid but not currently connected";
|
||||||
else error_str = "Internal error!";
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Calling... idx: %d", self->call_idx);
|
CallControl.pending_call = true;
|
||||||
|
callback_recv_ringing(self->num);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
@ -380,29 +497,38 @@ on_error:
|
|||||||
|
|
||||||
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
|
TOXAV_ERR_ANSWER error;
|
||||||
const char *error_str;
|
const char *error_str;
|
||||||
|
|
||||||
if (argc != 0) {
|
if ( argc != 0 ) {
|
||||||
error_str = "Unknown arguments.";
|
error_str = "Unknown arguments.";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !ASettins.av ) {
|
if ( !CallControl.av ) {
|
||||||
error_str = "Audio not supported!";
|
error_str = "Audio not supported!";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxAvError error = toxav_answer(ASettins.av, self->call_idx, &ASettins.cs);
|
if ( !CallControl.pending_call ) {
|
||||||
|
error_str = "No incoming call!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
if ( error != av_ErrorNone ) {
|
toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
|
||||||
if ( error == av_ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
|
if ( error != TOXAV_ERR_ANSWER_OK ) {
|
||||||
else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
|
if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!";
|
||||||
|
else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!";
|
||||||
|
else if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_FOUND ) error_str = "Friend not found!";
|
||||||
|
else if ( error == TOXAV_ERR_ANSWER_INVALID_BIT_RATE ) error_str = "Invalid bit rate!";
|
||||||
else error_str = "Internal error!";
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Callback will print status... */
|
/* Callback will print status... */
|
||||||
|
callback_recv_starting(self->num);
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
@ -413,27 +539,27 @@ void cmd_reject(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
{
|
{
|
||||||
const char *error_str;
|
const char *error_str;
|
||||||
|
|
||||||
if (argc != 0) {
|
if ( argc != 0 ) {
|
||||||
error_str = "Unknown arguments.";
|
error_str = "Unknown arguments.";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !ASettins.av ) {
|
if ( !CallControl.av ) {
|
||||||
error_str = "Audio not supported!";
|
error_str = "Audio not supported!";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxAvError error = toxav_reject(ASettins.av, self->call_idx, "Why not?");
|
if ( !CallControl.pending_call ) {
|
||||||
|
error_str = "No incoming call!";
|
||||||
if ( error != av_ErrorNone ) {
|
|
||||||
if ( error == av_ErrorInvalidState ) error_str = "Cannot reject in invalid state!";
|
|
||||||
else if ( error == av_ErrorNoCall ) error_str = "No incoming call!";
|
|
||||||
else error_str = "Internal error!";
|
|
||||||
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Manually send a cancel call control because call hasn't started */
|
||||||
|
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
|
||||||
|
CallControl.pending_call = false;
|
||||||
|
|
||||||
/* Callback will print status... */
|
/* Callback will print status... */
|
||||||
|
callback_call_rejected(self->num);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
@ -442,38 +568,38 @@ on_error:
|
|||||||
|
|
||||||
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
const char *error_str;
|
const char *error_str = NULL;
|
||||||
|
|
||||||
if (argc != 0) {
|
if ( !self->is_call ) {
|
||||||
|
error_str = "Not in a call.";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( argc != 0 ) {
|
||||||
error_str = "Unknown arguments.";
|
error_str = "Unknown arguments.";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !ASettins.av ) {
|
if ( !CallControl.av ) {
|
||||||
error_str = "Audio not supported!";
|
error_str = "Audio not supported!";
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
ToxAvError error;
|
#ifdef VIDEO
|
||||||
|
callback_video_end(self->num);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
if (toxav_get_call_state(ASettins.av, self->call_idx) == av_CallInviting) {
|
if ( CallControl.pending_call ) {
|
||||||
error = toxav_cancel(ASettins.av, self->call_idx, self->num,
|
/* Manually send a cancel call control because call hasn't started */
|
||||||
"Only those who appreciate small things know the beauty that is life");
|
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
|
||||||
#ifdef SOUND_NOTIFY
|
callback_call_canceled(self->num);
|
||||||
stop_sound(self->ringing_sound);
|
}
|
||||||
#endif
|
else {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
|
stop_transmission(&CallControl.calls[self->num], self->num);
|
||||||
} else {
|
callback_call_ended(self->num);
|
||||||
error = toxav_hangup(ASettins.av, self->call_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( error != av_ErrorNone ) {
|
CallControl.pending_call = false;
|
||||||
if ( error == av_ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
|
|
||||||
else if ( error == av_ErrorNoCall ) error_str = "No call!";
|
|
||||||
else error_str = "Internal error!";
|
|
||||||
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
on_error:
|
on_error:
|
||||||
@ -596,28 +722,26 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If call is active, change device */
|
/* If call is active, change device */
|
||||||
if ( self->call_idx > -1) {
|
if ( self->is_call ) {
|
||||||
Call* this_call = &ASettins.calls[self->call_idx];
|
Call* this_call = &CallControl.calls[self->num];
|
||||||
if (this_call->ttas) {
|
if ( this_call->ttas ) {
|
||||||
|
|
||||||
ToxAvCSettings csettings;
|
|
||||||
toxav_get_peer_csettings(ASettins.av, self->call_idx, 0, &csettings);
|
|
||||||
|
|
||||||
if (type == output) {
|
if ( type == output ) {
|
||||||
pthread_mutex_lock(&this_call->mutex);
|
pthread_mutex_lock(&this_call->mutex);
|
||||||
close_device(output, this_call->out_idx);
|
close_device(output, this_call->out_idx);
|
||||||
this_call->has_output = open_device(output, selection, &this_call->out_idx,
|
this_call->has_output = open_device(output, selection, &this_call->out_idx,
|
||||||
csettings.audio_sample_rate, csettings.audio_frame_duration, csettings.audio_channels)
|
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
|
||||||
== de_None ? 1 : 0;
|
== de_None ? 1 : 0;
|
||||||
pthread_mutex_unlock(&this_call->mutex);
|
pthread_mutex_unlock(&this_call->mutex);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* TODO: check for failure */
|
/* TODO: check for failure */
|
||||||
close_device(input, this_call->in_idx);
|
close_device(input, this_call->in_idx);
|
||||||
open_device(input, selection, &this_call->in_idx, csettings.audio_sample_rate,
|
open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
|
||||||
csettings.audio_frame_duration, csettings.audio_channels);
|
CallControl.audio_frame_duration, CallControl.audio_channels);
|
||||||
/* Set VAD as true for all; TODO: Make it more dynamic */
|
/* Set VAD as true for all; TODO: Make it more dynamic */
|
||||||
register_device_callback(self->call_idx, this_call->in_idx, read_device_callback, &self->call_idx, true);
|
register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -655,11 +779,11 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
|
|
||||||
|
|
||||||
/* If call is active, use this_call values */
|
/* If call is active, use this_call values */
|
||||||
if ( self->call_idx > -1) {
|
if ( self->is_call ) {
|
||||||
Call* this_call = &ASettins.calls[self->call_idx];
|
Call* this_call = &CallControl.calls[self->num];
|
||||||
|
|
||||||
pthread_mutex_lock(&this_call->mutex);
|
pthread_mutex_lock(&this_call->mutex);
|
||||||
if (type == input) {
|
if ( type == input ) {
|
||||||
device_mute(type, this_call->in_idx);
|
device_mute(type, this_call->in_idx);
|
||||||
self->chatwin->infobox.in_is_muted ^= 1;
|
self->chatwin->infobox.in_is_muted ^= 1;
|
||||||
} else {
|
} else {
|
||||||
@ -695,8 +819,8 @@ void cmd_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[M
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Call must be active */
|
/* Call must be active */
|
||||||
if ( self->call_idx > -1) {
|
if ( self->is_call ) {
|
||||||
device_set_VAD_treshold(ASettins.calls[self->call_idx].in_idx, value);
|
device_set_VAD_treshold(CallControl.calls[self->num].in_idx, value);
|
||||||
self->chatwin->infobox.vad_lvl = value;
|
self->chatwin->infobox.vad_lvl = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,23 +833,8 @@ on_error:
|
|||||||
|
|
||||||
void stop_current_call(ToxWindow* self)
|
void stop_current_call(ToxWindow* self)
|
||||||
{
|
{
|
||||||
ToxAvCallState callstate;
|
Call *this_call = &CallControl.calls[self->num];
|
||||||
if ( ASettins.av != NULL && self->call_idx != -1 &&
|
|
||||||
( callstate = toxav_get_call_state(ASettins.av, self->call_idx) ) != av_CallNonExistent) {
|
if (this_call && self->is_call)
|
||||||
switch (callstate)
|
stop_transmission(this_call, self->num);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef AUDIO_H
|
#ifndef AUDIO_CALL_H
|
||||||
#define AUDIO_H
|
#define AUDIO_CALL_H
|
||||||
|
|
||||||
#include <tox/toxav.h>
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
|
|
||||||
|
#define MAX_CALLS 10
|
||||||
|
|
||||||
typedef enum _AudioError {
|
typedef enum _AudioError {
|
||||||
ae_None = 0,
|
ae_None = 0,
|
||||||
@ -34,18 +36,58 @@ typedef enum _AudioError {
|
|||||||
ae_StartingCoreAudio = 1 << 2
|
ae_StartingCoreAudio = 1 << 2
|
||||||
} AudioError;
|
} AudioError;
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
typedef enum _VideoError {
|
||||||
|
ve_None = 0,
|
||||||
|
ve_StartingCaptureDevice = 1 << 0,
|
||||||
|
ve_StartingOutputDevice = 1 << 1,
|
||||||
|
ve_StartingCoreVideo = 1 << 2
|
||||||
|
} VideoError;
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
typedef struct Call {
|
typedef struct Call {
|
||||||
pthread_t ttid; /* Transmission thread id */
|
pthread_t ttid; /* Transmission thread id */
|
||||||
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||||
uint32_t in_idx, out_idx;
|
uint32_t in_idx, out_idx; /* Audio Index */
|
||||||
|
#ifdef VIDEO
|
||||||
|
uint32_t vin_idx, vout_idx; /* Video Index */
|
||||||
|
#endif /* VIDEO */
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
} Call;
|
} Call;
|
||||||
|
|
||||||
|
struct CallControl {
|
||||||
|
AudioError audio_errors;
|
||||||
|
#ifdef VIDEO
|
||||||
|
VideoError video_errors;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
ToxAV *av;
|
||||||
|
ToxWindow *prompt;
|
||||||
|
|
||||||
|
Call calls[MAX_CALLS];
|
||||||
|
uint32_t call_state;
|
||||||
|
bool pending_call;
|
||||||
|
bool audio_enabled;
|
||||||
|
bool video_enabled;
|
||||||
|
|
||||||
|
uint32_t audio_bit_rate;
|
||||||
|
int32_t audio_frame_duration;
|
||||||
|
uint32_t audio_sample_rate;
|
||||||
|
uint8_t audio_channels;
|
||||||
|
|
||||||
|
uint32_t video_bit_rate;
|
||||||
|
int32_t video_frame_duration;
|
||||||
|
|
||||||
|
} CallControl;
|
||||||
|
|
||||||
|
struct CallControl CallControl;
|
||||||
|
|
||||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||||
ToxAv *init_audio(ToxWindow *self, Tox *tox);
|
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||||
void terminate_audio();
|
void terminate_audio();
|
||||||
int start_transmission(ToxWindow *self, Call *call);
|
int start_transmission(ToxWindow *self, Call *call);
|
||||||
int stop_transmission(Call *call, int call_index);
|
int stop_transmission(Call *call, uint32_t friend_number);
|
||||||
void stop_current_call(ToxWindow *self);
|
void stop_current_call(ToxWindow *self);
|
||||||
|
|
||||||
#endif /* AUDIO_H */
|
#endif /* AUDIO_CALL_H */
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
/* device.c
|
/* audio_device.c
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -20,7 +20,7 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
@ -57,7 +57,7 @@ typedef struct Device {
|
|||||||
ALCcontext *ctx; /* Device context */
|
ALCcontext *ctx; /* Device context */
|
||||||
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
DataHandleCallback cb; /* Use this to handle data from input device usually */
|
||||||
void* cb_data; /* Data to be passed to callback */
|
void* cb_data; /* Data to be passed to callback */
|
||||||
int32_t call_idx; /* ToxAv call index */
|
int32_t friend_number; /* ToxAV friend number */
|
||||||
|
|
||||||
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
|
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
|
||||||
uint32_t ref_count;
|
uint32_t ref_count;
|
||||||
@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
|
|||||||
uint32_t primary_device[2]; /* Primary device */
|
uint32_t primary_device[2]; /* Primary device */
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
static ToxAv* av = NULL;
|
static ToxAV* av = NULL;
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
/* q_mutex */
|
/* q_mutex */
|
||||||
@ -95,7 +95,7 @@ bool thread_running = true,
|
|||||||
void* thread_poll(void*);
|
void* thread_poll(void*);
|
||||||
/* Meet devices */
|
/* Meet devices */
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
DeviceError init_devices(ToxAv* av_)
|
DeviceError init_devices(ToxAV* av_)
|
||||||
#else
|
#else
|
||||||
DeviceError init_devices()
|
DeviceError init_devices()
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
@ -140,7 +140,10 @@ DeviceError init_devices()
|
|||||||
DeviceError terminate_devices()
|
DeviceError terminate_devices()
|
||||||
{
|
{
|
||||||
/* Cleanup if needed */
|
/* Cleanup if needed */
|
||||||
|
lock;
|
||||||
thread_running = false;
|
thread_running = false;
|
||||||
|
unlock;
|
||||||
|
|
||||||
usleep(20000);
|
usleep(20000);
|
||||||
|
|
||||||
if (pthread_mutex_destroy(&mutex) != 0)
|
if (pthread_mutex_destroy(&mutex) != 0)
|
||||||
@ -339,7 +342,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
|
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD)
|
||||||
{
|
{
|
||||||
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
|
||||||
return de_InvalidSelection;
|
return de_InvalidSelection;
|
||||||
@ -348,7 +351,7 @@ DeviceError register_device_callback( int32_t call_idx, uint32_t device_idx, Dat
|
|||||||
running[input][device_idx]->cb = callback;
|
running[input][device_idx]->cb = callback;
|
||||||
running[input][device_idx]->cb_data = data;
|
running[input][device_idx]->cb_data = data;
|
||||||
running[input][device_idx]->enable_VAD = enable_VAD;
|
running[input][device_idx]->enable_VAD = enable_VAD;
|
||||||
running[input][device_idx]->call_idx = call_idx;
|
running[input][device_idx]->friend_number = friend_number;
|
||||||
unlock;
|
unlock;
|
||||||
|
|
||||||
return de_None;
|
return de_None;
|
||||||
@ -406,8 +409,15 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
|
|||||||
int32_t sample = 0;
|
int32_t sample = 0;
|
||||||
|
|
||||||
|
|
||||||
while (thread_running)
|
while (true)
|
||||||
{
|
{
|
||||||
|
lock;
|
||||||
|
if (!thread_running) {
|
||||||
|
unlock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unlock;
|
||||||
|
|
||||||
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
if (thread_paused) usleep(10000); /* Wait for unpause. */
|
||||||
else
|
else
|
||||||
{
|
{
|
@ -1,4 +1,4 @@
|
|||||||
/* device.h
|
/* audio_device.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
@ -26,8 +26,8 @@
|
|||||||
* Read from running input device(s) via select()/callback combo.
|
* Read from running input device(s) via select()/callback combo.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef DEVICE_H
|
#ifndef AUDIO_DEVICE_H
|
||||||
#define DEVICE_H
|
#define AUDIO_DEVICE_H
|
||||||
|
|
||||||
#define OPENAL_BUFS 5
|
#define OPENAL_BUFS 5
|
||||||
#define MAX_DEVICES 32
|
#define MAX_DEVICES 32
|
||||||
@ -56,7 +56,7 @@ typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
|
|||||||
|
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
DeviceError init_devices(ToxAv* av);
|
DeviceError init_devices(ToxAV* av);
|
||||||
#else
|
#else
|
||||||
DeviceError init_devices();
|
DeviceError init_devices();
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
@ -64,7 +64,7 @@ DeviceError init_devices();
|
|||||||
DeviceError terminate_devices();
|
DeviceError terminate_devices();
|
||||||
|
|
||||||
/* Callback handles ready data from INPUT device */
|
/* Callback handles ready data from INPUT device */
|
||||||
DeviceError register_device_callback(int32_t call_idx, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
|
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
|
||||||
void* get_device_callback_data(uint32_t device_idx);
|
void* get_device_callback_data(uint32_t device_idx);
|
||||||
|
|
||||||
/* toggle device mute */
|
/* toggle device mute */
|
||||||
@ -88,4 +88,4 @@ void print_devices(ToxWindow* self, DeviceType type);
|
|||||||
void get_primary_device_name(DeviceType type, char *buf, int size);
|
void get_primary_device_name(DeviceType type, char *buf, int size);
|
||||||
|
|
||||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||||
#endif /* DEVICE_H */
|
#endif /* AUDIO_DEVICE_H */
|
@ -93,7 +93,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
|||||||
{
|
{
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
const char *L = (char *) list;
|
const char *L = (char *) list;
|
||||||
@ -114,7 +114,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
|||||||
tmp[ctx->pos] = '\0';
|
tmp[ctx->pos] = '\0';
|
||||||
|
|
||||||
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
|
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
|
||||||
char *sub = malloc(strlen(ubuf) + 1);
|
char *sub = calloc(1, strlen(ubuf) + 1);
|
||||||
|
|
||||||
if (sub == NULL)
|
if (sub == NULL)
|
||||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||||
@ -136,20 +136,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string_is_empty(sub)) {
|
if (!sub[0]) {
|
||||||
free(sub);
|
free(sub);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int s_len = strlen(sub);
|
int s_len = strlen(sub);
|
||||||
const char *str;
|
|
||||||
int n_matches = 0;
|
int n_matches = 0;
|
||||||
char matches[n_items][MAX_STR_SIZE];
|
char matches[n_items][MAX_STR_SIZE];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
/* put all list matches in matches array */
|
/* put all list matches in matches array */
|
||||||
for (i = 0; i < n_items; ++i) {
|
for (i = 0; i < n_items; ++i) {
|
||||||
str = &L[i * size];
|
char str[MAX_CMDNAME_SIZE + 1];
|
||||||
|
snprintf(str, sizeof(str), "%s", &L[i * size]);
|
||||||
|
|
||||||
if (strncasecmp(str, sub, s_len) == 0)
|
if (strncasecmp(str, sub, s_len) == 0)
|
||||||
strcpy(matches[n_matches++], str);
|
strcpy(matches[n_matches++], str);
|
||||||
@ -165,10 +165,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
|||||||
|
|
||||||
char match[MAX_STR_SIZE];
|
char match[MAX_STR_SIZE];
|
||||||
get_str_match(self, match, matches, n_matches);
|
get_str_match(self, match, matches, n_matches);
|
||||||
|
size_t match_len = strlen(match);
|
||||||
|
|
||||||
if (dir_search) {
|
if (dir_search) {
|
||||||
if (n_matches == 1)
|
if (n_matches == 1)
|
||||||
endchrs = char_rfind(match, '.', strlen(match)) ? "\"" : "/";
|
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
|
||||||
else
|
else
|
||||||
endchrs = "";
|
endchrs = "";
|
||||||
} else if (n_matches > 1) {
|
} else if (n_matches > 1) {
|
||||||
@ -177,23 +178,26 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
|||||||
|
|
||||||
/* put match in correct spot in buf and append endchars */
|
/* put match in correct spot in buf and append endchars */
|
||||||
int n_endchrs = strlen(endchrs);
|
int n_endchrs = strlen(endchrs);
|
||||||
int m_len = strlen(match);
|
|
||||||
int strt = ctx->pos - s_len;
|
int strt = ctx->pos - s_len;
|
||||||
int diff = m_len - s_len + n_endchrs;
|
int diff = match_len - s_len + n_endchrs;
|
||||||
|
|
||||||
if (ctx->len + diff >= MAX_STR_SIZE)
|
if (ctx->len + diff >= MAX_STR_SIZE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
char tmpend[MAX_STR_SIZE];
|
char tmpend[MAX_STR_SIZE];
|
||||||
strcpy(tmpend, &ubuf[ctx->pos]);
|
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
|
||||||
|
|
||||||
|
if (match_len + n_endchrs + strlen(tmpend) >= sizeof(ubuf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
strcpy(&ubuf[strt], match);
|
strcpy(&ubuf[strt], match);
|
||||||
strcpy(&ubuf[strt + m_len], endchrs);
|
strcpy(&ubuf[strt + match_len], endchrs);
|
||||||
strcpy(&ubuf[strt + m_len + n_endchrs], tmpend);
|
strcpy(&ubuf[strt + match_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];
|
||||||
|
|
||||||
if (mbs_to_wcs_buf(newbuf, ubuf, MAX_STR_SIZE) == -1)
|
if (mbs_to_wcs_buf(newbuf, ubuf, sizeof(newbuf) / sizeof(wchar_t)) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
wcscpy(ctx->line, newbuf);
|
wcscpy(ctx->line, newbuf);
|
||||||
@ -218,7 +222,7 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha
|
|||||||
|
|
||||||
wchar_t wline[MAX_STR_SIZE];
|
wchar_t wline[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline)) == -1)
|
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int newlen = wcslen(wline);
|
int newlen = wcslen(wline);
|
||||||
@ -261,10 +265,10 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
|||||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||||
char tmp[MAX_STR_SIZE];
|
char tmp[MAX_STR_SIZE];
|
||||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||||
strcpy(b_path, tmp);
|
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(b_name, &b_path[si + 1]);
|
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||||
b_path[si + 1] = '\0';
|
b_path[si + 1] = '\0';
|
||||||
int b_name_len = strlen(b_name);
|
int b_name_len = strlen(b_name);
|
||||||
DIR *dp = opendir(b_path);
|
DIR *dp = opendir(b_path);
|
||||||
|
205
src/avatars.c
Normal file
205
src/avatars.c
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
/* avatars.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
#include "friendlist.h"
|
||||||
|
#include "avatars.h"
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
|
|
||||||
|
static struct Avatar {
|
||||||
|
char name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||||
|
size_t name_len;
|
||||||
|
char path[PATH_MAX + 1];
|
||||||
|
size_t path_len;
|
||||||
|
off_t size;
|
||||||
|
} Avatar;
|
||||||
|
|
||||||
|
|
||||||
|
static void avatar_clear(void)
|
||||||
|
{
|
||||||
|
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends avatar to friendnum.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_send(Tox *m, uint32_t friendnum)
|
||||||
|
{
|
||||||
|
TOX_ERR_FILE_SEND err;
|
||||||
|
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||||
|
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||||
|
if (Avatar.size == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||||
|
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||||
|
|
||||||
|
if (!ft)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ft->file = fopen(Avatar.path, "r");
|
||||||
|
|
||||||
|
if (ft->file == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
|
||||||
|
ft->file_size = Avatar.size;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sends avatar to all friends */
|
||||||
|
static void avatar_send_all(Tox *m)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < Friends.max_idx; ++i) {
|
||||||
|
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
|
||||||
|
avatar_send(m, Friends.list[i].num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets avatar to path and sends it to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||||
|
{
|
||||||
|
if (path_len == 0 || path_len >= sizeof(Avatar.path))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
FILE *fp = fopen(path, "rb");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||||
|
|
||||||
|
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
off_t size = file_size(path);
|
||||||
|
|
||||||
|
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
get_file_name(Avatar.name, sizeof(Avatar.name), path);
|
||||||
|
Avatar.name_len = strlen(Avatar.name);
|
||||||
|
snprintf(Avatar.path, sizeof(Avatar.path), "%s", path);
|
||||||
|
Avatar.path_len = path_len;
|
||||||
|
Avatar.size = size;
|
||||||
|
|
||||||
|
avatar_send_all(m);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unsets avatar and sends to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
void avatar_unset(Tox *m)
|
||||||
|
{
|
||||||
|
avatar_clear();
|
||||||
|
avatar_send_all(m);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
|
||||||
|
{
|
||||||
|
switch (control) {
|
||||||
|
case TOX_FILE_CONTROL_RESUME:
|
||||||
|
if (ft->state == FILE_TRANSFER_PENDING) {
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
} else if (ft->state == FILE_TRANSFER_PAUSED) {
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FILE_CONTROL_PAUSE:
|
||||||
|
ft->state = FILE_TRANSFER_PAUSED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOX_FILE_CONTROL_CANCEL:
|
||||||
|
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
|
||||||
|
{
|
||||||
|
if (ft->state != FILE_TRANSFER_STARTED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (length == 0) {
|
||||||
|
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->file == NULL) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->position != position) {
|
||||||
|
if (fseek(ft->file, position, SEEK_SET) == -1) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ft->position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t send_data[length];
|
||||||
|
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||||
|
|
||||||
|
if (send_length != length) {
|
||||||
|
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TOX_ERR_FILE_SEND_CHUNK err;
|
||||||
|
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||||
|
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||||
|
|
||||||
|
ft->position += send_length;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
}
|
52
src/avatars.h
Normal file
52
src/avatars.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/* avatars.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef AVATARS_H
|
||||||
|
#define AVATARS_H
|
||||||
|
|
||||||
|
#define MAX_AVATAR_FILE_SIZE 65536
|
||||||
|
|
||||||
|
/* Sends avatar to friendnum.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_send(Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
|
/* Sets avatar to path and sends it to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int avatar_set(Tox *m, const char *path, size_t length);
|
||||||
|
|
||||||
|
/* Unsets avatar and sends to all online contacts.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
void avatar_unset(Tox *m);
|
||||||
|
|
||||||
|
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||||
|
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
|
||||||
|
|
||||||
|
#endif /* AVATARS_H */
|
408
src/chat.c
408
src/chat.c
@ -49,7 +49,10 @@
|
|||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
#endif /* AUDIO */
|
#ifdef VIDEO
|
||||||
|
#include "video_call.h"
|
||||||
|
#endif /* VIDEO */
|
||||||
|
#endif /* AUDIO */
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern FriendsList Friends;
|
extern FriendsList Friends;
|
||||||
@ -63,9 +66,9 @@ static void kill_infobox(ToxWindow *self);
|
|||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#define AC_NUM_CHAT_COMMANDS 27
|
#define AC_NUM_CHAT_COMMANDS 30
|
||||||
#else
|
#else
|
||||||
#define AC_NUM_CHAT_COMMANDS 20
|
#define AC_NUM_CHAT_COMMANDS 22
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
/* Array of chat command names used for tab completion. */
|
/* Array of chat command names used for tab completion. */
|
||||||
@ -84,8 +87,10 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/join" },
|
{ "/join" },
|
||||||
{ "/log" },
|
{ "/log" },
|
||||||
{ "/myid" },
|
{ "/myid" },
|
||||||
|
{ "/myqr" },
|
||||||
{ "/nick" },
|
{ "/nick" },
|
||||||
{ "/note" },
|
{ "/note" },
|
||||||
|
{ "/nospam" },
|
||||||
{ "/quit" },
|
{ "/quit" },
|
||||||
{ "/savefile" },
|
{ "/savefile" },
|
||||||
{ "/sendfile" },
|
{ "/sendfile" },
|
||||||
@ -100,6 +105,7 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/sdev" },
|
{ "/sdev" },
|
||||||
{ "/mute" },
|
{ "/mute" },
|
||||||
{ "/sense" },
|
{ "/sense" },
|
||||||
|
{ "/video" },
|
||||||
|
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
};
|
};
|
||||||
@ -126,8 +132,11 @@ void kill_chat_window(ToxWindow *self, Tox *m)
|
|||||||
cqueue_cleanup(ctx->cqueue);
|
cqueue_cleanup(ctx->cqueue);
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
#ifdef VIDEO
|
||||||
|
stop_video_stream(self);
|
||||||
|
#endif /* VIDEO */
|
||||||
stop_current_call(self);
|
stop_current_call(self);
|
||||||
#endif
|
#endif /* AUDIO */
|
||||||
|
|
||||||
delwin(ctx->linewin);
|
delwin(ctx->linewin);
|
||||||
delwin(ctx->history);
|
delwin(ctx->history);
|
||||||
@ -188,8 +197,8 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TY
|
|||||||
return recv_action_helper(self, m, num, msg, len, nick, timefrmt);
|
return recv_action_helper(self, m, num, msg, len, nick, timefrmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_resume_file_transfers(Tox *m, uint32_t fnum);
|
static void chat_pause_file_transfers(Tox *m, uint32_t friendnum);
|
||||||
static void chat_stop_file_senders(uint32_t friendnum);
|
static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t fnum);
|
||||||
|
|
||||||
static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status)
|
static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status)
|
||||||
{
|
{
|
||||||
@ -206,28 +215,28 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
|
|||||||
char nick[TOX_MAX_NAME_LENGTH];
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
get_nick_truncate(m, nick, num);
|
get_nick_truncate(m, nick, num);
|
||||||
|
|
||||||
statusbar->connection = connection_status;
|
if (connection_status != TOX_CONNECTION_NONE && statusbar->connection == TOX_CONNECTION_NONE) {
|
||||||
|
|
||||||
if (connection_status != TOX_CONNECTION_NONE) {
|
|
||||||
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
|
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
|
||||||
? tox_friend_get_typing(m, num, NULL) : false;
|
? tox_friend_get_typing(m, num, NULL) : false;
|
||||||
chat_resume_file_transfers(m, num);
|
chat_resume_file_senders(self, m, num);
|
||||||
|
|
||||||
msg = "has come online";
|
msg = "has come online";
|
||||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||||
write_to_log(msg, nick, ctx->log, true);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
} else {
|
} else if (connection_status == TOX_CONNECTION_NONE) {
|
||||||
Friends.list[num].is_typing = false;
|
Friends.list[num].is_typing = false;
|
||||||
|
|
||||||
if (self->chatwin->self_is_typing)
|
if (self->chatwin->self_is_typing)
|
||||||
set_self_typingstatus(self, m, 0);
|
set_self_typingstatus(self, m, 0);
|
||||||
|
|
||||||
chat_stop_file_senders(num);
|
chat_pause_file_transfers(m, num);
|
||||||
|
|
||||||
msg = "has gone offline";
|
msg = "has gone offline";
|
||||||
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||||
write_to_log(msg, nick, ctx->log, true);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statusbar->connection = connection_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing)
|
static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing)
|
||||||
@ -277,196 +286,258 @@ static void chat_onReadReceipt(ToxWindow *self, Tox *m, uint32_t num, uint32_t r
|
|||||||
cqueue_remove(self, m, receipt);
|
cqueue_remove(self, m, receipt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stops active file senders for this friend. Call when a friend goes offline */
|
/* Stops active file transfers for this friend. Called when a friend goes offline */
|
||||||
static void chat_stop_file_senders(uint32_t friendnum)
|
static void chat_pause_file_transfers(Tox *m, uint32_t friendnum)
|
||||||
{
|
{
|
||||||
// size_t i;
|
ToxicFriend *friend = &Friends.list[friendnum];
|
||||||
|
|
||||||
// for (i = 0; i < MAX_FILES; ++i) {
|
size_t i;
|
||||||
// if (Friends.list[friendnum].file_sender[i].active)
|
|
||||||
// Friends.list[friendnum].file_sender[i].noconnection = true;
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
// }
|
if (friend->file_sender[i].state >= FILE_TRANSFER_STARTED)
|
||||||
|
friend->file_sender[i].state = FILE_TRANSFER_PAUSED;
|
||||||
|
|
||||||
|
if (friend->file_receiver[i].state >= FILE_TRANSFER_STARTED)
|
||||||
|
friend->file_receiver[i].state = FILE_TRANSFER_PAUSED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Tries to resume broken file transfers. Call when a friend comes online */
|
/* Tries to resume broken file senders. Called when a friend comes online */
|
||||||
static void chat_resume_file_transfers(Tox *m, uint32_t fnum)
|
static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||||
{
|
{
|
||||||
// size_t i;
|
size_t i;
|
||||||
|
|
||||||
// for (i = 0; i < MAX_FILES; ++i) {
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
// if (Friends.list[fnum].file_receiver[i].active) {
|
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||||
// uint8_t bytes_recv[sizeof(uint64_t)];
|
|
||||||
// memcpy(bytes_recv, &Friends.list[fnum].file_receiver[i].bytes_recv, sizeof(uint64_t));
|
if (ft->state != FILE_TRANSFER_PAUSED)
|
||||||
// net_to_host(bytes_recv, sizeof(uint64_t));
|
continue;
|
||||||
// uint32_t filenum = Friends.list[fnum].file_receiver[i].filenum;
|
|
||||||
// tox_file_send_control(m, fnum, 1, filenum, TOX_FILECONTROL_RESUME_BROKEN, bytes_recv, sizeof(uint64_t));
|
TOX_ERR_FILE_SEND err;
|
||||||
// }
|
ft->filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, ft->file_size, ft->file_id,
|
||||||
// }
|
(uint8_t *) ft->file_name, strlen(ft->file_name), &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name);
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint64_t position,
|
static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
|
||||||
size_t length)
|
size_t length)
|
||||||
{
|
{
|
||||||
if (num != self->num)
|
if (friendnum != self->num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
|
||||||
|
|
||||||
if (idx >= MAX_FILES)
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->state != FILE_TRANSFER_STARTED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
const char *file_name = Friends.list[num].file_sender[idx].file_name;
|
|
||||||
|
|
||||||
FILE *fp = Friends.list[num].file_sender[idx].file;
|
|
||||||
|
|
||||||
if (fp == NULL) {
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Null file pointer.", file_name);
|
|
||||||
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
snprintf(msg, sizeof(msg), "File '%s' successfully sent.", file_name);
|
snprintf(msg, sizeof(msg), "File '%s' successfully sent.", ft->file_name);
|
||||||
print_progress_bar(self, Friends.list[num].file_sender[idx].bps, 100.0, Friends.list[num].file_sender[idx].line_id);
|
print_progress_bar(self, ft->bps, 100.0, ft->line_id);
|
||||||
close_file_transfer(self, m, filenum, num, -1, msg, transfer_completed);
|
close_file_transfer(self, m, ft, -1, msg, transfer_completed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Friends.list[num].file_sender[idx].position != position) {
|
if (ft->file == NULL) {
|
||||||
if (fseek(fp, position, SEEK_SET) == -1) {
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Null file pointer.", ft->file_name);
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Seek fail.", file_name);
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->position != position) {
|
||||||
|
if (fseek(ft->file, position, SEEK_SET) == -1) {
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Seek fail.", ft->file_name);
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Friends.list[num].file_sender[idx].position = position;
|
ft->position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t send_data[length];
|
uint8_t send_data[length];
|
||||||
size_t send_length = fread(send_data, 1, sizeof(send_data), fp);
|
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||||
|
|
||||||
if (send_length != length) {
|
if (send_length != length) {
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read fail.", file_name);
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read fail.", ft->file_name);
|
||||||
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOX_ERR_FILE_SEND_CHUNK err;
|
TOX_ERR_FILE_SEND_CHUNK err;
|
||||||
tox_file_send_chunk(m, num, filenum, position, send_data, send_length, &err);
|
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||||
|
|
||||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||||
fprintf(stderr, "tox_file_send_chunk failed (error %d)\n", err);
|
fprintf(stderr, "tox_file_send_chunk failed in chat callback (error %d)\n", err);
|
||||||
|
|
||||||
Friends.list[num].file_sender[idx].position += send_length;
|
ft->position += send_length;
|
||||||
Friends.list[num].file_sender[idx].bps += send_length;
|
ft->bps += send_length;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint64_t position,
|
static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
|
||||||
const char *data, size_t length)
|
const char *data, size_t length)
|
||||||
{
|
{
|
||||||
if (num != self->num)
|
if (friendnum != self->num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
|
||||||
|
|
||||||
if (idx >= MAX_FILES)
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->state != FILE_TRANSFER_STARTED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
char file_name[MAX_STR_SIZE];
|
|
||||||
get_file_name(file_name, sizeof(file_name), Friends.list[num].file_receiver[idx].file_path);
|
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
snprintf(msg, sizeof(msg), "File '%s' successfully received.", file_name);
|
snprintf(msg, sizeof(msg), "File '%s' successfully received.", ft->file_name);
|
||||||
print_progress_bar(self, Friends.list[num].file_receiver[idx].bps, 100.0, Friends.list[num].file_receiver[idx].line_id);
|
print_progress_bar(self, ft->bps, 100.0, ft->line_id);
|
||||||
close_file_transfer(self, m, filenum, num, -1, msg, transfer_completed);
|
close_file_transfer(self, m, ft, -1, msg, transfer_completed);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *fp = Friends.list[num].file_receiver[idx].file;
|
if (ft->file == NULL) {
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Invalid file pointer.", ft->file_name);
|
||||||
if (fp == NULL) {
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Invalid file pointer.", file_name);
|
|
||||||
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fwrite(data, length, 1, fp) != 1) {
|
if (fwrite(data, length, 1, ft->file) != 1) {
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Write fail.", file_name);
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Write fail.", ft->file_name);
|
||||||
close_file_transfer(self, m, filenum, num, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Friends.list[num].file_receiver[idx].bps += length;
|
ft->bps += length;
|
||||||
Friends.list[num].file_receiver[idx].position += length;
|
ft->position += length;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, TOX_FILE_CONTROL control)
|
static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, TOX_FILE_CONTROL control)
|
||||||
{
|
{
|
||||||
if (self->num != num)
|
if (friendnum != self->num)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
|
||||||
bool sending = filenum_is_sending(filenum);
|
|
||||||
|
|
||||||
if (idx >= MAX_FILES)
|
if (!ft)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char file_name[MAX_STR_SIZE];
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
|
|
||||||
if (sending && Friends.list[num].file_sender[idx].active) {
|
|
||||||
snprintf(file_name, sizeof(file_name), "%s", Friends.list[num].file_sender[idx].file_name);
|
|
||||||
} else if (!sending && Friends.list[num].file_receiver[idx].active) {
|
|
||||||
get_file_name(file_name, sizeof(file_name), Friends.list[num].file_receiver[idx].file_path);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (control) {
|
switch (control) {
|
||||||
case TOX_FILE_CONTROL_RESUME:
|
case TOX_FILE_CONTROL_RESUME: {
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
|
||||||
/* transfer is accepted */
|
/* transfer is accepted */
|
||||||
if (sending && !Friends.list[num].file_sender[idx].started) {
|
if (ft->state == FILE_TRANSFER_PENDING) {
|
||||||
Friends.list[num].file_sender[idx].started = true;
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.",
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.",
|
||||||
idx, file_name);
|
ft->index, ft->file_name);
|
||||||
char progline[MAX_STR_SIZE];
|
char progline[MAX_STR_SIZE];
|
||||||
prep_prog_line(progline);
|
init_progress_bar(progline);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||||
Friends.list[num].file_sender[idx].line_id = self->chatwin->hst->line_end->id + 2;
|
|
||||||
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
|
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
|
||||||
|
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||||
|
} else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TOX_FILE_CONTROL_PAUSE:
|
case TOX_FILE_CONTROL_PAUSE: {
|
||||||
|
ft->state = FILE_TRANSFER_PAUSED;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TOX_FILE_CONTROL_CANCEL:
|
case TOX_FILE_CONTROL_CANCEL: {
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", file_name);
|
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
|
||||||
close_file_transfer(self, m, filenum, num, -1, msg, notif_error);
|
close_file_transfer(self, m, ft, -1, msg, notif_error);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint32_t kind,
|
/* Attempts to resume a broken inbound file transfer.
|
||||||
uint64_t file_size, const char *filename, size_t name_length)
|
*
|
||||||
|
* Returns true if resume is successful.
|
||||||
|
*/
|
||||||
|
static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum)
|
||||||
{
|
{
|
||||||
if (self->num != num)
|
char msg[MAX_STR_SIZE];
|
||||||
|
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||||
|
|
||||||
|
if (!tox_file_get_file_id(m, friendnum, filenum, file_id, NULL))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool resuming = false;
|
||||||
|
struct FileTransfer *ft = NULL;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
ft = &Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (memcmp(ft->file_id, file_id, TOX_FILE_ID_LENGTH) == 0) {
|
||||||
|
ft->filenum = filenum;
|
||||||
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
resuming = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resuming || !ft)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!tox_file_seek(m, ft->friendnum, ft->filenum, ft->position, NULL))
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if (!tox_file_control(m, ft->friendnum, ft->filenum, TOX_FILE_CONTROL_RESUME, NULL))
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' failed.", ft->file_name);
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t file_size,
|
||||||
|
const char *filename, size_t name_length)
|
||||||
|
{
|
||||||
|
if (self->num != friendnum)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
/* first check if we need to resume a broken transfer */
|
||||||
|
if (chat_resume_broken_ft(self, m, friendnum, filenum))
|
||||||
|
return;
|
||||||
|
|
||||||
if (idx >= MAX_FILES) {
|
struct FileTransfer *ft = new_file_transfer(self, friendnum, filenum, FILE_TRANSFER_RECV, TOX_FILE_KIND_DATA);
|
||||||
|
|
||||||
|
if (!ft) {
|
||||||
|
tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Too many concurrent file transfers.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Too many concurrent file transfers.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char sizestr[32];
|
char sizestr[32];
|
||||||
bytes_convert_str(sizestr, sizeof(sizestr), file_size);
|
bytes_convert_str(sizestr, sizeof(sizestr), file_size);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)",
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
|
||||||
filename, sizestr);
|
|
||||||
|
|
||||||
char file_path[MAX_STR_SIZE];
|
char file_path[MAX_STR_SIZE];
|
||||||
size_t path_len = name_length;
|
size_t path_len = name_length;
|
||||||
@ -479,8 +550,9 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t file
|
|||||||
snprintf(file_path, sizeof(file_path), "%s", filename);
|
snprintf(file_path, sizeof(file_path), "%s", filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path_len >= sizeof(Friends.list[num].file_receiver[idx].file_path)) {
|
if (path_len >= sizeof(file_path) || path_len >= sizeof(ft->file_path) || name_length >= sizeof(ft->file_name)) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer faield: File name too long.");
|
tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer faield: File path too long.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,16 +577,18 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t file
|
|||||||
file_path[path_len + d_len] = '\0';
|
file_path[path_len + d_len] = '\0';
|
||||||
|
|
||||||
if (count > 999) {
|
if (count > 999) {
|
||||||
|
tox_file_control(m, friendnum, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: invalid file path.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: invalid file path.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", idx);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", ft->index);
|
||||||
|
|
||||||
Friends.list[num].file_receiver[idx].pending = true;
|
ft->file_size = file_size;
|
||||||
Friends.list[num].file_receiver[idx].file_size = file_size;
|
snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path);
|
||||||
strcpy(Friends.list[num].file_receiver[idx].file_path, file_path);
|
snprintf(ft->file_name, sizeof(ft->file_name), "%s", filename);
|
||||||
|
tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL);
|
||||||
|
|
||||||
if (self->active_box != -1)
|
if (self->active_box != -1)
|
||||||
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
|
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
|
||||||
@ -558,32 +632,31 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui
|
|||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Av Stuff */
|
/* AV Stuff */
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
void chat_onInvite (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* call_index is set here and reset on call end */
|
/* call is flagged active here */
|
||||||
|
self->is_call = true;
|
||||||
|
|
||||||
self->call_idx = call_index;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
|
||||||
|
|
||||||
if (self->ringing_sound == -1)
|
if (self->ringing_sound == -1)
|
||||||
sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound);
|
sound_notify(self, call_incoming, NT_LOOP, &self->ringing_sound);
|
||||||
|
|
||||||
|
|
||||||
if (self->active_box != -1)
|
if (self->active_box != -1)
|
||||||
box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!");
|
box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!");
|
||||||
else
|
else
|
||||||
box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!");
|
box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!");
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
|
||||||
@ -594,40 +667,44 @@ void chat_onRinging (ToxWindow *self, ToxAv *av, int call_index)
|
|||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onStarting (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onStarting (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
init_infobox(self);
|
init_infobox(self);
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
|
||||||
|
|
||||||
|
/* call is flagged active here */
|
||||||
|
self->is_call = true;
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
stop_sound(self->ringing_sound);
|
stop_sound(self->ringing_sound);
|
||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onEnding (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onEnding (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
kill_infobox(self);
|
kill_infobox(self);
|
||||||
self->call_idx = -1;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
|
||||||
|
|
||||||
|
self->is_call = false;
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
stop_sound(self->ringing_sound);
|
stop_sound(self->ringing_sound);
|
||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onError (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onError (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self->call_idx = -1;
|
self->is_call = false;
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!");
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
@ -635,11 +712,14 @@ void chat_onError (ToxWindow *self, ToxAv *av, int call_index)
|
|||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onStart (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onStart (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* call is flagged active here */
|
||||||
|
self->is_call = true;
|
||||||
|
|
||||||
init_infobox(self);
|
init_infobox(self);
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
|
||||||
@ -649,13 +729,13 @@ void chat_onStart (ToxWindow *self, ToxAv *av, int call_index)
|
|||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onCancel (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if ( !self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
self->is_call = false;
|
||||||
kill_infobox(self);
|
kill_infobox(self);
|
||||||
self->call_idx = -1;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
@ -663,39 +743,39 @@ void chat_onCancel (ToxWindow *self, ToxAv *av, int call_index)
|
|||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onReject (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onReject (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self->call_idx = -1;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
|
||||||
|
self->is_call = false;
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
stop_sound(self->ringing_sound);
|
stop_sound(self->ringing_sound);
|
||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onEnd (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
kill_infobox(self);
|
kill_infobox(self);
|
||||||
self->call_idx = -1;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
|
||||||
|
self->is_call = false;
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
stop_sound(self->ringing_sound);
|
stop_sound(self->ringing_sound);
|
||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onRequestTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
self->call_idx = -1;
|
self->is_call = false;
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No answer!");
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
@ -703,13 +783,13 @@ void chat_onRequestTimeout (ToxWindow *self, ToxAv *av, int call_index)
|
|||||||
#endif /* SOUND_NOTIFY */
|
#endif /* SOUND_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
void chat_onPeerTimeout (ToxWindow *self, ToxAv *av, int call_index)
|
void chat_onPeerTimeout (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
if (!self || self->call_idx != call_index || self->num != toxav_get_peer_id(av, call_index, 0))
|
if (!self || self->num != friend_number)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
self->is_call = false;
|
||||||
kill_infobox(self);
|
kill_infobox(self);
|
||||||
self->call_idx = -1;
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Peer disconnected; call ended!");
|
||||||
|
|
||||||
#ifdef SOUND_NOTIFY
|
#ifdef SOUND_NOTIFY
|
||||||
@ -723,6 +803,10 @@ static void init_infobox(ToxWindow *self)
|
|||||||
|
|
||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
(void) y2;
|
(void) y2;
|
||||||
|
|
||||||
memset(&ctx->infobox, 0, sizeof(struct infobox));
|
memset(&ctx->infobox, 0, sizeof(struct infobox));
|
||||||
@ -829,7 +913,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= 0)
|
if (y2 <= 0 || x2 <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (self->help->active) {
|
if (self->help->active) {
|
||||||
@ -872,7 +956,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
if (diff != -1) {
|
if (diff != -1) {
|
||||||
if (x + diff > x2 - 1) {
|
if (x + diff > x2 - 1) {
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -927,9 +1011,15 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
@ -1023,7 +1113,7 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int y, x;
|
int y, x;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
(void) x;
|
(void) x;
|
||||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
@ -1048,6 +1138,10 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
|||||||
curs_set(1);
|
curs_set(1);
|
||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
exit_toxic_err("failed in chat_onInit", FATALERR_CURSES);
|
||||||
|
|
||||||
self->x = x2;
|
self->x = x2;
|
||||||
|
|
||||||
/* Init statusbar info */
|
/* Init statusbar info */
|
||||||
@ -1090,11 +1184,13 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
|||||||
char myid[TOX_ADDRESS_SIZE];
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT);
|
int log_ret = log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT);
|
||||||
load_chat_history(self, ctx->log);
|
load_chat_history(self, ctx->log);
|
||||||
|
|
||||||
if (!Friends.list[self->num].logging_on)
|
if (!Friends.list[self->num].logging_on)
|
||||||
log_disable(ctx->log);
|
log_disable(ctx->log);
|
||||||
|
else if (log_ret == -1)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||||
|
|
||||||
@ -1136,10 +1232,8 @@ ToxWindow new_chat(Tox *m, uint32_t friendnum)
|
|||||||
ret.onCancel = &chat_onCancel;
|
ret.onCancel = &chat_onCancel;
|
||||||
ret.onReject = &chat_onReject;
|
ret.onReject = &chat_onReject;
|
||||||
ret.onEnd = &chat_onEnd;
|
ret.onEnd = &chat_onEnd;
|
||||||
ret.onRequestTimeout = &chat_onRequestTimeout;
|
|
||||||
ret.onPeerTimeout = &chat_onPeerTimeout;
|
|
||||||
|
|
||||||
ret.call_idx = -1;
|
ret.is_call = false;
|
||||||
ret.device_selection[0] = ret.device_selection[1] = -1;
|
ret.device_selection[0] = ret.device_selection[1] = -1;
|
||||||
ret.ringing_sound = -1;
|
ret.ringing_sound = -1;
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
@ -45,38 +45,37 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
|||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
const char *inoutstr = argv[1];
|
const char *inoutstr = argv[1];
|
||||||
int idx = atoi(argv[2]);
|
long int idx = strtol(argv[2], NULL, 10);
|
||||||
|
|
||||||
if (idx >= MAX_FILES || idx < 0) {
|
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
|
struct FileTransfer *ft = NULL;
|
||||||
if (!Friends.list[self->num].file_receiver[idx].active) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path;
|
/* cancel an incoming file transfer */
|
||||||
char file_name[MAX_STR_SIZE];
|
if (strcasecmp(inoutstr, "in") == 0) {
|
||||||
get_file_name(file_name, sizeof(file_name), file_path);
|
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", file_name);
|
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||||
close_file_transfer(self, m, get_file_receiver_filenum(idx), self->num, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||||
return;
|
|
||||||
} else if (strcasecmp(inoutstr, "out") == 0) { /* cancel an outgoing file transfer */
|
|
||||||
if (!Friends.list[self->num].file_sender[idx].active) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' canceled.", Friends.list[self->num].file_sender[idx].file_name);
|
|
||||||
close_file_transfer(self, m, idx, self->num, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ft) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' aborted.", ft->file_name);
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -86,9 +85,9 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int groupnum = atoi(argv[1]);
|
long int groupnum = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if (groupnum == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -121,11 +120,11 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
|||||||
|
|
||||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||||
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
||||||
#ifdef AUDIO
|
/*#ifdef AUDIO
|
||||||
else
|
else
|
||||||
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
||||||
write_device_callback_group, NULL);
|
NULL, NULL);
|
||||||
#endif
|
#endif*/
|
||||||
|
|
||||||
if (groupnum == -1) {
|
if (groupnum == -1) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||||
@ -147,43 +146,46 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int idx = atoi(argv[1]);
|
long int idx = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if ((idx == 0 && strcmp(argv[1], "0")) || idx >= MAX_FILES) {
|
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t filenum = get_file_receiver_filenum(idx);
|
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||||
|
|
||||||
if (!Friends.list[self->num].file_receiver[idx].pending) {
|
if (!ft) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *file_path = Friends.list[self->num].file_receiver[idx].file_path;
|
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
|
||||||
|
const char *msg = "File transfer failed: Invalid file path.";
|
||||||
|
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TOX_ERR_FILE_CONTROL err;
|
TOX_ERR_FILE_CONTROL err;
|
||||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_RESUME, &err);
|
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||||
|
|
||||||
if (err != TOX_ERR_FILE_CONTROL_OK)
|
if (err != TOX_ERR_FILE_CONTROL_OK)
|
||||||
goto on_recv_error;
|
goto on_recv_error;
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, file_path);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||||
|
|
||||||
/* prep progress bar line */
|
/* prep progress bar line */
|
||||||
char progline[MAX_STR_SIZE];
|
char progline[MAX_STR_SIZE];
|
||||||
prep_prog_line(progline);
|
init_progress_bar(progline);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||||
Friends.list[self->num].file_receiver[idx].line_id = self->chatwin->hst->line_end->id + 2;
|
|
||||||
Friends.list[self->num].file_receiver[idx].pending = false;
|
|
||||||
|
|
||||||
if ((Friends.list[self->num].file_receiver[idx].file = fopen(file_path, "a")) == NULL) {
|
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
ft->state = FILE_TRANSFER_STARTED;
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid file path.");
|
|
||||||
} else {
|
|
||||||
Friends.list[self->num].file_receiver[idx].active = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -252,33 +254,30 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
}
|
}
|
||||||
|
|
||||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||||
get_file_name(file_name, sizeof(file_name), path);
|
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
|
||||||
size_t namelen = strlen(file_name);
|
|
||||||
|
|
||||||
TOX_ERR_FILE_SEND err;
|
TOX_ERR_FILE_SEND err;
|
||||||
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize,
|
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
|
||||||
NULL, (uint8_t *) file_name, namelen, &err);
|
(uint8_t *) file_name, namelen, &err);
|
||||||
|
|
||||||
if (err != TOX_ERR_FILE_SEND_OK)
|
if (err != TOX_ERR_FILE_SEND_OK)
|
||||||
goto on_send_error;
|
goto on_send_error;
|
||||||
|
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||||
|
|
||||||
if (idx >= MAX_FILES) {
|
if (!ft) {
|
||||||
errmsg = "File transfer failed: Too many concurrent file transfers";
|
err = TOX_ERR_FILE_SEND_TOO_MANY;
|
||||||
goto on_send_error;
|
goto on_send_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(Friends.list[self->num].file_sender[idx].file_name, file_name, namelen + 1);
|
memcpy(ft->file_name, file_name, namelen + 1);
|
||||||
Friends.list[self->num].file_sender[idx].active = true;
|
ft->file = file_to_send;
|
||||||
Friends.list[self->num].file_sender[idx].started = false;
|
ft->file_size = filesize;
|
||||||
Friends.list[self->num].file_sender[idx].file = file_to_send;
|
tox_file_get_file_id(m, self->num, filenum, ft->file_id, NULL);
|
||||||
Friends.list[self->num].file_sender[idx].timestamp = get_unix_time();
|
|
||||||
Friends.list[self->num].file_sender[idx].file_size = filesize;
|
|
||||||
|
|
||||||
char sizestr[32];
|
char sizestr[32];
|
||||||
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", idx, file_name, sizestr);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -301,7 +300,7 @@ on_send_error:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
errmsg = "File transfer failed";
|
errmsg = "File transfer failed.";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,4 +43,9 @@ void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
|
|||||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
#endif /* #define CHAT_COMMANDS_H */
|
#endif /* #define CHAT_COMMANDS_H */
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "configdir.h"
|
#include "configdir.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
|
||||||
/* get the user's home directory */
|
/* get the user's home directory */
|
||||||
void get_home_dir(char *home, int size)
|
void get_home_dir(char *home, int size)
|
||||||
@ -69,8 +70,8 @@ char *get_user_config_dir(void)
|
|||||||
char home[NSS_BUFLEN_PASSWD] = {0};
|
char home[NSS_BUFLEN_PASSWD] = {0};
|
||||||
get_home_dir(home, sizeof(home));
|
get_home_dir(home, sizeof(home));
|
||||||
|
|
||||||
char *user_config_dir;
|
char *user_config_dir = NULL;
|
||||||
size_t len;
|
size_t len = 0;
|
||||||
|
|
||||||
# if defined(__APPLE__)
|
# if defined(__APPLE__)
|
||||||
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
||||||
@ -82,9 +83,9 @@ char *get_user_config_dir(void)
|
|||||||
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
||||||
# else /* __APPLE__ */
|
# else /* __APPLE__ */
|
||||||
|
|
||||||
const char *tmp;
|
const char *tmp = getenv("XDG_CONFIG_HOME");
|
||||||
|
|
||||||
if (!(tmp = getenv("XDG_CONFIG_HOME"))) {
|
if (tmp == NULL) {
|
||||||
len = strlen(home) + strlen("/.config") + 1;
|
len = strlen(home) + strlen("/.config") + 1;
|
||||||
user_config_dir = malloc(len);
|
user_config_dir = malloc(len);
|
||||||
|
|
||||||
@ -98,6 +99,11 @@ char *get_user_config_dir(void)
|
|||||||
|
|
||||||
# endif /* __APPLE__ */
|
# endif /* __APPLE__ */
|
||||||
|
|
||||||
|
if (!file_exists(user_config_dir)) {
|
||||||
|
free(user_config_dir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return user_config_dir;
|
return user_config_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
428
src/dns.c
428
src/dns.c
@ -1,428 +0,0 @@
|
|||||||
/* dns.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h> /* for u_char */
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <resolv.h>
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <arpa/nameser_compat.h>
|
|
||||||
#else
|
|
||||||
#include <arpa/nameser.h>
|
|
||||||
#endif /* ifdef __APPLE__ */
|
|
||||||
|
|
||||||
#include <tox/toxdns.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "windows.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "dns.h"
|
|
||||||
#include "global_commands.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "configdir.h"
|
|
||||||
|
|
||||||
#define DNS3_KEY_SIZE 32
|
|
||||||
#define MAX_DNS_REQST_SIZE 255
|
|
||||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
|
||||||
|
|
||||||
extern struct Winthread Winthread;
|
|
||||||
extern struct dns3_servers dns3_servers;
|
|
||||||
extern struct arg_opts arg_opts;
|
|
||||||
|
|
||||||
#define NUM_DNS3_BACKUP_SERVERS 2
|
|
||||||
|
|
||||||
/* Hardcoded backup in case domain list is not loaded */
|
|
||||||
static struct dns3_server_backup {
|
|
||||||
const char *name;
|
|
||||||
char key[DNS3_KEY_SIZE];
|
|
||||||
} dns3_servers_backup[] = {
|
|
||||||
{
|
|
||||||
"utox.org",
|
|
||||||
{
|
|
||||||
0xD3, 0x15, 0x4F, 0x65, 0xD2, 0x8A, 0x5B, 0x41, 0xA0, 0x5D, 0x4A, 0xC7, 0xE4, 0xB3, 0x9C, 0x6B,
|
|
||||||
0x1C, 0x23, 0x3C, 0xC8, 0x57, 0xFB, 0x36, 0x5C, 0x56, 0xE8, 0x39, 0x27, 0x37, 0x46, 0x2A, 0x12
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"toxme.se",
|
|
||||||
{
|
|
||||||
0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
|
|
||||||
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct thread_data {
|
|
||||||
ToxWindow *self;
|
|
||||||
char id_bin[TOX_ADDRESS_SIZE];
|
|
||||||
char addr[MAX_STR_SIZE];
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
uint8_t busy;
|
|
||||||
Tox *m;
|
|
||||||
} t_data;
|
|
||||||
|
|
||||||
static struct dns_thread {
|
|
||||||
pthread_t tid;
|
|
||||||
pthread_attr_t attr;
|
|
||||||
} dns_thread;
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_DNS_SERVERS 50
|
|
||||||
#define MAX_DOMAIN_SIZE 32
|
|
||||||
#define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3
|
|
||||||
|
|
||||||
struct dns3_servers {
|
|
||||||
bool loaded;
|
|
||||||
int lines;
|
|
||||||
char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE];
|
|
||||||
char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE];
|
|
||||||
} dns3_servers;
|
|
||||||
|
|
||||||
static int load_dns_domainlist(const char *path)
|
|
||||||
{
|
|
||||||
FILE *fp = fopen(path, "r");
|
|
||||||
|
|
||||||
if (fp == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
char line[MAX_DNS_LINE];
|
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) {
|
|
||||||
int linelen = strlen(line);
|
|
||||||
|
|
||||||
if (linelen < DNS3_KEY_SIZE * 2 + 5)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (line[linelen - 1] == '\n')
|
|
||||||
line[--linelen] = '\0';
|
|
||||||
|
|
||||||
const char *name = strtok(line, " ");
|
|
||||||
const char *keystr = strtok(NULL, " ");
|
|
||||||
|
|
||||||
if (name == NULL || keystr == NULL)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (strlen(keystr) != DNS3_KEY_SIZE * 2)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name);
|
|
||||||
int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr);
|
|
||||||
|
|
||||||
if (res == -1)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
++dns3_servers.lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
if (dns3_servers.lines < 1)
|
|
||||||
return -2;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dns_error(ToxWindow *self, const char *errmsg)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "User lookup failed: %s", errmsg);
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void killdns_thread(void *dns_obj)
|
|
||||||
{
|
|
||||||
if (dns_obj)
|
|
||||||
tox_dns3_kill(dns_obj);
|
|
||||||
|
|
||||||
memset(&t_data, 0, sizeof(struct thread_data));
|
|
||||||
pthread_attr_destroy(&dns_thread.attr);
|
|
||||||
pthread_exit(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* puts TXT from dns response in buf. Returns length of TXT on success, -1 on fail.*/
|
|
||||||
static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char *buf)
|
|
||||||
{
|
|
||||||
uint8_t *ans_pt = answer + sizeof(HEADER);
|
|
||||||
uint8_t *ans_end = answer + ans_len;
|
|
||||||
char exp_ans[PACKETSZ];
|
|
||||||
|
|
||||||
int len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
|
||||||
|
|
||||||
if (len == -1)
|
|
||||||
return dns_error(self, "dn_expand failed.");
|
|
||||||
|
|
||||||
ans_pt += len;
|
|
||||||
|
|
||||||
if (ans_pt > ans_end - 4)
|
|
||||||
return dns_error(self, "DNS reply was too short.");
|
|
||||||
|
|
||||||
int type;
|
|
||||||
GETSHORT(type, ans_pt);
|
|
||||||
|
|
||||||
if (type != T_TXT)
|
|
||||||
return dns_error(self, "Broken DNS reply.");
|
|
||||||
|
|
||||||
|
|
||||||
ans_pt += INT16SZ; /* class */
|
|
||||||
uint32_t size = 0;
|
|
||||||
|
|
||||||
/* recurse through CNAME rr's */
|
|
||||||
do {
|
|
||||||
ans_pt += size;
|
|
||||||
len = dn_expand(answer, ans_end, ans_pt, exp_ans, sizeof(exp_ans));
|
|
||||||
|
|
||||||
if (len == -1)
|
|
||||||
return dns_error(self, "Second dn_expand failed.");
|
|
||||||
|
|
||||||
ans_pt += len;
|
|
||||||
|
|
||||||
if (ans_pt > ans_end - 10)
|
|
||||||
return dns_error(self, "DNS reply was too short.");
|
|
||||||
|
|
||||||
GETSHORT(type, ans_pt);
|
|
||||||
ans_pt += INT16SZ;
|
|
||||||
ans_pt += 4;
|
|
||||||
GETSHORT(size, ans_pt);
|
|
||||||
|
|
||||||
if (ans_pt + size < answer || ans_pt + size > ans_end)
|
|
||||||
return dns_error(self, "RR overflow.");
|
|
||||||
|
|
||||||
} while (type == T_CNAME);
|
|
||||||
|
|
||||||
if (type != T_TXT)
|
|
||||||
return dns_error(self, "DNS response failed.");
|
|
||||||
|
|
||||||
uint32_t txt_len = *ans_pt;
|
|
||||||
|
|
||||||
if (!size || txt_len >= size || !txt_len)
|
|
||||||
return dns_error(self, "No record found.");
|
|
||||||
|
|
||||||
if (txt_len > MAX_DNS_REQST_SIZE)
|
|
||||||
return dns_error(self, "Invalid DNS response.");
|
|
||||||
|
|
||||||
ans_pt++;
|
|
||||||
ans_pt[txt_len] = '\0';
|
|
||||||
memcpy(buf, ans_pt, txt_len + 1);
|
|
||||||
|
|
||||||
return txt_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
|
||||||
and the domain in dombuf.
|
|
||||||
|
|
||||||
return length of username on success, -1 on failure */
|
|
||||||
static int parse_addr(const char *addr, char *namebuf, char *dombuf)
|
|
||||||
{
|
|
||||||
char tmpaddr[MAX_STR_SIZE];
|
|
||||||
char *tmpname, *tmpdom;
|
|
||||||
|
|
||||||
strcpy(tmpaddr, addr);
|
|
||||||
tmpname = strtok(tmpaddr, "@");
|
|
||||||
tmpdom = strtok(NULL, "");
|
|
||||||
|
|
||||||
if (tmpname == NULL || tmpdom == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
str_to_lower(tmpdom);
|
|
||||||
strcpy(namebuf, tmpname);
|
|
||||||
strcpy(dombuf, tmpdom);
|
|
||||||
|
|
||||||
return strlen(namebuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
|
|
||||||
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
|
|
||||||
{
|
|
||||||
/* check server list first */
|
|
||||||
int i;
|
|
||||||
bool match = false;
|
|
||||||
|
|
||||||
for (i = 0; i < dns3_servers.lines; ++i) {
|
|
||||||
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
|
|
||||||
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
|
|
||||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* fall back to hard-coded domains on server list failure */
|
|
||||||
if (!match) {
|
|
||||||
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
|
|
||||||
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
|
|
||||||
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
|
|
||||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
|
|
||||||
match = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!match)
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
|
|
||||||
void *dns3_lookup_thread(void *data)
|
|
||||||
{
|
|
||||||
ToxWindow *self = t_data.self;
|
|
||||||
|
|
||||||
char inputdomain[MAX_STR_SIZE];
|
|
||||||
char name[MAX_STR_SIZE];
|
|
||||||
|
|
||||||
int namelen = parse_addr(t_data.addr, name, inputdomain);
|
|
||||||
|
|
||||||
if (namelen == -1) {
|
|
||||||
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
|
||||||
killdns_thread(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
char DNS_pubkey[DNS3_KEY_SIZE];
|
|
||||||
char domain[MAX_DOMAIN_SIZE];
|
|
||||||
|
|
||||||
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
|
|
||||||
|
|
||||||
if (match == -1) {
|
|
||||||
dns_error(self, "Domain not found.");
|
|
||||||
killdns_thread(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *dns_obj = tox_dns3_new((uint8_t *) DNS_pubkey);
|
|
||||||
|
|
||||||
if (dns_obj == NULL) {
|
|
||||||
dns_error(self, "Core failed to create DNS object.");
|
|
||||||
killdns_thread(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
char string[MAX_DNS_REQST_SIZE + 1];
|
|
||||||
uint32_t request_id;
|
|
||||||
|
|
||||||
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
|
|
||||||
(uint8_t *) name, namelen);
|
|
||||||
|
|
||||||
if (str_len == -1) {
|
|
||||||
dns_error(self, "Core failed to generate DNS3 string.");
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
string[str_len] = '\0';
|
|
||||||
|
|
||||||
u_char answer[PACKETSZ];
|
|
||||||
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
|
|
||||||
|
|
||||||
/* format string and create dns query */
|
|
||||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
|
|
||||||
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
|
|
||||||
|
|
||||||
if (ans_len <= 0) {
|
|
||||||
dns_error(self, "DNS query failed.");
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
char ans_id[MAX_DNS_REQST_SIZE + 1];
|
|
||||||
|
|
||||||
/* extract TXT from DNS response */
|
|
||||||
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
|
|
||||||
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
|
|
||||||
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
|
|
||||||
|
|
||||||
/* extract the encrypted ID from TXT response */
|
|
||||||
if (strncmp(ans_id, TOX_DNS3_TXT_PREFIX, prfx_len) != 0) {
|
|
||||||
dns_error(self, "Bad DNS3 TXT response.");
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(encrypted_id, ans_id + prfx_len, ans_len - prfx_len);
|
|
||||||
|
|
||||||
if (tox_decrypt_dns3_TXT(dns_obj, (uint8_t *) t_data.id_bin, (uint8_t *) encrypted_id,
|
|
||||||
strlen(encrypted_id), request_id) == -1) {
|
|
||||||
dns_error(self, "Core failed to decrypt DNS response.");
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
|
||||||
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
|
||||||
|
|
||||||
killdns_thread(dns_obj);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
|
||||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
|
|
||||||
{
|
|
||||||
if (arg_opts.proxy_type != TOX_PROXY_TYPE_NONE && arg_opts.force_tcp) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (t_data.busy) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous user lookup to finish.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dns3_servers.loaded) {
|
|
||||||
const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers";
|
|
||||||
dns3_servers.loaded = true;
|
|
||||||
int ret = load_dns_domainlist(path);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list.";
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
|
|
||||||
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
|
|
||||||
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);
|
|
||||||
t_data.self = self;
|
|
||||||
t_data.m = m;
|
|
||||||
t_data.busy = 1;
|
|
||||||
|
|
||||||
if (pthread_attr_init(&dns_thread.attr) != 0) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to init");
|
|
||||||
memset(&t_data, 0, sizeof(struct thread_data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pthread_attr_setdetachstate(&dns_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread attr failed to set");
|
|
||||||
pthread_attr_destroy(&dns_thread.attr);
|
|
||||||
memset(&t_data, 0, sizeof(struct thread_data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pthread_create(&dns_thread.tid, &dns_thread.attr, dns3_lookup_thread, NULL) != 0) {
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: DNS thread failed to init");
|
|
||||||
pthread_attr_destroy(&dns_thread.attr);
|
|
||||||
memset(&t_data, 0, sizeof(struct thread_data));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
@ -51,8 +51,10 @@ static struct cmd_func global_commands[] = {
|
|||||||
{ "/help", cmd_prompt_help },
|
{ "/help", cmd_prompt_help },
|
||||||
{ "/log", cmd_log },
|
{ "/log", cmd_log },
|
||||||
{ "/myid", cmd_myid },
|
{ "/myid", cmd_myid },
|
||||||
|
{ "/myqr", cmd_myqr },
|
||||||
{ "/nick", cmd_nick },
|
{ "/nick", cmd_nick },
|
||||||
{ "/note", cmd_note },
|
{ "/note", cmd_note },
|
||||||
|
{ "/nospam", cmd_nospam },
|
||||||
{ "/q", cmd_quit },
|
{ "/q", cmd_quit },
|
||||||
{ "/quit", cmd_quit },
|
{ "/quit", cmd_quit },
|
||||||
{ "/requests", cmd_requests },
|
{ "/requests", cmd_requests },
|
||||||
@ -61,6 +63,10 @@ static struct cmd_func global_commands[] = {
|
|||||||
{ "/lsdev", cmd_list_devices },
|
{ "/lsdev", cmd_list_devices },
|
||||||
{ "/sdev", cmd_change_device },
|
{ "/sdev", cmd_change_device },
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
#ifdef VIDEO
|
||||||
|
{ "/lsvdev", cmd_list_video_devices },
|
||||||
|
{ "/svdev" , cmd_change_video_device },
|
||||||
|
#endif /* VIDEO */
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -78,6 +84,9 @@ static struct cmd_func chat_commands[] = {
|
|||||||
{ "/mute", cmd_mute },
|
{ "/mute", cmd_mute },
|
||||||
{ "/sense", cmd_sense },
|
{ "/sense", cmd_sense },
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
#ifdef VIDEO
|
||||||
|
{ "/video", cmd_video },
|
||||||
|
#endif /* VIDEO */
|
||||||
{ NULL, NULL },
|
{ NULL, NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,297 +0,0 @@
|
|||||||
/* file_senders.c
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "windows.h"
|
|
||||||
#include "friendlist.h"
|
|
||||||
#include "file_senders.h"
|
|
||||||
#include "line_info.h"
|
|
||||||
#include "misc_tools.h"
|
|
||||||
#include "notify.h"
|
|
||||||
|
|
||||||
FileSender file_senders[MAX_FILES];
|
|
||||||
uint8_t max_file_senders_index;
|
|
||||||
uint8_t num_active_file_senders;
|
|
||||||
extern FriendsList Friends;
|
|
||||||
|
|
||||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
|
||||||
|
|
||||||
/* creates initial progress line that will be updated during file transfer.
|
|
||||||
Assumes progline is of size MAX_STR_SIZE */
|
|
||||||
void prep_prog_line(char *progline)
|
|
||||||
{
|
|
||||||
strcpy(progline, "0.0 B/s [");
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
|
||||||
strcat(progline, "-");
|
|
||||||
|
|
||||||
strcat(progline, "] 0%");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* prints a progress bar for file transfers.
|
|
||||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
|
||||||
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done)
|
|
||||||
{
|
|
||||||
double bps;
|
|
||||||
uint32_t line_id;
|
|
||||||
|
|
||||||
if (friendnum < 0) {
|
|
||||||
bps = file_senders[idx].bps;
|
|
||||||
line_id = file_senders[idx].line_id;
|
|
||||||
} else {
|
|
||||||
bps = Friends.list[friendnum].file_receiver[idx].bps;
|
|
||||||
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
bytes_convert_str(msg, sizeof(msg), bps);
|
|
||||||
strcat(msg, "/s [");
|
|
||||||
|
|
||||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for (i = 0; i < n; ++i)
|
|
||||||
strcat(msg, "#");
|
|
||||||
|
|
||||||
for (j = i; j < NUM_PROG_MARKS; ++j)
|
|
||||||
strcat(msg, "-");
|
|
||||||
|
|
||||||
strcat(msg, "] ");
|
|
||||||
|
|
||||||
char pctstr[16];
|
|
||||||
const char *frmt = pct_done == 100 ? "%.f%%" : "%.1f%%";
|
|
||||||
snprintf(pctstr, sizeof(pctstr), frmt, pct_done);
|
|
||||||
strcat(msg, pctstr);
|
|
||||||
|
|
||||||
line_info_set(self, line_id, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* refreshes active file receiver status bars */
|
|
||||||
static void refresh_recv_prog(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint64_t curtime = get_unix_time();
|
|
||||||
|
|
||||||
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
|
||||||
ToxWindow *toxwin = get_window_ptr(i);
|
|
||||||
|
|
||||||
if (toxwin == NULL || !toxwin->is_chat)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int fnum = toxwin->num;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = 0; j < MAX_FILES; ++j) {
|
|
||||||
if (!Friends.list[fnum].file_receiver[j].active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int filenum = Friends.list[fnum].file_receiver[j].filenum;
|
|
||||||
double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1);
|
|
||||||
|
|
||||||
/* must be called once per second */
|
|
||||||
if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) {
|
|
||||||
Friends.list[fnum].file_receiver[filenum].last_progress = curtime;
|
|
||||||
uint64_t size = Friends.list[fnum].file_receiver[filenum].size;
|
|
||||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
|
||||||
print_progress_bar(toxwin, filenum, fnum, pct_done);
|
|
||||||
Friends.list[fnum].file_receiver[filenum].bps = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* refreshes active file sender status bars */
|
|
||||||
static void refresh_sender_prog(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint64_t curtime = get_unix_time();
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (!file_senders[i].active || file_senders[i].finished)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int filenum = file_senders[i].filenum;
|
|
||||||
uint32_t friendnum = file_senders[i].friendnum;
|
|
||||||
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
|
||||||
|
|
||||||
/* must be called once per second */
|
|
||||||
if (timed_out(file_senders[i].last_progress, curtime, 1)) {
|
|
||||||
file_senders[i].last_progress = curtime;
|
|
||||||
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
|
|
||||||
print_progress_bar(file_senders[i].toxwin, i, -1, pct_done);
|
|
||||||
file_senders[i].bps = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_max_file_senders_index(void)
|
|
||||||
{
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = max_file_senders_index; j > 0; --j) {
|
|
||||||
if (file_senders[j - 1].active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_file_senders_index = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* called whenever a file sender is opened or closed */
|
|
||||||
void reset_file_sender_queue(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
int pos = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (file_senders[i].active)
|
|
||||||
file_senders[i].queue_pos = pos++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set CTRL to -1 if we don't want to send a control signal.
|
|
||||||
set msg to NULL if we don't want to display a message */
|
|
||||||
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, uint32_t friendnum)
|
|
||||||
{
|
|
||||||
if (msg != NULL)
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
|
||||||
|
|
||||||
if (CTRL > 0)
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, CTRL, 0, 0);
|
|
||||||
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
|
||||||
set_max_file_senders_index();
|
|
||||||
reset_file_sender_queue();
|
|
||||||
--num_active_file_senders;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close_all_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
uint8_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (file_senders[i].active) {
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
tox_file_send_control(m, file_senders[i].friendnum, 0, file_senders[i].filenum,
|
|
||||||
TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
|
||||||
}
|
|
||||||
|
|
||||||
set_max_file_senders_index();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_file_data(ToxWindow *self, Tox *m, uint8_t i, uint32_t friendnum, uint32_t filenum,
|
|
||||||
const char *filename)
|
|
||||||
{
|
|
||||||
FILE *fp = file_senders[i].file;
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
TOX_ERR_FILE_SEND_CHUNK err;
|
|
||||||
if (!tox_file_send_chunk(m, friendnum, filenum, (uint8_t *) file_senders[i].nextpiece,
|
|
||||||
file_senders[i].piecelen, &err) {
|
|
||||||
fprintf(stderr, "tox_file_send_chunk failed with error %d\n", err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file_senders[i].timestamp = get_unix_time();
|
|
||||||
file_senders[i].bps += file_senders[i].piecelen;
|
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
|
||||||
tox_file_data_size(m, friendnum), fp);
|
|
||||||
|
|
||||||
/* note: file sender is closed in chat_onFileControl callback after receiving reply */
|
|
||||||
if (file_senders[i].piecelen == 0) {
|
|
||||||
if (feof(fp) != 0) { /* make sure we're really at eof */
|
|
||||||
print_progress_bar(self, i, -1, 100.0);
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
|
||||||
file_senders[i].finished = true;
|
|
||||||
} else {
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename);
|
|
||||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
|
||||||
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
|
|
||||||
|
|
||||||
if (self->active_box != -1)
|
|
||||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
|
||||||
else
|
|
||||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (!file_senders[i].active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (file_senders[i].queue_pos > 0) {
|
|
||||||
--file_senders[i].queue_pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ToxWindow *self = file_senders[i].toxwin;
|
|
||||||
char *filename = file_senders[i].filename;
|
|
||||||
int filenum = file_senders[i].filenum;
|
|
||||||
uint32_t friendnum = file_senders[i].friendnum;
|
|
||||||
|
|
||||||
/* kill file transfer if chatwindow is closed */
|
|
||||||
if (self->chatwin == NULL) {
|
|
||||||
close_file_sender(self, m, i, NULL, TOX_FILECONTROL_KILL, filenum, friendnum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If file transfer has timed out kill transfer and send kill control */
|
|
||||||
if (timed_out(file_senders[i].timestamp, get_unix_time(), TIMEOUT_FILESENDER)
|
|
||||||
&& (!file_senders[i].paused || (file_senders[i].paused && file_senders[i].noconnection))) {
|
|
||||||
char msg[MAX_STR_SIZE];
|
|
||||||
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", filename);
|
|
||||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
|
||||||
|
|
||||||
if (self->active_box != -1)
|
|
||||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
|
||||||
else
|
|
||||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !(file_senders[i].paused | file_senders[i].noconnection | file_senders[i].finished) )
|
|
||||||
send_file_data(self, m, i, friendnum, filenum, filename);
|
|
||||||
|
|
||||||
file_senders[i].queue_pos = num_active_file_senders - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_sender_prog(m);
|
|
||||||
refresh_recv_prog(m);
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/* file_senders.h
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
|
||||||
*
|
|
||||||
* This file is part of Toxic.
|
|
||||||
*
|
|
||||||
* Toxic is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Toxic is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FILESENDERS_H
|
|
||||||
#define FILESENDERS_H
|
|
||||||
|
|
||||||
#include "toxic.h"
|
|
||||||
#include "windows.h"
|
|
||||||
|
|
||||||
#define KiB 1024
|
|
||||||
#define MiB 1048576 /* 1024 ^ 2 */
|
|
||||||
#define GiB 1073741824 /* 1024 ^ 3 */
|
|
||||||
|
|
||||||
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
|
||||||
#define MAX_FILES 32
|
|
||||||
#define TIMEOUT_FILESENDER 120
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
FILE *file;
|
|
||||||
ToxWindow *toxwin;
|
|
||||||
uint32_t friendnum;
|
|
||||||
bool active;
|
|
||||||
bool noconnection; /* set when the connection has been interrupted */
|
|
||||||
bool paused; /* set when transfer has been explicitly paused */
|
|
||||||
bool finished; /* set after entire file has been sent but no TOX_FILECONTROL_FINISHED receieved */
|
|
||||||
bool started; /* set after TOX_FILECONTROL_ACCEPT received */
|
|
||||||
int filenum;
|
|
||||||
char nextpiece[FILE_PIECE_SIZE];
|
|
||||||
size_t piecelen;
|
|
||||||
char filename[MAX_STR_SIZE];
|
|
||||||
uint64_t timestamp; /* marks the last time data was successfully transfered */
|
|
||||||
uint64_t last_progress; /* marks the last time the progress bar was refreshed */
|
|
||||||
double bps;
|
|
||||||
uint64_t size;
|
|
||||||
uint32_t line_id;
|
|
||||||
uint8_t queue_pos;
|
|
||||||
} FileSender;
|
|
||||||
|
|
||||||
/* creates initial progress line that will be updated during file transfer.
|
|
||||||
Assumes progline is of size MAX_STR_SIZE */
|
|
||||||
void prep_prog_line(char *progline);
|
|
||||||
|
|
||||||
/* prints a progress bar for file transfers.
|
|
||||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
|
||||||
void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_remain);
|
|
||||||
|
|
||||||
/* set CTRL to -1 if we don't want to send a control signal.
|
|
||||||
set msg to NULL if we don't want to display a message */
|
|
||||||
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum);
|
|
||||||
|
|
||||||
/* called whenever a file sender is opened or closed */
|
|
||||||
void reset_file_sender_queue(void);
|
|
||||||
|
|
||||||
void close_all_file_senders(Tox *m);
|
|
||||||
void do_file_senders(Tox *m);
|
|
||||||
|
|
||||||
#endif /* #define FILESENDERS_H */
|
|
@ -35,11 +35,52 @@
|
|||||||
|
|
||||||
extern FriendsList Friends;
|
extern FriendsList Friends;
|
||||||
|
|
||||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||||
|
#define NUM_PROG_MARKS 50
|
||||||
|
|
||||||
|
/* Checks for timed out file transfers and closes them. */
|
||||||
|
#define CHECK_FILE_TIMEOUT_INTERAVAL 5
|
||||||
|
void check_file_transfer_timeouts(Tox *m)
|
||||||
|
{
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
static uint64_t last_check = 0;
|
||||||
|
|
||||||
|
if (!timed_out(last_check, CHECK_FILE_TIMEOUT_INTERAVAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
last_check = get_unix_time();
|
||||||
|
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < Friends.max_idx; ++i) {
|
||||||
|
if (!Friends.list[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; j < MAX_FILES; ++j) {
|
||||||
|
struct FileTransfer *ft_send = &Friends.list[i].file_sender[j];
|
||||||
|
|
||||||
|
if (ft_send->state > FILE_TRANSFER_PAUSED) {
|
||||||
|
if (timed_out(ft_send->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_send->file_name);
|
||||||
|
close_file_transfer(ft_send->window, m, ft_send, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FileTransfer *ft_recv = &Friends.list[i].file_receiver[j];
|
||||||
|
|
||||||
|
if (ft_recv->state > FILE_TRANSFER_PAUSED) {
|
||||||
|
if (timed_out(ft_recv->last_keep_alive, TIMEOUT_FILESENDER)) {
|
||||||
|
snprintf(msg, sizeof(msg), "File transfer for '%s' timed out.", ft_recv->file_name);
|
||||||
|
close_file_transfer(ft_recv->window, m, ft_recv, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* creates initial progress line that will be updated during file transfer.
|
/* creates initial progress line that will be updated during file transfer.
|
||||||
Assumes progline is of size MAX_STR_SIZE */
|
Assumes progline is of size MAX_STR_SIZE */
|
||||||
void prep_prog_line(char *progline)
|
void init_progress_bar(char *progline)
|
||||||
{
|
{
|
||||||
strcpy(progline, "0.0 B/s [");
|
strcpy(progline, "0.0 B/s [");
|
||||||
int i;
|
int i;
|
||||||
@ -54,6 +95,9 @@ void prep_prog_line(char *progline)
|
|||||||
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
if friendnum is -1 we're sending the file, otherwise we're receiving. */
|
||||||
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
||||||
{
|
{
|
||||||
|
if (bps < 0 || pct_done < 0 || pct_done > 100)
|
||||||
|
return;
|
||||||
|
|
||||||
char msg[MAX_STR_SIZE];
|
char msg[MAX_STR_SIZE];
|
||||||
bytes_convert_str(msg, sizeof(msg), bps);
|
bytes_convert_str(msg, sizeof(msg), bps);
|
||||||
strcat(msg, "/s [");
|
strcat(msg, "/s [");
|
||||||
@ -77,106 +121,179 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
|
|||||||
line_info_set(self, line_id, msg);
|
line_info_set(self, line_id, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Filenumbers >= this number are receiving, otherwise sending.
|
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
|
||||||
* Warning: This behaviour is not defined by the Tox API and is subject to change at any time.
|
|
||||||
*/
|
|
||||||
#define FILE_NUMBER_MAGIC_NUM (1 << 16)
|
|
||||||
|
|
||||||
/* Returns filenum's file transfer array index */
|
|
||||||
uint32_t get_file_transfer_index(uint32_t filenum)
|
|
||||||
{
|
{
|
||||||
return filenum >= FILE_NUMBER_MAGIC_NUM ? (filenum >> 16) - 1 : filenum;
|
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Timeout must be set to 1 second to show correct bytes per second */
|
||||||
|
if (!timed_out(ft->last_line_progress, 1))
|
||||||
|
return;
|
||||||
|
|
||||||
|
double remain = ft->file_size - ft->position;
|
||||||
|
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
|
||||||
|
print_progress_bar(self, ft->bps, pct_done, ft->line_id);
|
||||||
|
|
||||||
|
ft->bps = 0;
|
||||||
|
ft->last_line_progress = get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns the filenumber of a file receiver's index */
|
/* refreshes active file transfer status bars. */
|
||||||
uint32_t get_file_receiver_filenum(uint32_t idx)
|
|
||||||
{
|
|
||||||
return (idx + 1) << 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return true if filenum is associated with a file receiver, false if file sender */
|
|
||||||
bool filenum_is_sending(uint32_t filenum)
|
|
||||||
{
|
|
||||||
return filenum < FILE_NUMBER_MAGIC_NUM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* refreshes active file receiver status bars for friendnum */
|
|
||||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||||
{
|
{
|
||||||
uint64_t curtime = get_unix_time();
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_FILES; ++i) {
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
if (Friends.list[friendnum].file_receiver[i].active) {
|
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
|
||||||
if (timed_out(Friends.list[friendnum].file_receiver[i].last_progress, curtime, 1)) {
|
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
|
||||||
uint64_t size = Friends.list[friendnum].file_receiver[i].file_size;
|
|
||||||
double remain = size - Friends.list[friendnum].file_receiver[i].position;
|
|
||||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
|
||||||
|
|
||||||
print_progress_bar(self, Friends.list[friendnum].file_receiver[i].bps, pct_done,
|
|
||||||
Friends.list[friendnum].file_receiver[i].line_id);
|
|
||||||
|
|
||||||
Friends.list[friendnum].file_receiver[i].bps = 0;
|
|
||||||
Friends.list[friendnum].file_receiver[i].last_progress = curtime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Friends.list[friendnum].file_sender[i].active) {
|
|
||||||
if (timed_out(Friends.list[friendnum].file_sender[i].last_progress, curtime, 1)) {
|
|
||||||
uint64_t size = Friends.list[friendnum].file_sender[i].file_size;
|
|
||||||
double remain = size - Friends.list[friendnum].file_sender[i].position;
|
|
||||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
|
||||||
|
|
||||||
print_progress_bar(self, Friends.list[friendnum].file_sender[i].bps, pct_done,
|
|
||||||
Friends.list[friendnum].file_sender[i].line_id);
|
|
||||||
|
|
||||||
Friends.list[friendnum].file_sender[i].bps = 0;
|
|
||||||
Friends.list[friendnum].file_sender[i].last_progress = curtime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Closes file transfer with filenum.
|
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||||
|
* Returns NULL if filenum is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||||
|
|
||||||
|
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
|
||||||
|
return ft_send;
|
||||||
|
|
||||||
|
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
|
||||||
|
return ft_recv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||||
|
FILE_TRANSFER_DIRECTION direction)
|
||||||
|
{
|
||||||
|
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||||
|
&Friends.list[friendnum].file_sender[i] :
|
||||||
|
&Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to an unused file sender.
|
||||||
|
* Returns NULL if all file senders are in use.
|
||||||
|
*/
|
||||||
|
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnum = friendnum;
|
||||||
|
ft->filenum = filenum;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
ft->direction = FILE_TRANSFER_SEND;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pointer to an unused file receiver.
|
||||||
|
* Returns NULL if all file receivers are in use.
|
||||||
|
*/
|
||||||
|
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
|
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
|
ft->window = window;
|
||||||
|
ft->index = i;
|
||||||
|
ft->friendnum = friendnum;
|
||||||
|
ft->filenum = filenum;
|
||||||
|
ft->file_type = type;
|
||||||
|
ft->last_keep_alive = get_unix_time();
|
||||||
|
ft->state = FILE_TRANSFER_PENDING;
|
||||||
|
ft->direction = FILE_TRANSFER_RECV;
|
||||||
|
return ft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes an unused file transfer and returns its pointer.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||||
|
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||||
|
{
|
||||||
|
if (direction == FILE_TRANSFER_RECV)
|
||||||
|
return new_file_receiver(window, friendnum, filenum, type);
|
||||||
|
|
||||||
|
if (direction == FILE_TRANSFER_SEND)
|
||||||
|
return new_file_sender(window, friendnum, filenum, type);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Closes file transfer ft.
|
||||||
|
*
|
||||||
* Set CTRL to -1 if we don't want to send a control signal.
|
* Set CTRL to -1 if we don't want to send a control signal.
|
||||||
* Set message or self to NULL if we don't want to display a message.
|
* Set message or self to NULL if we don't want to display a message.
|
||||||
*/
|
*/
|
||||||
void close_file_transfer(ToxWindow *self, Tox *m, uint32_t filenum, uint32_t friendnum, int CTRL,
|
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||||
const char *message, Notification sound_type)
|
Notification sound_type)
|
||||||
{
|
{
|
||||||
uint32_t idx = get_file_transfer_index(filenum);
|
if (!ft)
|
||||||
bool sending = filenum_is_sending(filenum);
|
|
||||||
|
|
||||||
if (sending && Friends.list[friendnum].file_sender[idx].active) {
|
|
||||||
FILE *fp = Friends.list[friendnum].file_sender[idx].file;
|
|
||||||
|
|
||||||
if (fp)
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
memset(&Friends.list[friendnum].file_sender[idx], 0, sizeof(struct FileSender));
|
|
||||||
}
|
|
||||||
else if (!sending && Friends.list[friendnum].file_receiver[idx].active) {
|
|
||||||
FILE *fp = Friends.list[friendnum].file_receiver[idx].file;
|
|
||||||
|
|
||||||
if (fp)
|
|
||||||
fclose(fp);
|
|
||||||
|
|
||||||
memset(&Friends.list[friendnum].file_receiver[idx], 0, sizeof(struct FileReceiver));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->file)
|
||||||
|
fclose(ft->file);
|
||||||
|
|
||||||
if (CTRL >= 0)
|
if (CTRL >= 0)
|
||||||
tox_file_control(m, friendnum, filenum, CTRL, NULL);
|
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
|
||||||
|
|
||||||
if (message && self) {
|
if (message && self) {
|
||||||
if (self->active_box != -1)
|
if (self->active_box != -1 && sound_type != silent)
|
||||||
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||||
else
|
else
|
||||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
memset(ft, 0, sizeof(struct FileTransfer));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kills all active file transfers for friendnum */
|
/* Kills all active file transfers for friendnum */
|
||||||
@ -185,10 +302,15 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_FILES; ++i) {
|
for (i = 0; i < MAX_FILES; ++i) {
|
||||||
fprintf(stderr, "%lu\n", i);
|
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
if (Friends.list[friendnum].file_sender[i].active)
|
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||||
close_file_transfer(NULL, m, i, friendnum, -1, NULL, silent);
|
|
||||||
if (Friends.list[friendnum].file_receiver[i].active)
|
|
||||||
close_file_transfer(NULL, m, get_file_receiver_filenum(i), friendnum, -1, NULL, silent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < Friends.max_idx; ++i)
|
||||||
|
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||||
|
}
|
||||||
|
@ -30,67 +30,86 @@
|
|||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
|
|
||||||
#define KiB 1024
|
#define KiB 1024
|
||||||
#define MiB 1048576 /* 1024 ^ 2 */
|
#define MiB 1048576 /* 1024^2 */
|
||||||
#define GiB 1073741824 /* 1024 ^ 3 */
|
#define GiB 1073741824 /* 1024^3 */
|
||||||
|
|
||||||
#define FILE_PIECE_SIZE 2048
|
|
||||||
#define MAX_FILES 32
|
#define MAX_FILES 32
|
||||||
#define TIMEOUT_FILESENDER 120
|
#define TIMEOUT_FILESENDER 120
|
||||||
|
|
||||||
struct FileSender {
|
typedef enum FILE_TRANSFER_STATE {
|
||||||
|
FILE_TRANSFER_INACTIVE,
|
||||||
|
FILE_TRANSFER_PAUSED,
|
||||||
|
FILE_TRANSFER_PENDING,
|
||||||
|
FILE_TRANSFER_STARTED,
|
||||||
|
} FILE_TRANSFER_STATE;
|
||||||
|
|
||||||
|
typedef enum FILE_TRANSFER_DIRECTION {
|
||||||
|
FILE_TRANSFER_SEND,
|
||||||
|
FILE_TRANSFER_RECV
|
||||||
|
} FILE_TRANSFER_DIRECTION;
|
||||||
|
|
||||||
|
struct FileTransfer {
|
||||||
|
ToxWindow *window;
|
||||||
FILE *file;
|
FILE *file;
|
||||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
FILE_TRANSFER_STATE state;
|
||||||
bool active;
|
FILE_TRANSFER_DIRECTION direction;
|
||||||
bool noconnection; /* set when the connection has been interrupted */
|
uint8_t file_type;
|
||||||
bool paused; /* set when transfer has been explicitly paused */
|
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||||
bool started; /* set after TOX_FILECONTROL_ACCEPT received */
|
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||||
uint64_t timestamp; /* marks the last time data was successfully transfered */
|
double bps;
|
||||||
double bps;
|
uint32_t filenum;
|
||||||
|
uint32_t friendnum;
|
||||||
|
size_t index;
|
||||||
uint64_t file_size;
|
uint64_t file_size;
|
||||||
uint64_t last_progress; /* marks the last time the progress bar was refreshed */
|
|
||||||
uint64_t position;
|
uint64_t position;
|
||||||
|
uint64_t last_line_progress; /* The last time we updated the progress bar */
|
||||||
|
uint64_t last_keep_alive; /* The last time we sent or received data */
|
||||||
uint32_t line_id;
|
uint32_t line_id;
|
||||||
|
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FileReceiver {
|
/* Checks for timed out file transfers and closes them. */
|
||||||
FILE *file;
|
void check_file_transfer_timeouts(Tox *m);
|
||||||
char file_path[PATH_MAX + 1];
|
|
||||||
bool pending;
|
|
||||||
bool active;
|
|
||||||
double bps;
|
|
||||||
uint64_t file_size;
|
|
||||||
uint64_t last_progress;
|
|
||||||
uint64_t position;
|
|
||||||
uint32_t line_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* creates initial progress line that will be updated during file transfer.
|
/* creates initial progress line that will be updated during file transfer.
|
||||||
progline must be at lesat MAX_STR_SIZE bytes */
|
progline must be at lesat MAX_STR_SIZE bytes */
|
||||||
void prep_prog_line(char *progline);
|
void init_progress_bar(char *progline);
|
||||||
|
|
||||||
/* prints a progress bar for file transfers */
|
/* prints a progress bar for file transfers */
|
||||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||||
|
|
||||||
/* refreshes active file receiver status bars for friendnum */
|
/* refreshes active file transfer status bars. */
|
||||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
/* Returns filenum's file transfer array index */
|
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||||
uint32_t get_file_transfer_index(uint32_t filenum);
|
* Returns NULL if filenum is invalid.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||||
|
|
||||||
/* Returns the filenumber of a file receiver's index */
|
|
||||||
uint32_t get_file_receiver_filenum(uint32_t idx);
|
|
||||||
|
|
||||||
/* Return true if filenum is associated with a file receiver, false if file sender */
|
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||||
bool filenum_is_sending(uint32_t filenum);
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||||
|
FILE_TRANSFER_DIRECTION direction);
|
||||||
|
|
||||||
/* Closes file transfer with filenum.
|
/* Initializes an unused file transfer and returns its pointer.
|
||||||
|
* Returns NULL on failure.
|
||||||
|
*/
|
||||||
|
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||||
|
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||||
|
|
||||||
|
/* Closes file transfer ft.
|
||||||
|
*
|
||||||
* Set CTRL to -1 if we don't want to send a control signal.
|
* Set CTRL to -1 if we don't want to send a control signal.
|
||||||
* Set message or self to NULL if we don't want to display a message.
|
* Set message or self to NULL if we don't want to display a message.
|
||||||
*/
|
*/
|
||||||
void close_file_transfer(ToxWindow *self, Tox *m, uint32_t filenum, uint32_t friendnum, int CTRL,
|
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||||
const char *message, Notification sound_type);
|
Notification sound_type);
|
||||||
|
|
||||||
/* Kills all active file transfers for friendnum */
|
/* Kills all active file transfers for friendnum */
|
||||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||||
|
|
||||||
|
void kill_all_file_transfers(Tox *m);
|
||||||
|
|
||||||
#endif /* #define FILE_TRANSFERS_H */
|
#endif /* #define FILE_TRANSFERS_H */
|
||||||
|
235
src/friendlist.c
235
src/friendlist.c
@ -25,6 +25,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
|
||||||
@ -38,6 +39,7 @@
|
|||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "avatars.h"
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
@ -122,24 +124,26 @@ void kill_friendlist(void)
|
|||||||
realloc_friends(0);
|
realloc_friends(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Saves the blocklist to path. If there are no items in the blocklist the
|
||||||
|
* empty file will be removed.
|
||||||
|
*
|
||||||
|
* Returns 0 if stored successfully.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
#define TEMP_BLOCKLIST_EXT ".tmp"
|
||||||
static int save_blocklist(char *path)
|
static int save_blocklist(char *path)
|
||||||
{
|
{
|
||||||
if (path == NULL)
|
if (path == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
int len = sizeof(BlockedFriend) * Blocked.num_blocked;
|
int len = sizeof(BlockedFriend) * Blocked.num_blocked;
|
||||||
char *data = malloc(len);
|
char data[len];
|
||||||
|
|
||||||
if (data == NULL)
|
int i, count = 0;
|
||||||
exit_toxic_err("Failed in save_blocklist", FATALERR_MEMORY);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < Blocked.max_idx; ++i) {
|
for (i = 0; i < Blocked.max_idx; ++i) {
|
||||||
if (count > Blocked.num_blocked)
|
if (count > Blocked.num_blocked)
|
||||||
goto on_error;
|
return -1;
|
||||||
|
|
||||||
if (Blocked.list[i].active) {
|
if (Blocked.list[i].active) {
|
||||||
BlockedFriend tmp;
|
BlockedFriend tmp;
|
||||||
@ -158,21 +162,34 @@ static int save_blocklist(char *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *fp = fopen(path, "wb");
|
/* Blocklist is empty, we can remove the empty file */
|
||||||
|
if (count == 0) {
|
||||||
|
if (remove(path) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char temp_path[strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1];
|
||||||
|
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_BLOCKLIST_EXT);
|
||||||
|
|
||||||
|
FILE *fp = fopen(temp_path, "wb");
|
||||||
|
|
||||||
if (fp == NULL)
|
if (fp == NULL)
|
||||||
goto on_error;
|
return -1;
|
||||||
|
|
||||||
if (fwrite(data, len, 1, fp) != 1)
|
if (fwrite(data, len, 1, fp) != 1) {
|
||||||
goto on_error;
|
fprintf(stderr, "Failed to write blocklist data.\n");
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
free(data);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
on_error:
|
if (rename(temp_path, path) != 0)
|
||||||
free(data);
|
return -1;
|
||||||
return -1;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sort_blocklist_index(void);
|
static void sort_blocklist_index(void);
|
||||||
@ -194,22 +211,15 @@ int load_blocklist(char *path)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *data = malloc(len);
|
char data[len];
|
||||||
|
|
||||||
if (data == NULL) {
|
|
||||||
fclose(fp);
|
|
||||||
exit_toxic_err("Failed in load_blocklist", FATALERR_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fread(data, len, 1, fp) != 1) {
|
if (fread(data, len, 1, fp) != 1) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len % sizeof(BlockedFriend) != 0) {
|
if (len % sizeof(BlockedFriend) != 0) {
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
free(data);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,13 +230,14 @@ int load_blocklist(char *path)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < num; ++i) {
|
for (i = 0; i < num; ++i) {
|
||||||
|
BlockedFriend tmp;
|
||||||
|
memset(&tmp, 0, sizeof(BlockedFriend));
|
||||||
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
|
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
|
||||||
|
|
||||||
BlockedFriend tmp;
|
|
||||||
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
|
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
|
||||||
Blocked.list[i].active = true;
|
Blocked.list[i].active = true;
|
||||||
Blocked.list[i].num = i;
|
Blocked.list[i].num = i;
|
||||||
Blocked.list[i].namelength = ntohs(tmp.namelength);
|
Blocked.list[i].namelength = MIN(TOXIC_MAX_NAME_LENGTH, ntohs(tmp.namelength));
|
||||||
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
|
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
|
||||||
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
|
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
|
||||||
|
|
||||||
@ -238,7 +249,6 @@ int load_blocklist(char *path)
|
|||||||
++Blocked.num_blocked;
|
++Blocked.num_blocked;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(data);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
sort_blocklist_index();
|
sort_blocklist_index();
|
||||||
|
|
||||||
@ -330,11 +340,15 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num,
|
|||||||
if (num >= Friends.max_idx)
|
if (num >= Friends.max_idx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (connection_status == TOX_CONNECTION_NONE)
|
if (connection_status == TOX_CONNECTION_NONE) {
|
||||||
--Friends.num_online;
|
--Friends.num_online;
|
||||||
else
|
} else if (Friends.list[num].connection_status == TOX_CONNECTION_NONE) {
|
||||||
++Friends.num_online;
|
++Friends.num_online;
|
||||||
|
|
||||||
|
if (avatar_send(m, num) == -1)
|
||||||
|
fprintf(stderr, "avatar_send failed for friend %d\n", num);
|
||||||
|
}
|
||||||
|
|
||||||
Friends.list[num].connection_status = connection_status;
|
Friends.list[num].connection_status = connection_status;
|
||||||
update_friend_last_online(num, get_unix_time());
|
update_friend_last_online(num, get_unix_time());
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
@ -403,13 +417,19 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
|
|||||||
Friends.list[i].status = TOX_USER_STATUS_NONE;
|
Friends.list[i].status = TOX_USER_STATUS_NONE;
|
||||||
Friends.list[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON;
|
Friends.list[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON;
|
||||||
|
|
||||||
TOX_ERR_FRIEND_GET_PUBLIC_KEY err;
|
TOX_ERR_FRIEND_GET_PUBLIC_KEY pkerr;
|
||||||
tox_friend_get_public_key(m, num, (uint8_t *) Friends.list[i].pub_key, &err);
|
tox_friend_get_public_key(m, num, (uint8_t *) Friends.list[i].pub_key, &pkerr);
|
||||||
|
|
||||||
if (err != TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK)
|
if (pkerr != TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK)
|
||||||
fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", err);
|
fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr);
|
||||||
|
|
||||||
// update_friend_last_online(i, 0);
|
TOX_ERR_FRIEND_GET_LAST_ONLINE loerr;
|
||||||
|
uint64_t t = tox_friend_get_last_online(m, num, &loerr);
|
||||||
|
|
||||||
|
if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK)
|
||||||
|
t = 0;
|
||||||
|
|
||||||
|
update_friend_last_online(i, t);
|
||||||
|
|
||||||
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
||||||
get_nick_truncate(m, tempname, num);
|
get_nick_truncate(m, tempname, num);
|
||||||
@ -460,7 +480,7 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, uint32_t kind,
|
static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum,
|
||||||
uint64_t file_size, const char *filename, size_t name_length)
|
uint64_t file_size, const char *filename, size_t name_length)
|
||||||
{
|
{
|
||||||
if (num >= Friends.max_idx)
|
if (num >= Friends.max_idx)
|
||||||
@ -609,10 +629,12 @@ static void draw_del_popup(void)
|
|||||||
wprintw(PendingDelete.popup, "Delete contact ");
|
wprintw(PendingDelete.popup, "Delete contact ");
|
||||||
wattron(PendingDelete.popup, A_BOLD);
|
wattron(PendingDelete.popup, A_BOLD);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
if (blocklist_view == 0)
|
if (blocklist_view == 0)
|
||||||
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
|
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
|
||||||
else
|
else
|
||||||
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
|
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wattroff(PendingDelete.popup, A_BOLD);
|
wattroff(PendingDelete.popup, A_BOLD);
|
||||||
wprintw(PendingDelete.popup, "? y/n");
|
wprintw(PendingDelete.popup, "? y/n");
|
||||||
@ -687,7 +709,7 @@ static void unblock_friend(Tox *m, uint32_t bnum)
|
|||||||
uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err);
|
uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err);
|
||||||
|
|
||||||
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)\n", err);
|
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -873,12 +895,13 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint64_t cur_time = get_unix_time();
|
uint64_t cur_time = time(NULL);
|
||||||
// struct tm cur_loc_tm = *localtime((const time_t *) &cur_time);
|
struct tm cur_loc_tm = *localtime((const time_t *) &cur_time);
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " Online: ");
|
wprintw(self->window, " Online: ");
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends);
|
wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends);
|
||||||
|
|
||||||
if ((y2 - FLIST_OFST) <= 0)
|
if ((y2 - FLIST_OFST) <= 0)
|
||||||
@ -887,18 +910,30 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
uint32_t selected_num = 0;
|
uint32_t 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 */
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
int page = Friends.num_selected / (y2 - FLIST_OFST);
|
int page = Friends.num_selected / (y2 - FLIST_OFST);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
int start = (y2 - FLIST_OFST) * page;
|
int start = (y2 - FLIST_OFST) * page;
|
||||||
int end = y2 - FLIST_OFST + start;
|
int end = y2 - FLIST_OFST + start;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
size_t num_friends = Friends.num_friends;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = start; i < Friends.num_friends && i < end; ++i) {
|
for (i = start; i < num_friends && i < end; ++i) {
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
uint32_t f = Friends.index[i];
|
uint32_t f = Friends.index[i];
|
||||||
|
bool is_active = Friends.list[f].active;
|
||||||
|
int num_selected = Friends.num_selected;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
bool f_selected = false;
|
bool f_selected = false;
|
||||||
|
|
||||||
if (Friends.list[f].active) {
|
if (is_active) {
|
||||||
if (i == Friends.num_selected) {
|
if (i == num_selected) {
|
||||||
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);
|
||||||
@ -908,8 +943,12 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wprintw(self->window, " ");
|
wprintw(self->window, " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Friends.list[f].connection_status != TOX_CONNECTION_NONE) {
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
TOX_USER_STATUS status = Friends.list[f].status;
|
TOX_CONNECTION connection_status = Friends.list[f].connection_status;
|
||||||
|
TOX_USER_STATUS status = Friends.list[f].status;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (connection_status != TOX_CONNECTION_NONE) {
|
||||||
int colour = MAGENTA;
|
int colour = MAGENTA;
|
||||||
|
|
||||||
switch (status) {
|
switch (status) {
|
||||||
@ -932,7 +971,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattron(self->window, COLOR_PAIR(BLUE));
|
wattron(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(self->window, "%s", Friends.list[f].name);
|
wprintw(self->window, "%s", Friends.list[f].name);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
@ -947,14 +988,21 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
size_t s_len = tox_friend_get_status_message_size(m, Friends.list[f].num, NULL);
|
size_t s_len = tox_friend_get_status_message_size(m, Friends.list[f].num, NULL);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
statusmsg[s_len] = '\0';
|
||||||
|
|
||||||
filter_str(statusmsg, s_len);
|
filter_str(statusmsg, s_len);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg);
|
snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg);
|
||||||
Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg);
|
Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Truncate note if it doesn't fit on one line */
|
/* Truncate note if it doesn't fit on one line */
|
||||||
size_t maxlen = x2 - getcurx(self->window) - 2;
|
size_t maxlen = x2 - getcurx(self->window) - 2;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
|
||||||
if (Friends.list[f].statusmsg_len > maxlen) {
|
if (Friends.list[f].statusmsg_len > maxlen) {
|
||||||
Friends.list[f].statusmsg[maxlen - 3] = '\0';
|
Friends.list[f].statusmsg[maxlen - 3] = '\0';
|
||||||
strcat(Friends.list[f].statusmsg, "...");
|
strcat(Friends.list[f].statusmsg, "...");
|
||||||
@ -962,9 +1010,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
Friends.list[f].statusmsg_len = maxlen;
|
Friends.list[f].statusmsg_len = maxlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Friends.list[f].statusmsg[0])
|
if (Friends.list[f].statusmsg_len > 0)
|
||||||
wprintw(self->window, " %s", Friends.list[f].statusmsg);
|
wprintw(self->window, " %s", Friends.list[f].statusmsg);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wprintw(self->window, "\n");
|
wprintw(self->window, "\n");
|
||||||
} else {
|
} else {
|
||||||
wprintw(self->window, "%s ", OFFLINE_CHAR);
|
wprintw(self->window, "%s ", OFFLINE_CHAR);
|
||||||
@ -973,47 +1023,52 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattron(self->window, COLOR_PAIR(BLUE));
|
wattron(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(self->window, "%s", Friends.list[f].name);
|
wprintw(self->window, "%s", Friends.list[f].name);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
if (f_selected)
|
if (f_selected)
|
||||||
wattroff(self->window, COLOR_PAIR(BLUE));
|
wattroff(self->window, COLOR_PAIR(BLUE));
|
||||||
|
|
||||||
wprintw(self->window, "\n");
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
/* Last online is currently broken in core */
|
uint64_t last_seen = Friends.list[f].last_online.last_on;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
// uint64_t last_seen = Friends.list[f].last_online.last_on;
|
if (last_seen != 0) {
|
||||||
//
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
// if (last_seen != 0) {
|
|
||||||
// int day_dist = (
|
|
||||||
// cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
|
|
||||||
// + ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
|
|
||||||
// );
|
|
||||||
// const char *hourmin = Friends.list[f].last_online.hour_min_str;
|
|
||||||
|
|
||||||
// switch (day_dist) {
|
int day_dist = (
|
||||||
// case 0:
|
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
|
||||||
// wprintw(self->window, " Last seen: Today %s\n", hourmin);
|
+ ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
|
||||||
// break;
|
);
|
||||||
|
const char *hourmin = Friends.list[f].last_online.hour_min_str;
|
||||||
|
|
||||||
// case 1:
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
// wprintw(self->window, " Last seen: Yesterday %s\n", hourmin);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// default:
|
switch (day_dist) {
|
||||||
// wprintw(self->window, " Last seen: %d days ago\n", day_dist);
|
case 0:
|
||||||
// break;
|
wprintw(self->window, " Last seen: Today %s\n", hourmin);
|
||||||
// }
|
break;
|
||||||
// } else {
|
|
||||||
// wprintw(self->window, " Last seen: Never\n");
|
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 (Friends.num_friends) {
|
if (num_friends) {
|
||||||
wmove(self->window, y2 - 1, 1);
|
wmove(self->window, y2 - 1, 1);
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
@ -1039,23 +1094,24 @@ void disable_chatwin(uint32_t f_num)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
|
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
|
||||||
{
|
{
|
||||||
int id = toxav_get_peer_id(av, call_index, 0);
|
assert(0);
|
||||||
|
if( friend_number >= Friends.max_idx)
|
||||||
if ( id != av_ErrorUnknown && id >= Friends.max_idx)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
assert(0);
|
||||||
Tox *m = toxav_get_tox(av);
|
Tox *m = toxav_get_tox(av);
|
||||||
|
|
||||||
if (Friends.list[id].chatwin == -1) {
|
if (Friends.list[friend_number].chatwin == -1) {
|
||||||
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
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 */
|
if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
|
||||||
Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num));
|
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
|
||||||
|
set_active_window(Friends.list[friend_number].chatwin);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
char nick[TOX_MAX_NAME_LENGTH];
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
get_nick_truncate(m, nick, Friends.list[id].num);
|
get_nick_truncate(m, nick, Friends.list[friend_number].num);
|
||||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
|
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
|
||||||
|
|
||||||
const char *errmsg = "* Warning: Too many windows are open.";
|
const char *errmsg = "* Warning: Too many windows are open.";
|
||||||
@ -1087,22 +1143,21 @@ ToxWindow new_friendlist(void)
|
|||||||
ret.onGroupInvite = &friendlist_onGroupInvite;
|
ret.onGroupInvite = &friendlist_onGroupInvite;
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
ret.onInvite = &friendlist_onAv;
|
ret.onInvite = &friendlist_onAV;
|
||||||
ret.onRinging = &friendlist_onAv;
|
ret.onRinging = &friendlist_onAV;
|
||||||
ret.onStarting = &friendlist_onAv;
|
ret.onStarting = &friendlist_onAV;
|
||||||
ret.onEnding = &friendlist_onAv;
|
ret.onEnding = &friendlist_onAV;
|
||||||
ret.onError = &friendlist_onAv;
|
ret.onError = &friendlist_onAV;
|
||||||
ret.onStart = &friendlist_onAv;
|
ret.onStart = &friendlist_onAV;
|
||||||
ret.onCancel = &friendlist_onAv;
|
ret.onCancel = &friendlist_onAV;
|
||||||
ret.onReject = &friendlist_onAv;
|
ret.onReject = &friendlist_onAV;
|
||||||
ret.onEnd = &friendlist_onAv;
|
ret.onEnd = &friendlist_onAV;
|
||||||
ret.onRequestTimeout = &friendlist_onAv;
|
|
||||||
ret.onPeerTimeout = &friendlist_onAv;
|
|
||||||
|
|
||||||
ret.call_idx = -1;
|
ret.is_call = false;
|
||||||
ret.device_selection[0] = ret.device_selection[1] = -1;
|
ret.device_selection[0] = ret.device_selection[1] = -1;
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
ret.num = -1;
|
||||||
ret.active_box = -1;
|
ret.active_box = -1;
|
||||||
|
|
||||||
Help *help = calloc(1, sizeof(Help));
|
Help *help = calloc(1, sizeof(Help));
|
||||||
|
@ -59,8 +59,8 @@ typedef struct {
|
|||||||
struct LastOnline last_online;
|
struct LastOnline last_online;
|
||||||
struct GroupChatInvite group_invite;
|
struct GroupChatInvite group_invite;
|
||||||
|
|
||||||
struct FileReceiver file_receiver[MAX_FILES];
|
struct FileTransfer file_receiver[MAX_FILES];
|
||||||
struct FileSender file_sender[MAX_FILES];
|
struct FileTransfer file_sender[MAX_FILES];
|
||||||
} ToxicFriend;
|
} ToxicFriend;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <arpa/inet.h>
|
|
||||||
|
|
||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
@ -30,11 +29,13 @@
|
|||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "dns.h"
|
|
||||||
#include "groupchat.h"
|
#include "groupchat.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "term_mplex.h"
|
#include "term_mplex.h"
|
||||||
|
#include "avatars.h"
|
||||||
|
#include "name_lookup.h"
|
||||||
|
#include "qr_code.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
@ -49,9 +50,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int req = atoi(argv[1]);
|
long int req = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -191,79 +192,46 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmd_add_helper(self, m, id_bin, msg);
|
cmd_add_helper(self, m, id_bin, msg);
|
||||||
} else { /* assume id is a username@domain address and do DNS lookup */
|
} else { /* assume id is a username@domain address and do http name server lookup */
|
||||||
dns3_lookup(self, m, id_bin, id, msg);
|
name_lookup(self, m, id_bin, id, msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
// if (argc < 2) {
|
if (argc < 2 || strlen(argv[1]) < 3) {
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: No file path supplied.");
|
avatar_unset(m);
|
||||||
// return;
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar is not set.");
|
||||||
// }
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// /* turns the avatar off */
|
if (argv[1][0] != '\"') {
|
||||||
// if (strlen(argv[1]) < 3) {
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
|
||||||
// tox_unset_avatar(m);
|
return;
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No avatar set.");
|
}
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (argv[1][0] != '\"') {
|
/* remove opening and closing quotes */
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
|
char path[MAX_STR_SIZE];
|
||||||
// return;
|
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||||
// }
|
int len = strlen(path) - 1;
|
||||||
|
|
||||||
// /* remove opening and closing quotes */
|
if (len <= 0) {
|
||||||
// char path[MAX_STR_SIZE];
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||||
// snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
return;
|
||||||
// int len = strlen(path) - 1;
|
}
|
||||||
// path[len] = '\0';
|
|
||||||
|
|
||||||
// off_t sz = file_size(path);
|
path[len] = '\0';
|
||||||
|
char filename[MAX_STR_SIZE];
|
||||||
|
get_file_name(filename, sizeof(filename), path);
|
||||||
|
|
||||||
// if (sz <= 8) {
|
if (avatar_set(m, path, len) == -1) {
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Invalid file.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||||
// return;
|
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
|
||||||
// }
|
MAX_AVATAR_FILE_SIZE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// FILE *fp = fopen(path, "rb");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||||
|
|
||||||
// if (fp == NULL) {
|
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Could not open file.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
|
||||||
|
|
||||||
// if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
|
||||||
// fclose(fp);
|
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: File type not supported.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// char *avatar = malloc(sz);
|
|
||||||
|
|
||||||
// if (avatar == NULL)
|
|
||||||
// exit_toxic_err("Failed in cmd_avatar", FATALERR_MEMORY);
|
|
||||||
|
|
||||||
// if (fread(avatar, sz, 1, fp) != 1) {
|
|
||||||
// fclose(fp);
|
|
||||||
// free(avatar);
|
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar: Read fail.");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (tox_set_avatar(m, TOX_AVATAR_FORMAT_PNG, (const uint8_t *) avatar, (uint32_t) sz) == -1)
|
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set avatar");
|
|
||||||
|
|
||||||
// char filename[MAX_STR_SIZE];
|
|
||||||
// get_file_name(filename, sizeof(filename), path);
|
|
||||||
// line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
|
||||||
|
|
||||||
// fclose(fp);
|
|
||||||
// free(avatar);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
@ -280,19 +248,25 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *ip = argv[1];
|
const char *ip = argv[1];
|
||||||
const char *port = argv[2];
|
const char *port_str = argv[2];
|
||||||
const char *key = argv[3];
|
const char *ascii_key = argv[3];
|
||||||
|
|
||||||
if (atoi(port) == 0) {
|
long int port = strtol(port_str, NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *binary_string = hex_string_to_bin(key);
|
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
|
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
TOX_ERR_BOOTSTRAP err;
|
TOX_ERR_BOOTSTRAP err;
|
||||||
tox_bootstrap(m, ip, atoi(port), (uint8_t *) binary_string, &err);
|
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
|
||||||
free(binary_string);
|
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
|
||||||
|
|
||||||
switch (err) {
|
switch (err) {
|
||||||
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
||||||
@ -318,9 +292,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int req = atoi(argv[1]);
|
long int req = strtol(argv[1], NULL, 10);
|
||||||
|
|
||||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -370,10 +344,10 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
|||||||
|
|
||||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||||
groupnum = tox_add_groupchat(m);
|
groupnum = tox_add_groupchat(m);
|
||||||
#ifdef AUDIO
|
/*#ifdef AUDIO
|
||||||
else
|
else
|
||||||
groupnum = toxav_add_av_groupchat(m, write_device_callback_group, NULL);
|
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
|
||||||
#endif
|
#endif*/
|
||||||
|
|
||||||
if (groupnum == -1) {
|
if (groupnum == -1) {
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||||
@ -396,9 +370,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
if (log->log_on)
|
if (log->log_on)
|
||||||
msg = "Logging for this window is ON. Type \"/log off\" to disable.";
|
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
|
||||||
else
|
else
|
||||||
msg = "Logging for this window is OFF. Type \"/log on\" to enable.";
|
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
@ -410,16 +384,18 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
char myid[TOX_ADDRESS_SIZE];
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
|
|
||||||
|
int log_ret = -1;
|
||||||
|
|
||||||
if (self->is_chat) {
|
if (self->is_chat) {
|
||||||
Friends.list[self->num].logging_on = true;
|
Friends.list[self->num].logging_on = true;
|
||||||
log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||||
} else if (self->is_prompt) {
|
} else if (self->is_prompt) {
|
||||||
log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||||
} else if (self->is_groupchat) {
|
} else if (self->is_groupchat) {
|
||||||
log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||||
}
|
}
|
||||||
|
|
||||||
msg = "Logging enabled";
|
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||||
@ -428,7 +404,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
|
|
||||||
log_disable(log);
|
log_disable(log);
|
||||||
|
|
||||||
msg = "Logging disabled";
|
msg = "Logging disabled.";
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -439,19 +415,57 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
|||||||
|
|
||||||
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_ADDRESS_SIZE * 2 + 1] = {0};
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
char address[TOX_ADDRESS_SIZE];
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
tox_self_get_address(m, (uint8_t *) address);
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
size_t i;
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
return;
|
||||||
char xx[3];
|
|
||||||
snprintf(xx, sizeof(xx), "%02X", address[i] & 0xff);
|
|
||||||
strcat(id, xx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id);
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
char bin_id[TOX_ADDRESS_SIZE];
|
||||||
|
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||||
|
|
||||||
|
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
|
tox_self_get_name(m, (uint8_t *) nick);
|
||||||
|
size_t nick_len = tox_self_get_name_size(m);
|
||||||
|
nick[nick_len] = '\0';
|
||||||
|
|
||||||
|
size_t data_file_len = strlen(DATA_FILE);
|
||||||
|
char dir[data_file_len + 1];
|
||||||
|
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
|
||||||
|
|
||||||
|
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
|
||||||
|
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||||
|
|
||||||
|
FILE *output = fopen(qr_path, "wb");
|
||||||
|
|
||||||
|
if (output == NULL) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ID_to_QRcode(id_string, output) == -1) {
|
||||||
|
fclose(output);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||||
|
|
||||||
|
fclose(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
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])
|
||||||
@ -508,6 +522,15 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
|||||||
prompt_update_statusmessage(prompt, m, msg);
|
prompt_update_statusmessage(prompt, m, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
uint32_t nospam = rand(); /* should be random enough */
|
||||||
|
tox_self_set_nospam(m, nospam);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your Tox ID has been changed to:");
|
||||||
|
cmd_myid(window, self, m, 0, NULL);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Any services that relied on your old ID will need to be updated manually.");
|
||||||
|
}
|
||||||
|
|
||||||
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])
|
||||||
{
|
{
|
||||||
help_init_menu(self);
|
help_init_menu(self);
|
||||||
|
@ -35,8 +35,10 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
|||||||
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_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_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
@ -49,4 +51,9 @@ void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_S
|
|||||||
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
#endif /* #define GLOBAL_COMMANDS_H */
|
#endif /* #define GLOBAL_COMMANDS_H */
|
||||||
|
122
src/groupchat.c
122
src/groupchat.c
@ -59,7 +59,7 @@
|
|||||||
#include "help.h"
|
#include "help.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "autocomplete.h"
|
#include "autocomplete.h"
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
|
|
||||||
@ -70,9 +70,9 @@ extern struct user_settings *user_settings;
|
|||||||
extern struct Winthread Winthread;
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#define AC_NUM_GROUP_COMMANDS 22
|
#define AC_NUM_GROUP_COMMANDS 24
|
||||||
#else
|
#else
|
||||||
#define AC_NUM_GROUP_COMMANDS 18
|
#define AC_NUM_GROUP_COMMANDS 20
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
/* Array of groupchat command names used for tab completion. */
|
/* Array of groupchat command names used for tab completion. */
|
||||||
@ -89,8 +89,10 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/help" },
|
{ "/help" },
|
||||||
{ "/log" },
|
{ "/log" },
|
||||||
{ "/myid" },
|
{ "/myid" },
|
||||||
|
{ "/myqr" },
|
||||||
{ "/nick" },
|
{ "/nick" },
|
||||||
{ "/note" },
|
{ "/note" },
|
||||||
|
{ "/nospam" },
|
||||||
{ "/quit" },
|
{ "/quit" },
|
||||||
{ "/requests" },
|
{ "/requests" },
|
||||||
{ "/status" },
|
{ "/status" },
|
||||||
@ -209,6 +211,9 @@ void redraw_groupchat_win(ToxWindow *self)
|
|||||||
getmaxyx(stdscr, y2, x2);
|
getmaxyx(stdscr, y2, x2);
|
||||||
y2 -= 2;
|
y2 -= 2;
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
if (ctx->sidebar) {
|
if (ctx->sidebar) {
|
||||||
delwin(ctx->sidebar);
|
delwin(ctx->sidebar);
|
||||||
ctx->sidebar = NULL;
|
ctx->sidebar = NULL;
|
||||||
@ -323,7 +328,7 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum,
|
|||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
|
|
||||||
/* don't announce title when we join the room */
|
/* don't announce title when we join the room */
|
||||||
if (!timed_out(groupchats[self->num].start_time, get_unix_time(), GROUP_EVENT_WAIT))
|
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char nick[TOX_MAX_NAME_LENGTH];
|
char nick[TOX_MAX_NAME_LENGTH];
|
||||||
@ -402,7 +407,7 @@ void *group_add_wait(void *data)
|
|||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
|
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
|
||||||
|
|
||||||
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, get_unix_time(), GROUP_EVENT_WAIT)) {
|
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -478,7 +483,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
|
|
||||||
switch (change) {
|
switch (change) {
|
||||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
case TOX_CHAT_CHANGE_PEER_ADD:
|
||||||
if (!timed_out(groupchats[groupnum].start_time, get_unix_time(), GROUP_EVENT_WAIT))
|
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
|
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
|
||||||
@ -518,7 +523,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||||
if (!timed_out(groupchats[self->num].start_time, get_unix_time(), GROUP_EVENT_WAIT))
|
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* ignore initial name change (TODO: this is a bad way to do this) */
|
/* ignore initial name change (TODO: this is a bad way to do this) */
|
||||||
@ -558,7 +563,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= 0)
|
if (x2 <= 0 || y2 <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (self->help->active) {
|
if (self->help->active) {
|
||||||
@ -593,7 +598,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
if (diff != -1) {
|
if (diff != -1) {
|
||||||
if (x + diff > x2 - 1) {
|
if (x + diff > x2 - 1) {
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -648,9 +653,15 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (x2 <= 0 || y2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
@ -695,7 +706,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int y, x;
|
int y, x;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
(void) x;
|
(void) x;
|
||||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
@ -709,6 +720,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (x2 <= 0 || y2 <= 0)
|
||||||
|
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||||
@ -726,7 +740,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
|||||||
if (user_settings->autolog == AUTOLOG_ON) {
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP);
|
|
||||||
|
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||||
@ -790,66 +806,66 @@ static int group_audio_close_out_device(int groupnum)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
|
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
|
||||||
unsigned int sample_rate)
|
// unsigned int sample_rate)
|
||||||
{
|
// {
|
||||||
if (!pcm)
|
// if (!pcm)
|
||||||
return -1;
|
// return -1;
|
||||||
|
|
||||||
if (channels == 0 || channels > 2)
|
// if (channels == 0 || channels > 2)
|
||||||
return -2;
|
// return -2;
|
||||||
|
|
||||||
ALuint bufid;
|
// ALuint bufid;
|
||||||
ALint processed = 0, queued = 0;
|
// ALint processed = 0, queued = 0;
|
||||||
|
|
||||||
alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
|
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
|
||||||
alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
|
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
|
||||||
fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
|
// fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
|
||||||
|
|
||||||
if (processed) {
|
// if (processed) {
|
||||||
ALuint bufids[processed];
|
// ALuint bufids[processed];
|
||||||
alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
|
// alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
|
||||||
alDeleteBuffers(processed - 1, bufids + 1);
|
// alDeleteBuffers(processed - 1, bufids + 1);
|
||||||
bufid = bufids[0];
|
// bufid = bufids[0];
|
||||||
} else if (queued < 16) {
|
// } else if (queued < 16) {
|
||||||
alGenBuffers(1, &bufid);
|
// alGenBuffers(1, &bufid);
|
||||||
} else {
|
// } else {
|
||||||
return -3;
|
// return -3;
|
||||||
}
|
// }
|
||||||
|
|
||||||
int length = samples * channels * sizeof(int16_t);
|
// int length = samples * channels * sizeof(int16_t);
|
||||||
|
|
||||||
alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
|
// alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
|
||||||
alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
|
// alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
|
||||||
|
|
||||||
ALint state;
|
// ALint state;
|
||||||
alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
|
// alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
|
||||||
|
|
||||||
if (state != AL_PLAYING)
|
// if (state != AL_PLAYING)
|
||||||
alSourcePlay(groupchats[groupnum].audio.source);
|
// alSourcePlay(groupchats[groupnum].audio.source);
|
||||||
|
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
// }
|
||||||
|
|
||||||
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, int groupnum, int peernum, const int16_t *pcm,
|
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, uint32_t groupnum, int peernum, const int16_t *pcm,
|
||||||
unsigned int samples, uint8_t channels, unsigned int sample_rate)
|
unsigned int samples, uint8_t channels, unsigned int sample_rate)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (groupnum != self->num)
|
// if (groupnum != self->num)
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
if (peernum < 0)
|
// if (peernum < 0)
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
if (groupchats[groupnum].audio.dvhandle == NULL)
|
// if (groupchats[groupnum].audio.dvhandle == NULL)
|
||||||
fprintf(stderr, "dvhandle is null)\n");
|
// fprintf(stderr, "dvhandle is null)\n");
|
||||||
|
|
||||||
if (groupchats[groupnum].audio.dvctx == NULL)
|
// if (groupchats[groupnum].audio.dvctx == NULL)
|
||||||
fprintf(stderr, "ctx is null\n");
|
// fprintf(stderr, "ctx is null\n");
|
||||||
|
|
||||||
int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
|
// int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
|
||||||
fprintf(stderr, "write: %d\n", ret);
|
// fprintf(stderr, "write: %d\n", ret);
|
||||||
}
|
}
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
48
src/help.c
48
src/help.c
@ -60,6 +60,9 @@ static void help_init_window(ToxWindow *self, int height, int width)
|
|||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(stdscr, y2, x2);
|
getmaxyx(stdscr, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
height = MIN(height, y2);
|
height = MIN(height, y2);
|
||||||
width = MIN(width, x2);
|
width = MIN(width, x2);
|
||||||
|
|
||||||
@ -144,16 +147,18 @@ static void help_draw_global(ToxWindow *self)
|
|||||||
|
|
||||||
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||||
wprintw(win, " /accept <id> : Accept friend request\n");
|
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||||
wprintw(win, " /avatar <path> : Set a personal avatar\n");
|
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
|
||||||
wprintw(win, " /decline <id> : Decline friend request\n");
|
wprintw(win, " /decline <id> : Decline friend request\n");
|
||||||
wprintw(win, " /requests : List pending friend requests\n");
|
wprintw(win, " /requests : List pending friend requests\n");
|
||||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\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, " /status <type> <msg> : Set status with optional note\n");
|
||||||
wprintw(win, " /note <msg> : Set a personal note\n");
|
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||||
|
wprintw(win, " /nospam : Change part of your Tox ID to stop spam\n");
|
||||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||||
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
||||||
wprintw(win, " /myid : Print your Tox ID\n");
|
wprintw(win, " /myid : Print your Tox ID\n");
|
||||||
|
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
|
||||||
wprintw(win, " /clear : Clear window history\n");
|
wprintw(win, " /clear : Clear window history\n");
|
||||||
wprintw(win, " /close : Close the current chat window\n");
|
wprintw(win, " /close : Close the current chat window\n");
|
||||||
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
||||||
@ -167,6 +172,15 @@ static void help_draw_global(ToxWindow *self)
|
|||||||
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
wprintw(win, " /sdev <type> <id> : Set active device\n");
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Video:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
|
||||||
|
wprintw(win, " /lsvdev <type> : List video devices where type: in|out\n");
|
||||||
|
wprintw(win, " /svdev <type> <id> : Set active video device\n");
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
@ -203,6 +217,13 @@ static void help_draw_chat(ToxWindow *self)
|
|||||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
|
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
wattron(win, A_BOLD);
|
||||||
|
wprintw(win, "\n Video:\n");
|
||||||
|
wattroff(win, A_BOLD);
|
||||||
|
wprintw(win, " /video : Toggle video call\n");
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
@ -245,13 +266,6 @@ static void help_draw_group(ToxWindow *self)
|
|||||||
|
|
||||||
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
||||||
|
|
||||||
wattron(win, A_BOLD);
|
|
||||||
wprintw(win, " Audio commands:\n");
|
|
||||||
wattroff(win, A_BOLD);
|
|
||||||
|
|
||||||
wprintw(win, " /mute <type> : Mute active device where type: in | out\n");
|
|
||||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
|
||||||
|
|
||||||
help_draw_bottom_menu(win);
|
help_draw_bottom_menu(win);
|
||||||
|
|
||||||
box(win, ACS_VLINE, ACS_HLINE);
|
box(win, ACS_VLINE, ACS_HLINE);
|
||||||
@ -289,29 +303,31 @@ void help_onKey(ToxWindow *self, wint_t key)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'c':
|
case 'c':
|
||||||
#ifdef AUDIO
|
#ifdef VIDEO
|
||||||
|
help_init_window(self, 22, 80);
|
||||||
|
#elif AUDIO
|
||||||
help_init_window(self, 19, 80);
|
help_init_window(self, 19, 80);
|
||||||
#else
|
#else
|
||||||
help_init_window(self, 9, 80);
|
help_init_window(self, 10, 80);
|
||||||
#endif
|
#endif
|
||||||
self->help->type = HELP_CHAT;
|
self->help->type = HELP_CHAT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'g':
|
case 'g':
|
||||||
#ifdef AUDIO
|
#ifdef VIDEO
|
||||||
help_init_window(self, 24, 80);
|
help_init_window(self, 30, 80);
|
||||||
|
#elif AUDIO
|
||||||
|
help_init_window(self, 26, 80);
|
||||||
#else
|
#else
|
||||||
help_init_window(self, 20, 80);
|
help_init_window(self, 22, 80);
|
||||||
#endif
|
#endif
|
||||||
self->help->type = HELP_GLOBAL;
|
self->help->type = HELP_GLOBAL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#ifdef AUDIO /* remove if/when we add non-audio group commands */
|
|
||||||
case 'r':
|
case 'r':
|
||||||
help_init_window(self, 10, 80);
|
help_init_window(self, 6, 80);
|
||||||
self->help->type = HELP_GROUP;
|
self->help->type = HELP_GROUP;
|
||||||
break;
|
break;
|
||||||
#endif
|
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
help_init_window(self, 10, 80);
|
help_init_window(self, 10, 80);
|
||||||
|
@ -125,7 +125,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
|||||||
|
|
||||||
if (x + yank_cols >= mx_x) {
|
if (x + yank_cols >= mx_x) {
|
||||||
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
int rmdr = MAX(0, (x + yank_cols) - mx_x);
|
||||||
int s_len = wcswidth(&ctx->line[ctx->start], rmdr);
|
int s_len = MAX(0, wcswidth(&ctx->line[ctx->start], rmdr));
|
||||||
ctx->start += s_len + 1;
|
ctx->start += s_len + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
|||||||
|
|
||||||
ctx->pos = ctx->len;
|
ctx->pos = ctx->len;
|
||||||
|
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
ctx->start = MAX(0, 1 + (mx_x * (wlen / mx_x) - mx_x) + (wlen % mx_x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
|||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
fetch_hist_item(ctx, key);
|
fetch_hist_item(ctx, key);
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
|
ctx->start = wlen < mx_x ? 0 : wlen - mx_x + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,6 +133,9 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
|||||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||||
{
|
{
|
||||||
|
if (!self)
|
||||||
|
return;
|
||||||
|
|
||||||
struct history *hst = self->chatwin->hst;
|
struct history *hst = self->chatwin->hst;
|
||||||
|
|
||||||
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
|
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
|
||||||
@ -155,11 +158,13 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
|
|||||||
/* for type-specific formatting in print function */
|
/* for type-specific formatting in print function */
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case IN_ACTION:
|
case IN_ACTION:
|
||||||
|
/* fallthrough */
|
||||||
case OUT_ACTION:
|
case OUT_ACTION:
|
||||||
len += strlen(user_settings->line_normal) + 2;
|
len += strlen(user_settings->line_normal) + 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case IN_MSG:
|
case IN_MSG:
|
||||||
|
/* fallthrough */
|
||||||
case OUT_MSG:
|
case OUT_MSG:
|
||||||
len += strlen(user_settings->line_normal) + 3;
|
len += strlen(user_settings->line_normal) + 3;
|
||||||
break;
|
break;
|
||||||
@ -299,7 +304,9 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case OUT_MSG:
|
case OUT_MSG:
|
||||||
|
/* fallthrough */
|
||||||
case OUT_MSG_READ:
|
case OUT_MSG_READ:
|
||||||
|
/* fallthrough */
|
||||||
case IN_MSG:
|
case IN_MSG:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s ", line->timestr);
|
wprintw(win, "%s ", line->timestr);
|
||||||
@ -318,13 +325,17 @@ void line_info_print(ToxWindow *self)
|
|||||||
|
|
||||||
if (line->msg[0] == '>')
|
if (line->msg[0] == '>')
|
||||||
wattron(win, COLOR_PAIR(GREEN));
|
wattron(win, COLOR_PAIR(GREEN));
|
||||||
|
else if (line->msg[0] == '<')
|
||||||
|
wattron(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
wprintw(win, "%s", line->msg);
|
wprintw(win, "%s", line->msg);
|
||||||
|
|
||||||
if (line->msg[0] == '>')
|
if (line->msg[0] == '>')
|
||||||
wattroff(win, COLOR_PAIR(GREEN));
|
wattroff(win, COLOR_PAIR(GREEN));
|
||||||
|
else if (line->msg[0] == '<')
|
||||||
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
|
||||||
if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
|
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
wattron(win, COLOR_PAIR(RED));
|
wattron(win, COLOR_PAIR(RED));
|
||||||
wprintw(win, " x", line->msg);
|
wprintw(win, " x", line->msg);
|
||||||
wattroff(win, COLOR_PAIR(RED));
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
@ -339,7 +350,9 @@ void line_info_print(ToxWindow *self)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OUT_ACTION_READ:
|
case OUT_ACTION_READ:
|
||||||
|
/* fallthrough */
|
||||||
case OUT_ACTION:
|
case OUT_ACTION:
|
||||||
|
/* fallthrough */
|
||||||
case IN_ACTION:
|
case IN_ACTION:
|
||||||
wattron(win, COLOR_PAIR(BLUE));
|
wattron(win, COLOR_PAIR(BLUE));
|
||||||
wprintw(win, "%s ", line->timestr);
|
wprintw(win, "%s ", line->timestr);
|
||||||
@ -349,7 +362,7 @@ void line_info_print(ToxWindow *self)
|
|||||||
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
||||||
wattroff(win, COLOR_PAIR(YELLOW));
|
wattroff(win, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
|
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||||
wattron(win, COLOR_PAIR(RED));
|
wattron(win, COLOR_PAIR(RED));
|
||||||
wprintw(win, " x", line->msg);
|
wprintw(win, " x", line->msg);
|
||||||
wattroff(win, COLOR_PAIR(RED));
|
wattroff(win, COLOR_PAIR(RED));
|
||||||
|
20
src/log.c
20
src/log.c
@ -139,11 +139,9 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
|
|||||||
strftime(s, MAX_STR_SIZE, t, get_time());
|
strftime(s, MAX_STR_SIZE, t, get_time());
|
||||||
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
fprintf(log->file, "%s %s %s\n", s, name_frmt, msg);
|
||||||
|
|
||||||
uint64_t curtime = get_unix_time();
|
if (timed_out(log->lastwrite, LOG_FLUSH_LIMIT)) {
|
||||||
|
|
||||||
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
|
||||||
fflush(log->file);
|
fflush(log->file);
|
||||||
log->lastwrite = curtime;
|
log->lastwrite = get_unix_time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,15 +153,19 @@ void log_disable(struct chatlog *log)
|
|||||||
memset(log, 0, sizeof(struct chatlog));
|
memset(log, 0, sizeof(struct chatlog));
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||||
{
|
{
|
||||||
log->log_on = true;
|
log->log_on = true;
|
||||||
|
|
||||||
if (log->file != NULL)
|
if (log->file != NULL)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1)
|
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
||||||
log_disable(log);
|
log_disable(log);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loads previous history from chat log */
|
/* Loads previous history from chat log */
|
||||||
@ -177,7 +179,7 @@ void load_chat_history(ToxWindow *self, struct chatlog *log)
|
|||||||
if (sz <= 0)
|
if (sz <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char *hstbuf = malloc(sz);
|
char *hstbuf = malloc(sz + 1);
|
||||||
|
|
||||||
if (hstbuf == NULL)
|
if (hstbuf == NULL)
|
||||||
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
||||||
@ -194,6 +196,8 @@ void load_chat_history(ToxWindow *self, struct chatlog *log)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hstbuf[sz] = '\0';
|
||||||
|
|
||||||
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||||
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||||
int start, count = 0;
|
int start, count = 0;
|
||||||
|
@ -39,8 +39,12 @@ enum {
|
|||||||
/* formats/writes line to log file */
|
/* formats/writes line to log file */
|
||||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
||||||
|
|
||||||
/* enables logging for specified log and creates/fetches file if necessary */
|
/* enables logging for specified log and creates/fetches file if necessary.
|
||||||
void log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
||||||
|
|
||||||
/* disables logging for specified log and closes file */
|
/* disables logging for specified log and closes file */
|
||||||
void log_disable(struct chatlog *log);
|
void log_disable(struct chatlog *log);
|
||||||
|
@ -140,9 +140,7 @@ void cqueue_try_send(ToxWindow *self, Tox *m)
|
|||||||
if (!msg)
|
if (!msg)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint64_t curtime = get_unix_time();
|
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL))
|
||||||
|
|
||||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint32_t receipt = 0;
|
uint32_t receipt = 0;
|
||||||
@ -150,7 +148,7 @@ void cqueue_try_send(ToxWindow *self, Tox *m)
|
|||||||
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||||
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
||||||
|
|
||||||
msg->last_send_try = curtime;
|
msg->last_send_try = get_unix_time();
|
||||||
msg->receipt = receipt;
|
msg->receipt = receipt;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ void hst_to_net(uint8_t *num, uint16_t numbytes)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Note: The time functions are not thread safe */
|
||||||
void update_unix_time(void)
|
void update_unix_time(void)
|
||||||
{
|
{
|
||||||
current_unix_time = (uint64_t) time(NULL);
|
current_unix_time = (uint64_t) time(NULL);
|
||||||
@ -65,9 +66,9 @@ uint64_t get_unix_time(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
int timed_out(uint64_t timestamp, uint64_t timeout)
|
||||||
{
|
{
|
||||||
return timestamp + timeout <= curtime;
|
return timestamp + timeout <= get_unix_time();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the current local time */
|
/* Get the current local time */
|
||||||
@ -109,20 +110,24 @@ void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs)
|
|||||||
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *hex_string_to_bin(const char *hex_string)
|
/*
|
||||||
|
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||||
|
* output_size must be exactly half of hex_len.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
|
||||||
{
|
{
|
||||||
size_t len = strlen(hex_string);
|
if (output_size == 0 || hex_len != output_size * 2)
|
||||||
char *val = malloc(len);
|
return -1;
|
||||||
|
|
||||||
if (val == NULL)
|
for (size_t i = 0; i < output_size; ++i) {
|
||||||
exit_toxic_err("failed in hex_string_to_bin", FATALERR_MEMORY);
|
sscanf(hex_string, "%2hhx", &output[i]);
|
||||||
|
hex_string += 2;
|
||||||
|
}
|
||||||
|
|
||||||
size_t i;
|
return 0;
|
||||||
|
|
||||||
for (i = 0; i < len; ++i, hex_string += 2)
|
|
||||||
sscanf(hex_string, "%2hhx", &val[i]);
|
|
||||||
|
|
||||||
return val;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||||
@ -144,9 +149,30 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Converts a binary representation of a Tox ID into a string.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
|
||||||
|
{
|
||||||
|
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
|
||||||
|
snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Returns 1 if the string is empty, 0 otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
int string_is_empty(const char *string)
|
int string_is_empty(const char *string)
|
||||||
{
|
{
|
||||||
|
if (!string)
|
||||||
|
return true;
|
||||||
|
|
||||||
return string[0] == '\0';
|
return string[0] == '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,22 +243,24 @@ void filter_str(char *str, size_t len)
|
|||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < len; ++i) {
|
for (i = 0; i < len; ++i) {
|
||||||
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v')
|
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0')
|
||||||
str[i] = ' ';
|
str[i] = ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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(char *namebuf, int bufsize, const char *pathname)
|
* Returns the file name length
|
||||||
|
*/
|
||||||
|
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
|
||||||
{
|
{
|
||||||
int idx = strlen(pathname) - 1;
|
int len = strlen(pathname) - 1;
|
||||||
char *path = strdup(pathname);
|
char *path = strdup(pathname);
|
||||||
|
|
||||||
if (path == NULL)
|
if (path == NULL)
|
||||||
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
|
||||||
|
|
||||||
while (idx >= 0 && pathname[idx] == '/')
|
while (len >= 0 && pathname[len] == '/')
|
||||||
path[idx--] = '\0';
|
path[len--] = '\0';
|
||||||
|
|
||||||
char *finalname = strdup(path);
|
char *finalname = strdup(path);
|
||||||
|
|
||||||
@ -249,6 +277,29 @@ void get_file_name(char *namebuf, int bufsize, const char *pathname)
|
|||||||
snprintf(namebuf, bufsize, "%s", finalname);
|
snprintf(namebuf, bufsize, "%s", finalname);
|
||||||
free(finalname);
|
free(finalname);
|
||||||
free(path);
|
free(path);
|
||||||
|
|
||||||
|
return strlen(namebuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gets the base directory of path and puts it in dir.
|
||||||
|
* dir must have at least as much space as path_len + 1.
|
||||||
|
*
|
||||||
|
* Returns the length of the base directory.
|
||||||
|
*/
|
||||||
|
size_t get_base_dir(const char *path, size_t path_len, char *dir)
|
||||||
|
{
|
||||||
|
if (path_len == 0 || path == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
size_t dir_len = char_rfind(path, '/', path_len);
|
||||||
|
|
||||||
|
if (dir_len != 0 && dir_len < path_len)
|
||||||
|
++dir_len; /* Leave trailing slash */
|
||||||
|
|
||||||
|
memcpy(dir, path, dir_len);
|
||||||
|
dir[dir_len] = '\0';
|
||||||
|
|
||||||
|
return dir_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* converts str to all lowercase */
|
/* converts str to all lowercase */
|
||||||
@ -320,8 +371,8 @@ int char_find(int idx, const char *s, char ch)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns index of the last instance of ch in s starting at len
|
/* returns index of the last instance of ch in s starting at len.
|
||||||
returns 0 if char not found (skips 0th index) */
|
returns 0 if char not found (skips 0th index). */
|
||||||
int char_rfind(const char *s, char ch, int len)
|
int char_rfind(const char *s, char ch, int len)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -363,7 +414,7 @@ bool file_exists(const char *path)
|
|||||||
return stat(path, &s) == 0;
|
return stat(path, &s) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* returns file size or 0 on error */
|
/* returns file size. If file doesn't exist returns 0. */
|
||||||
off_t file_size(const char *path)
|
off_t file_size(const char *path)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -41,22 +41,35 @@
|
|||||||
|
|
||||||
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
||||||
|
|
||||||
/* convert a hex string to binary */
|
/*
|
||||||
char *hex_string_to_bin(const char *hex_string);
|
* Converts a hexidecimal string of length hex_len to binary format and puts the result in output.
|
||||||
|
* output_size must be exactly half of hex_len.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size);
|
||||||
|
|
||||||
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
|
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
|
||||||
int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||||
|
|
||||||
/* get the current unix time */
|
/* Converts a binary representation of a Tox ID into a string.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
|
||||||
|
|
||||||
|
/* get the current unix time (not thread safe) */
|
||||||
uint64_t get_unix_time(void);
|
uint64_t get_unix_time(void);
|
||||||
|
|
||||||
/* Puts the current time in buf in the format of [HH:mm:ss] */
|
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
|
||||||
void get_time_str(char *buf, int bufsize);
|
void get_time_str(char *buf, int bufsize);
|
||||||
|
|
||||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
/* 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);
|
void get_elapsed_time_str(char *buf, int bufsize, uint64_t secs);
|
||||||
|
|
||||||
/* get the current local time */
|
/* get the current local time (not thread safe) */
|
||||||
struct tm *get_time(void);
|
struct tm *get_time(void);
|
||||||
|
|
||||||
/* updates current unix time (should be run once per do_toxic loop) */
|
/* updates current unix time (should be run once per do_toxic loop) */
|
||||||
@ -75,7 +88,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
|
|||||||
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||||
|
|
||||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
int timed_out(uint64_t timestamp, uint64_t timeout);
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* 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);
|
||||||
@ -92,10 +105,18 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
|
|||||||
int valid_nick(const char *nick);
|
int valid_nick(const char *nick);
|
||||||
|
|
||||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||||
void filter_str(char *str, size_t len);;
|
void filter_str(char *str, size_t len);
|
||||||
|
|
||||||
/* 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(char *namebuf, int bufsize, const char *pathname);
|
size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname);
|
||||||
|
|
||||||
|
/* Gets the base directory of path and puts it in dir.
|
||||||
|
* dir must have at least as much space as path_len.
|
||||||
|
*
|
||||||
|
* Returns the length of the base directory on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
size_t get_base_dir(const char *path, size_t path_len, char *dir);
|
||||||
|
|
||||||
/* converts str to all lowercase */
|
/* converts str to all lowercase */
|
||||||
void str_to_lower(char *str);
|
void str_to_lower(char *str);
|
||||||
@ -125,7 +146,7 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
|
|||||||
/* checks if a file exists. Returns true or false */
|
/* checks if a file exists. Returns true or false */
|
||||||
bool file_exists(const char *path);
|
bool file_exists(const char *path);
|
||||||
|
|
||||||
/* returns file size or 0 on error */
|
/* returns file size. If file doesn't exist returns 0. */
|
||||||
off_t file_size(const char *path);
|
off_t file_size(const char *path);
|
||||||
|
|
||||||
/* compares the first size bytes of fp and signature.
|
/* compares the first size bytes of fp and signature.
|
||||||
|
461
src/name_lookup.c
Normal file
461
src/name_lookup.c
Normal file
@ -0,0 +1,461 @@
|
|||||||
|
/* name_lookup.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h> /* for u_char */
|
||||||
|
#include <curl/curl.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "misc_tools.h"
|
||||||
|
#include "configdir.h"
|
||||||
|
|
||||||
|
extern struct arg_opts arg_opts;
|
||||||
|
extern struct Winthread Winthread;;
|
||||||
|
|
||||||
|
#define NAMESERVER_API_PATH "api"
|
||||||
|
#define SERVER_KEY_SIZE 32
|
||||||
|
#define MAX_SERVERS 50
|
||||||
|
#define MAX_DOMAIN_SIZE 32
|
||||||
|
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
|
||||||
|
|
||||||
|
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||||
|
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||||
|
|
||||||
|
struct Nameservers {
|
||||||
|
int lines;
|
||||||
|
char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
|
||||||
|
char keys[MAX_SERVERS][SERVER_KEY_SIZE];
|
||||||
|
} Nameservers;
|
||||||
|
|
||||||
|
static struct thread_data {
|
||||||
|
Tox *m;
|
||||||
|
ToxWindow *self;
|
||||||
|
char id_bin[TOX_ADDRESS_SIZE];
|
||||||
|
char addr[MAX_STR_SIZE];
|
||||||
|
char msg[MAX_STR_SIZE];
|
||||||
|
bool busy;
|
||||||
|
bool disabled;
|
||||||
|
} t_data;
|
||||||
|
|
||||||
|
static struct lookup_thread {
|
||||||
|
pthread_t tid;
|
||||||
|
pthread_attr_t attr;
|
||||||
|
} lookup_thread;
|
||||||
|
|
||||||
|
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||||
|
{
|
||||||
|
char frmt_msg[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, errmsg);
|
||||||
|
vsnprintf(frmt_msg, sizeof(frmt_msg), errmsg, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kill_lookup_thread(void)
|
||||||
|
{
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempts to load the nameserver list pointed at by path into the Nameservers structure.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* -1 is reserved.
|
||||||
|
* Returns -2 if the supplied path does not exist.
|
||||||
|
* Returns -3 if the list does not contain any valid entries.
|
||||||
|
*/
|
||||||
|
static int load_nameserver_list(const char *path)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path, "r");
|
||||||
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -2;
|
||||||
|
|
||||||
|
char line[MAX_SERVER_LINE];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
|
||||||
|
int linelen = strlen(line);
|
||||||
|
|
||||||
|
if (linelen < SERVER_KEY_SIZE * 2 + 5)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (line[linelen - 1] == '\n')
|
||||||
|
line[--linelen] = '\0';
|
||||||
|
|
||||||
|
const char *name = strtok(line, " ");
|
||||||
|
const char *keystr = strtok(NULL, " ");
|
||||||
|
|
||||||
|
if (name == NULL || keystr == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (strlen(keystr) != SERVER_KEY_SIZE * 2)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
|
||||||
|
int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
|
||||||
|
|
||||||
|
if (res == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
++Nameservers.lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (Nameservers.lines < 1)
|
||||||
|
return -3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Takes address addr in the form "username@domain", puts the username in namebuf,
|
||||||
|
* and the domain in dombuf.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure
|
||||||
|
*/
|
||||||
|
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
|
||||||
|
{
|
||||||
|
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char tmpaddr[MAX_STR_SIZE];
|
||||||
|
char *tmpname = NULL;
|
||||||
|
char *tmpdom = NULL;
|
||||||
|
|
||||||
|
snprintf(tmpaddr, sizeof(tmpaddr), "%s", addr);
|
||||||
|
tmpname = strtok(tmpaddr, "@");
|
||||||
|
tmpdom = strtok(NULL, "");
|
||||||
|
|
||||||
|
if (tmpname == NULL || tmpdom == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
str_to_lower(tmpdom);
|
||||||
|
snprintf(namebuf, namebuf_sz, "%s", tmpname);
|
||||||
|
snprintf(dombuf, dombuf_sz, "%s", tmpdom);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* matches input domain name with domains in list and obtains key.
|
||||||
|
* Turns out_domain into the full domain we need to make a POST request.
|
||||||
|
*
|
||||||
|
* Return true on match.
|
||||||
|
* Returns false on no match.
|
||||||
|
*/
|
||||||
|
static bool get_domain_match(char *pubkey, char *out_domain, size_t out_domain_size, const char *inputdomain)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < Nameservers.lines; ++i) {
|
||||||
|
if (strcmp(Nameservers.names[i], inputdomain) == 0) {
|
||||||
|
memcpy(pubkey, Nameservers.keys[i], SERVER_KEY_SIZE);
|
||||||
|
snprintf(out_domain, out_domain_size, "https://%s/%s", Nameservers.names[i], NAMESERVER_API_PATH);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MAX_RECV_LOOKUP_DATA_SIZE 1024
|
||||||
|
|
||||||
|
/* Holds raw data received from name server */
|
||||||
|
struct Recv_Data {
|
||||||
|
char data[MAX_RECV_LOOKUP_DATA_SIZE];
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t write_lookup_data(void *data, size_t size, size_t nmemb, void *user_pointer)
|
||||||
|
{
|
||||||
|
struct Recv_Data *recv_data = (struct Recv_Data *) user_pointer;
|
||||||
|
size_t real_size = size * nmemb;
|
||||||
|
|
||||||
|
if (real_size >= MAX_RECV_LOOKUP_DATA_SIZE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memcpy(&recv_data->data, data, real_size);
|
||||||
|
recv_data->size = real_size;
|
||||||
|
recv_data->data[real_size] = '\0';
|
||||||
|
|
||||||
|
return real_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Converts Tox ID string contained in recv_data to binary format and puts it in thread's ID buffer.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
#define ID_PREFIX "\"tox_id\": \""
|
||||||
|
static int process_response(struct Recv_Data *recv_data)
|
||||||
|
{
|
||||||
|
size_t prefix_size = strlen(ID_PREFIX);
|
||||||
|
|
||||||
|
if (recv_data->size < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
|
||||||
|
|
||||||
|
if (IDstart == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
|
||||||
|
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
|
||||||
|
ID_string[TOX_ADDRESS_SIZE * 2] = 0;
|
||||||
|
|
||||||
|
if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sets proxy info for given CURL handler.
|
||||||
|
*
|
||||||
|
* Returns 0 on success or if no proxy is set by the client.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
static int set_lookup_proxy(ToxWindow *self, CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
|
||||||
|
{
|
||||||
|
if (proxy_type == TOX_PROXY_TYPE_NONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (proxy_address == NULL || port == 0) {
|
||||||
|
lookup_error(self, "Unknown proxy error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_PROXYPORT, (long) port);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "Failed to set proxy port (libcurl error %d)", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
long int type = proxy_type == TOX_PROXY_TYPE_SOCKS5 ? CURLPROXY_SOCKS5_HOSTNAME : CURLPROXY_HTTP;
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXYTYPE, type);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "Failed to set proxy type (libcurl error %d)", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_PROXY, proxy_address);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "Failed to set proxy (libcurl error %d)", ret);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *lookup_thread_func(void *data)
|
||||||
|
{
|
||||||
|
ToxWindow *self = t_data.self;
|
||||||
|
|
||||||
|
char input_domain[MAX_STR_SIZE];
|
||||||
|
char name[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
if (parse_addr(t_data.addr, name, sizeof(name), input_domain, sizeof(input_domain)) == -1) {
|
||||||
|
lookup_error(self, "Input must be a 76 character Tox ID or an address in the form: username@domain");
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
char nameserver_key[SERVER_KEY_SIZE];
|
||||||
|
char real_domain[MAX_DOMAIN_SIZE];
|
||||||
|
|
||||||
|
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
|
||||||
|
if (!strcasecmp(input_domain, "utox.org"))
|
||||||
|
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic");
|
||||||
|
else
|
||||||
|
lookup_error(self, "Name server domain not found.");
|
||||||
|
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
CURL *c_handle = curl_easy_init();
|
||||||
|
|
||||||
|
if (!c_handle) {
|
||||||
|
lookup_error(self, "curl handler error");
|
||||||
|
kill_lookup_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Recv_Data recv_data;
|
||||||
|
memset(&recv_data, 0, sizeof(struct Recv_Data));
|
||||||
|
|
||||||
|
char post_data[MAX_STR_SIZE];
|
||||||
|
snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
|
||||||
|
|
||||||
|
struct curl_slist *headers = NULL;
|
||||||
|
|
||||||
|
headers = curl_slist_append(headers, "Content-Type: application/json");
|
||||||
|
headers = curl_slist_append(headers, "charsets: utf-8");
|
||||||
|
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_URL, real_domain);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, write_lookup_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, (void *) &recv_data);
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
|
||||||
|
|
||||||
|
if (set_lookup_proxy(self, c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type) == -1)
|
||||||
|
goto on_exit;
|
||||||
|
|
||||||
|
int ret = curl_easy_setopt(c_handle, CURLOPT_USE_SSL, CURLUSESSL_ALL);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "TLS could not be enabled (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "TLSv1.2 could not be set (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, TLS_CIPHER_SUITE_LIST);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "Failed to set TLS cipher list (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
/* If system doesn't support any of the specified ciphers suites, fall back to default */
|
||||||
|
if (ret == CURLE_SSL_CIPHER) {
|
||||||
|
curl_easy_setopt(c_handle, CURLOPT_SSL_CIPHER_LIST, NULL);
|
||||||
|
ret = curl_easy_perform(c_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != CURLE_OK) {
|
||||||
|
lookup_error(self, "HTTPS lookup error (libcurl error %d)", ret);
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process_response(&recv_data) == -1) {
|
||||||
|
lookup_error(self, "Bad response.");
|
||||||
|
goto on_exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
cmd_add_helper(self, t_data.m, t_data.id_bin, t_data.msg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
on_exit:
|
||||||
|
curl_slist_free_all(headers);
|
||||||
|
curl_easy_cleanup(c_handle);
|
||||||
|
kill_lookup_thread();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
|
||||||
|
{
|
||||||
|
if (t_data.disabled) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t_data.busy) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
|
||||||
|
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", message);
|
||||||
|
t_data.self = self;
|
||||||
|
t_data.m = m;
|
||||||
|
t_data.busy = true;
|
||||||
|
|
||||||
|
if (pthread_attr_init(&lookup_thread.attr) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
|
||||||
|
pthread_attr_destroy(&lookup_thread.attr);
|
||||||
|
memset(&t_data, 0, sizeof(struct thread_data));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||||
|
* threads are spawned.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 if curl failed to init.
|
||||||
|
* Returns -2 if the nameserver list cannot be found.
|
||||||
|
* Returns -3 if the nameserver list does not contain any valid entries.
|
||||||
|
*/
|
||||||
|
int name_lookup_init(void)
|
||||||
|
{
|
||||||
|
if (curl_global_init(CURL_GLOBAL_ALL) != 0) {
|
||||||
|
t_data.disabled = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *path = arg_opts.nameserver_path[0] ? arg_opts.nameserver_path : PACKAGE_DATADIR "/nameservers";
|
||||||
|
int ret = load_nameserver_list(path);
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
t_data.disabled = true;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void name_lookup_cleanup(void)
|
||||||
|
{
|
||||||
|
curl_global_cleanup();
|
||||||
|
}
|
37
src/name_lookup.h
Normal file
37
src/name_lookup.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/* name_lookup.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NAME_LOOKUP
|
||||||
|
#define NAME_LOOKUP
|
||||||
|
|
||||||
|
/* Initializes http based name lookups. Note: This function must be called only once before additional
|
||||||
|
* threads are spawned.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int name_lookup_init(void);
|
||||||
|
void name_lookup_cleanup(void);
|
||||||
|
|
||||||
|
int name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message);
|
||||||
|
|
||||||
|
#endif /* NAME_LOOKUP */
|
87
src/notify.c
87
src/notify.c
@ -31,7 +31,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
@ -90,7 +90,7 @@ struct _ActiveNotifications {
|
|||||||
#ifdef BOX_NOTIFY
|
#ifdef BOX_NOTIFY
|
||||||
NotifyNotification* box;
|
NotifyNotification* box;
|
||||||
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
|
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
|
||||||
char title[24];
|
char title[64];
|
||||||
size_t size;
|
size_t size;
|
||||||
time_t n_timeout;
|
time_t n_timeout;
|
||||||
#endif
|
#endif
|
||||||
@ -117,9 +117,12 @@ static void tab_notify(ToxWindow *self, uint64_t flags)
|
|||||||
|
|
||||||
static bool notifications_are_disabled(uint64_t flags)
|
static bool notifications_are_disabled(uint64_t flags)
|
||||||
{
|
{
|
||||||
bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time();
|
if (user_settings->alerts != ALERTS_ENABLED)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
return res || (flags & NT_NOFOCUS && is_focused());
|
return res || ((flags & NT_NOFOCUS) && is_focused());
|
||||||
#else
|
#else
|
||||||
return res;
|
return res;
|
||||||
#endif
|
#endif
|
||||||
@ -153,33 +156,36 @@ bool is_playing(int source)
|
|||||||
static bool device_opened = false;
|
static bool device_opened = false;
|
||||||
time_t last_opened_update = 0;
|
time_t last_opened_update = 0;
|
||||||
|
|
||||||
bool m_open_device()
|
/* Opens primary device. Returns true on succe*/
|
||||||
|
void m_open_device()
|
||||||
{
|
{
|
||||||
last_opened_update = get_unix_time();
|
last_opened_update = get_unix_time();
|
||||||
|
|
||||||
if (device_opened) return true;
|
if (device_opened) return;
|
||||||
|
|
||||||
/* Blah error check */
|
/* Blah error check */
|
||||||
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
||||||
|
|
||||||
return (device_opened = true);
|
device_opened = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool m_close_device()
|
void m_close_device()
|
||||||
{
|
{
|
||||||
if (!device_opened) return true;
|
if (!device_opened) return;
|
||||||
|
|
||||||
close_device(output, Control.device_idx);
|
close_device(output, Control.device_idx);
|
||||||
|
|
||||||
return !(device_opened = false);
|
device_opened = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Terminate all sounds but wait for them to finish first */
|
/* Terminate all sounds but wait for them to finish first */
|
||||||
void graceful_clear()
|
void graceful_clear()
|
||||||
{
|
{
|
||||||
int i;
|
|
||||||
control_lock();
|
control_lock();
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||||
if (actives[i].active) {
|
if (actives[i].active) {
|
||||||
#ifdef BOX_NOTIFY
|
#ifdef BOX_NOTIFY
|
||||||
@ -211,18 +217,24 @@ void graceful_clear()
|
|||||||
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
control_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* do_playing(void* _p)
|
void* do_playing(void* _p)
|
||||||
{
|
{
|
||||||
(void)_p;
|
(void)_p;
|
||||||
int i;
|
|
||||||
|
|
||||||
bool has_looping = false;
|
while(true) {
|
||||||
|
|
||||||
while(Control.poll_active) {
|
|
||||||
control_lock();
|
control_lock();
|
||||||
|
|
||||||
|
if (!Control.poll_active) {
|
||||||
|
control_unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_looping = false;
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||||
|
|
||||||
@ -245,7 +257,7 @@ void* do_playing(void* _p)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef BOX_NOTIFY
|
#ifdef BOX_NOTIFY
|
||||||
else if (actives[i].box && get_unix_time() >= actives[i].n_timeout)
|
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
|
||||||
{
|
{
|
||||||
GError* ignore;
|
GError* ignore;
|
||||||
notify_notification_close(actives[i].box, &ignore);
|
notify_notification_close(actives[i].box, &ignore);
|
||||||
@ -266,7 +278,7 @@ void* do_playing(void* _p)
|
|||||||
|
|
||||||
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
|
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
|
||||||
if (device_opened && !has_looping &&
|
if (device_opened && !has_looping &&
|
||||||
(get_unix_time() - last_opened_update) > DEVICE_COOLDOWN) {
|
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
|
||||||
m_close_device();
|
m_close_device();
|
||||||
}
|
}
|
||||||
has_looping = false;
|
has_looping = false;
|
||||||
@ -299,11 +311,19 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
|
|||||||
void* do_playing(void* _p)
|
void* do_playing(void* _p)
|
||||||
{
|
{
|
||||||
(void)_p;
|
(void)_p;
|
||||||
int i;
|
|
||||||
while(Control.poll_active) {
|
while(true) {
|
||||||
control_lock();
|
control_lock();
|
||||||
|
|
||||||
|
if (!Control.poll_active) {
|
||||||
|
control_unlock();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||||
if (actives[i].box && get_unix_time() >= actives[i].n_timeout)
|
if (actives[i].box && time(NULL) >= actives[i].n_timeout)
|
||||||
{
|
{
|
||||||
GError* ignore;
|
GError* ignore;
|
||||||
notify_notification_close(actives[i].box, &ignore);
|
notify_notification_close(actives[i].box, &ignore);
|
||||||
@ -361,16 +381,17 @@ int init_notify(int login_cooldown, int notification_timeout)
|
|||||||
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
|
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
Control.poll_active = 1;
|
||||||
pthread_t thread;
|
pthread_t thread;
|
||||||
|
|
||||||
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
|
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) {
|
||||||
pthread_mutex_destroy(Control.poll_mutex);
|
pthread_mutex_destroy(Control.poll_mutex);
|
||||||
|
Control.poll_active = 0;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Control.poll_active = 1;
|
|
||||||
#endif
|
#endif
|
||||||
Control.cooldown = get_unix_time() + login_cooldown;
|
Control.cooldown = time(NULL) + login_cooldown;
|
||||||
|
|
||||||
|
|
||||||
#ifdef BOX_NOTIFY
|
#ifdef BOX_NOTIFY
|
||||||
@ -383,8 +404,15 @@ int init_notify(int login_cooldown, int notification_timeout)
|
|||||||
void terminate_notify()
|
void terminate_notify()
|
||||||
{
|
{
|
||||||
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
|
||||||
if ( !Control.poll_active ) return;
|
control_lock();
|
||||||
|
|
||||||
|
if ( !Control.poll_active ) {
|
||||||
|
control_unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Control.poll_active = 0;
|
Control.poll_active = 0;
|
||||||
|
control_unlock();
|
||||||
|
|
||||||
graceful_clear();
|
graceful_clear();
|
||||||
#endif
|
#endif
|
||||||
@ -503,7 +531,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
|
|||||||
int id = -1;
|
int id = -1;
|
||||||
control_lock();
|
control_lock();
|
||||||
|
|
||||||
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY) && user_settings->alerts == ALERTS_ENABLED)
|
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY))
|
||||||
id = m_play_sound(notif, flags);
|
id = m_play_sound(notif, flags);
|
||||||
else if (flags & NT_ALWAYS)
|
else if (flags & NT_ALWAYS)
|
||||||
id = m_play_sound(notif, flags);
|
id = m_play_sound(notif, flags);
|
||||||
@ -598,9 +626,12 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
|
|||||||
actives[id].id_indicator = id_indicator;
|
actives[id].id_indicator = id_indicator;
|
||||||
if (id_indicator) *id_indicator = id;
|
if (id_indicator) *id_indicator = id;
|
||||||
}
|
}
|
||||||
#endif
|
#else
|
||||||
|
if (id == -1)
|
||||||
|
return -1;
|
||||||
|
#endif /* SOUND_NOTIFY */
|
||||||
|
|
||||||
strncpy(actives[id].title, title, 24);
|
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||||
|
|
||||||
va_list __ARGS__; va_start (__ARGS__, format);
|
va_list __ARGS__; va_start (__ARGS__, format);
|
||||||
@ -623,7 +654,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
|
|||||||
return id;
|
return id;
|
||||||
#else
|
#else
|
||||||
return sound_notify(self, notif, flags, id_indicator);
|
return sound_notify(self, notif, flags, id_indicator);
|
||||||
#endif
|
#endif /* BOX_NOTIFY */
|
||||||
}
|
}
|
||||||
|
|
||||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
|
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
|
||||||
@ -699,7 +730,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
|
|||||||
*id_indicator = id;
|
*id_indicator = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(actives[id].title, title, 24);
|
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
|
||||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||||
|
|
||||||
va_list __ARGS__; va_start (__ARGS__, format);
|
va_list __ARGS__; va_start (__ARGS__, format);
|
||||||
|
54
src/osx_video.h
Normal file
54
src/osx_video.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/* osx_video.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 OSX_VIDEO_H
|
||||||
|
#define OSX_VIDEO_H
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
#endif /* __OBJC__ */
|
||||||
|
|
||||||
|
#define RELEASE_CHK(func, obj) if ((obj))\
|
||||||
|
func((obj));
|
||||||
|
|
||||||
|
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
|
||||||
|
|
||||||
|
#ifdef __OBJC__
|
||||||
|
@interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
|
||||||
|
- (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size;
|
||||||
|
@end
|
||||||
|
#endif /* __OBJC__ */
|
||||||
|
|
||||||
|
int osx_video_init(char **device_names, int *size);
|
||||||
|
void osx_video_release();
|
||||||
|
/* Start device */
|
||||||
|
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
|
||||||
|
/* Stop device */
|
||||||
|
void osx_video_close_device(uint32_t device_idx);
|
||||||
|
/* Read data from device */
|
||||||
|
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* OSX_VIDEO_H */
|
308
src/osx_video.m
Normal file
308
src/osx_video.m
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
/* osx_video.m
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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 __OBJC__
|
||||||
|
#include "osx_video.h"
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <AVFoundation/AVFoundation.h>
|
||||||
|
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper video format functions
|
||||||
|
*/
|
||||||
|
static uint8_t rgb_to_y(int r, int g, int b)
|
||||||
|
{
|
||||||
|
int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
|
||||||
|
return y>255? 255 : y<0 ? 0 : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t rgb_to_u(int r, int g, int b)
|
||||||
|
{
|
||||||
|
int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
|
||||||
|
return u>255? 255 : u<0 ? 0 : u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t rgb_to_v(int r, int g, int b)
|
||||||
|
{
|
||||||
|
int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
|
||||||
|
return v>255? 255 : v<0 ? 0 : v;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
|
||||||
|
{
|
||||||
|
uint16_t x, y;
|
||||||
|
uint8_t *p;
|
||||||
|
uint8_t r, g, b;
|
||||||
|
|
||||||
|
for(y = 0; y != height; y += 2) {
|
||||||
|
p = rgb;
|
||||||
|
for(x = 0; x != width; x++) {
|
||||||
|
b = *rgb++;
|
||||||
|
g = *rgb++;
|
||||||
|
r = *rgb++;
|
||||||
|
rgb++;
|
||||||
|
|
||||||
|
*plane_y++ = rgb_to_y(r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(x = 0; x != width / 2; x++) {
|
||||||
|
b = *rgb++;
|
||||||
|
g = *rgb++;
|
||||||
|
r = *rgb++;
|
||||||
|
rgb++;
|
||||||
|
|
||||||
|
*plane_y++ = rgb_to_y(r, g, b);
|
||||||
|
|
||||||
|
b = *rgb++;
|
||||||
|
g = *rgb++;
|
||||||
|
r = *rgb++;
|
||||||
|
rgb++;
|
||||||
|
|
||||||
|
*plane_y++ = rgb_to_y(r, g, b);
|
||||||
|
|
||||||
|
b = ((int)b + (int)*(rgb - 8) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||||
|
g = ((int)g + (int)*(rgb - 7) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||||
|
r = ((int)r + (int)*(rgb - 6) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
|
||||||
|
p++;
|
||||||
|
|
||||||
|
*plane_u++ = rgb_to_u(r, g, b);
|
||||||
|
*plane_v++ = rgb_to_v(r, g, b);
|
||||||
|
|
||||||
|
p += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of helper video format functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation for OSXVideo
|
||||||
|
*/
|
||||||
|
@implementation OSXVideo {
|
||||||
|
dispatch_queue_t _processingQueue;
|
||||||
|
AVCaptureSession *_session;
|
||||||
|
AVCaptureVideoDataOutput *_linkerVideo;
|
||||||
|
|
||||||
|
CVImageBufferRef _currentFrame;
|
||||||
|
pthread_mutex_t _frameLock;
|
||||||
|
BOOL _shouldMangleDimensions;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
|
||||||
|
_session = [[AVCaptureSession alloc] init];
|
||||||
|
|
||||||
|
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < [devices count]; ++i) {
|
||||||
|
AVCaptureDevice *device = [devices objectAtIndex:i];
|
||||||
|
char *video_input_name;
|
||||||
|
NSString *localizedName = [device localizedName];
|
||||||
|
video_input_name = (char*)malloc(strlen([localizedName cStringUsingEncoding:NSUTF8StringEncoding]) + 1);
|
||||||
|
strcpy(video_input_name, (char*)[localizedName cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||||
|
device_names[i] = video_input_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( i <= 0 )
|
||||||
|
return nil;
|
||||||
|
*size = i;
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
pthread_mutex_destroy(&_frameLock);
|
||||||
|
[_session release];
|
||||||
|
[_linkerVideo release];
|
||||||
|
dispatch_release(_processingQueue);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
|
||||||
|
pthread_mutex_init(&_frameLock, NULL);
|
||||||
|
pthread_mutex_lock(&_frameLock);
|
||||||
|
_processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
|
||||||
|
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||||
|
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
|
||||||
|
NSError *error = NULL;
|
||||||
|
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
|
||||||
|
|
||||||
|
if ( error != NULL ) {
|
||||||
|
[input release];
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_session beginConfiguration];
|
||||||
|
[_session addInput:input];
|
||||||
|
//_session.sessionPreset = AVCaptureSessionPreset640x480;
|
||||||
|
//*width = 640;
|
||||||
|
//*height = 480;
|
||||||
|
_shouldMangleDimensions = YES;
|
||||||
|
[_session commitConfiguration];
|
||||||
|
[input release];
|
||||||
|
[device release];
|
||||||
|
|
||||||
|
/* Obtain device resolution */
|
||||||
|
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
|
||||||
|
CMFormatDescriptionRef format_description = port.formatDescription;
|
||||||
|
if ( format_description ) {
|
||||||
|
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
|
||||||
|
*width = dimensions.width;
|
||||||
|
*height = dimensions.height;
|
||||||
|
} else {
|
||||||
|
*width = 0;
|
||||||
|
*height = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
|
||||||
|
[_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
|
||||||
|
// TODO possibly get a better pixel format
|
||||||
|
if (_shouldMangleDimensions) {
|
||||||
|
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
|
||||||
|
(id)kCVPixelBufferWidthKey: @640,
|
||||||
|
(id)kCVPixelBufferHeightKey: @480}];
|
||||||
|
} else {
|
||||||
|
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
|
||||||
|
}
|
||||||
|
[_session addOutput:_linkerVideo];
|
||||||
|
[_session startRunning];
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&_frameLock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)closeVideoDeviceIndex: (uint32_t)device_idx {
|
||||||
|
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
|
||||||
|
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
|
||||||
|
NSError *error = NULL;
|
||||||
|
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
|
||||||
|
[_session stopRunning];
|
||||||
|
[_session removeOutput:_linkerVideo];
|
||||||
|
[_session removeInput:input];
|
||||||
|
[_linkerVideo release];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
|
||||||
|
pthread_mutex_lock(&_frameLock);
|
||||||
|
CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
|
||||||
|
if (!img) {
|
||||||
|
NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
|
||||||
|
} else {
|
||||||
|
CVPixelBufferUnlockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
|
||||||
|
RELEASE_CHK(CFRelease, _currentFrame);
|
||||||
|
|
||||||
|
_currentFrame = (CVImageBufferRef)CFRetain(img);
|
||||||
|
// we're not going to do anything to it, so it's safe to lock it always
|
||||||
|
CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&_frameLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (int)getVideoFrameY: (uint8_t *)y U: (uint8_t *)u V: (uint8_t *)v Width: (uint16_t *)width Height: (uint16_t *)height {
|
||||||
|
if (!_currentFrame) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&_frameLock);
|
||||||
|
CFRetain(_currentFrame);
|
||||||
|
|
||||||
|
CFTypeID imageType = CFGetTypeID(_currentFrame);
|
||||||
|
if (imageType == CVPixelBufferGetTypeID()) {
|
||||||
|
// TODO maybe handle other formats
|
||||||
|
bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
|
||||||
|
} else if (imageType == CVOpenGLBufferGetTypeID()) {
|
||||||
|
// OpenGL pbuffer
|
||||||
|
} else if (imageType == CVOpenGLTextureGetTypeID()) {
|
||||||
|
// OpenGL Texture (Do we need to handle these?)
|
||||||
|
}
|
||||||
|
|
||||||
|
CVPixelBufferRelease(_currentFrame);
|
||||||
|
pthread_mutex_unlock(&_frameLock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
/*
|
||||||
|
* End of implementation for OSXVideo
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* C-interface for OSXVideo
|
||||||
|
*/
|
||||||
|
static OSXVideo* _OSXVideo = nil;
|
||||||
|
|
||||||
|
int osx_video_init(char **device_names, int *size)
|
||||||
|
{
|
||||||
|
_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
|
||||||
|
|
||||||
|
if ( _OSXVideo == nil )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void osx_video_release()
|
||||||
|
{
|
||||||
|
[_OSXVideo release];
|
||||||
|
_OSXVideo = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
|
||||||
|
{
|
||||||
|
if ( _OSXVideo == nil )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
|
||||||
|
}
|
||||||
|
|
||||||
|
void osx_video_close_device(uint32_t device_idx)
|
||||||
|
{
|
||||||
|
[_OSXVideo closeVideoDeviceIndex: device_idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
|
||||||
|
{
|
||||||
|
if ( _OSXVideo == nil )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of C-interface for OSXVideo
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endif /* __OBJC__ */
|
83
src/prompt.c
83
src/prompt.c
@ -31,6 +31,7 @@
|
|||||||
#include "toxic.h"
|
#include "toxic.h"
|
||||||
#include "windows.h"
|
#include "windows.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
|
#include "friendlist.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
@ -46,13 +47,15 @@ extern ToxWindow *prompt;
|
|||||||
extern struct user_settings *user_settings;
|
extern struct user_settings *user_settings;
|
||||||
extern struct Winthread Winthread;
|
extern struct Winthread Winthread;
|
||||||
|
|
||||||
|
extern FriendsList Friends;
|
||||||
FriendRequests FrndRequests;
|
FriendRequests FrndRequests;
|
||||||
|
#ifdef VIDEO
|
||||||
#ifdef AUDIO
|
#define AC_NUM_GLOB_COMMANDS 22
|
||||||
#define AC_NUM_GLOB_COMMANDS 18
|
#elif AUDIO
|
||||||
|
#define AC_NUM_GLOB_COMMANDS 20
|
||||||
#else
|
#else
|
||||||
#define AC_NUM_GLOB_COMMANDS 16
|
#define AC_NUM_GLOB_COMMANDS 18
|
||||||
#endif /* AUDIO */
|
#endif
|
||||||
|
|
||||||
/* Array of global command names used for tab completion. */
|
/* Array of global command names used for tab completion. */
|
||||||
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
@ -67,8 +70,10 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/help" },
|
{ "/help" },
|
||||||
{ "/log" },
|
{ "/log" },
|
||||||
{ "/myid" },
|
{ "/myid" },
|
||||||
|
{ "/myqr" },
|
||||||
{ "/nick" },
|
{ "/nick" },
|
||||||
{ "/note" },
|
{ "/note" },
|
||||||
|
{ "/nospam" },
|
||||||
{ "/quit" },
|
{ "/quit" },
|
||||||
{ "/requests" },
|
{ "/requests" },
|
||||||
{ "/status" },
|
{ "/status" },
|
||||||
@ -79,6 +84,14 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/sdev" },
|
{ "/sdev" },
|
||||||
|
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
|
||||||
|
{ "/lsvdev" },
|
||||||
|
{ "/svdev" },
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void kill_prompt_window(ToxWindow *self)
|
void kill_prompt_window(ToxWindow *self)
|
||||||
@ -173,7 +186,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
if (x2 <= 0)
|
if (x2 <= 0 || y2 <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* ignore non-menu related input if active */
|
/* ignore non-menu related input if active */
|
||||||
@ -210,7 +223,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
|||||||
|
|
||||||
if (diff != -1) {
|
if (diff != -1) {
|
||||||
if (x + diff > x2 - 1) {
|
if (x + diff > x2 - 1) {
|
||||||
int wlen = wcswidth(ctx->line, sizeof(ctx->line));
|
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -244,9 +257,15 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
line_info_print(self);
|
line_info_print(self);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wclear(ctx->linewin);
|
wclear(ctx->linewin);
|
||||||
|
|
||||||
curs_set(1);
|
curs_set(1);
|
||||||
@ -255,14 +274,23 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||||
|
|
||||||
StatusBar *statusbar = self->stb;
|
StatusBar *statusbar = self->stb;
|
||||||
|
|
||||||
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->connection != TOX_CONNECTION_NONE) {
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
TOX_CONNECTION connection = statusbar->connection;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
if (connection != TOX_CONNECTION_NONE) {
|
||||||
int colour = MAGENTA;
|
int colour = MAGENTA;
|
||||||
const char *status_text = "ERROR";
|
const char *status_text = "ERROR";
|
||||||
|
|
||||||
switch (statusbar->status) {
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
TOX_USER_STATUS status = statusbar->status;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
case TOX_USER_STATUS_NONE:
|
case TOX_USER_STATUS_NONE:
|
||||||
status_text = "Online";
|
status_text = "Online";
|
||||||
colour = GREEN;
|
colour = GREEN;
|
||||||
@ -282,12 +310,16 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||||
|
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wattron(statusbar->topline, A_BOLD);
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
} else {
|
} else {
|
||||||
wprintw(statusbar->topline, " [Offline]");
|
wprintw(statusbar->topline, " [Offline]");
|
||||||
wattron(statusbar->topline, A_BOLD);
|
wattron(statusbar->topline, A_BOLD);
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
wattroff(statusbar->topline, A_BOLD);
|
wattroff(statusbar->topline, A_BOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,11 +328,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||||
|
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
tox_self_get_status_message(m, (uint8_t *) statusmsg);
|
size_t slen = tox_self_get_status_message_size(m);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
tox_self_get_status_message (m, (uint8_t*) statusmsg);
|
||||||
|
statusmsg[slen] = '\0';
|
||||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
self->x = x2;
|
self->x = x2;
|
||||||
@ -308,6 +341,8 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
/* Truncate note if it doesn't fit in statusbar */
|
/* Truncate note if it doesn't fit in statusbar */
|
||||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
|
||||||
if (statusbar->statusmsg_len > maxlen) {
|
if (statusbar->statusmsg_len > maxlen) {
|
||||||
statusbar->statusmsg[maxlen - 3] = '\0';
|
statusbar->statusmsg[maxlen - 3] = '\0';
|
||||||
strcat(statusbar->statusmsg, "...");
|
strcat(statusbar->statusmsg, "...");
|
||||||
@ -317,13 +352,15 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
if (statusbar->statusmsg[0])
|
if (statusbar->statusmsg[0])
|
||||||
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||||
|
|
||||||
int y, x;
|
int y, x;
|
||||||
getyx(self->window, y, x);
|
getyx(self->window, y, x);
|
||||||
(void) x;
|
(void) x;
|
||||||
|
|
||||||
int new_x = ctx->start ? x2 - 1 : wcswidth(ctx->line, ctx->pos);
|
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||||
wmove(self->window, y + 1, new_x);
|
wmove(self->window, y + 1, new_x);
|
||||||
|
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
@ -334,9 +371,6 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
||||||
{
|
{
|
||||||
if (friendnum < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
|
||||||
@ -349,7 +383,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
|
|||||||
get_time_str(timefrmt, sizeof(timefrmt));
|
get_time_str(timefrmt, sizeof(timefrmt));
|
||||||
const char *msg;
|
const char *msg;
|
||||||
|
|
||||||
if (connection_status != TOX_CONNECTION_NONE) {
|
if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
|
||||||
msg = "has come online";
|
msg = "has come online";
|
||||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||||
write_to_log(msg, nick, ctx->log, true);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
@ -360,7 +394,8 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
|
|||||||
else
|
else
|
||||||
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
|
||||||
"Toxic", "%s has come online", nick );
|
"Toxic", "%s has come online", nick );
|
||||||
} else {
|
}
|
||||||
|
else if (connection_status == TOX_CONNECTION_NONE) {
|
||||||
msg = "has gone offline";
|
msg = "has gone offline";
|
||||||
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||||
write_to_log(msg, nick, ctx->log, true);
|
write_to_log(msg, nick, ctx->log, true);
|
||||||
@ -400,6 +435,10 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
|||||||
{
|
{
|
||||||
int x2, y2;
|
int x2, y2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
|
||||||
|
|
||||||
(void) y2;
|
(void) y2;
|
||||||
|
|
||||||
/* Init statusbar info */
|
/* Init statusbar info */
|
||||||
@ -457,6 +496,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
|||||||
int y2, x2;
|
int y2, x2;
|
||||||
getmaxyx(self->window, y2, x2);
|
getmaxyx(self->window, y2, x2);
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES);
|
||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
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->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||||
@ -472,7 +514,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
|||||||
if (user_settings->autolog == AUTOLOG_ON) {
|
if (user_settings->autolog == AUTOLOG_ON) {
|
||||||
char myid[TOX_ADDRESS_SIZE];
|
char myid[TOX_ADDRESS_SIZE];
|
||||||
tox_self_get_address(m, (uint8_t *) myid);
|
tox_self_get_address(m, (uint8_t *) myid);
|
||||||
log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT);
|
|
||||||
|
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||||
}
|
}
|
||||||
|
|
||||||
scrollok(ctx->history, 0);
|
scrollok(ctx->history, 0);
|
||||||
@ -487,6 +531,7 @@ ToxWindow new_prompt(void)
|
|||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
memset(&ret, 0, sizeof(ret));
|
memset(&ret, 0, sizeof(ret));
|
||||||
|
|
||||||
|
ret.num = -1;
|
||||||
ret.active = true;
|
ret.active = true;
|
||||||
ret.is_prompt = true;
|
ret.is_prompt = true;
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ typedef struct {
|
|||||||
} FriendRequests;
|
} FriendRequests;
|
||||||
|
|
||||||
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, const char *nick);
|
void prompt_update_nick(ToxWindow *prompt, const char *nick);
|
||||||
|
89
src/qr_code.c
Normal file
89
src/qr_code.c
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* qr_obj_code.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <qrencode.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "toxic.h"
|
||||||
|
#include "windows.h"
|
||||||
|
#include "qr_code.h"
|
||||||
|
|
||||||
|
#define BORDER_LEN 1
|
||||||
|
#define CHAR_1 "\342\226\210"
|
||||||
|
#define CHAR_2 "\342\226\204"
|
||||||
|
#define CHAR_3 "\342\226\200"
|
||||||
|
|
||||||
|
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
|
||||||
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int ID_to_QRcode(const char *tox_id, FILE *fp)
|
||||||
|
{
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
|
||||||
|
|
||||||
|
if (qr_obj == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t width = qr_obj->width;
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < width + BORDER_LEN * 2; ++i)
|
||||||
|
fprintf(fp, "%s", CHAR_1);
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
|
||||||
|
for (i = 0; i < width; i += 2) {
|
||||||
|
for (j = 0; j < BORDER_LEN; ++j)
|
||||||
|
fprintf(fp, "%s", CHAR_1);
|
||||||
|
|
||||||
|
const unsigned char *row_1 = qr_obj->data + width * i;
|
||||||
|
const unsigned char *row_2 = row_1 + width;
|
||||||
|
|
||||||
|
for (j = 0; j < width; ++j) {
|
||||||
|
bool x = row_1[j] & 1;
|
||||||
|
bool y = (i + 1) < width ? (row_2[j] & 1) : false;
|
||||||
|
|
||||||
|
if (x && y)
|
||||||
|
fprintf(fp, " ");
|
||||||
|
else if (x)
|
||||||
|
fprintf(fp, "%s", CHAR_2);
|
||||||
|
else if (y)
|
||||||
|
fprintf(fp, "%s", CHAR_3);
|
||||||
|
else
|
||||||
|
fprintf(fp, "%s", CHAR_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < BORDER_LEN; ++j)
|
||||||
|
fprintf(fp, "%s", CHAR_1);
|
||||||
|
|
||||||
|
fprintf(fp, "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
QRcode_free(qr_obj);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
/* dns.c
|
/* qr_code.h
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
* Copyright (C) 2015 Toxic All Rights Reserved.
|
||||||
*
|
*
|
||||||
* This file is part of Toxic.
|
* This file is part of Toxic.
|
||||||
*
|
*
|
||||||
@ -20,13 +20,16 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin.
|
#ifndef QR_CODE
|
||||||
Return 0 on success, -1 on failure. */
|
#define QR_CODE
|
||||||
|
|
||||||
#ifndef DNS_H
|
#define QRCODE_FILENAME_EXT ".QRcode"
|
||||||
#define DNS_H
|
|
||||||
|
|
||||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
|
||||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg);
|
*
|
||||||
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
int ID_to_QRcode(const char *tox_id, FILE *fp);
|
||||||
|
|
||||||
#endif /* #define DNS_H */
|
#endif /* QR_CODE */
|
@ -32,7 +32,7 @@
|
|||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -167,6 +167,10 @@ static int detect_gnu_screen ()
|
|||||||
|
|
||||||
free (dyn_buffer);
|
free (dyn_buffer);
|
||||||
dyn_buffer = NULL;
|
dyn_buffer = NULL;
|
||||||
|
|
||||||
|
if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data))
|
||||||
|
goto nomplex;
|
||||||
|
|
||||||
strcpy (mplex_data, socket_path);
|
strcpy (mplex_data, socket_path);
|
||||||
strcat (mplex_data, PATH_SEP_S);
|
strcat (mplex_data, PATH_SEP_S);
|
||||||
strcat (mplex_data, socket_name);
|
strcat (mplex_data, socket_name);
|
||||||
@ -181,6 +185,8 @@ nomplex:
|
|||||||
pclose (session_info_stream);
|
pclose (session_info_stream);
|
||||||
if (dyn_buffer)
|
if (dyn_buffer)
|
||||||
free (dyn_buffer);
|
free (dyn_buffer);
|
||||||
|
if (socket_path)
|
||||||
|
free(socket_path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +202,7 @@ static int detect_tmux ()
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* store the session number string for later use */
|
/* store the session number string for later use */
|
||||||
strcpy (mplex_data, pos + 1);
|
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1);
|
||||||
mplex = MPLEX_TMUX;
|
mplex = MPLEX_TMUX;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -339,7 +345,9 @@ static void mplex_timer_handler (Tox *m)
|
|||||||
prev_status = current_status;
|
prev_status = current_status;
|
||||||
new_status = TOX_USER_STATUS_AWAY;
|
new_status = TOX_USER_STATUS_AWAY;
|
||||||
pthread_mutex_lock (&Winthread.lock);
|
pthread_mutex_lock (&Winthread.lock);
|
||||||
|
size_t slen = tox_self_get_status_message_size(m);
|
||||||
tox_self_get_status_message (m, (uint8_t*) prev_note);
|
tox_self_get_status_message (m, (uint8_t*) prev_note);
|
||||||
|
prev_note[slen] = '\0';
|
||||||
pthread_mutex_unlock (&Winthread.lock);
|
pthread_mutex_unlock (&Winthread.lock);
|
||||||
new_note = user_settings->mplex_away_note;
|
new_note = user_settings->mplex_away_note;
|
||||||
}
|
}
|
||||||
|
499
src/toxic.c
499
src/toxic.c
@ -54,10 +54,11 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
#include "device.h"
|
#include "audio_device.h"
|
||||||
#include "message_queue.h"
|
#include "message_queue.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
#include "term_mplex.h"
|
#include "term_mplex.h"
|
||||||
|
#include "name_lookup.h"
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
#include "xtra.h"
|
#include "xtra.h"
|
||||||
@ -65,7 +66,10 @@
|
|||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
#include "audio_call.h"
|
#include "audio_call.h"
|
||||||
ToxAv *av;
|
#ifdef VIDEO
|
||||||
|
#include "video_call.h"
|
||||||
|
#endif /* VIDEO */
|
||||||
|
ToxAV *av;
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
#ifndef PACKAGE_DATADIR
|
#ifndef PACKAGE_DATADIR
|
||||||
@ -77,13 +81,16 @@ char *DATA_FILE = NULL;
|
|||||||
char *BLOCK_FILE = NULL;
|
char *BLOCK_FILE = NULL;
|
||||||
ToxWindow *prompt = NULL;
|
ToxWindow *prompt = NULL;
|
||||||
|
|
||||||
#define AUTOSAVE_FREQ 60
|
#define DATANAME "toxic_profile.tox"
|
||||||
|
#define BLOCKNAME "toxic_blocklist"
|
||||||
|
|
||||||
|
#define AUTOSAVE_FREQ 600
|
||||||
#define MIN_PASSWORD_LEN 6
|
#define MIN_PASSWORD_LEN 6
|
||||||
#define MAX_PASSWORD_LEN 64
|
#define MAX_PASSWORD_LEN 64
|
||||||
|
|
||||||
struct Winthread Winthread;
|
struct Winthread Winthread;
|
||||||
struct cqueue_thread cqueue_thread;
|
struct cqueue_thread cqueue_thread;
|
||||||
struct audio_thread audio_thread;
|
struct av_thread av_thread;
|
||||||
struct arg_opts arg_opts;
|
struct arg_opts arg_opts;
|
||||||
struct user_settings *user_settings = NULL;
|
struct user_settings *user_settings = NULL;
|
||||||
|
|
||||||
@ -119,23 +126,43 @@ static void init_signal_catchers(void)
|
|||||||
signal(SIGSEGV, catch_SIGSEGV);
|
signal(SIGSEGV, catch_SIGSEGV);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void free_global_data(void)
|
||||||
|
{
|
||||||
|
if (DATA_FILE) {
|
||||||
|
free(DATA_FILE);
|
||||||
|
DATA_FILE = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BLOCK_FILE) {
|
||||||
|
free(BLOCK_FILE);
|
||||||
|
BLOCK_FILE = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user_settings) {
|
||||||
|
free(user_settings);
|
||||||
|
user_settings = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void exit_toxic_success(Tox *m)
|
void exit_toxic_success(Tox *m)
|
||||||
{
|
{
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
memset(&user_password, 0, sizeof(struct user_password));
|
memset(&user_password, 0, sizeof(struct user_password));
|
||||||
|
kill_all_file_transfers(m);
|
||||||
kill_all_windows(m);
|
kill_all_windows(m);
|
||||||
terminate_notify();
|
terminate_notify();
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
#ifdef VIDEO
|
||||||
|
terminate_video();
|
||||||
|
#endif /* VIDEO */
|
||||||
terminate_audio();
|
terminate_audio();
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
free(DATA_FILE);
|
free_global_data();
|
||||||
free(BLOCK_FILE);
|
|
||||||
free(user_settings);
|
|
||||||
|
|
||||||
tox_kill(m);
|
tox_kill(m);
|
||||||
endwin();
|
endwin();
|
||||||
|
name_lookup_cleanup();
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
/* We have to terminate xtra last coz reasons
|
/* We have to terminate xtra last coz reasons
|
||||||
@ -149,6 +176,7 @@ void exit_toxic_success(Tox *m)
|
|||||||
|
|
||||||
void exit_toxic_err(const char *errmsg, int errcode)
|
void exit_toxic_err(const char *errmsg, int errcode)
|
||||||
{
|
{
|
||||||
|
free_global_data();
|
||||||
freopen("/dev/tty", "w", stderr);
|
freopen("/dev/tty", "w", stderr);
|
||||||
endwin();
|
endwin();
|
||||||
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
|
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
|
||||||
@ -250,7 +278,7 @@ static void print_init_messages(ToxWindow *toxwin)
|
|||||||
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
|
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.chat = 8) */
|
||||||
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||||
#define MAXNODES 50
|
#define MAXNODES 50
|
||||||
#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7)
|
#define NODELEN (MAX_NODE_LINE - TOX_PUBLIC_KEY_SIZE - 7)
|
||||||
@ -275,22 +303,39 @@ static int load_nodelist(const char *filename)
|
|||||||
char line[MAX_NODE_LINE];
|
char line[MAX_NODE_LINE];
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
||||||
if (strlen(line) > MIN_NODE_LINE) {
|
size_t line_len = strlen(line);
|
||||||
|
|
||||||
|
if (line_len >= MIN_NODE_LINE && line_len <= MAX_NODE_LINE) {
|
||||||
const char *name = strtok(line, " ");
|
const char *name = strtok(line, " ");
|
||||||
const char *port = strtok(NULL, " ");
|
const char *port_str = strtok(NULL, " ");
|
||||||
const char *key_ascii = strtok(NULL, " ");
|
const char *key_ascii = strtok(NULL, " ");
|
||||||
|
|
||||||
/* invalid line */
|
if (name == NULL || port_str == NULL || key_ascii == NULL)
|
||||||
if (name == NULL || port == NULL || key_ascii == NULL)
|
continue;
|
||||||
|
|
||||||
|
long int port = strtol(port_str, NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
size_t key_len = strlen(key_ascii);
|
||||||
|
size_t name_len = strlen(name);
|
||||||
|
|
||||||
|
if (key_len < TOX_PUBLIC_KEY_SIZE * 2 || name_len >= NODELEN)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
||||||
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
||||||
toxNodes.ports[toxNodes.lines] = atoi(port);
|
toxNodes.ports[toxNodes.lines] = port;
|
||||||
|
|
||||||
char *key_binary = hex_string_to_bin(key_ascii);
|
/* remove possible trailing newline from key string */
|
||||||
memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_PUBLIC_KEY_SIZE);
|
char real_ascii_key[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||||
free(key_binary);
|
memcpy(real_ascii_key, key_ascii, TOX_PUBLIC_KEY_SIZE * 2);
|
||||||
|
key_len = TOX_PUBLIC_KEY_SIZE * 2;
|
||||||
|
real_ascii_key[key_len] = '\0';
|
||||||
|
|
||||||
|
if (hex_string_to_bin(real_ascii_key, key_len, toxNodes.keys[toxNodes.lines], TOX_PUBLIC_KEY_SIZE) == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
toxNodes.lines++;
|
toxNodes.lines++;
|
||||||
}
|
}
|
||||||
@ -304,14 +349,34 @@ static int load_nodelist(const char *filename)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Bootstraps and adds as TCP relay.
|
||||||
|
* Returns 0 if both actions are successful.
|
||||||
|
* Returns -1 otherwise.
|
||||||
|
*/
|
||||||
int init_connection_helper(Tox *m, int line)
|
int init_connection_helper(Tox *m, int line)
|
||||||
{
|
{
|
||||||
return tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], NULL);
|
TOX_ERR_BOOTSTRAP err;
|
||||||
|
tox_bootstrap(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to bootstrap %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_add_tcp_relay(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line], &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
fprintf(stderr, "Failed to add TCP relay %s:%d\n", toxNodes.nodes[line], toxNodes.ports[line]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connects to a random DHT node listed in the DHTnodes file
|
/* Connects to a random DHT node listed in the DHTnodes file
|
||||||
*
|
*
|
||||||
* return codes:
|
* return codes:
|
||||||
|
* 0: success
|
||||||
* 1: failed to open node file
|
* 1: failed to open node file
|
||||||
* 2: no line of sufficient length in node file
|
* 2: no line of sufficient length in node file
|
||||||
* 3: failed to resolve name to IP
|
* 3: failed to resolve name to IP
|
||||||
@ -323,8 +388,10 @@ static bool srvlist_loaded = false;
|
|||||||
|
|
||||||
int init_connection(Tox *m)
|
int init_connection(Tox *m)
|
||||||
{
|
{
|
||||||
if (toxNodes.lines > 0) /* already loaded nodelist */
|
if (toxNodes.lines > 0) { /* already loaded nodelist */
|
||||||
return init_connection_helper(m, rand() % toxNodes.lines) ? 0 : 3;
|
init_connection_helper(m, rand() % toxNodes.lines);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* only once:
|
/* only once:
|
||||||
* - load the nodelist
|
* - load the nodelist
|
||||||
@ -347,7 +414,7 @@ int init_connection(Tox *m)
|
|||||||
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
|
int n = MIN(NUM_INIT_NODES, toxNodes.lines);
|
||||||
|
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
if (init_connection_helper(m, rand() % toxNodes.lines))
|
if (init_connection_helper(m, rand() % toxNodes.lines) == 0)
|
||||||
res = 0;
|
res = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +527,7 @@ static void first_time_encrypt(const char *msg)
|
|||||||
valid_password = true;
|
valid_password = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
queue_init_message("Data file '%s' will be encrypted", DATA_FILE);
|
queue_init_message("Data file '%s' is encrypted", DATA_FILE);
|
||||||
memset(passconfirm, 0, sizeof(passconfirm));
|
memset(passconfirm, 0, sizeof(passconfirm));
|
||||||
user_password.data_is_encrypted = true;
|
user_password.data_is_encrypted = true;
|
||||||
}
|
}
|
||||||
@ -468,45 +535,62 @@ static void first_time_encrypt(const char *msg)
|
|||||||
system("clear");
|
system("clear");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Store Tox profile data to path.
|
||||||
* Store Messenger to given location
|
*
|
||||||
* Return 0 if stored successfully or ignoring data file. Return -1 on error
|
* Return 0 if stored successfully.
|
||||||
|
* Return -1 on error.
|
||||||
*/
|
*/
|
||||||
|
#define TEMP_PROFILE_EXT ".tmp"
|
||||||
int store_data(Tox *m, const char *path)
|
int store_data(Tox *m, const char *path)
|
||||||
{
|
{
|
||||||
if (path == NULL)
|
if (path == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
size_t len = user_password.data_is_encrypted ? tox_encrypted_size(m) : tox_get_savedata_size(m);
|
char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1];
|
||||||
char *buf = malloc(len);
|
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT);
|
||||||
|
|
||||||
if (buf == NULL)
|
FILE *fp = fopen(temp_path, "wb");
|
||||||
exit_toxic_err("failed in store_data", FATALERR_MEMORY);
|
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
size_t data_len = tox_get_savedata_size(m);
|
||||||
|
char data[data_len];
|
||||||
|
|
||||||
|
tox_get_savedata(m, (uint8_t *) data);
|
||||||
|
|
||||||
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) {
|
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data) {
|
||||||
if (tox_encrypted_save(m, (uint8_t *) buf, (uint8_t *) user_password.pass, user_password.len) != 0) {
|
size_t enc_len = data_len + TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||||
free(buf);
|
char enc_data[enc_len];
|
||||||
|
|
||||||
|
TOX_ERR_ENCRYPTION err;
|
||||||
|
tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len,
|
||||||
|
(uint8_t *) enc_data, &err);
|
||||||
|
|
||||||
|
if (err != TOX_ERR_ENCRYPTION_OK) {
|
||||||
|
fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err);
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(enc_data, enc_len, 1, fp) != 1) {
|
||||||
|
fprintf(stderr, "Failed to write profile data.\n");
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
} else { /* data will not be encrypted */
|
||||||
|
if (fwrite(data, data_len, 1, fp) != 1) {
|
||||||
|
fprintf(stderr, "Failed to write profile data.\n");
|
||||||
|
fclose(fp);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
tox_get_savedata(m, (uint8_t *) buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *fp = fopen(path, "wb");
|
|
||||||
|
|
||||||
if (fp == NULL) {
|
|
||||||
free(buf);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fwrite(buf, len, 1, fp) != 1) {
|
|
||||||
free(buf);
|
|
||||||
fclose(fp);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buf);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
|
if (rename(temp_path, path) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,13 +618,19 @@ static void init_tox_callbacks(Tox *m)
|
|||||||
|
|
||||||
static void init_tox_options(struct Tox_Options *tox_opts)
|
static void init_tox_options(struct Tox_Options *tox_opts)
|
||||||
{
|
{
|
||||||
|
tox_options_default(tox_opts);
|
||||||
|
|
||||||
tox_opts->ipv6_enabled = !arg_opts.use_ipv4;
|
tox_opts->ipv6_enabled = !arg_opts.use_ipv4;
|
||||||
tox_opts->udp_enabled = !arg_opts.force_tcp;
|
tox_opts->udp_enabled = !arg_opts.force_tcp;
|
||||||
tox_opts->proxy_type = arg_opts.proxy_type;
|
tox_opts->proxy_type = arg_opts.proxy_type;
|
||||||
|
tox_opts->tcp_port = arg_opts.tcp_port;
|
||||||
|
|
||||||
if (!tox_opts->ipv6_enabled)
|
if (!tox_opts->ipv6_enabled)
|
||||||
queue_init_message("Forcing IPv4 connection");
|
queue_init_message("Forcing IPv4 connection");
|
||||||
|
|
||||||
|
if (tox_opts->tcp_port)
|
||||||
|
queue_init_message("TCP relaying enabled on port %d", tox_opts->tcp_port);
|
||||||
|
|
||||||
if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) {
|
if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) {
|
||||||
tox_opts->proxy_port = arg_opts.proxy_port;
|
tox_opts->proxy_port = arg_opts.proxy_port;
|
||||||
tox_opts->proxy_host = arg_opts.proxy_address;
|
tox_opts->proxy_host = arg_opts.proxy_address;
|
||||||
@ -564,13 +654,13 @@ static void init_tox_options(struct Tox_Options *tox_opts)
|
|||||||
/* Returns a new Tox object on success.
|
/* Returns a new Tox object on success.
|
||||||
* If object fails to initialize the toxic process will terminate.
|
* If object fails to initialize the toxic process will terminate.
|
||||||
*/
|
*/
|
||||||
static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts)
|
static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW *new_err)
|
||||||
{
|
{
|
||||||
Tox *m = NULL;
|
Tox *m = NULL;
|
||||||
|
|
||||||
FILE *fp = fopen(data_path, "rb");
|
FILE *fp = fopen(data_path, "rb");
|
||||||
|
|
||||||
if (fp != NULL) {
|
if (fp != NULL) { /* Data file exists */
|
||||||
off_t len = file_size(data_path);
|
off_t len = file_size(data_path);
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
@ -578,24 +668,20 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts)
|
|||||||
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *buf = malloc(len);
|
char data[len];
|
||||||
|
|
||||||
if (buf == NULL) {
|
if (fread(data, sizeof(data), 1, fp) != 1) {
|
||||||
fclose(fp);
|
|
||||||
exit_toxic_err("failed in load_toxic", FATALERR_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fread(buf, len, 1, fp) != 1) {
|
|
||||||
free(buf);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_encrypted = tox_is_data_encrypted((uint8_t *) buf);
|
bool is_encrypted = tox_is_data_encrypted((uint8_t *) data);
|
||||||
|
|
||||||
/* attempt to encrypt an already encrypted data file */
|
/* attempt to encrypt an already encrypted data file */
|
||||||
if (arg_opts.encrypt_data && is_encrypted)
|
if (arg_opts.encrypt_data && is_encrypted) {
|
||||||
|
fclose(fp);
|
||||||
exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT);
|
exit_toxic_err("failed in load_toxic", FATALERR_ENCRYPT);
|
||||||
|
}
|
||||||
|
|
||||||
if (arg_opts.unencrypt_data && is_encrypted)
|
if (arg_opts.unencrypt_data && is_encrypted)
|
||||||
queue_init_message("Data file '%s' has been unencrypted", data_path);
|
queue_init_message("Data file '%s' has been unencrypted", data_path);
|
||||||
@ -610,12 +696,17 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts)
|
|||||||
system("clear"); // TODO: is this portable?
|
system("clear"); // TODO: is this portable?
|
||||||
printf("Enter password (q to quit) ");
|
printf("Enter password (q to quit) ");
|
||||||
|
|
||||||
|
size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||||
|
char plain[plain_len];
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
|
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
|
||||||
user_password.len = pwlen;
|
user_password.len = pwlen;
|
||||||
|
|
||||||
if (strcasecmp(user_password.pass, "q") == 0)
|
if (strcasecmp(user_password.pass, "q") == 0) {
|
||||||
|
fclose(fp);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
if (pwlen < MIN_PASSWORD_LEN) {
|
if (pwlen < MIN_PASSWORD_LEN) {
|
||||||
system("clear");
|
system("clear");
|
||||||
@ -624,39 +715,56 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
TOX_ERR_ENCRYPTED_NEW enc_err;
|
TOX_ERR_DECRYPTION pwerr;
|
||||||
m = tox_encrypted_new(tox_opts, (uint8_t *) buf, len, (uint8_t *) user_password.pass, pwlen, &enc_err);
|
tox_pass_decrypt((uint8_t *) data, len, (uint8_t *) user_password.pass, pwlen,
|
||||||
|
(uint8_t *) plain, &pwerr);
|
||||||
|
|
||||||
|
if (pwerr == TOX_ERR_DECRYPTION_OK) {
|
||||||
|
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
|
||||||
|
tox_opts->savedata_data = (uint8_t *) plain;
|
||||||
|
tox_opts->savedata_length = plain_len;
|
||||||
|
|
||||||
|
m = tox_new(tox_opts, new_err);
|
||||||
|
|
||||||
|
if (m == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (enc_err == TOX_ERR_ENCRYPTED_NEW_OK) {
|
|
||||||
break;
|
break;
|
||||||
} else if (enc_err == TOX_ERR_ENCRYPTED_NEW_LOAD_DECRYPTION_FAILED) {
|
} else if (pwerr == TOX_ERR_DECRYPTION_FAILED) {
|
||||||
system("clear");
|
system("clear");
|
||||||
sleep(1);
|
sleep(1);
|
||||||
printf("Invalid password. Try again. ");
|
printf("Invalid password. Try again. ");
|
||||||
} else {
|
} else {
|
||||||
exit_toxic_err("tox_encrypted_new() failed", enc_err);
|
fclose(fp);
|
||||||
|
exit_toxic_err("tox_pass_decrypt() failed", pwerr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else { /* data is not encrypted */
|
||||||
TOX_ERR_NEW err;
|
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
|
||||||
m = tox_new(tox_opts, (uint8_t *) buf, len, &err);
|
tox_opts->savedata_data = (uint8_t *) data;
|
||||||
|
tox_opts->savedata_length = len;
|
||||||
|
|
||||||
if (err != TOX_ERR_NEW_OK)
|
m = tox_new(tox_opts, new_err);
|
||||||
exit_toxic_err("tox_new() failed", err);
|
|
||||||
|
if (m == NULL) {
|
||||||
|
fclose(fp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
} else {
|
} else { /* Data file does not/should not exist */
|
||||||
/* if file exists then open() failing is fatal */
|
|
||||||
if (file_exists(data_path))
|
if (file_exists(data_path))
|
||||||
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
||||||
|
|
||||||
TOX_ERR_NEW err;
|
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE;
|
||||||
m = tox_new(tox_opts, NULL, 0, &err);
|
|
||||||
|
|
||||||
if (err != TOX_ERR_NEW_OK)
|
m = tox_new(tox_opts, new_err);
|
||||||
exit_toxic_err("tox_new() failed", err);
|
|
||||||
|
if (m == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (store_data(m, data_path) == -1)
|
if (store_data(m, data_path) == -1)
|
||||||
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
exit_toxic_err("failed in load_toxic", FATALERR_FILEOP);
|
||||||
@ -670,10 +778,20 @@ static Tox *load_toxic(char *data_path)
|
|||||||
struct Tox_Options tox_opts;
|
struct Tox_Options tox_opts;
|
||||||
init_tox_options(&tox_opts);
|
init_tox_options(&tox_opts);
|
||||||
|
|
||||||
Tox *m = load_tox(data_path, &tox_opts);
|
TOX_ERR_NEW new_err;
|
||||||
|
Tox *m = load_tox(data_path, &tox_opts, &new_err);
|
||||||
|
|
||||||
if (m == NULL)
|
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_opts.ipv6_enabled) {
|
||||||
exit_toxic_err("load_tox() failed", FATALERR_TOX_INIT);
|
queue_init_message("Falling back to ipv4");
|
||||||
|
tox_opts.ipv6_enabled = false;
|
||||||
|
m = load_tox(data_path, &tox_opts, &new_err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!m)
|
||||||
|
exit_toxic_err("tox_new returned fatal error", new_err);
|
||||||
|
|
||||||
|
if (new_err != TOX_ERR_NEW_OK)
|
||||||
|
queue_init_message("tox_new returned non-fatal error %d", new_err);
|
||||||
|
|
||||||
init_tox_callbacks(m);
|
init_tox_callbacks(m);
|
||||||
load_friendlist(m);
|
load_friendlist(m);
|
||||||
@ -685,13 +803,42 @@ static Tox *load_toxic(char *data_path)
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define TRY_BOOTSTRAP_INTERVAL 5
|
||||||
|
static uint64_t last_bootstrap_time = 0;
|
||||||
|
|
||||||
|
static void do_bootstrap(Tox *m)
|
||||||
|
{
|
||||||
|
static int conn_err = 0;
|
||||||
|
|
||||||
|
if (!timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (tox_self_get_connection_status(m) != TOX_CONNECTION_NONE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (conn_err != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
last_bootstrap_time = get_unix_time();
|
||||||
|
conn_err = init_connection(m);
|
||||||
|
|
||||||
|
if (conn_err != 0)
|
||||||
|
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Auto-connect failed with error code %d", conn_err);
|
||||||
|
}
|
||||||
|
|
||||||
static void do_toxic(Tox *m, ToxWindow *prompt)
|
static void do_toxic(Tox *m, ToxWindow *prompt)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
|
update_unix_time();
|
||||||
|
|
||||||
if (arg_opts.no_connect == 0)
|
if (arg_opts.no_connect) {
|
||||||
tox_iterate(m); /* main toxcore loop */
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_iterate(m);
|
||||||
|
do_bootstrap(m);
|
||||||
|
check_file_transfer_timeouts(m);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -700,6 +847,7 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
|
|||||||
void *thread_winref(void *data)
|
void *thread_winref(void *data)
|
||||||
{
|
{
|
||||||
Tox *m = (Tox *) data;
|
Tox *m = (Tox *) data;
|
||||||
|
|
||||||
uint8_t draw_count = 0;
|
uint8_t draw_count = 0;
|
||||||
init_signal_catchers();
|
init_signal_catchers();
|
||||||
|
|
||||||
@ -746,16 +894,16 @@ void *thread_cqueue(void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
void *thread_audio(void *data)
|
void *thread_av(void *data)
|
||||||
{
|
{
|
||||||
ToxAv *av = (ToxAv *) data;
|
ToxAV *av = (ToxAV *) data;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
toxav_do(av);
|
toxav_iterate(av);
|
||||||
pthread_mutex_unlock(&Winthread.lock);
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
usleep(toxav_do_interval(av) * 1000);
|
usleep(toxav_iteration_interval(av) * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
@ -774,8 +922,9 @@ static void print_usage(void)
|
|||||||
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
|
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
|
||||||
fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n");
|
fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n");
|
||||||
fprintf(stderr, " -P, --HTTP-proxy Use HTTP proxy: Requires [IP] [port]\n");
|
fprintf(stderr, " -P, --HTTP-proxy Use HTTP proxy: Requires [IP] [port]\n");
|
||||||
fprintf(stderr, " -r, --dnslist Use specified DNSservers file\n");
|
fprintf(stderr, " -r, --namelist Use specified name lookup server list\n");
|
||||||
fprintf(stderr, " -t, --force-tcp Force TCP connection (use this with proxies)\n");
|
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
|
||||||
|
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
|
||||||
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
|
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,16 +950,18 @@ static void parse_args(int argc, char *argv[])
|
|||||||
{"nodes", required_argument, 0, 'n'},
|
{"nodes", required_argument, 0, 'n'},
|
||||||
{"help", no_argument, 0, 'h'},
|
{"help", no_argument, 0, 'h'},
|
||||||
{"noconnect", no_argument, 0, 'o'},
|
{"noconnect", no_argument, 0, 'o'},
|
||||||
{"dnslist", required_argument, 0, 'r'},
|
{"namelist", required_argument, 0, 'r'},
|
||||||
{"force-tcp", no_argument, 0, 't'},
|
{"force-tcp", no_argument, 0, 't'},
|
||||||
|
{"tcp-server", required_argument, 0, 'T'},
|
||||||
{"SOCKS5-proxy", required_argument, 0, 'p'},
|
{"SOCKS5-proxy", required_argument, 0, 'p'},
|
||||||
{"HTTP-proxy", required_argument, 0, 'P'},
|
{"HTTP-proxy", required_argument, 0, 'P'},
|
||||||
{"unencrypt-data", no_argument, 0, 'u'},
|
{"unencrypt-data", no_argument, 0, 'u'},
|
||||||
{NULL, no_argument, NULL, 0},
|
{NULL, no_argument, NULL, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *opts_str = "4bdehotuxc:f:n:r:p:P:";
|
const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:";
|
||||||
int opt, indexptr;
|
int opt, indexptr;
|
||||||
|
long int port = 0;
|
||||||
|
|
||||||
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
|
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
@ -842,10 +993,22 @@ static void parse_args(int argc, char *argv[])
|
|||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
arg_opts.use_custom_data = 1;
|
arg_opts.use_custom_data = 1;
|
||||||
DATA_FILE = strdup(optarg);
|
|
||||||
|
if (DATA_FILE)
|
||||||
|
free(DATA_FILE);
|
||||||
|
|
||||||
|
if (BLOCK_FILE)
|
||||||
|
free(BLOCK_FILE);
|
||||||
|
|
||||||
|
DATA_FILE = malloc(strlen(optarg) + 1);
|
||||||
|
strcpy(DATA_FILE, optarg);
|
||||||
|
|
||||||
|
if (DATA_FILE == NULL)
|
||||||
|
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
|
||||||
|
|
||||||
BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1);
|
BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1);
|
||||||
|
|
||||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
if (BLOCK_FILE == NULL)
|
||||||
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
|
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
|
||||||
|
|
||||||
strcpy(BLOCK_FILE, optarg);
|
strcpy(BLOCK_FILE, optarg);
|
||||||
@ -875,7 +1038,12 @@ static void parse_args(int argc, char *argv[])
|
|||||||
if (++optind > argc || argv[optind-1][0] == '-')
|
if (++optind > argc || argv[optind-1][0] == '-')
|
||||||
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||||
|
|
||||||
arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]);
|
port = strtol(argv[optind-1], NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE)
|
||||||
|
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||||
|
|
||||||
|
arg_opts.proxy_port = port;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
@ -885,14 +1053,19 @@ static void parse_args(int argc, char *argv[])
|
|||||||
if (++optind > argc || argv[optind-1][0] == '-')
|
if (++optind > argc || argv[optind-1][0] == '-')
|
||||||
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||||
|
|
||||||
arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]);
|
port = strtol(argv[optind-1], NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE)
|
||||||
|
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||||
|
|
||||||
|
arg_opts.proxy_port = port;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
snprintf(arg_opts.dns_path, sizeof(arg_opts.dns_path), "%s", optarg);
|
snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg);
|
||||||
|
|
||||||
if (!file_exists(arg_opts.dns_path))
|
if (!file_exists(arg_opts.nameserver_path))
|
||||||
queue_init_message("DNSservers file not found");
|
queue_init_message("nameserver list not found");
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -900,6 +1073,15 @@ static void parse_args(int argc, char *argv[])
|
|||||||
arg_opts.force_tcp = 1;
|
arg_opts.force_tcp = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'T':
|
||||||
|
port = strtol(optarg, NULL, 10);
|
||||||
|
|
||||||
|
if (port <= 0 || port > MAX_PORT_RANGE)
|
||||||
|
port = 14191;
|
||||||
|
|
||||||
|
arg_opts.tcp_port = port;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
arg_opts.unencrypt_data = 1;
|
arg_opts.unencrypt_data = 1;
|
||||||
break;
|
break;
|
||||||
@ -912,28 +1094,72 @@ static void parse_args(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DATANAME "data"
|
/* Looks for an old default profile data file and blocklist, and renames them to the new default names.
|
||||||
#define BLOCKNAME "data-blocklist"
|
*
|
||||||
static int init_default_data_files(void)
|
* Returns 0 on success.
|
||||||
|
* Returns -1 on failure.
|
||||||
|
*/
|
||||||
|
#define OLD_DATA_NAME "data"
|
||||||
|
#define OLD_DATA_BLOCKLIST_NAME "data-blocklist"
|
||||||
|
static int rename_old_profile(const char *user_config_dir)
|
||||||
{
|
{
|
||||||
if (arg_opts.use_custom_data)
|
char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1];
|
||||||
|
snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME);
|
||||||
|
|
||||||
|
if (!file_exists(old_data_file))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (file_exists(DATA_FILE))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (rename(old_data_file, DATA_FILE) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
queue_init_message("Data file has been moved to %s", DATA_FILE);
|
||||||
|
|
||||||
|
char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1];
|
||||||
|
snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME);
|
||||||
|
|
||||||
|
if (!file_exists(old_data_blocklist))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (file_exists(BLOCK_FILE))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (rename(old_data_blocklist, BLOCK_FILE) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initializes the default config directory and data files used by toxic.
|
||||||
|
*
|
||||||
|
* Exits the process with an error on failure.
|
||||||
|
*/
|
||||||
|
static void init_default_data_files(void)
|
||||||
|
{
|
||||||
|
if (arg_opts.use_custom_data)
|
||||||
|
return;
|
||||||
|
|
||||||
char *user_config_dir = get_user_config_dir();
|
char *user_config_dir = get_user_config_dir();
|
||||||
|
|
||||||
|
if (user_config_dir == NULL)
|
||||||
|
exit_toxic_err("failed in init_default_data_files()", FATALERR_FILEOP);
|
||||||
|
|
||||||
int config_err = create_user_config_dirs(user_config_dir);
|
int config_err = create_user_config_dirs(user_config_dir);
|
||||||
|
|
||||||
if (config_err) {
|
if (config_err == -1) {
|
||||||
DATA_FILE = strdup(DATANAME);
|
DATA_FILE = strdup(DATANAME);
|
||||||
BLOCK_FILE = strdup(BLOCKNAME);
|
BLOCK_FILE = strdup(BLOCKNAME);
|
||||||
|
|
||||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY);
|
||||||
} else {
|
} else {
|
||||||
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
|
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
|
||||||
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
|
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
|
||||||
|
|
||||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY);
|
||||||
|
|
||||||
strcpy(DATA_FILE, user_config_dir);
|
strcpy(DATA_FILE, user_config_dir);
|
||||||
strcat(DATA_FILE, CONFIGDIR);
|
strcat(DATA_FILE, CONFIGDIR);
|
||||||
@ -944,8 +1170,11 @@ static int init_default_data_files(void)
|
|||||||
strcat(BLOCK_FILE, BLOCKNAME);
|
strcat(BLOCK_FILE, BLOCKNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* For backwards compatibility with old toxic profile names. TODO: remove this some day */
|
||||||
|
if (rename_old_profile(user_config_dir) == -1)
|
||||||
|
queue_init_message("Warning: Profile backwards compatibility failed.");
|
||||||
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
return config_err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define REC_TOX_DO_LOOPS_PER_SEC 25
|
#define REC_TOX_DO_LOOPS_PER_SEC 25
|
||||||
@ -953,7 +1182,7 @@ static int init_default_data_files(void)
|
|||||||
/* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */
|
/* Adjusts usleep value so that tox_do runs close to the recommended number of times per second */
|
||||||
static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval)
|
static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, uint64_t cur_time, useconds_t msleepval)
|
||||||
{
|
{
|
||||||
useconds_t new_sleep = msleepval;
|
useconds_t new_sleep = MAX(msleepval, 3);
|
||||||
++(*loopcount);
|
++(*loopcount);
|
||||||
|
|
||||||
if (*looptimer == cur_time)
|
if (*looptimer == cur_time)
|
||||||
@ -967,18 +1196,20 @@ static useconds_t optimal_msleepval(uint64_t *looptimer, uint64_t *loopcount, ui
|
|||||||
return new_sleep;
|
return new_sleep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this doesn't do anything (yet)
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
// FIXME
|
|
||||||
void DnD_callback(const char* asdv, DropType dt)
|
void DnD_callback(const char* asdv, DropType dt)
|
||||||
{
|
{
|
||||||
if (dt != DT_plain)
|
// if (dt != DT_plain)
|
||||||
return;
|
// return;
|
||||||
|
|
||||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv);
|
// pthread_mutex_lock(&Winthread.lock);
|
||||||
|
// line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv);
|
||||||
|
// pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
#endif /* X11 */
|
#endif /* X11 */
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
parse_args(argc, argv);
|
parse_args(argc, argv);
|
||||||
|
|
||||||
@ -995,9 +1226,13 @@ int main(int argc, char *argv[])
|
|||||||
/* Make sure all written files are read/writeable only by the current user. */
|
/* Make sure all written files are read/writeable only by the current user. */
|
||||||
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||||
|
|
||||||
int config_err = init_default_data_files();
|
init_default_data_files();
|
||||||
|
|
||||||
bool datafile_exists = file_exists(DATA_FILE);
|
bool datafile_exists = file_exists(DATA_FILE);
|
||||||
|
|
||||||
|
if (datafile_exists)
|
||||||
|
last_bootstrap_time = get_unix_time();
|
||||||
|
|
||||||
if (!datafile_exists && !arg_opts.unencrypt_data)
|
if (!datafile_exists && !arg_opts.unencrypt_data)
|
||||||
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
|
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
|
||||||
else if (arg_opts.encrypt_data)
|
else if (arg_opts.encrypt_data)
|
||||||
@ -1011,7 +1246,18 @@ int main(int argc, char *argv[])
|
|||||||
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||||
|
|
||||||
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||||
int settings_err = settings_load(user_settings, p);
|
|
||||||
|
if (settings_load(user_settings, p) == -1)
|
||||||
|
queue_init_message("Failed to load user settings");
|
||||||
|
|
||||||
|
int nameserver_ret = name_lookup_init();
|
||||||
|
|
||||||
|
if (nameserver_ret == -1)
|
||||||
|
queue_init_message("curl failed to initialize; name lookup service is disabled.");
|
||||||
|
else if (nameserver_ret == -2)
|
||||||
|
queue_init_message("Name lookup server list could not be found.");
|
||||||
|
else if (nameserver_ret == -3)
|
||||||
|
queue_init_message("Name lookup server list does not contain any valid entries.");
|
||||||
|
|
||||||
#ifdef X11
|
#ifdef X11
|
||||||
if (init_xtra(DnD_callback) == -1)
|
if (init_xtra(DnD_callback) == -1)
|
||||||
@ -1040,12 +1286,18 @@ int main(int argc, char *argv[])
|
|||||||
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
|
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
|
||||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||||
|
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
av = init_audio(prompt, m);
|
av = init_audio(prompt, m);
|
||||||
|
|
||||||
/* audio thread */
|
#ifdef VIDEO
|
||||||
if (pthread_create(&audio_thread.tid, NULL, thread_audio, (void *) av) != 0)
|
init_video(prompt, m);
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
/* AV thread */
|
||||||
|
if (pthread_create(&av_thread.tid, NULL, thread_av, (void *) av) != 0)
|
||||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||||
|
|
||||||
set_primary_device(input, user_settings->audio_in_dev);
|
set_primary_device(input, user_settings->audio_in_dev);
|
||||||
@ -1059,21 +1311,14 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
init_notify(60, 3000);
|
init_notify(60, 3000);
|
||||||
|
|
||||||
const char *msg;
|
|
||||||
|
|
||||||
if (config_err) {
|
|
||||||
msg = "Unable to determine configuration directory. Defaulting to 'data' for data file...";
|
|
||||||
queue_init_message("%s", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings_err == -1)
|
|
||||||
queue_init_message("Failed to load user settings");
|
|
||||||
|
|
||||||
/* screen/tmux auto-away timer */
|
/* screen/tmux auto-away timer */
|
||||||
if (init_mplex_away_timer(m) == -1)
|
if (init_mplex_away_timer(m) == -1)
|
||||||
queue_init_message("Failed to init mplex auto-away.");
|
queue_init_message("Failed to init mplex auto-away.");
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
print_init_messages(prompt);
|
print_init_messages(prompt);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
cleanup_init_messages();
|
cleanup_init_messages();
|
||||||
|
|
||||||
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
|
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
|
||||||
@ -1087,11 +1332,11 @@ int main(int argc, char *argv[])
|
|||||||
uint64_t loopcount = 0;
|
uint64_t loopcount = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
update_unix_time();
|
|
||||||
do_toxic(m, prompt);
|
do_toxic(m, prompt);
|
||||||
|
|
||||||
uint64_t cur_time = get_unix_time();
|
uint64_t cur_time = get_unix_time();
|
||||||
|
|
||||||
if (timed_out(last_save, cur_time, AUTOSAVE_FREQ)) {
|
if (timed_out(last_save, AUTOSAVE_FREQ)) {
|
||||||
pthread_mutex_lock(&Winthread.lock);
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
if (store_data(m, DATA_FILE) != 0)
|
if (store_data(m, DATA_FILE) != 0)
|
||||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
|
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
|
||||||
|
12
src/toxic.h
12
src/toxic.h
@ -49,6 +49,10 @@
|
|||||||
#define KEY_IDENT_DIGITS 3 /* number of hex digits to display for the pub-key based identifier */
|
#define KEY_IDENT_DIGITS 3 /* number of hex digits to display for the pub-key based identifier */
|
||||||
#define TIME_STR_SIZE 32
|
#define TIME_STR_SIZE 32
|
||||||
|
|
||||||
|
#ifndef MAX_PORT_RANGE
|
||||||
|
#define MAX_PORT_RANGE 65535
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ASCII key codes */
|
/* ASCII key codes */
|
||||||
#define T_KEY_ESC 0x1B /* ESC key */
|
#define T_KEY_ESC 0x1B /* ESC key */
|
||||||
#define T_KEY_KILL 0x0B /* ctrl-k */
|
#define T_KEY_KILL 0x0B /* ctrl-k */
|
||||||
@ -83,7 +87,8 @@ typedef enum _FATAL_ERRS {
|
|||||||
FATALERR_WININIT = -9, /* window init failed */
|
FATALERR_WININIT = -9, /* window init failed */
|
||||||
FATALERR_PROXY = -10, /* Tox network failed to init using a proxy */
|
FATALERR_PROXY = -10, /* Tox network failed to init using a proxy */
|
||||||
FATALERR_ENCRYPT = -11, /* Data file encryption failure */
|
FATALERR_ENCRYPT = -11, /* Data file encryption failure */
|
||||||
FATALERR_TOX_INIT = -12, /* Tox instance failed to initialize */
|
FATALERR_TOX_INIT = -12, /* Tox instance failed to initialize */
|
||||||
|
FATALERR_CURSES = -13, /* Unrecoverable Ncurses error */
|
||||||
} FATAL_ERRS;
|
} FATAL_ERRS;
|
||||||
|
|
||||||
/* Fixes text color problem on some terminals.
|
/* Fixes text color problem on some terminals.
|
||||||
@ -121,9 +126,4 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
|
|||||||
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
|
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
|
||||||
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
|
void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
|
||||||
|
|
||||||
#ifdef AUDIO
|
|
||||||
void write_device_callback_group(Tox *m, int groupnum, int peernum, const int16_t *pcm, unsigned int samples,
|
|
||||||
uint8_t channels, unsigned int sample_rate, void *arg);
|
|
||||||
#endif /* AUDIO */
|
|
||||||
|
|
||||||
#endif /* #define TOXIC_H */
|
#endif /* #define TOXIC_H */
|
||||||
|
423
src/video_call.c
Normal file
423
src/video_call.c
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
/* video_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 "video_call.h"
|
||||||
|
#include "video_device.h"
|
||||||
|
#include "chat_commands.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "notify.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <curses.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#define default_video_bit_rate 5000
|
||||||
|
|
||||||
|
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
|
||||||
|
uint16_t width, uint16_t height,
|
||||||
|
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||||
|
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||||
|
void *user_data );
|
||||||
|
|
||||||
|
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||||
|
uint32_t video_bit_rate, void *user_data);
|
||||||
|
|
||||||
|
static void print_err (ToxWindow *self, const char *error_str)
|
||||||
|
{
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToxAV *init_video(ToxWindow *self, Tox *tox)
|
||||||
|
{
|
||||||
|
CallControl.video_errors = ve_None;
|
||||||
|
|
||||||
|
CallControl.video_enabled = true;
|
||||||
|
CallControl.video_bit_rate = 0;
|
||||||
|
CallControl.video_frame_duration = 10;
|
||||||
|
|
||||||
|
if ( !CallControl.av ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( init_video_devices(CallControl.av) == vde_InternalError ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices");
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl);
|
||||||
|
toxav_callback_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl);
|
||||||
|
|
||||||
|
return CallControl.av;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate_video()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < MAX_CALLS; ++i) {
|
||||||
|
Call* this_call = &CallControl.calls[i];
|
||||||
|
|
||||||
|
stop_video_transmission(this_call, i);
|
||||||
|
|
||||||
|
if( this_call->vout_idx != -1 )
|
||||||
|
close_video_device(vdt_output, this_call->vout_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
terminate_video_devices();
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data)
|
||||||
|
{
|
||||||
|
uint32_t friend_number = *((uint32_t*)data); /* TODO: Or pass an array of call_idx's */
|
||||||
|
Call* this_call = &CallControl.calls[friend_number];
|
||||||
|
TOXAV_ERR_SEND_FRAME error;
|
||||||
|
|
||||||
|
/* Drop frame if video sending is disabled */
|
||||||
|
if ( CallControl.video_bit_rate == 0 || this_call->vin_idx == -1 ) {
|
||||||
|
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error ) == false ) {
|
||||||
|
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame");
|
||||||
|
|
||||||
|
if ( error == TOXAV_ERR_SEND_FRAME_NULL )
|
||||||
|
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame");
|
||||||
|
else if ( error == TOXAV_ERR_SEND_FRAME_INVALID )
|
||||||
|
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_t height,
|
||||||
|
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||||
|
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
|
||||||
|
{
|
||||||
|
if ( !self || !av) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallControl.video_bit_rate = default_video_bit_rate;
|
||||||
|
|
||||||
|
if ( toxav_bit_rate_set(CallControl.av, self->num, -1, CallControl.video_bit_rate, NULL) == false ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( open_primary_video_device(vdt_input, &call->vin_idx) != vde_None ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None ) {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stop_video_transmission(Call *call, int friend_number)
|
||||||
|
{
|
||||||
|
CallControl.video_bit_rate = 0;
|
||||||
|
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
|
||||||
|
|
||||||
|
if ( call->vin_idx != -1 ) {
|
||||||
|
close_video_device(vdt_input, call->vin_idx);
|
||||||
|
call->vin_idx = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of transmission
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbacks
|
||||||
|
*/
|
||||||
|
void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||||
|
uint16_t width, uint16_t height,
|
||||||
|
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||||
|
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||||
|
uint32_t video_bit_rate, void *user_data)
|
||||||
|
{
|
||||||
|
CallControl.video_bit_rate = video_bit_rate;
|
||||||
|
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback_recv_video_starting(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
Call* this_call = &CallControl.calls[friend_number];
|
||||||
|
|
||||||
|
if ( this_call->vout_idx != -1 )
|
||||||
|
return;
|
||||||
|
|
||||||
|
open_primary_video_device(vdt_output, &this_call->vout_idx);
|
||||||
|
}
|
||||||
|
void callback_recv_video_end(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
Call* this_call = &CallControl.calls[friend_number];
|
||||||
|
|
||||||
|
close_video_device(vdt_output, this_call->vout_idx);
|
||||||
|
this_call->vout_idx = -1;
|
||||||
|
}
|
||||||
|
void callback_video_starting(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
ToxWindow* windows = CallControl.prompt;
|
||||||
|
Call* this_call = &CallControl.calls[friend_number];
|
||||||
|
|
||||||
|
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
|
||||||
|
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error);
|
||||||
|
|
||||||
|
if (error == TOXAV_ERR_CALL_CONTROL_OK) {
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
|
if ( windows[i].is_call && windows[i].num == friend_number ) {
|
||||||
|
if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) {
|
||||||
|
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void callback_video_end(uint32_t friend_number)
|
||||||
|
{
|
||||||
|
stop_video_transmission(&CallControl.calls[friend_number], friend_number);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commands from chat_commands.h
|
||||||
|
*/
|
||||||
|
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char *error_str;
|
||||||
|
Call* this_call = &CallControl.calls[self->num];
|
||||||
|
|
||||||
|
if ( argc != 0 ) {
|
||||||
|
error_str = "Unknown arguments.";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !CallControl.av ) {
|
||||||
|
error_str = "ToxAV not supported!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !self->stb->connection ) {
|
||||||
|
error_str = "Friend is offline.";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !self->is_call ) {
|
||||||
|
error_str = "Not in call!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this_call->vin_idx == -1 )
|
||||||
|
callback_video_starting(self->num);
|
||||||
|
else
|
||||||
|
callback_video_end(self->num);
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else error_str = "Only one argument allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceType type;
|
||||||
|
|
||||||
|
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = vdt_input;
|
||||||
|
|
||||||
|
else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = vdt_output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print_video_devices(self, type);
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This changes primary video device only */
|
||||||
|
void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else if ( argc < 2 ) error_str = "Must have id!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = vdt_input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = vdt_output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
long int selection = strtol(argv[2], &end, 10);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( set_primary_video_device(type, selection) == vde_InvalidSelection ) {
|
||||||
|
error_str = "Invalid selection!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char *error_str;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else if ( argc < 2 ) error_str = "Must have id!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceType type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = vdt_input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = vdt_output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
long int selection = strtol(argv[2], &end, 10);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
|
||||||
|
error_str="Invalid selection!";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If call is active, change device */
|
||||||
|
if ( self->is_call ) {
|
||||||
|
Call* this_call = &CallControl.calls[self->num];
|
||||||
|
if ( this_call->ttas ) {
|
||||||
|
|
||||||
|
if ( type == vdt_output ) {
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* TODO: check for failure */
|
||||||
|
close_video_device(vdt_input, this_call->vin_idx);
|
||||||
|
open_video_device(vdt_input, selection, &this_call->vin_idx);
|
||||||
|
register_video_device_callback(self->num, this_call->vin_idx, read_video_device_callback, &self->num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->video_device_selection[type] = selection;
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
print_err (self, error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop_video_stream(ToxWindow *self)
|
||||||
|
{
|
||||||
|
Call *this_call = &CallControl.calls[self->num];
|
||||||
|
|
||||||
|
if (this_call && this_call->vin_idx != -1)
|
||||||
|
stop_video_transmission(this_call, self->num);
|
||||||
|
}
|
44
src/video_call.h
Normal file
44
src/video_call.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* video_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 VIDEO_CALL_H
|
||||||
|
#define VIDEO_CALL_H
|
||||||
|
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
|
#include "audio_call.h"
|
||||||
|
|
||||||
|
#include "video_device.h"
|
||||||
|
|
||||||
|
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||||
|
ToxAV *init_video(ToxWindow *self, Tox *tox);
|
||||||
|
void terminate_video();
|
||||||
|
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call);
|
||||||
|
int stop_video_transmission(Call *call, int friend_number);
|
||||||
|
void stop_video_stream(ToxWindow *self);
|
||||||
|
|
||||||
|
void callback_recv_video_starting(uint32_t friend_number);
|
||||||
|
void callback_recv_video_end(uint32_t friend_number);
|
||||||
|
void callback_video_starting(uint32_t friend_number);
|
||||||
|
void callback_video_end(uint32_t friend_number);
|
||||||
|
|
||||||
|
#endif /* VIDEO_CALL_H */
|
782
src/video_device.c
Normal file
782
src/video_device.c
Normal file
@ -0,0 +1,782 @@
|
|||||||
|
/* video_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 "video_device.h"
|
||||||
|
#include "video_call.h"
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/Xos.h>
|
||||||
|
|
||||||
|
#include <vpx/vpx_image.h>
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/videodev2.h>
|
||||||
|
#else /* __OSX__ */
|
||||||
|
#import "osx_video.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "line_info.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define inline__ inline __attribute__((always_inline))
|
||||||
|
|
||||||
|
extern struct user_settings *user_settings;
|
||||||
|
|
||||||
|
struct VideoBuffer {
|
||||||
|
void *start;
|
||||||
|
size_t length;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct VideoDevice {
|
||||||
|
VideoDataHandleCallback cb; /* Use this to handle data from input device usually */
|
||||||
|
void* cb_data; /* Data to be passed to callback */
|
||||||
|
int32_t friend_number; /* ToxAV friend number */
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
int fd; /* File descriptor of video device selected/opened */
|
||||||
|
struct v4l2_format fmt;
|
||||||
|
struct VideoBuffer *buffers;
|
||||||
|
uint32_t n_buffers;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t ref_count;
|
||||||
|
int32_t selection;
|
||||||
|
pthread_mutex_t mutex[1];
|
||||||
|
uint16_t video_width;
|
||||||
|
uint16_t video_height;
|
||||||
|
|
||||||
|
vpx_image_t input;
|
||||||
|
|
||||||
|
Display *x_display;
|
||||||
|
Window x_window;
|
||||||
|
GC x_gc;
|
||||||
|
|
||||||
|
} VideoDevice;
|
||||||
|
|
||||||
|
const char *dvideo_device_names[2]; /* Default device */
|
||||||
|
const char *video_devices_names[2][MAX_DEVICES]; /* Container of available devices */
|
||||||
|
static int size[2]; /* Size of above containers */
|
||||||
|
VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
|
||||||
|
uint32_t primary_video_device[2]; /* Primary device */
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
static ToxAV* av = NULL;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
/* q_mutex */
|
||||||
|
#define lock pthread_mutex_lock(&video_mutex);
|
||||||
|
#define unlock pthread_mutex_unlock(&video_mutex);
|
||||||
|
pthread_mutex_t video_mutex;
|
||||||
|
|
||||||
|
bool video_thread_running = true,
|
||||||
|
video_thread_paused = true; /* Thread control */
|
||||||
|
|
||||||
|
void* video_thread_poll(void*);
|
||||||
|
|
||||||
|
static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
|
||||||
|
const uint8_t *u, const uint8_t *v, unsigned int ystride,
|
||||||
|
unsigned int ustride, unsigned int vstride, uint8_t *out)
|
||||||
|
{
|
||||||
|
unsigned long int i, j;
|
||||||
|
for (i = 0; i < height; ++i) {
|
||||||
|
for (j = 0; j < width; ++j) {
|
||||||
|
uint8_t *point = out + 4 * ((i * width) + j);
|
||||||
|
int t_y = y[((i * ystride) + j)];
|
||||||
|
int t_u = u[(((i / 2) * ustride) + (j / 2))];
|
||||||
|
int t_v = v[(((i / 2) * vstride) + (j / 2))];
|
||||||
|
t_y = t_y < 16 ? 16 : t_y;
|
||||||
|
|
||||||
|
int r = (298 * (t_y - 16) + 409 * (t_v - 128) + 128) >> 8;
|
||||||
|
int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8;
|
||||||
|
int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
|
||||||
|
|
||||||
|
point[2] = r>255? 255 : r<0 ? 0 : r;
|
||||||
|
point[1] = g>255? 255 : g<0 ? 0 : g;
|
||||||
|
point[0] = b>255? 255 : b<0 ? 0 : b;
|
||||||
|
point[3] = ~0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
|
||||||
|
uint8_t *input, uint16_t width, uint16_t height)
|
||||||
|
{
|
||||||
|
uint8_t *end = input + width * height * 2;
|
||||||
|
while (input != end) {
|
||||||
|
uint8_t *line_end = input + width * 2;
|
||||||
|
while (input != line_end) {
|
||||||
|
*plane_y++ = *input++;
|
||||||
|
*plane_u++ = *input++;
|
||||||
|
*plane_y++ = *input++;
|
||||||
|
*plane_v++ = *input++;
|
||||||
|
}
|
||||||
|
|
||||||
|
line_end = input + width * 2;
|
||||||
|
while (input != line_end) {
|
||||||
|
*plane_y++ = *input++;
|
||||||
|
input++;//u
|
||||||
|
*plane_y++ = *input++;
|
||||||
|
input++;//v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xioctl(int fh, unsigned long request, void *arg)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
do {
|
||||||
|
r = ioctl(fh, request, arg);
|
||||||
|
} while (-1 == r && EINTR == errno);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
/* Meet devices */
|
||||||
|
#ifdef VIDEO
|
||||||
|
VideoDeviceError init_video_devices(ToxAV* av_)
|
||||||
|
#else
|
||||||
|
VideoDeviceError init_video_devices()
|
||||||
|
#endif /* VIDEO */
|
||||||
|
{
|
||||||
|
size[vdt_input] = 0;
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
|
||||||
|
int fd;
|
||||||
|
char device_address[] = "/dev/videoXX";
|
||||||
|
snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
|
||||||
|
|
||||||
|
fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
|
||||||
|
if ( fd == -1 ) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
struct v4l2_capability cap;
|
||||||
|
char* video_input_name;
|
||||||
|
|
||||||
|
/* Query V4L for capture capabilities */
|
||||||
|
if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) {
|
||||||
|
video_input_name = (char*)malloc(strlen((const char*)cap.card) + strlen(device_address) + 4);
|
||||||
|
strcpy(video_input_name, (char*)cap.card);
|
||||||
|
strcat(video_input_name, " (");
|
||||||
|
strcat(video_input_name, (char*)device_address);
|
||||||
|
strcat(video_input_name, ")");
|
||||||
|
} else {
|
||||||
|
video_input_name = (char*)malloc(strlen(device_address) + 3);
|
||||||
|
strcpy(video_input_name, "(");
|
||||||
|
strcat(video_input_name, device_address);
|
||||||
|
strcat(video_input_name, ")");
|
||||||
|
}
|
||||||
|
video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* __OSX__ */
|
||||||
|
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
|
||||||
|
return vde_InternalError;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size[vdt_output] = 1;
|
||||||
|
char* video_output_name = "Toxic Video Receiver";
|
||||||
|
video_devices_names[vdt_output][0] = video_output_name;
|
||||||
|
|
||||||
|
// Start poll thread
|
||||||
|
if ( pthread_mutex_init(&video_mutex, NULL) != 0 )
|
||||||
|
return vde_InternalError;
|
||||||
|
|
||||||
|
pthread_t thread_id;
|
||||||
|
if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 )
|
||||||
|
return vde_InternalError;
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
av = av_;
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
return (VideoDeviceError) vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError terminate_video_devices()
|
||||||
|
{
|
||||||
|
/* Cleanup if needed */
|
||||||
|
video_thread_running = false;
|
||||||
|
usleep(20000);
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < size[vdt_input]; ++i) {
|
||||||
|
free((void*)video_devices_names[vdt_input][i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( pthread_mutex_destroy(&video_mutex) != 0 )
|
||||||
|
return (VideoDeviceError) vde_InternalError;
|
||||||
|
|
||||||
|
#ifdef __OSX__
|
||||||
|
osx_video_release();
|
||||||
|
#endif /* __OSX__ */
|
||||||
|
|
||||||
|
return (VideoDeviceError) vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
|
||||||
|
VideoDataHandleCallback callback, void* data)
|
||||||
|
{
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] || !video_devices_running[vdt_input][device_idx]->fd )
|
||||||
|
return vde_InvalidSelection;
|
||||||
|
#else /* __OSX__ */
|
||||||
|
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
|
||||||
|
return vde_InvalidSelection;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
lock;
|
||||||
|
video_devices_running[vdt_input][device_idx]->cb = callback;
|
||||||
|
video_devices_running[vdt_input][device_idx]->cb_data = data;
|
||||||
|
video_devices_running[vdt_input][device_idx]->friend_number = friend_number;
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
return vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
|
||||||
|
|
||||||
|
primary_video_device[type] = selection;
|
||||||
|
|
||||||
|
return vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx)
|
||||||
|
{
|
||||||
|
return open_video_device(type, primary_video_device[type], device_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
|
||||||
|
{
|
||||||
|
memcpy(buf, dvideo_device_names[type], size);
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx)
|
||||||
|
{
|
||||||
|
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
|
||||||
|
|
||||||
|
lock;
|
||||||
|
|
||||||
|
uint32_t i, temp_idx = -1;
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEVICES; ++i) {
|
||||||
|
if ( !video_devices_running[type][i] ) {
|
||||||
|
temp_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (temp_idx == -1) {
|
||||||
|
unlock;
|
||||||
|
return vde_AllDevicesBusy;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
|
||||||
|
if ( video_devices_running[type][i] && video_devices_running[type][i]->selection == selection ) {
|
||||||
|
|
||||||
|
video_devices_running[type][temp_idx] = video_devices_running[type][i];
|
||||||
|
video_devices_running[type][i]->ref_count++;
|
||||||
|
|
||||||
|
unlock;
|
||||||
|
return vde_None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDevice* device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
|
||||||
|
device->selection = selection;
|
||||||
|
|
||||||
|
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) {
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( type == vdt_input ) {
|
||||||
|
video_thread_paused = true;
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
/* Open selected device */
|
||||||
|
char device_address[] = "/dev/videoXX";
|
||||||
|
snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection);
|
||||||
|
|
||||||
|
device->fd = open(device_address, O_RDWR);
|
||||||
|
if ( device->fd == -1 ) {
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Obtain video device capabilities */
|
||||||
|
struct v4l2_capability cap;
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) {
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup video format */
|
||||||
|
struct v4l2_format fmt;
|
||||||
|
memset(&(fmt), 0, sizeof(fmt));
|
||||||
|
|
||||||
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||||
|
if( -1 == xioctl(device->fd, VIDIOC_G_FMT, &fmt) ) {
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->video_width = fmt.fmt.pix.width;
|
||||||
|
device->video_height = fmt.fmt.pix.height;
|
||||||
|
|
||||||
|
/* Request buffers */
|
||||||
|
struct v4l2_requestbuffers req;
|
||||||
|
memset(&(req), 0, sizeof(req));
|
||||||
|
req.count = 4;
|
||||||
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
req.memory = V4L2_MEMORY_MMAP;
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( req.count < 2 ) {
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->buffers = calloc(req.count, sizeof(struct VideoBuffer));
|
||||||
|
|
||||||
|
for (i = 0; i < req.count; ++i) {
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
memset(&(buf), 0, sizeof(buf));
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf.index = i;
|
||||||
|
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf) ) {
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
device->buffers[i].length = buf.length;
|
||||||
|
device->buffers[i].start = mmap(NULL /* start anywhere */,
|
||||||
|
buf.length,
|
||||||
|
PROT_READ | PROT_WRITE /* required */,
|
||||||
|
MAP_SHARED /* recommended */,
|
||||||
|
device->fd, buf.m.offset);
|
||||||
|
|
||||||
|
if ( MAP_FAILED == device->buffers[i].start ) {
|
||||||
|
for (i = 0; i < buf.index; ++i)
|
||||||
|
munmap(device->buffers[i].start, device->buffers[i].length);
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
device->n_buffers = i;
|
||||||
|
|
||||||
|
enum v4l2_buf_type type;
|
||||||
|
|
||||||
|
for (i = 0; i < device->n_buffers; ++i) {
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
memset(&(buf), 0, sizeof(buf));
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
buf.index = i;
|
||||||
|
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
|
||||||
|
for (i = 0; i < device->n_buffers; ++i)
|
||||||
|
munmap(device->buffers[i].start, device->buffers[i].length);
|
||||||
|
close(device->fd);
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
|
||||||
|
/* Turn on video stream */
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_STREAMON, &type) ) {
|
||||||
|
close_video_device(vdt_input, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* __OSX__ */
|
||||||
|
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
|
||||||
|
free(device);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Create X11 window associated to device */
|
||||||
|
if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) {
|
||||||
|
close_video_device(vdt_input, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
int screen = DefaultScreen(device->x_display);
|
||||||
|
|
||||||
|
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
|
||||||
|
device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
|
||||||
|
BlackPixel(device->x_display, screen))) ) {
|
||||||
|
close_video_device(vdt_input, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
XStoreName(device->x_display, device->x_window, "Video Preview");
|
||||||
|
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
|
||||||
|
|
||||||
|
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
|
||||||
|
close_video_device(vdt_input, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable user from manually closing the X11 window */
|
||||||
|
Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
|
||||||
|
XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
|
||||||
|
|
||||||
|
XMapWindow(device->x_display, device->x_window);
|
||||||
|
XClearWindow(device->x_display, device->x_window);
|
||||||
|
XMapRaised(device->x_display, device->x_window);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
|
||||||
|
vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
|
||||||
|
|
||||||
|
video_thread_paused = false;
|
||||||
|
} else { /* vdt_output */
|
||||||
|
|
||||||
|
/* Create X11 window associated to device */
|
||||||
|
if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) {
|
||||||
|
close_video_device(vdt_output, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
int screen = DefaultScreen(device->x_display);
|
||||||
|
|
||||||
|
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
|
||||||
|
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
|
||||||
|
close_video_device(vdt_output, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
XStoreName(device->x_display, device->x_window, "Video Receive");
|
||||||
|
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
|
||||||
|
|
||||||
|
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
|
||||||
|
close_video_device(vdt_output, temp_idx);
|
||||||
|
unlock;
|
||||||
|
return vde_FailedStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable user from manually closing the X11 window */
|
||||||
|
Atom wm_delete_window = XInternAtom(device->x_display, "WM_DELETE_WINDOW", false);
|
||||||
|
XSetWMProtocols(device->x_display, device->x_window, &wm_delete_window, 1);
|
||||||
|
|
||||||
|
XMapWindow(device->x_display, device->x_window);
|
||||||
|
XClearWindow(device->x_display, device->x_window);
|
||||||
|
XMapRaised(device->x_display, device->x_window);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
|
||||||
|
vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
*device_idx = temp_idx;
|
||||||
|
unlock;
|
||||||
|
|
||||||
|
return vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
__inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
|
||||||
|
uint8_t const *y, uint8_t const *u, uint8_t const *v,
|
||||||
|
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||||
|
void *user_data)
|
||||||
|
{
|
||||||
|
VideoDevice* device = video_devices_running[vdt_output][0];
|
||||||
|
|
||||||
|
if ( !device ) return vde_DeviceNotActive;
|
||||||
|
|
||||||
|
if( !device->x_window ) return vde_DeviceNotActive;
|
||||||
|
|
||||||
|
pthread_mutex_lock(device->mutex);
|
||||||
|
|
||||||
|
/* Resize X11 window to correct size */
|
||||||
|
if ( device->video_width != width || device->video_height != height ) {
|
||||||
|
device->video_width = width;
|
||||||
|
device->video_height = height;
|
||||||
|
XResizeWindow(device->x_display, device->x_window, width, height);
|
||||||
|
|
||||||
|
vpx_img_free(&device->input);
|
||||||
|
vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, width, height, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert YUV420 data to BGR */
|
||||||
|
ystride = abs(ystride);
|
||||||
|
ustride = abs(ustride);
|
||||||
|
vstride = abs(vstride);
|
||||||
|
uint8_t *img_data = malloc(width * height * 4);
|
||||||
|
yuv420tobgr(width, height, y, u, v, ystride, ustride, vstride, img_data);
|
||||||
|
|
||||||
|
/* Allocate image data in X11 */
|
||||||
|
XImage image = {
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
.depth = 24,
|
||||||
|
.bits_per_pixel = 32,
|
||||||
|
.format = ZPixmap,
|
||||||
|
.byte_order = LSBFirst,
|
||||||
|
.bitmap_unit = 8,
|
||||||
|
.bitmap_bit_order = LSBFirst,
|
||||||
|
.bytes_per_line = width * 4,
|
||||||
|
.red_mask = 0xFF0000,
|
||||||
|
.green_mask = 0xFF00,
|
||||||
|
.blue_mask = 0xFF,
|
||||||
|
.data = (char*)img_data
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Render image data */
|
||||||
|
Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, width, height, 24);
|
||||||
|
XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, width, height);
|
||||||
|
XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, width, height, 0, 0);
|
||||||
|
XFreePixmap(device->x_display, pixmap);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
free(img_data);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(device->mutex);
|
||||||
|
return vde_None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* video_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;
|
||||||
|
|
||||||
|
while (video_thread_running)
|
||||||
|
{
|
||||||
|
if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (i = 0; i < size[vdt_input]; ++i)
|
||||||
|
{
|
||||||
|
lock;
|
||||||
|
if ( video_devices_running[vdt_input][i] != NULL )
|
||||||
|
{
|
||||||
|
/* Obtain frame image data from device buffers */
|
||||||
|
VideoDevice* device = video_devices_running[vdt_input][i];
|
||||||
|
uint16_t video_width = device->video_width;
|
||||||
|
uint16_t video_height = device->video_height;
|
||||||
|
uint8_t *y = device->input.planes[0];
|
||||||
|
uint8_t *u = device->input.planes[1];
|
||||||
|
uint8_t *v = device->input.planes[2];
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
struct v4l2_buffer buf;
|
||||||
|
memset(&(buf), 0, sizeof(buf));
|
||||||
|
|
||||||
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
buf.memory = V4L2_MEMORY_MMAP;
|
||||||
|
|
||||||
|
if ( -1 == ioctl(device->fd, VIDIOC_DQBUF, &buf) ) {
|
||||||
|
unlock;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *data = (void*)device->buffers[buf.index].start;
|
||||||
|
|
||||||
|
/* Convert frame image data to YUV420 for ToxAV */
|
||||||
|
yuv422to420(y, u, v, data, video_width, video_height);
|
||||||
|
|
||||||
|
#else /* __OSX__*/
|
||||||
|
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
|
||||||
|
unlock;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Send frame data to friend through ToxAV */
|
||||||
|
if ( device->cb )
|
||||||
|
device->cb(video_width, video_height, y, u, v, device->cb_data);
|
||||||
|
|
||||||
|
/* Convert YUV420 data to BGR */
|
||||||
|
uint8_t *img_data = malloc(video_width * video_height * 4);
|
||||||
|
yuv420tobgr(video_width, video_height, y, u, v,
|
||||||
|
video_width, video_width/2, video_width/2, img_data);
|
||||||
|
|
||||||
|
/* Allocate image data in X11 */
|
||||||
|
XImage image = {
|
||||||
|
.width = video_width,
|
||||||
|
.height = video_height,
|
||||||
|
.depth = 24,
|
||||||
|
.bits_per_pixel = 32,
|
||||||
|
.format = ZPixmap,
|
||||||
|
.byte_order = LSBFirst,
|
||||||
|
.bitmap_unit = 8,
|
||||||
|
.bitmap_bit_order = LSBFirst,
|
||||||
|
.bytes_per_line = video_width * 4,
|
||||||
|
.red_mask = 0xFF0000,
|
||||||
|
.green_mask = 0xFF00,
|
||||||
|
.blue_mask = 0xFF,
|
||||||
|
.data = (char*)img_data
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Render image data */
|
||||||
|
Pixmap pixmap = XCreatePixmap(device->x_display, device->x_window, video_width, video_height, 24);
|
||||||
|
XPutImage(device->x_display, pixmap, device->x_gc, &image, 0, 0, 0, 0, video_width, video_height);
|
||||||
|
XCopyArea(device->x_display, pixmap, device->x_window, device->x_gc, 0, 0, video_width, video_height, 0, 0);
|
||||||
|
XFreePixmap(device->x_display, pixmap);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
free(img_data);
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
|
||||||
|
unlock;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
}
|
||||||
|
unlock;
|
||||||
|
}
|
||||||
|
usleep(1000 * 1000 / 24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
|
||||||
|
{
|
||||||
|
if ( device_idx >= MAX_DEVICES ) return vde_InvalidSelection;
|
||||||
|
|
||||||
|
lock;
|
||||||
|
VideoDevice *device = video_devices_running[type][device_idx];
|
||||||
|
VideoDeviceError rc = vde_None;
|
||||||
|
|
||||||
|
if ( !device ) {
|
||||||
|
unlock;
|
||||||
|
return vde_DeviceNotActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
video_devices_running[type][device_idx] = NULL;
|
||||||
|
|
||||||
|
if ( !device->ref_count ) {
|
||||||
|
|
||||||
|
if ( type == vdt_input ) {
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||||
|
if( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < device->n_buffers; ++i) {
|
||||||
|
if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(device->fd);
|
||||||
|
|
||||||
|
#else /* __OSX__ */
|
||||||
|
osx_video_close_device(device_idx);
|
||||||
|
#endif
|
||||||
|
vpx_img_free(&device->input);
|
||||||
|
XDestroyWindow(device->x_display, device->x_window);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
XCloseDisplay(device->x_display);
|
||||||
|
pthread_mutex_destroy(device->mutex);
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
|
free(device->buffers);
|
||||||
|
#endif /* __linux__ */
|
||||||
|
|
||||||
|
free(device);
|
||||||
|
} else {
|
||||||
|
vpx_img_free(&device->input);
|
||||||
|
XDestroyWindow(device->x_display, device->x_window);
|
||||||
|
XFlush(device->x_display);
|
||||||
|
XCloseDisplay(device->x_display);
|
||||||
|
pthread_mutex_destroy(device->mutex);
|
||||||
|
free(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else device->ref_count--;
|
||||||
|
|
||||||
|
unlock;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_video_devices(ToxWindow* self, VideoDeviceType type)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < size[type]; ++i)
|
||||||
|
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection)
|
||||||
|
{
|
||||||
|
return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None;
|
||||||
|
}
|
76
src/video_device.h
Normal file
76
src/video_device.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
/* video_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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VIDEO_DEVICE_H
|
||||||
|
#define VIDEO_DEVICE_H
|
||||||
|
|
||||||
|
#define MAX_DEVICES 32
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "windows.h"
|
||||||
|
|
||||||
|
typedef enum VideoDeviceType {
|
||||||
|
vdt_input,
|
||||||
|
vdt_output,
|
||||||
|
} VideoDeviceType;
|
||||||
|
|
||||||
|
typedef enum VideoDeviceError {
|
||||||
|
vde_None,
|
||||||
|
vde_InternalError = -1,
|
||||||
|
vde_InvalidSelection = -2,
|
||||||
|
vde_FailedStart = -3,
|
||||||
|
vde_Busy = -4,
|
||||||
|
vde_AllDevicesBusy = -5,
|
||||||
|
vde_DeviceNotActive = -6,
|
||||||
|
vde_BufferError = -7,
|
||||||
|
vde_UnsupportedMode = -8,
|
||||||
|
vde_CaptureError = -9,
|
||||||
|
} VideoDeviceError;
|
||||||
|
|
||||||
|
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data);
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
VideoDeviceError init_video_devices(ToxAV* av);
|
||||||
|
#else
|
||||||
|
VideoDeviceError init_video_devices();
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
|
VideoDeviceError terminate_video_devices();
|
||||||
|
|
||||||
|
/* Callback handles ready data from INPUT device */
|
||||||
|
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, void* data);
|
||||||
|
void* get_video_device_callback_data(uint32_t device_idx);
|
||||||
|
|
||||||
|
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection);
|
||||||
|
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx);
|
||||||
|
/* Start device */
|
||||||
|
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx);
|
||||||
|
/* Stop device */
|
||||||
|
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
|
||||||
|
|
||||||
|
/* Write data to device */
|
||||||
|
VideoDeviceError write_video_out(uint16_t width, uint16_t height, uint8_t const *y, uint8_t const *u, uint8_t const *v, int32_t ystride, int32_t ustride, int32_t vstride, void *user_data);
|
||||||
|
|
||||||
|
void print_video_devices(ToxWindow* self, VideoDeviceType type);
|
||||||
|
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size);
|
||||||
|
|
||||||
|
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);
|
||||||
|
#endif /* VIDEO_DEVICE_H */
|
@ -33,8 +33,10 @@
|
|||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
#include "line_info.h"
|
#include "line_info.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "avatars.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "file_transfers.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern struct Winthread Winthread;
|
extern struct Winthread Winthread;
|
||||||
static ToxWindow windows[MAX_WINDOWS_NUM];
|
static ToxWindow windows[MAX_WINDOWS_NUM];
|
||||||
@ -215,6 +217,16 @@ void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t
|
|||||||
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
|
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
|
||||||
size_t length, void *userdata)
|
size_t length, void *userdata)
|
||||||
{
|
{
|
||||||
|
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
|
||||||
|
|
||||||
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
|
||||||
|
on_avatar_chunk_request(m, ft, position, length);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
@ -226,6 +238,11 @@ void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, u
|
|||||||
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
|
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
|
||||||
const uint8_t *data, size_t length, void *user_data)
|
const uint8_t *data, size_t length, void *user_data)
|
||||||
{
|
{
|
||||||
|
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
|
||||||
|
|
||||||
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
@ -237,6 +254,16 @@ void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint
|
|||||||
void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control,
|
void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
|
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
|
||||||
|
|
||||||
|
if (!ft)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
|
||||||
|
on_avatar_file_control(m, ft, control);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
@ -248,11 +275,17 @@ void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FIL
|
|||||||
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
|
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
|
||||||
const uint8_t *filename, size_t filename_length, void *userdata)
|
const uint8_t *filename, size_t filename_length, void *userdata)
|
||||||
{
|
{
|
||||||
|
/* We don't care about receiving avatars */
|
||||||
|
if (kind != TOX_FILE_KIND_DATA) {
|
||||||
|
tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
if (windows[i].onFileRecv != NULL)
|
if (windows[i].onFileRecv != NULL)
|
||||||
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, kind, file_size, (char *) filename,
|
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, file_size, (char *) filename,
|
||||||
filename_length);
|
filename_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -266,20 +299,6 @@ void on_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *user
|
|||||||
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
|
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AUDIO
|
|
||||||
void write_device_callback_group(Tox *m, int groupnum, int peernum, const int16_t *pcm, unsigned int samples,
|
|
||||||
uint8_t channels, unsigned int sample_rate, void *arg)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
|
||||||
if (windows[i].onWriteDevice != NULL)
|
|
||||||
windows[i].onWriteDevice(&windows[i], m, groupnum, peernum, pcm, samples, channels, samples);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* AUDIO */
|
|
||||||
|
|
||||||
/* CALLBACKS END */
|
/* CALLBACKS END */
|
||||||
|
|
||||||
int add_window(Tox *m, ToxWindow w)
|
int add_window(Tox *m, ToxWindow w)
|
||||||
@ -381,6 +400,9 @@ void on_window_resize(void)
|
|||||||
getmaxyx(stdscr, y2, x2);
|
getmaxyx(stdscr, y2, x2);
|
||||||
y2 -= 2;
|
y2 -= 2;
|
||||||
|
|
||||||
|
if (y2 <= 0 || x2 <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
@ -435,10 +457,16 @@ void on_window_resize(void)
|
|||||||
|
|
||||||
static void draw_window_tab(ToxWindow *toxwin)
|
static void draw_window_tab(ToxWindow *toxwin)
|
||||||
{
|
{
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
|
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
clrtoeol();
|
clrtoeol();
|
||||||
printw(" [%s]", toxwin->name);
|
printw(" [%s]", toxwin->name);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
|
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void draw_bar(void)
|
static void draw_bar(void)
|
||||||
@ -486,7 +514,10 @@ static void draw_bar(void)
|
|||||||
void draw_active_window(Tox *m)
|
void draw_active_window(Tox *m)
|
||||||
{
|
{
|
||||||
ToxWindow *a = active_window;
|
ToxWindow *a = active_window;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
a->alert = WINDOW_ALERT_NONE;
|
a->alert = WINDOW_ALERT_NONE;
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
|
||||||
wint_t ch = 0;
|
wint_t ch = 0;
|
||||||
|
|
||||||
@ -536,8 +567,11 @@ void refresh_inactive_windows(void)
|
|||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||||
ToxWindow *a = &windows[i];
|
ToxWindow *a = &windows[i];
|
||||||
|
|
||||||
if (a->active && a != active_window && !a->is_friendlist)
|
if (a->active && a != active_window && !a->is_friendlist) {
|
||||||
|
pthread_mutex_lock(&Winthread.lock);
|
||||||
line_info_print(a);
|
line_info_print(a);
|
||||||
|
pthread_mutex_unlock(&Winthread.lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -546,7 +580,7 @@ ToxWindow *get_window_ptr(int i)
|
|||||||
{
|
{
|
||||||
ToxWindow *toxwin = NULL;
|
ToxWindow *toxwin = NULL;
|
||||||
|
|
||||||
if (i >= 0 && i <= MAX_WINDOWS_NUM && windows[i].active)
|
if (i >= 0 && i < MAX_WINDOWS_NUM && windows[i].active)
|
||||||
toxwin = &windows[i];
|
toxwin = &windows[i];
|
||||||
|
|
||||||
return toxwin;
|
return toxwin;
|
||||||
@ -573,7 +607,7 @@ void kill_all_windows(Tox *m)
|
|||||||
if (windows[i].is_chat)
|
if (windows[i].is_chat)
|
||||||
kill_chat_window(&windows[i], m);
|
kill_chat_window(&windows[i], m);
|
||||||
else if (windows[i].is_groupchat)
|
else if (windows[i].is_groupchat)
|
||||||
close_groupchat(&windows[i], m, i);
|
close_groupchat(&windows[i], m, windows[i].num);
|
||||||
}
|
}
|
||||||
|
|
||||||
kill_prompt_window(prompt);
|
kill_prompt_window(prompt);
|
||||||
|
@ -76,7 +76,7 @@ struct cqueue_thread {
|
|||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct audio_thread {
|
struct av_thread {
|
||||||
pthread_t tid;
|
pthread_t tid;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -90,13 +90,15 @@ struct arg_opts {
|
|||||||
bool encrypt_data;
|
bool encrypt_data;
|
||||||
bool unencrypt_data;
|
bool unencrypt_data;
|
||||||
|
|
||||||
char dns_path[MAX_STR_SIZE];
|
char nameserver_path[MAX_STR_SIZE];
|
||||||
char config_path[MAX_STR_SIZE];
|
char config_path[MAX_STR_SIZE];
|
||||||
char nodes_path[MAX_STR_SIZE];
|
char nodes_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
char proxy_address[256];
|
char proxy_address[256];
|
||||||
uint8_t proxy_type;
|
uint8_t proxy_type;
|
||||||
uint16_t proxy_port;
|
uint16_t proxy_port;
|
||||||
|
|
||||||
|
uint16_t tcp_port;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct ToxWindow ToxWindow;
|
typedef struct ToxWindow ToxWindow;
|
||||||
@ -127,30 +129,33 @@ struct ToxWindow {
|
|||||||
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
|
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
|
||||||
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
||||||
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
|
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
|
||||||
void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
||||||
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
|
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
|
||||||
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
|
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
|
||||||
|
|
||||||
#ifdef AUDIO
|
#ifdef AUDIO
|
||||||
|
|
||||||
void(*onInvite)(ToxWindow *, ToxAv *, int);
|
void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onRinging)(ToxWindow *, ToxAv *, int);
|
void(*onRinging)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onStarting)(ToxWindow *, ToxAv *, int);
|
void(*onStarting)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onEnding)(ToxWindow *, ToxAv *, int);
|
void(*onEnding)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onError)(ToxWindow *, ToxAv *, int);
|
void(*onError)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onStart)(ToxWindow *, ToxAv *, int);
|
void(*onStart)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onCancel)(ToxWindow *, ToxAv *, int);
|
void(*onCancel)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onReject)(ToxWindow *, ToxAv *, int);
|
void(*onReject)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onEnd)(ToxWindow *, ToxAv *, int);
|
void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||||
void(*onRequestTimeout)(ToxWindow *, ToxAv *, int);
|
void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int);
|
||||||
void(*onPeerTimeout)(ToxWindow *, ToxAv *, int);
|
|
||||||
void(*onWriteDevice)(ToxWindow *, Tox *, int, int, const int16_t *, unsigned int, uint8_t, unsigned int);
|
|
||||||
|
|
||||||
int call_idx; /* If in a call will have this index set, otherwise it's -1.
|
|
||||||
* Don't modify outside av callbacks. */
|
|
||||||
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
|
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
|
||||||
|
bool is_call;
|
||||||
int ringing_sound;
|
int ringing_sound;
|
||||||
|
|
||||||
|
#ifdef VIDEO
|
||||||
|
|
||||||
|
int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */
|
||||||
|
|
||||||
|
#endif /* VIDEO */
|
||||||
|
|
||||||
#endif /* AUDIO */
|
#endif /* AUDIO */
|
||||||
|
|
||||||
int active_box; /* For box notify */
|
int active_box; /* For box notify */
|
||||||
|
@ -333,7 +333,9 @@ int init_xtra(drop_callback d)
|
|||||||
(unsigned char*)&XdndVersion, 1);
|
(unsigned char*)&XdndVersion, 1);
|
||||||
|
|
||||||
pthread_t id;
|
pthread_t id;
|
||||||
pthread_create(&id, NULL, event_loop, NULL);
|
if (pthread_create(&id, NULL, event_loop, NULL) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
pthread_detach(id);
|
pthread_detach(id);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Reference in New Issue
Block a user