mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-30 03:36:46 +02:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
cc3513968e | |||
dd697d7af1 | |||
a32d76ed16 | |||
e6d307f65a | |||
b210068c1d | |||
0151b9b49f | |||
ab0da36cb7 | |||
ed3e9b476d | |||
7c63bd80d6 | |||
9f06331a0b | |||
a63cba645f | |||
9d50d52216 | |||
6cb36e71fe | |||
0b52de3773 | |||
22ac65c4a9 | |||
3b90c3495f | |||
f4e4fbbef1 | |||
6aee8c136b | |||
2a0740821c | |||
e6f285adc7 | |||
a80da2b58f | |||
da924f07a9 | |||
e8cd1417b7 | |||
d08feb2cc5 | |||
fe0641e981 | |||
1fd07837ea | |||
6c2ae4ad24 | |||
c678d41709 | |||
63745afe09 | |||
416419a6e7 | |||
33e16fe870 | |||
d712d6c898 | |||
2ae478d546 | |||
4b8de0d16d | |||
2fcfa954ab | |||
675c8fa89f | |||
d1153f96ca | |||
2f473300cd | |||
92d5b2fefc | |||
70f8b103de | |||
41292c1ded | |||
90393f1dba | |||
87bd0f9b34 | |||
13e67f4ce3 | |||
6c9dbfe3bc | |||
24b763bce6 | |||
e41008bd4e | |||
fd86f01fd0 | |||
e775c51a06 |
43
.travis.yml
43
.travis.yml
@ -4,23 +4,52 @@ compiler:
|
|||||||
- clang
|
- clang
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
# installing libsodium, needed for Core
|
#installing libsodium, needed for Core
|
||||||
- git clone git://github.com/jedisct1/libsodium.git
|
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
|
||||||
- cd libsodium
|
- cd libsodium
|
||||||
- git checkout tags/0.4.2
|
- git checkout tags/0.4.2 > /dev/null
|
||||||
- ./autogen.sh
|
- ./autogen.sh > /dev/null
|
||||||
- ./configure && make -j3
|
- ./configure > /dev/null
|
||||||
- sudo make install
|
- make check -j3 > /dev/null
|
||||||
|
- sudo make install >/dev/null
|
||||||
- cd ..
|
- cd ..
|
||||||
|
#installing yasm, needed for compiling vpx
|
||||||
|
- sudo apt-get install yasm > /dev/null
|
||||||
|
#installing libconfig, needed for DHT_bootstrap_daemon
|
||||||
|
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
|
||||||
|
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
|
||||||
|
- cd libconfig-1.4.9
|
||||||
|
- ./configure > /dev/null
|
||||||
|
- make -j3 > /dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
|
- cd ..
|
||||||
|
#installing libopus, needed for audio encoding/decoding
|
||||||
|
- wget http://downloads.xiph.org/releases/opus/opus-1.0.3.tar.gz > /dev/null
|
||||||
|
- tar xzf opus-1.0.3.tar.gz > /dev/null
|
||||||
|
- cd opus-1.0.3
|
||||||
|
- ./configure > /dev/null
|
||||||
|
- make -j3 > /dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
|
- cd ..
|
||||||
|
#installing vpx
|
||||||
|
- git clone http://git.chromium.org/webm/libvpx.git > /dev/null
|
||||||
|
- cd libvpx
|
||||||
|
- ./configure --enable-shared > /dev/null
|
||||||
|
- make -j3 >/dev/null
|
||||||
|
- sudo make install > /dev/null
|
||||||
|
- cd ..
|
||||||
|
#creating libraries links and updating cache
|
||||||
|
- sudo ldconfig > /dev/null
|
||||||
# creating librarys' links and updating cache
|
# creating librarys' links and updating cache
|
||||||
- sudo ldconfig
|
- sudo ldconfig
|
||||||
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
|
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
|
||||||
- cd toxcore
|
- cd toxcore
|
||||||
- autoreconf -i
|
- autoreconf -i
|
||||||
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon
|
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
|
||||||
- make -j2
|
- make -j2
|
||||||
- sudo make install
|
- sudo make install
|
||||||
- cd ..
|
- cd ..
|
||||||
|
- sudo apt-get install libopenal-dev -yq
|
||||||
script:
|
script:
|
||||||
- autoreconf -i
|
- autoreconf -i
|
||||||
- ./configure
|
- ./configure
|
||||||
|
15
README.md
15
README.md
@ -1,15 +1,16 @@
|
|||||||
## Toxic
|
# Toxic
|
||||||
|
|
||||||
Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png).
|
Toxic is an ncurses based instant messaging client for [Tox](http://tox.im) which formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://i.imgur.com/hL7WhVl.png).
|
||||||
|
## Installation
|
||||||
|
* Generate the configure script by running the ```autoreconf -i``` command.
|
||||||
|
|
||||||
To compile, first generate the configure script by running the ```autoreconf -i``` command.
|
* Execute the configure script with ```./configure``` (you may need to pass it the location of your dependency libraries, i.e.):
|
||||||
|
```./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/toxcore --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/toxcore --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/ ```
|
||||||
|
|
||||||
Then execute the configure script with ./configure (you may need to pass it the location of your dependency libraries, i.e.):
|
* Compile and install the program with ```make && sudo make install```
|
||||||
```
|
|
||||||
./configure --prefix=/where/to/install --with-libtoxcore-headers=/path/to/ProjectTox-Core/core --with-libtoxcore-libs=/path/to/ProjectTox-Core/build/core --with-libsodium-headers=/path/to/libsodium/include/ --with-libsodium-libs=/path/to/sodiumtest/lib/
|
|
||||||
|
|
||||||
```
|
#### Notes
|
||||||
*Note:* If your default prefix is /usr/local and you happen to get an error that says "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
|
If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
|
||||||
```
|
```
|
||||||
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
@ -27,7 +27,9 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \
|
|||||||
$(top_srcdir)/src/toxic_strings.c \
|
$(top_srcdir)/src/toxic_strings.c \
|
||||||
$(top_srcdir)/src/toxic_strings.h \
|
$(top_srcdir)/src/toxic_strings.h \
|
||||||
$(top_srcdir)/src/log.c \
|
$(top_srcdir)/src/log.c \
|
||||||
$(top_srcdir)/src/log.h
|
$(top_srcdir)/src/log.h \
|
||||||
|
$(top_srcdir)/src/file_senders.c \
|
||||||
|
$(top_srcdir)/src/file_senders.h
|
||||||
|
|
||||||
toxic_CFLAGS = -I$(top_srcdir) \
|
toxic_CFLAGS = -I$(top_srcdir) \
|
||||||
$(NCURSES_CFLAGS) \
|
$(NCURSES_CFLAGS) \
|
||||||
@ -43,3 +45,16 @@ toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
|
|||||||
$(LIBSODIUM_LIBS) \
|
$(LIBSODIUM_LIBS) \
|
||||||
$(WINSOCK2_LIBS)
|
$(WINSOCK2_LIBS)
|
||||||
|
|
||||||
|
|
||||||
|
# For audio support
|
||||||
|
if BUILD_AV
|
||||||
|
|
||||||
|
toxic_SOURCES += $(top_srcdir)/src/audio_call.c \
|
||||||
|
$(top_srcdir)/src/audio_call.h
|
||||||
|
|
||||||
|
toxic_CFLAGS += $(LIBTOXAV_CFLAGS) \
|
||||||
|
$(OPENAL_CFLAGS)
|
||||||
|
|
||||||
|
toxic_LDADD += $(LIBTOXAV_LIBS) \
|
||||||
|
$(OPENAL_LIBS)
|
||||||
|
endif
|
||||||
|
69
configure.ac
69
configure.ac
@ -2,7 +2,7 @@
|
|||||||
# Process this file with autoconf to produce a configure script.
|
# Process this file with autoconf to produce a configure script.
|
||||||
|
|
||||||
AC_PREREQ([2.65])
|
AC_PREREQ([2.65])
|
||||||
AC_INIT([toxic], [0.2.7], [https://tox.im/])
|
AC_INIT([toxic], [0.3.0], [https://tox.im/])
|
||||||
AC_CONFIG_AUX_DIR(configure_aux)
|
AC_CONFIG_AUX_DIR(configure_aux)
|
||||||
AC_CONFIG_SRCDIR([src/main.c])
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
@ -381,6 +381,73 @@ if test "x$LIBTOXCORE_FOUND" = "xno"; then
|
|||||||
AC_SUBST(LIBTOXCORE_LDFLAGS)
|
AC_SUBST(LIBTOXCORE_LDFLAGS)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
####
|
||||||
|
#### A/V Stuff
|
||||||
|
|
||||||
|
AV_SEARCH_DIR=
|
||||||
|
BUILD_AV="yes"
|
||||||
|
|
||||||
|
AC_ARG_WITH(libtoxav-prefix,
|
||||||
|
AC_HELP_STRING([--with-libtoxav-prefix=DIR],
|
||||||
|
[search for libtoxav in DIR, i.e. look for libraries in
|
||||||
|
DIR/lib and for headers in DIR/include]),
|
||||||
|
[
|
||||||
|
AV_SEARCH_DIR="$withval"
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if test -n "$AV_SEARCH_DIR"; then
|
||||||
|
CFLAGS="$CFLAGS -I$AV_SEARCH_DIR/include"
|
||||||
|
CPPFLAGS="$CPPFLAGS -I$AV_SEARCH_DIR/include"
|
||||||
|
LDFLAGS="$LDFLAGS -L$AV_SEARCH_DIR/lib"
|
||||||
|
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:$AV_SEARCH_DIR/lib/pkgconfig
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if specified enable
|
||||||
|
AC_ARG_ENABLE([av],
|
||||||
|
[AC_HELP_STRING([--disable-av], [build AV support libraries (default: auto)]) ],
|
||||||
|
[
|
||||||
|
if test "x$enableval" = "xno"; then
|
||||||
|
BUILD_AV="no"
|
||||||
|
elif test "x$enableval" = "xyes"; then
|
||||||
|
BUILD_AV="yes"
|
||||||
|
fi
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check for A/V library
|
||||||
|
|
||||||
|
if test "x$BUILD_AV" = "xyes"; then
|
||||||
|
PKG_CHECK_MODULES([OPENAL], [openal],
|
||||||
|
[
|
||||||
|
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig
|
||||||
|
|
||||||
|
PKG_CHECK_MODULES([LIBTOXAV], [libtoxav],
|
||||||
|
[
|
||||||
|
AC_CHECK_HEADER([tox/toxav.h],
|
||||||
|
[
|
||||||
|
# Place define for audio support
|
||||||
|
AC_DEFINE([_SUPPORT_AUDIO], [], [Is audio supported])
|
||||||
|
AC_MSG_NOTICE([Building with audio support])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AC_MSG_NOTICE([No A/V headers; disabling A/V support])
|
||||||
|
BUILD_AV="no"
|
||||||
|
],)
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AC_MSG_NOTICE([No A/V library; disabling A/V support])
|
||||||
|
])
|
||||||
|
],
|
||||||
|
[
|
||||||
|
AC_MSG_NOTICE([No openal library; disabling A/V support])
|
||||||
|
])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL(BUILD_AV, test "x$BUILD_AV" = "xyes")
|
||||||
|
|
||||||
|
|
||||||
TOXIC_VERSION="$PACKAGE_VERSION"
|
TOXIC_VERSION="$PACKAGE_VERSION"
|
||||||
AC_PATH_PROG([GIT], [git], [no])
|
AC_PATH_PROG([GIT], [git], [no])
|
||||||
if test "x$GIT" != "xno"; then
|
if test "x$GIT" != "xno"; then
|
||||||
|
@ -1 +1 @@
|
|||||||
dist_pkgdata_DATA = DHTservers
|
dist_pkgdata_DATA = DHTnodes
|
||||||
|
600
src/audio_call.c
Normal file
600
src/audio_call.c
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
/*
|
||||||
|
* Toxic -- Tox Curses Client
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "audio_call.h"
|
||||||
|
#include "toxic_windows.h"
|
||||||
|
#include "chat_commands.h"
|
||||||
|
#include "global_commands.h"
|
||||||
|
#include "toxic_windows.h"
|
||||||
|
|
||||||
|
#include <curses.h>
|
||||||
|
#include <AL/al.h>
|
||||||
|
#include <AL/alc.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define MAX_DEVICES 32
|
||||||
|
#define _cbend pthread_exit(NULL)
|
||||||
|
|
||||||
|
typedef struct _DeviceIx {
|
||||||
|
|
||||||
|
ALCdevice* dhndl; /* Handle of device selected/opened */
|
||||||
|
ALCcontext* ctx; /* Device context */
|
||||||
|
const char* devices[MAX_DEVICES]; /* Container */
|
||||||
|
int size;
|
||||||
|
int dix; /* Index of default device */
|
||||||
|
} DeviceIx;
|
||||||
|
|
||||||
|
typedef enum _devices
|
||||||
|
{
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
} _devices;
|
||||||
|
|
||||||
|
struct _ASettings {
|
||||||
|
|
||||||
|
DeviceIx device[2];
|
||||||
|
|
||||||
|
AudioError errors;
|
||||||
|
|
||||||
|
ToxAv* av;
|
||||||
|
|
||||||
|
pthread_t ttid; /* Transmission thread id */
|
||||||
|
int ttas; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||||
|
} ASettins;
|
||||||
|
|
||||||
|
|
||||||
|
void callback_recv_invite ( void *arg );
|
||||||
|
void callback_recv_ringing ( void *arg );
|
||||||
|
void callback_recv_starting ( void *arg );
|
||||||
|
void callback_recv_ending ( void *arg );
|
||||||
|
void callback_recv_error ( void *arg );
|
||||||
|
void callback_call_started ( void *arg );
|
||||||
|
void callback_call_canceled ( void *arg );
|
||||||
|
void callback_call_rejected ( void *arg );
|
||||||
|
void callback_call_ended ( void *arg );
|
||||||
|
void callback_requ_timeout ( void *arg );
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ToxAv* init_audio(ToxWindow* window, Tox* tox)
|
||||||
|
{
|
||||||
|
ASettins.errors = NoError;
|
||||||
|
ASettins.ttas = 0; /* Not running */
|
||||||
|
|
||||||
|
/* Capture device */
|
||||||
|
const char* stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
||||||
|
ASettins.device[input].size = 0;
|
||||||
|
|
||||||
|
if ( stringed_device_list ) {
|
||||||
|
const char* default_device = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
for ( ; *stringed_device_list; ++ASettins.device[input].size ) {
|
||||||
|
ASettins.device[input].devices[ASettins.device[input].size] = stringed_device_list;
|
||||||
|
|
||||||
|
if ( strcmp( default_device , ASettins.device[input].devices[ASettins.device[input].size] ) == 0 )
|
||||||
|
ASettins.device[input].dix = ASettins.device[input].size;
|
||||||
|
|
||||||
|
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ASettins.device[input].size ) { /* Have device */
|
||||||
|
ASettins.device[input].dhndl = alcCaptureOpenDevice(
|
||||||
|
ASettins.device[input].devices[ASettins.device[input].dix], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
|
||||||
|
|
||||||
|
if (alcGetError(ASettins.device[input].dhndl) != AL_NO_ERROR) {
|
||||||
|
ASettins.errors |= ErrorStartingCaptureDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(window->window, "Input device: %s\n", ASettins.device[input].devices[ASettins.device[input].dix]);
|
||||||
|
} else { /* No device */
|
||||||
|
wprintw(window->window, "No input device!\n");
|
||||||
|
ASettins.device[input].dhndl = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASettins.device[input].ctx = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Output device */
|
||||||
|
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||||
|
ASettins.device[output].size = 0;
|
||||||
|
|
||||||
|
if ( stringed_device_list ) {
|
||||||
|
const char* default_device = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||||
|
|
||||||
|
for ( ; *stringed_device_list; ++ASettins.device[output].size ) {
|
||||||
|
ASettins.device[output].devices[ASettins.device[output].size] = stringed_device_list;
|
||||||
|
|
||||||
|
if ( strcmp( default_device , ASettins.device[output].devices[ASettins.device[output].size] ) == 0 )
|
||||||
|
ASettins.device[output].dix = ASettins.device[output].size;
|
||||||
|
|
||||||
|
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ASettins.device[output].size ) { /* Have device */
|
||||||
|
ASettins.device[output].dhndl = alcOpenDevice(ASettins.device[output].devices[ASettins.device[output].dix]);
|
||||||
|
|
||||||
|
if (alcGetError(ASettins.device[output].dhndl) != AL_NO_ERROR) {
|
||||||
|
ASettins.errors |= ErrorStartingOutputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(window->window, "Output device: %s\n", ASettins.device[output].devices[ASettins.device[output].dix]);
|
||||||
|
ASettins.device[output].ctx = alcCreateContext(ASettins.device[output].dhndl, NULL);
|
||||||
|
} else { /* No device */
|
||||||
|
wprintw(window->window, "No output device!\n");
|
||||||
|
ASettins.device[output].dhndl = NULL;
|
||||||
|
ASettins.device[output].ctx = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Streaming stuff from core */
|
||||||
|
ASettins.av = toxav_new(tox, 0, 0);
|
||||||
|
|
||||||
|
if ( !ASettins.av ) {
|
||||||
|
ASettins.errors |= ErrorStartingCoreAudio;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_call_started, av_OnStart, window);
|
||||||
|
toxav_register_callstate_callback(callback_call_canceled, av_OnCancel, window);
|
||||||
|
toxav_register_callstate_callback(callback_call_rejected, av_OnReject, window);
|
||||||
|
toxav_register_callstate_callback(callback_call_ended, av_OnEnd, window);
|
||||||
|
toxav_register_callstate_callback(callback_recv_invite, av_OnInvite, window);
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_recv_ringing, av_OnRinging, window);
|
||||||
|
toxav_register_callstate_callback(callback_recv_starting, av_OnStarting, window);
|
||||||
|
toxav_register_callstate_callback(callback_recv_ending, av_OnEnding, window);
|
||||||
|
|
||||||
|
toxav_register_callstate_callback(callback_recv_error, av_OnError, window);
|
||||||
|
toxav_register_callstate_callback(callback_requ_timeout, av_OnRequestTimeout, window);
|
||||||
|
|
||||||
|
return ASettins.av;
|
||||||
|
}
|
||||||
|
|
||||||
|
void terminate_audio()
|
||||||
|
{
|
||||||
|
if ( ASettins.device[input].dhndl ) {
|
||||||
|
alcCaptureCloseDevice(ASettins.device[input].dhndl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ASettins.device[output].dhndl ) {
|
||||||
|
alcCloseDevice(ASettins.device[output].dhndl);
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(ASettins.device[output].ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
toxav_kill(ASettins.av);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int errors()
|
||||||
|
{
|
||||||
|
return ASettins.errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmission
|
||||||
|
*/
|
||||||
|
|
||||||
|
void* transmission(void* arg)
|
||||||
|
{
|
||||||
|
(void)arg; /* Avoid warning */
|
||||||
|
|
||||||
|
ASettins.ttas = 1;
|
||||||
|
|
||||||
|
/* Prepare devices */
|
||||||
|
alcCaptureStart(ASettins.device[input].dhndl);
|
||||||
|
alcMakeContextCurrent(ASettins.device[output].ctx);
|
||||||
|
|
||||||
|
int32_t dec_frame_len;
|
||||||
|
int16_t frame[4096];
|
||||||
|
int32_t sample = 0;
|
||||||
|
uint32_t buffer;
|
||||||
|
int32_t ready;
|
||||||
|
int32_t openal_buffers = 5;
|
||||||
|
uint32_t source, *buffers;
|
||||||
|
|
||||||
|
/* Prepare buffers */
|
||||||
|
buffers = calloc(sizeof(uint32_t), openal_buffers);
|
||||||
|
alGenBuffers(openal_buffers, buffers);
|
||||||
|
alGenSources((uint32_t)1, &source);
|
||||||
|
alSourcei(source, AL_LOOPING, AL_FALSE);
|
||||||
|
|
||||||
|
|
||||||
|
uint16_t zeros[AUDIO_FRAME_SIZE];
|
||||||
|
memset(zeros, 0, AUDIO_FRAME_SIZE);
|
||||||
|
int16_t PCM[AUDIO_FRAME_SIZE];
|
||||||
|
|
||||||
|
int32_t i = 0;
|
||||||
|
for (; i < openal_buffers; ++i) {
|
||||||
|
alBufferData(buffers[i], AL_FORMAT_MONO16, zeros, AUDIO_FRAME_SIZE, 48000);
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceQueueBuffers(source, openal_buffers, buffers);
|
||||||
|
alSourcePlay(source);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR) {
|
||||||
|
/* Print something? */
|
||||||
|
/*fprintf(stderr, "Error starting audio\n");*/
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Start transmission */
|
||||||
|
while (ASettins.ttas) {
|
||||||
|
|
||||||
|
alcGetIntegerv(ASettins.device[input].dhndl, ALC_CAPTURE_SAMPLES, (int32_t) sizeof(int32_t), &sample);
|
||||||
|
|
||||||
|
/* RECORD AND SEND */
|
||||||
|
if (sample >= AUDIO_FRAME_SIZE) {
|
||||||
|
alcCaptureSamples(ASettins.device[input].dhndl, frame, AUDIO_FRAME_SIZE);
|
||||||
|
|
||||||
|
if (toxav_send_audio(ASettins.av, frame, AUDIO_FRAME_SIZE) < 0)
|
||||||
|
/*fprintf(stderr, "Could not encode or send audio packet\n")*/;
|
||||||
|
|
||||||
|
} else usleep(1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* PLAYBACK */
|
||||||
|
|
||||||
|
alGetSourcei(source, AL_BUFFERS_PROCESSED, &ready);
|
||||||
|
|
||||||
|
if (ready <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dec_frame_len = toxav_recv_audio(ASettins.av, AUDIO_FRAME_SIZE, PCM);
|
||||||
|
|
||||||
|
/* Play the packet */
|
||||||
|
if (dec_frame_len > 0) {
|
||||||
|
alSourceUnqueueBuffers(source, 1, &buffer);
|
||||||
|
alBufferData(buffer, AL_FORMAT_MONO16, PCM, dec_frame_len * 2 * 1, 48000);
|
||||||
|
int32_t error = alGetError();
|
||||||
|
|
||||||
|
if (error != AL_NO_ERROR) {
|
||||||
|
/*fprintf(stderr, "Error setting buffer %d\n", error);*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
alSourceQueueBuffers(source, 1, &buffer);
|
||||||
|
|
||||||
|
if (alGetError() != AL_NO_ERROR) {
|
||||||
|
/*fprintf(stderr, "Error: could not buffer audio\n");*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
alGetSourcei(source, AL_SOURCE_STATE, &ready);
|
||||||
|
|
||||||
|
if (ready != AL_PLAYING) alSourcePlay(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
|
||||||
|
alDeleteSources(1, &source);
|
||||||
|
alDeleteBuffers(openal_buffers, buffers);
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
|
||||||
|
_cbend;
|
||||||
|
}
|
||||||
|
|
||||||
|
int start_transmission()
|
||||||
|
{
|
||||||
|
if ( !toxav_capability_supported(ASettins.av, AudioDecoding) ||
|
||||||
|
!toxav_capability_supported(ASettins.av, AudioEncoding) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Don't provide support for video */
|
||||||
|
toxav_prepare_transmission(ASettins.av, 0);
|
||||||
|
|
||||||
|
if ( 0 != pthread_create(&ASettins.ttid, NULL, transmission, NULL ) &&
|
||||||
|
0 != pthread_detach(ASettins.ttid) ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int stop_transmission()
|
||||||
|
{
|
||||||
|
ASettins.ttas = 0;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* End of transmission
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define CB_BODY(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); } while (0)
|
||||||
|
|
||||||
|
void callback_recv_invite ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onInvite);
|
||||||
|
}
|
||||||
|
void callback_recv_ringing ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onRinging);
|
||||||
|
}
|
||||||
|
void callback_recv_starting ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onStarting);
|
||||||
|
}
|
||||||
|
void callback_recv_ending ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onEnding);
|
||||||
|
stop_transmission();
|
||||||
|
}
|
||||||
|
void callback_recv_error ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onError);
|
||||||
|
}
|
||||||
|
void callback_call_started ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onStart);
|
||||||
|
}
|
||||||
|
void callback_call_canceled ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onCancel);
|
||||||
|
}
|
||||||
|
void callback_call_rejected ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onReject);
|
||||||
|
}
|
||||||
|
void callback_call_ended ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onEnd);
|
||||||
|
stop_transmission();
|
||||||
|
}
|
||||||
|
|
||||||
|
void callback_requ_timeout ( void* arg )
|
||||||
|
{
|
||||||
|
CB_BODY(arg, onTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* End of Callbacks
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Commands from chat_commands.h
|
||||||
|
*/
|
||||||
|
void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; }
|
||||||
|
|
||||||
|
if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; }
|
||||||
|
|
||||||
|
ToxAvError error = toxav_call(ASettins.av, self->num, TypeAudio, 30);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorAlreadyInCall ) error_str = "Already in a call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(window, "Calling...\n");
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s %d\n", error_str, argc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; }
|
||||||
|
|
||||||
|
if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; }
|
||||||
|
|
||||||
|
ToxAvError error = toxav_answer(ASettins.av, TypeAudio);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorInvalidState ) error_str = "Cannot answer in invalid state!";
|
||||||
|
else if ( error == ErrorNoCall ) error_str = "No incomming call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback will print status... */
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s\n", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_hangup(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; }
|
||||||
|
|
||||||
|
if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; }
|
||||||
|
|
||||||
|
ToxAvError error = toxav_hangup(ASettins.av);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorInvalidState ) error_str = "Cannot hangup in invalid state!";
|
||||||
|
else if ( error == ErrorNoCall ) error_str = "No call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s\n", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_cancel(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if (argc != 0) { error_str = "Invalid syntax!"; goto on_error; }
|
||||||
|
|
||||||
|
if ( !ASettins.av ) { error_str = "No audio supported!"; goto on_error; }
|
||||||
|
|
||||||
|
ToxAvError error = toxav_hangup(ASettins.av);
|
||||||
|
|
||||||
|
if ( error != ErrorNone ) {
|
||||||
|
if ( error == ErrorNoCall ) error_str = "No call!";
|
||||||
|
else error_str = "Internal error!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Callback will print status... */
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s\n", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if ( argc != 1 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else error_str = "Only one argument allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_devices type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
wprintw(window, "Invalid type: %s\n", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for ( ; i < ASettins.device[type].size; i ++)
|
||||||
|
wprintw(window, "%d: %s\n", i, ASettins.device[type].devices[i]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s\n", error_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
|
{
|
||||||
|
const char* error_str;
|
||||||
|
|
||||||
|
if ( argc != 2 ) {
|
||||||
|
if ( argc < 1 ) error_str = "Type must be specified!";
|
||||||
|
else if ( argc < 2 ) error_str = "Must have id!";
|
||||||
|
else error_str = "Only two arguments allowed!";
|
||||||
|
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ASettins.ttas ) { /* Transmission is active */
|
||||||
|
error_str = "Cannot change device while active transmission";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
_devices type;
|
||||||
|
|
||||||
|
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
|
||||||
|
type = input;
|
||||||
|
|
||||||
|
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
|
||||||
|
type = output;
|
||||||
|
|
||||||
|
else {
|
||||||
|
wprintw(window, "Invalid type: %s\n", argv[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *end;
|
||||||
|
long int selection = strtol(argv[2], &end, 10);
|
||||||
|
|
||||||
|
if ( *end ) {
|
||||||
|
error_str = "Invalid input";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( selection < 0 || selection >= ASettins.device[type].size ) {
|
||||||
|
error_str = "No device with such index";
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close default device */
|
||||||
|
if ( ASettins.device[type].dhndl ) {
|
||||||
|
alcCloseDevice(ASettins.device[type].dhndl);
|
||||||
|
|
||||||
|
if ( ASettins.device[type].ctx) { /* Output device has context with it */
|
||||||
|
alcMakeContextCurrent(NULL);
|
||||||
|
alcDestroyContext(ASettins.device[type].ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open new device */
|
||||||
|
|
||||||
|
if ( type == input ) {
|
||||||
|
ASettins.device[input].dhndl = alcCaptureOpenDevice(
|
||||||
|
ASettins.device[input].devices[selection], AUDIO_SAMPLE_RATE, AL_FORMAT_MONO16, AUDIO_FRAME_SIZE * 4);
|
||||||
|
|
||||||
|
if (alcGetError(ASettins.device[input].dhndl) != AL_NO_ERROR) {
|
||||||
|
error_str = "Error starting input device!";
|
||||||
|
ASettins.errors |= ErrorStartingCaptureDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASettins.device[input].ctx = NULL;
|
||||||
|
|
||||||
|
wprintw(window, "Input device: %s\n", ASettins.device[type].devices[selection]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ASettins.device[output].dhndl = alcOpenDevice(ASettins.device[output].devices[selection]);
|
||||||
|
|
||||||
|
if (alcGetError(ASettins.device[output].dhndl) != AL_NO_ERROR) {
|
||||||
|
error_str = "Error starting output device!";
|
||||||
|
ASettins.errors |= ErrorStartingOutputDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASettins.device[output].ctx = alcCreateContext(ASettins.device[output].dhndl, NULL);
|
||||||
|
|
||||||
|
wprintw(window, "Output device: %s\n", ASettins.device[type].devices[selection]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return;
|
||||||
|
on_error:
|
||||||
|
wprintw(window, "%s\n", error_str);
|
||||||
|
}
|
30
src/audio_call.h
Normal file
30
src/audio_call.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Toxic -- Tox Curses Client
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _audio_h
|
||||||
|
#define _audio_h
|
||||||
|
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
|
||||||
|
typedef struct ToxWindow ToxWindow;
|
||||||
|
|
||||||
|
typedef enum _AudioError
|
||||||
|
{
|
||||||
|
NoError = 0,
|
||||||
|
ErrorStartingCaptureDevice = 1 << 0,
|
||||||
|
ErrorStartingOutputDevice = 1 << 1,
|
||||||
|
ErrorStartingCoreAudio = 1 << 2
|
||||||
|
} AudioError;
|
||||||
|
|
||||||
|
/* You will have to pass pointer to first member of 'windows'
|
||||||
|
* declared in windows.c otherwise undefined behaviour will
|
||||||
|
*/
|
||||||
|
ToxAv* init_audio(ToxWindow* window, Tox* tox);
|
||||||
|
void terminate_audio();
|
||||||
|
|
||||||
|
int errors();
|
||||||
|
|
||||||
|
int start_transmission();
|
||||||
|
|
||||||
|
#endif /* _audio_h */
|
196
src/chat.c
196
src/chat.c
@ -35,13 +35,22 @@
|
|||||||
#include "toxic_strings.h"
|
#include "toxic_strings.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include "audio_call.h"
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern int store_data(Tox *m, char *path);
|
extern int store_data(Tox *m, char *path);
|
||||||
|
|
||||||
extern FileSender file_senders[MAX_FILES];
|
extern FileSender file_senders[MAX_FILES];
|
||||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
|
|
||||||
#define AC_NUM_CHAT_COMMANDS 18
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define AC_NUM_CHAT_COMMANDS 22
|
||||||
|
#else
|
||||||
|
#define AC_NUM_CHAT_COMMANDS 18
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
/* Array of chat command names used for tab completion. */
|
/* Array of chat command names used for tab completion. */
|
||||||
static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||||
@ -63,6 +72,15 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/savefile" },
|
{ "/savefile" },
|
||||||
{ "/sendfile" },
|
{ "/sendfile" },
|
||||||
{ "/status" },
|
{ "/status" },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
{ "/call" },
|
||||||
|
{ "/cancel" },
|
||||||
|
{ "/answer" },
|
||||||
|
{ "/hangup" },
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
|
static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
|
||||||
@ -115,7 +133,7 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint1
|
|||||||
} else
|
} else
|
||||||
wprintw(ctx->history, "%s\n", msg);
|
wprintw(ctx->history, "%s\n", msg);
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, ctx->log, false);
|
write_to_log(msg, nick, ctx->log, false);
|
||||||
alert_window(self, WINDOW_ALERT_1, true);
|
alert_window(self, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +177,7 @@ static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uin
|
|||||||
wprintw(ctx->history, "* %s %s\n", nick, action);
|
wprintw(ctx->history, "* %s %s\n", nick, action);
|
||||||
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
add_to_log_buf(action, nick, ctx->log, true);
|
write_to_log(action, nick, ctx->log, true);
|
||||||
alert_window(self, WINDOW_ALERT_1, true);
|
alert_window(self, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +241,7 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
|
|||||||
strcat(filename, d);
|
strcat(filename, d);
|
||||||
filename[len + strlen(d)] = '\0';
|
filename[len + strlen(d)] = '\0';
|
||||||
|
|
||||||
if (count > 999999) {
|
if (count > 999) {
|
||||||
wprintw(ctx->history, "Error saving file to disk.\n");
|
wprintw(ctx->history, "Error saving file to disk.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -237,6 +255,15 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
|
|||||||
alert_window(self, WINDOW_ALERT_2, true);
|
alert_window(self, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void chat_close_file_receiver(int num, uint8_t filenum)
|
||||||
|
{
|
||||||
|
friends[num].file_receiver.pending[filenum] = false;
|
||||||
|
FILE *file = friends[num].file_receiver.files[filenum];
|
||||||
|
|
||||||
|
if (file != NULL)
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
|
||||||
static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send,
|
static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive_send,
|
||||||
uint8_t filenum, uint8_t control_type, uint8_t *data, uint16_t length)
|
uint8_t filenum, uint8_t control_type, uint8_t *data, uint16_t length)
|
||||||
{
|
{
|
||||||
@ -255,20 +282,20 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive
|
|||||||
case TOX_FILECONTROL_ACCEPT:
|
case TOX_FILECONTROL_ACCEPT:
|
||||||
wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename);
|
||||||
break;
|
break;
|
||||||
case TOX_FILECONTROL_PAUSE:
|
/*case TOX_FILECONTROL_PAUSE:
|
||||||
// wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
|
||||||
break;
|
break; */
|
||||||
case TOX_FILECONTROL_KILL:
|
case TOX_FILECONTROL_KILL:
|
||||||
wprintw(ctx->history, "File transfer for '%s' failed.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' failed.\n", filename);
|
||||||
|
|
||||||
if (receive_send == 0)
|
if (receive_send == 0)
|
||||||
friends[num].file_receiver.pending[filenum] = false;
|
chat_close_file_receiver(num, filenum);
|
||||||
else
|
else
|
||||||
close_file_sender(filenum);
|
chat_close_file_receiver(num, filenum);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TOX_FILECONTROL_FINISHED:
|
case TOX_FILECONTROL_FINISHED:
|
||||||
wprintw(ctx->history, "File transfer for '%s' complete.\n", filename);
|
wprintw(ctx->history, "File transfer for '%s' complete.\n", filename);
|
||||||
|
chat_close_file_receiver(num, filenum);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,25 +310,14 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, u
|
|||||||
|
|
||||||
ChatContext *ctx = self->chatwin;
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
uint8_t *filename = friends[num].file_receiver.filenames[filenum];
|
if (fwrite(data, length, 1, friends[num].file_receiver.files[filenum]) != 1) {
|
||||||
FILE *file_to_save = fopen(filename, "a");
|
|
||||||
|
|
||||||
if (file_to_save == NULL) {
|
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
wprintw(ctx->history, "* Error writing to file.\n");
|
wprintw(ctx->history, "* Error writing to file.\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fwrite(data, length, 1, file_to_save) != 1) {
|
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
|
||||||
wprintw(ctx->history, "* Error writing to file.\n");
|
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
|
||||||
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
chat_close_file_receiver(num, filenum);
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(file_to_save);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_t *group_pub_key)
|
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_t *group_pub_key)
|
||||||
@ -322,6 +338,123 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_
|
|||||||
alert_window(self, WINDOW_ALERT_2, true);
|
alert_window(self, WINDOW_ALERT_2, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Av Stuff */
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
void chat_onInvite (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Incomming audio call!\n"
|
||||||
|
"Answer: \"/answer\" \"/cancel\"\n");
|
||||||
|
|
||||||
|
alert_window(self, WINDOW_ALERT_0, true);
|
||||||
|
}
|
||||||
|
void chat_onRinging (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Ringing...\n"
|
||||||
|
"\"/cancel\" ?\n");
|
||||||
|
}
|
||||||
|
void chat_onStarting (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if ( 0 != start_transmission() ) {/* YEAH! */
|
||||||
|
wprintw(ctx->history, "Error starting transmission!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Call started! \n"
|
||||||
|
"Type: \"/hangup\" to end it.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
void chat_onEnding (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
toxav_kill_transmission(av);
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Call ended! \n");
|
||||||
|
}
|
||||||
|
void chat_onError (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Error! \n");
|
||||||
|
}
|
||||||
|
void chat_onStart (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
if ( 0 != start_transmission() ) {/* YEAH! */
|
||||||
|
wprintw(ctx->history, "Error starting transmission!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Call started! \n"
|
||||||
|
"Type: \"/hangup\" to end it.\n");
|
||||||
|
}
|
||||||
|
void chat_onCancel (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Call canceled! \n");
|
||||||
|
}
|
||||||
|
void chat_onReject (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Rejected! \n");
|
||||||
|
}
|
||||||
|
void chat_onEnd (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
toxav_kill_transmission(av);
|
||||||
|
|
||||||
|
wprintw(ctx->history, "Call ended! \n");
|
||||||
|
}
|
||||||
|
void chat_onTimeout (ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
if (self->num != toxav_get_peer_id(av, 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ChatContext *ctx = self->chatwin;
|
||||||
|
|
||||||
|
wprintw(ctx->history, "No answer! \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *action) {
|
||||||
if (action == NULL) {
|
if (action == NULL) {
|
||||||
wprintw(ctx->history, "Invalid syntax.\n");
|
wprintw(ctx->history, "Invalid syntax.\n");
|
||||||
@ -341,7 +474,7 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *acti
|
|||||||
wprintw(ctx->history, " * Failed to send action\n");
|
wprintw(ctx->history, " * Failed to send action\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
} else {
|
} else {
|
||||||
add_to_log_buf(action, selfname, ctx->log, true);
|
write_to_log(action, selfname, ctx->log, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -532,7 +665,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
wprintw(ctx->history, " * Failed to send message.\n");
|
wprintw(ctx->history, " * Failed to send message.\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(RED));
|
wattroff(ctx->history, COLOR_PAIR(RED));
|
||||||
} else {
|
} else {
|
||||||
add_to_log_buf(line, selfname, ctx->log, false);
|
write_to_log(line, selfname, ctx->log, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,6 +845,19 @@ ToxWindow new_chat(Tox *m, int friendnum)
|
|||||||
ret.onFileSendRequest = &chat_onFileSendRequest;
|
ret.onFileSendRequest = &chat_onFileSendRequest;
|
||||||
ret.onFileControl = &chat_onFileControl;
|
ret.onFileControl = &chat_onFileControl;
|
||||||
ret.onFileData = &chat_onFileData;
|
ret.onFileData = &chat_onFileData;
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
ret.onInvite = &chat_onInvite;
|
||||||
|
ret.onRinging = &chat_onRinging;
|
||||||
|
ret.onStarting = &chat_onStarting;
|
||||||
|
ret.onEnding = &chat_onEnding;
|
||||||
|
ret.onError = &chat_onError;
|
||||||
|
ret.onStart = &chat_onStart;
|
||||||
|
ret.onCancel = &chat_onCancel;
|
||||||
|
ret.onReject = &chat_onReject;
|
||||||
|
ret.onEnd = &chat_onEnd;
|
||||||
|
ret.onTimeout = &chat_onTimeout;
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t name[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
uint16_t len = tox_get_name(m, friendnum, name);
|
uint16_t len = tox_get_name(m, friendnum, name);
|
||||||
|
@ -52,6 +52,15 @@ void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
|||||||
wprintw(window, "Chat commands:\n");
|
wprintw(window, "Chat commands:\n");
|
||||||
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
wprintw(window, " /call : Audio call\n");
|
||||||
|
wprintw(window, " /cancel : Cancel call\n");
|
||||||
|
wprintw(window, " /answer : Answer incomming call\n");
|
||||||
|
wprintw(window, " /hangup : Hangup active call\n");
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
|
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
|
||||||
wprintw(window, " /join : Join a pending group chat\n");
|
wprintw(window, " /join : Join a pending group chat\n");
|
||||||
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
|
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
|
||||||
@ -90,7 +99,7 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
|
|
||||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wattron(window, COLOR_PAIR(RED));
|
wattron(window, COLOR_PAIR(RED));
|
||||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||||
wattron(window, COLOR_PAIR(RED));
|
wattron(window, COLOR_PAIR(RED));
|
||||||
@ -139,10 +148,18 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
|||||||
|
|
||||||
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
uint8_t *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||||
|
|
||||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0)
|
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
|
||||||
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
|
wprintw(window, "Accepted file transfer %u. Saving file as: '%s'\n", filenum, filename);
|
||||||
else
|
|
||||||
|
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
|
||||||
|
wattron(window, COLOR_PAIR(RED));
|
||||||
|
wprintw(window, "* Error writing to file.\n");
|
||||||
|
wattroff(window, COLOR_PAIR(RED));
|
||||||
|
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
wprintw(window, "File transfer failed.\n");
|
wprintw(window, "File transfer failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
friends[self->num].file_receiver.pending[filenum] = false;
|
friends[self->num].file_receiver.pending[filenum] = false;
|
||||||
}
|
}
|
||||||
|
@ -25,3 +25,11 @@ void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_ST
|
|||||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
void cmd_call(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_answer(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_hangup(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
@ -20,8 +20,13 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
#include "execute.h"
|
#include "execute.h"
|
||||||
@ -34,20 +39,24 @@ struct cmd_func {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static struct cmd_func global_commands[] = {
|
static struct cmd_func global_commands[] = {
|
||||||
{ "/accept", cmd_accept },
|
{ "/accept", cmd_accept },
|
||||||
{ "/add", cmd_add },
|
{ "/add", cmd_add },
|
||||||
{ "/clear", cmd_clear },
|
{ "/clear", cmd_clear },
|
||||||
{ "/connect", cmd_connect },
|
{ "/connect", cmd_connect },
|
||||||
{ "/exit", cmd_quit },
|
{ "/exit", cmd_quit },
|
||||||
{ "/groupchat", cmd_groupchat },
|
{ "/groupchat", cmd_groupchat },
|
||||||
{ "/help", cmd_prompt_help },
|
{ "/help", cmd_prompt_help },
|
||||||
{ "/log", cmd_log },
|
{ "/log", cmd_log },
|
||||||
{ "/myid", cmd_myid },
|
{ "/myid", cmd_myid },
|
||||||
{ "/nick", cmd_nick },
|
{ "/nick", cmd_nick },
|
||||||
{ "/note", cmd_note },
|
{ "/note", cmd_note },
|
||||||
{ "/q", cmd_quit },
|
{ "/q", cmd_quit },
|
||||||
{ "/quit", cmd_quit },
|
{ "/quit", cmd_quit },
|
||||||
{ "/status", cmd_status },
|
{ "/status", cmd_status },
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
{ "/lsdev", cmd_list_devices },
|
||||||
|
{ "/sdev", cmd_change_device },
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cmd_func chat_commands[] = {
|
static struct cmd_func chat_commands[] = {
|
||||||
@ -56,6 +65,13 @@ static struct cmd_func chat_commands[] = {
|
|||||||
{ "/join", cmd_join_group },
|
{ "/join", cmd_join_group },
|
||||||
{ "/savefile", cmd_savefile },
|
{ "/savefile", cmd_savefile },
|
||||||
{ "/sendfile", cmd_sendfile },
|
{ "/sendfile", cmd_sendfile },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
{ "/call", cmd_call },
|
||||||
|
{ "/cancel", cmd_cancel },
|
||||||
|
{ "/answer", cmd_answer },
|
||||||
|
{ "/hangup", cmd_hangup },
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Parses input command and puts args into arg array.
|
/* Parses input command and puts args into arg array.
|
||||||
@ -63,8 +79,8 @@ static struct cmd_func chat_commands[] = {
|
|||||||
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
|
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
int num_args = 0;
|
int num_args = 0;
|
||||||
bool cmd_end = false; // flags when we get to the end of cmd
|
bool cmd_end = false; /* flags when we get to the end of cmd */
|
||||||
char *end; // points to the end of the current arg
|
char *end; /* points to the end of the current arg */
|
||||||
|
|
||||||
/* characters wrapped in double quotes count as one arg */
|
/* characters wrapped in double quotes count as one arg */
|
||||||
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
while (!cmd_end && num_args < MAX_NUM_ARGS) {
|
||||||
|
@ -21,8 +21,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||||
#define GLOBAL_NUM_COMMANDS 14
|
|
||||||
#define CHAT_NUM_COMMANDS 5
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define GLOBAL_NUM_COMMANDS 16
|
||||||
|
#define CHAT_NUM_COMMANDS 9
|
||||||
|
#else
|
||||||
|
#define GLOBAL_NUM_COMMANDS 14
|
||||||
|
#define CHAT_NUM_COMMANDS 5
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GLOBAL_COMMAND_MODE,
|
GLOBAL_COMMAND_MODE,
|
||||||
|
113
src/file_senders.c
Normal file
113
src/file_senders.c
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
/* file_senders.c
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Toxic.
|
||||||
|
*
|
||||||
|
* Toxic is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Toxic is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "toxic_windows.h"
|
||||||
|
|
||||||
|
FileSender file_senders[MAX_FILES];
|
||||||
|
uint8_t max_file_senders_index;
|
||||||
|
|
||||||
|
static void close_file_sender(int i)
|
||||||
|
{
|
||||||
|
fclose(file_senders[i].file);
|
||||||
|
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||||
|
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = max_file_senders_index; j > 0; --j) {
|
||||||
|
if (file_senders[j-1].active)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_file_senders_index = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should only be called on exit */
|
||||||
|
void close_all_file_senders(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
|
if (file_senders[i].active)
|
||||||
|
fclose(file_senders[i].file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_file_senders(Tox *m)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < max_file_senders_index; ++i) {
|
||||||
|
if (!file_senders[i].active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
uint8_t *pathname = file_senders[i].pathname;
|
||||||
|
uint8_t filenum = file_senders[i].filenum;
|
||||||
|
int friendnum = file_senders[i].friendnum;
|
||||||
|
FILE *fp = file_senders[i].file;
|
||||||
|
uint64_t current_time = (uint64_t) time(NULL);
|
||||||
|
|
||||||
|
/* If file transfer has timed out kill transfer and send kill control */
|
||||||
|
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
||||||
|
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||||
|
|
||||||
|
if (ctx != NULL) {
|
||||||
|
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
||||||
|
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
|
close_file_sender(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
||||||
|
file_senders[i].piecelen) == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
file_senders[i].timestamp = current_time;
|
||||||
|
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||||
|
tox_file_data_size(m, friendnum), fp);
|
||||||
|
|
||||||
|
if (file_senders[i].piecelen == 0) {
|
||||||
|
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
||||||
|
|
||||||
|
if (ctx != NULL) {
|
||||||
|
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
||||||
|
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||||
|
close_file_sender(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
src/file_senders.h
Normal file
26
src/file_senders.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* 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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Should only be called on exit */
|
||||||
|
void close_all_file_senders(void);
|
||||||
|
|
||||||
|
void do_file_senders(Tox *m);
|
116
src/friendlist.c
116
src/friendlist.c
@ -33,6 +33,7 @@
|
|||||||
#include "chat.h"
|
#include "chat.h"
|
||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "audio_call.h"
|
||||||
|
|
||||||
extern char *DATA_FILE;
|
extern char *DATA_FILE;
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
@ -44,6 +45,8 @@ static int num_friends = 0;
|
|||||||
ToxicFriend friends[MAX_FRIENDS_NUM];
|
ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||||
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
||||||
|
|
||||||
|
static PendingDel pendingdelete;
|
||||||
|
|
||||||
#define S_WEIGHT 100
|
#define S_WEIGHT 100
|
||||||
|
|
||||||
static int index_name_cmp(const void *n1, const void *n2)
|
static int index_name_cmp(const void *n1, const void *n2)
|
||||||
@ -77,7 +80,7 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
@ -133,7 +136,7 @@ static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *
|
|||||||
friends[num].statusmsg_len = len;
|
friends[num].statusmsg_len = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort)
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort)
|
||||||
{
|
{
|
||||||
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
||||||
return;
|
return;
|
||||||
@ -178,7 +181,7 @@ static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||||
@ -203,7 +206,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (friends[num].chatwin == -1) {
|
if (friends[num].chatwin == -1) {
|
||||||
if (num_active_windows() < MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||||
} else {
|
} else {
|
||||||
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
@ -230,7 +233,7 @@ static void select_friend(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
static void delete_friend(Tox *m, int f_num)
|
||||||
{
|
{
|
||||||
tox_del_friend(m, f_num);
|
tox_del_friend(m, f_num);
|
||||||
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
||||||
@ -253,6 +256,49 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
|
|||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* activates delete friend popup */
|
||||||
|
static void del_friend_activate(ToxWindow *self, Tox *m, int f_num)
|
||||||
|
{
|
||||||
|
int x2, y2;
|
||||||
|
getmaxyx(self->window, y2, x2);
|
||||||
|
self->popup = newwin(3, 22 + TOXIC_MAX_NAME_LENGTH, 8, 8);
|
||||||
|
|
||||||
|
pendingdelete.active = true;
|
||||||
|
pendingdelete.num = f_num;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* deactivates delete friend popup and deletes friend if instructed */
|
||||||
|
static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key)
|
||||||
|
{
|
||||||
|
if (key == 'y')
|
||||||
|
delete_friend(m, pendingdelete.num);
|
||||||
|
|
||||||
|
memset(&pendingdelete, 0, sizeof(PendingDel));
|
||||||
|
delwin(self->popup);
|
||||||
|
self->popup = NULL;
|
||||||
|
clear();
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_popup(ToxWindow *self, Tox *m)
|
||||||
|
{
|
||||||
|
if (self->popup == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
wattron(self->popup, A_BOLD);
|
||||||
|
box(self->popup, ACS_VLINE, ACS_HLINE);
|
||||||
|
wattroff(self->popup, A_BOLD);
|
||||||
|
|
||||||
|
wmove(self->popup, 1, 1);
|
||||||
|
wprintw(self->popup, "Delete contact ");
|
||||||
|
wattron(self->popup, A_BOLD);
|
||||||
|
wprintw(self->popup, "%s", friends[pendingdelete.num].name);
|
||||||
|
wattroff(self->popup, A_BOLD);
|
||||||
|
wprintw(self->popup, "? y/n");
|
||||||
|
|
||||||
|
wrefresh(self->popup);
|
||||||
|
}
|
||||||
|
|
||||||
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
||||||
{
|
{
|
||||||
if (num_friends == 0)
|
if (num_friends == 0)
|
||||||
@ -260,11 +306,19 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
|
|
||||||
int f = friendlist_index[num_selected];
|
int f = friendlist_index[num_selected];
|
||||||
|
|
||||||
|
/* lock screen and force decision on deletion popup */
|
||||||
|
if (pendingdelete.active) {
|
||||||
|
if (key == 'y' || key == 'n')
|
||||||
|
del_friend_deactivate(self, m, key);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (key == '\n') {
|
if (key == '\n') {
|
||||||
/* Jump to chat window if already open */
|
/* Jump to chat window if already open */
|
||||||
if (friends[f].chatwin != -1) {
|
if (friends[f].chatwin != -1) {
|
||||||
set_active_window(friends[f].chatwin);
|
set_active_window(friends[f].chatwin);
|
||||||
} else if (num_active_windows() < MAX_WINDOWS_NUM) {
|
} else if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
friends[f].chatwin = add_window(m, new_chat(m, friends[f].num));
|
||||||
set_active_window(friends[f].chatwin);
|
set_active_window(friends[f].chatwin);
|
||||||
} else {
|
} else {
|
||||||
@ -276,7 +330,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
|
|||||||
alert_window(prompt, WINDOW_ALERT_1, true);
|
alert_window(prompt, WINDOW_ALERT_1, true);
|
||||||
}
|
}
|
||||||
} else if (key == KEY_DC) {
|
} else if (key == KEY_DC) {
|
||||||
delete_friend(m, self, f, key);
|
del_friend_activate(self, m, f);
|
||||||
} else {
|
} else {
|
||||||
select_friend(self, m, key);
|
select_friend(self, m, key);
|
||||||
}
|
}
|
||||||
@ -306,7 +360,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
wattroff(self->window, COLOR_PAIR(CYAN));
|
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||||
|
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, " Friends: %d/%d \n\n", tox_get_num_online_friends(m), num_friends);
|
wprintw(self->window, " Online: %d/%d \n\n", tox_get_num_online_friends(m), num_friends);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
|
|
||||||
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
|
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
|
||||||
@ -401,6 +455,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
self->x = x2;
|
self->x = x2;
|
||||||
wrefresh(self->window);
|
wrefresh(self->window);
|
||||||
|
draw_popup(self, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disable_chatwin(int f_num)
|
void disable_chatwin(int f_num)
|
||||||
@ -413,6 +468,36 @@ static void friendlist_onInit(ToxWindow *self, Tox *m)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
static void friendlist_onAv(ToxWindow *self, ToxAv *av)
|
||||||
|
{
|
||||||
|
int id = toxav_get_peer_id(av, 0);
|
||||||
|
/*id++;*/
|
||||||
|
if ( id != ErrorInternal && id >= max_friends_index)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Tox* m = toxav_get_tox(av);
|
||||||
|
|
||||||
|
if (friends[id].chatwin == -1) {
|
||||||
|
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||||
|
friends[id].chatwin = add_window(m, new_chat(m, friends[id].num));
|
||||||
|
} else {
|
||||||
|
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
|
||||||
|
tox_get_name(m, id, nick);
|
||||||
|
nick[TOXIC_MAX_NAME_LENGTH] = '\0';
|
||||||
|
wprintw(prompt->window, "Audio action from: %s!\n", nick);
|
||||||
|
|
||||||
|
prep_prompt_win();
|
||||||
|
wattron(prompt->window, COLOR_PAIR(RED));
|
||||||
|
wprintw(prompt->window, "* Warning: Too many windows are open.\n");
|
||||||
|
wattron(prompt->window, COLOR_PAIR(RED));
|
||||||
|
|
||||||
|
alert_window(prompt, WINDOW_ALERT_0, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
ToxWindow new_friendlist(void)
|
ToxWindow new_friendlist(void)
|
||||||
{
|
{
|
||||||
ToxWindow ret;
|
ToxWindow ret;
|
||||||
@ -426,12 +511,25 @@ ToxWindow new_friendlist(void)
|
|||||||
ret.onFriendAdded = &friendlist_onFriendAdded;
|
ret.onFriendAdded = &friendlist_onFriendAdded;
|
||||||
ret.onMessage = &friendlist_onMessage;
|
ret.onMessage = &friendlist_onMessage;
|
||||||
ret.onConnectionChange = &friendlist_onConnectionChange;
|
ret.onConnectionChange = &friendlist_onConnectionChange;
|
||||||
ret.onAction = &friendlist_onMessage; // Action has identical behaviour to message
|
ret.onAction = &friendlist_onMessage; /* Action has identical behaviour to message */
|
||||||
ret.onNickChange = &friendlist_onNickChange;
|
ret.onNickChange = &friendlist_onNickChange;
|
||||||
ret.onStatusChange = &friendlist_onStatusChange;
|
ret.onStatusChange = &friendlist_onStatusChange;
|
||||||
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
|
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
|
||||||
ret.onFileSendRequest = &friendlist_onFileSendRequest;
|
ret.onFileSendRequest = &friendlist_onFileSendRequest;
|
||||||
ret.onGroupInvite = &friendlist_onGroupInvite;
|
ret.onGroupInvite = &friendlist_onGroupInvite;
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
ret.onInvite = &friendlist_onAv;
|
||||||
|
ret.onRinging = &friendlist_onAv;
|
||||||
|
ret.onStarting = &friendlist_onAv;
|
||||||
|
ret.onEnding = &friendlist_onAv;
|
||||||
|
ret.onError = &friendlist_onAv;
|
||||||
|
ret.onStart = &friendlist_onAv;
|
||||||
|
ret.onCancel = &friendlist_onAv;
|
||||||
|
ret.onReject = &friendlist_onAv;
|
||||||
|
ret.onEnd = &friendlist_onAv;
|
||||||
|
ret.onTimeout = &friendlist_onAv;
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
strcpy(ret.name, "friends");
|
strcpy(ret.name, "friends");
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -42,10 +42,17 @@ typedef struct {
|
|||||||
struct FileReceiver file_receiver;
|
struct FileReceiver file_receiver;
|
||||||
} ToxicFriend;
|
} ToxicFriend;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int num;
|
||||||
|
bool active;
|
||||||
|
} PendingDel;
|
||||||
|
|
||||||
ToxWindow new_friendlist(void);
|
ToxWindow new_friendlist(void);
|
||||||
void disable_chatwin(int f_num);
|
void disable_chatwin(int f_num);
|
||||||
int get_friendnum(uint8_t *name);
|
int get_friendnum(uint8_t *name);
|
||||||
|
|
||||||
|
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int num, bool sort);
|
||||||
|
|
||||||
/* sorts friendlist_index first by connection status then alphabetically */
|
/* sorts friendlist_index first by connection status then alphabetically */
|
||||||
void sort_friendlist_index(Tox *m);
|
void sort_friendlist_index(Tox *m);
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
|||||||
|
|
||||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||||
{
|
{
|
||||||
if (num_active_windows() >= MAX_WINDOWS_NUM) {
|
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||||
wattron(window, COLOR_PAIR(RED));
|
wattron(window, COLOR_PAIR(RED));
|
||||||
wprintw(window, " * Warning: Too many windows are open.\n");
|
wprintw(window, " * Warning: Too many windows are open.\n");
|
||||||
wattron(window, COLOR_PAIR(RED));
|
wattron(window, COLOR_PAIR(RED));
|
||||||
@ -367,7 +367,7 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
|
|
||||||
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
|
||||||
wprintw(window, " /accept <n> : Accept friend request\n");
|
wprintw(window, " /accept <n> : Accept friend request\n");
|
||||||
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
|
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||||
wprintw(window, " /status <type> <msg> : Set status with optional note\n");
|
wprintw(window, " /status <type> <msg> : Set status with optional note\n");
|
||||||
wprintw(window, " /note <msg> : Set a personal note\n");
|
wprintw(window, " /note <msg> : Set a personal note\n");
|
||||||
wprintw(window, " /nick <nick> : Set your nickname\n");
|
wprintw(window, " /nick <nick> : Set your nickname\n");
|
||||||
@ -377,7 +377,12 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
|||||||
wprintw(window, " /help : Print this message again\n");
|
wprintw(window, " /help : Print this message again\n");
|
||||||
wprintw(window, " /clear : Clear the window\n");
|
wprintw(window, " /clear : Clear the window\n");
|
||||||
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
wprintw(window, " /quit or /exit : Exit Toxic\n");
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
wprintw(window, " /lsdev <type> : List devices where type: in|out\n");
|
||||||
|
wprintw(window, " /sdev <type> <id> : Set active device\n");
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
|
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n");
|
||||||
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
|
wprintw(window, " * Use ctrl-o and ctrl-p to navigate through the tabs.\n\n");
|
||||||
|
@ -32,3 +32,8 @@ void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]
|
|||||||
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
void cmd_list_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
void cmd_change_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
@ -43,7 +43,7 @@ static GroupChat groupchats[MAX_WINDOWS_NUM];
|
|||||||
static int max_groupchat_index = 0;
|
static int max_groupchat_index = 0;
|
||||||
|
|
||||||
/* temporary until group chats have unique commands */
|
/* temporary until group chats have unique commands */
|
||||||
extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
extern const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
|
||||||
|
|
||||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
|
||||||
{
|
{
|
||||||
@ -123,6 +123,9 @@ static void print_groupchat_help(ChatContext *ctx)
|
|||||||
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
|
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
|
||||||
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
|
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
|
||||||
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
|
||||||
|
wattron(ctx->history, COLOR_PAIR(WHITE) | A_BOLD);
|
||||||
|
wprintw(ctx->history, " Notice, some friends will be missing names while finding peers\n\n");
|
||||||
|
wattroff(ctx->history, COLOR_PAIR(WHITE) | A_BOLD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||||
@ -168,7 +171,7 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
|||||||
wprintw(ctx->history, "%s\n", msg);
|
wprintw(ctx->history, "%s\n", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, ctx->log, false);
|
write_to_log(msg, nick, ctx->log, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
|
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
|
||||||
@ -204,7 +207,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
|||||||
wprintw(ctx->history, "* %s %s\n", nick, action);
|
wprintw(ctx->history, "* %s %s\n", nick, action);
|
||||||
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
|
|
||||||
add_to_log_buf(action, nick, ctx->log, true);
|
write_to_log(action, nick, ctx->log, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Puts two copies of peerlist in chat instance */
|
/* Puts two copies of peerlist in chat instance */
|
||||||
@ -282,7 +285,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
wprintw(ctx->history, " %s\n", event);
|
wprintw(ctx->history, " %s\n", event);
|
||||||
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
wattroff(ctx->history, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
add_to_log_buf(event, peername, ctx->log, true);
|
write_to_log(event, peername, ctx->log, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
case TOX_CHAT_CHANGE_PEER_DEL:
|
||||||
@ -296,7 +299,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
if (groupchats[self->num].side_pos > 0)
|
if (groupchats[self->num].side_pos > 0)
|
||||||
--groupchats[self->num].side_pos;
|
--groupchats[self->num].side_pos;
|
||||||
|
|
||||||
add_to_log_buf(event, oldpeername, ctx->log, true);
|
write_to_log(event, oldpeername, ctx->log, true);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||||
@ -314,7 +317,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
|||||||
|
|
||||||
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
|
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
|
||||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
|
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
|
||||||
add_to_log_buf(tmp_event, oldpeername, ctx->log, true);
|
write_to_log(tmp_event, oldpeername, ctx->log, true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,13 +330,13 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
/* uint8_t selfname[TOX_MAX_NAME_LENGTH];
|
||||||
// tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
|
||||||
|
|
||||||
// print_time(ctx->history);
|
print_time(ctx->history);
|
||||||
// wattron(ctx->history, COLOR_PAIR(YELLOW));
|
wattron(ctx->history, COLOR_PAIR(YELLOW));
|
||||||
// wprintw(ctx->history, "* %s %s\n", selfname, action);
|
wprintw(ctx->history, "* %s %s\n", selfname, action);
|
||||||
// wattroff(ctx->history, COLOR_PAIR(YELLOW));
|
wattroff(ctx->history, COLOR_PAIR(YELLOW)); */
|
||||||
|
|
||||||
if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) {
|
if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) {
|
||||||
wattron(ctx->history, COLOR_PAIR(RED));
|
wattron(ctx->history, COLOR_PAIR(RED));
|
||||||
|
68
src/log.c
68
src/log.c
@ -20,6 +20,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -28,7 +32,7 @@
|
|||||||
#include "toxic_windows.h"
|
#include "toxic_windows.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
|
||||||
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
|
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
||||||
{
|
{
|
||||||
if (!log->log_on)
|
if (!log->log_on)
|
||||||
@ -49,7 +53,7 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
|||||||
} else {
|
} else {
|
||||||
struct tm *tminfo = get_time();
|
struct tm *tminfo = get_time();
|
||||||
snprintf(ident, sizeof(ident),
|
snprintf(ident, sizeof(ident),
|
||||||
"%04d-%02d-%02d[%02d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday,
|
"%04d-%02d-%02d[%d:%02d:%02d]", tminfo->tm_year+1900,tminfo->tm_mon+1, tminfo->tm_mday,
|
||||||
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
|
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
|
||||||
path_len += strlen(ident) + 1;
|
path_len += strlen(ident) + 1;
|
||||||
}
|
}
|
||||||
@ -59,52 +63,32 @@ void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(log->log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
uint8_t log_path[MAX_STR_SIZE];
|
||||||
|
|
||||||
|
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
|
||||||
user_config_dir, CONFIGDIR, name, ident);
|
user_config_dir, CONFIGDIR, name, ident);
|
||||||
|
|
||||||
FILE *logfile = fopen(log->log_path, "a");
|
log->file = fopen(log_path, "a");
|
||||||
|
|
||||||
if (logfile == NULL) {
|
if (log->file == NULL) {
|
||||||
log->log_on = false;
|
log->log_on = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(logfile, "\n*** NEW SESSION ***\n\n");
|
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
||||||
|
|
||||||
fclose(logfile);
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
|
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
|
||||||
This is triggered when the log buffer is full, but may be forced. */
|
|
||||||
void write_to_log(struct chatlog *log)
|
|
||||||
{
|
{
|
||||||
if (!log->log_on)
|
if (!log->log_on)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
FILE *logfile = fopen(log->log_path, "a");
|
if (log->file == NULL) {
|
||||||
|
|
||||||
if (logfile == NULL) {
|
|
||||||
log->log_on = false;
|
log->log_on = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < log->pos; ++i)
|
|
||||||
fprintf(logfile, "%s", log->log_buf[i]);
|
|
||||||
|
|
||||||
log->pos = 0;
|
|
||||||
fclose(logfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
|
|
||||||
If event is true, formats line as an event, e.g. * name has gone offline */
|
|
||||||
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
|
|
||||||
{
|
|
||||||
if (!log->log_on)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
|
||||||
|
|
||||||
if (event)
|
if (event)
|
||||||
@ -113,26 +97,32 @@ void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event
|
|||||||
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
|
||||||
|
|
||||||
struct tm *tminfo = get_time();
|
struct tm *tminfo = get_time();
|
||||||
snprintf(log->log_buf[log->pos], MAX_LOG_LINE_SIZE, "%04d/%02d/%02d [%02d:%02d:%02d] %s %s\n",
|
fprintf(log->file,"%04d/%02d/%02d [%d:%02d:%02d] %s %s\n", tminfo->tm_year+1900,
|
||||||
tminfo->tm_year + 1900, tminfo->tm_mon + 1, tminfo->tm_mday,
|
tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour, tminfo->tm_min,
|
||||||
tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec, name_frmt, msg);
|
tminfo->tm_sec, name_frmt, msg);
|
||||||
|
|
||||||
if (++(log->pos) >= MAX_LOG_BUF_LINES)
|
uint64_t curtime = (uint64_t) time(NULL);
|
||||||
write_to_log(log);
|
|
||||||
|
if (timed_out(log->lastwrite, curtime, LOG_FLUSH_LIMIT)) {
|
||||||
|
fflush(log->file);
|
||||||
|
log->lastwrite = curtime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
|
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log)
|
||||||
{
|
{
|
||||||
log->log_on = true;
|
log->log_on = true;
|
||||||
|
|
||||||
if (!log->log_path[0])
|
if (log->file == NULL)
|
||||||
init_logging_session(name, key, log);
|
init_logging_session(name, key, log);
|
||||||
}
|
}
|
||||||
|
|
||||||
void log_disable(struct chatlog *log)
|
void log_disable(struct chatlog *log)
|
||||||
{
|
{
|
||||||
if (log->log_on) {
|
log->log_on = false;
|
||||||
write_to_log(log);
|
|
||||||
log->log_on = false;
|
if (log->file != NULL) {
|
||||||
|
fclose(log->file);
|
||||||
|
log->file = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
14
src/log.h
14
src/log.h
@ -20,16 +20,14 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* gets the log path by appending to the config dir the name and a pseudo-unique identity */
|
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
|
||||||
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
|
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log);
|
||||||
|
|
||||||
/* Adds line/event to log_buf with timestamp and name. If buf is full, triggers write_to_log.
|
/* formats/writes line to log file */
|
||||||
If event is true, formats line as an event, e.g. * name has gone offline */
|
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
|
||||||
void add_to_log_buf(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
|
|
||||||
|
|
||||||
/* writes contents from a chatcontext's log buffer to respective log file and resets log pos.
|
|
||||||
This is triggered automatically when the log buffer is full, but may be forced. */
|
|
||||||
void write_to_log(struct chatlog *log);
|
|
||||||
|
|
||||||
|
/* enables logging for specified log and creates/fetches file if necessary */
|
||||||
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
|
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
|
||||||
|
|
||||||
|
/* disables logging for specified log and closes file */
|
||||||
void log_disable(struct chatlog *log);
|
void log_disable(struct chatlog *log);
|
||||||
|
220
src/main.c
220
src/main.c
@ -48,6 +48,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
@ -57,22 +58,26 @@
|
|||||||
#include "friendlist.h"
|
#include "friendlist.h"
|
||||||
#include "prompt.h"
|
#include "prompt.h"
|
||||||
#include "misc_tools.h"
|
#include "misc_tools.h"
|
||||||
|
#include "file_senders.h"
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include "audio_call.h"
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
#ifndef PACKAGE_DATADIR
|
#ifndef PACKAGE_DATADIR
|
||||||
#define PACKAGE_DATADIR "."
|
#define PACKAGE_DATADIR "."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
ToxAv* av;
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
/* Export for use in Callbacks */
|
/* Export for use in Callbacks */
|
||||||
char *DATA_FILE = NULL;
|
char *DATA_FILE = NULL;
|
||||||
char *SRVLIST_FILE = NULL;
|
|
||||||
|
|
||||||
ToxWindow *prompt = NULL;
|
ToxWindow *prompt = NULL;
|
||||||
|
|
||||||
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
|
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
|
||||||
|
|
||||||
FileSender file_senders[MAX_FILES];
|
|
||||||
uint8_t max_file_senders_index;
|
|
||||||
|
|
||||||
void on_window_resize(int sig)
|
void on_window_resize(int sig)
|
||||||
{
|
{
|
||||||
endwin();
|
endwin();
|
||||||
@ -119,8 +124,12 @@ static Tox *init_tox(int ipv4)
|
|||||||
int ipv6 = !ipv4;
|
int ipv6 = !ipv4;
|
||||||
Tox *m = tox_new(ipv6);
|
Tox *m = tox_new(ipv6);
|
||||||
|
|
||||||
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
|
/*
|
||||||
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
|
* TOX_ENABLE_IPV6_DEFAULT is always 1.
|
||||||
|
* Checking it is redundant, this *should* be doing ipv4 fallback
|
||||||
|
*/
|
||||||
|
if (ipv6 && m == NULL) {
|
||||||
|
fprintf(stderr, "IPv6 didn't initialize, trying IPv4\n");
|
||||||
m = tox_new(0);
|
m = tox_new(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,7 +158,7 @@ static Tox *init_tox(int ipv4)
|
|||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
|
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
|
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); /* This used to users of other Unixes are hipsters */
|
||||||
#else
|
#else
|
||||||
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
|
||||||
#endif
|
#endif
|
||||||
@ -159,28 +168,26 @@ static Tox *init_tox(int ipv4)
|
|||||||
|
|
||||||
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
||||||
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||||
#define MAXSERVERS 50
|
#define MAXNODES 50
|
||||||
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
||||||
|
|
||||||
static int linecnt = 0;
|
static int linecnt = 0;
|
||||||
static char servers[MAXSERVERS][SERVERLEN];
|
static char nodes[MAXNODES][NODELEN];
|
||||||
static uint16_t ports[MAXSERVERS];
|
static uint16_t ports[MAXNODES];
|
||||||
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
|
static uint8_t keys[MAXNODES][TOX_CLIENT_ID_SIZE];
|
||||||
|
|
||||||
static int serverlist_load(const char *filename)
|
static int nodelist_load(char *filename)
|
||||||
{
|
{
|
||||||
FILE *fp = NULL;
|
|
||||||
|
|
||||||
if (!filename)
|
if (!filename)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
fp = fopen(filename, "r");
|
FILE *fp = fopen(filename, "r");
|
||||||
|
|
||||||
if (fp == NULL)
|
if (fp == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
char line[MAXLINE];
|
char line[MAXLINE];
|
||||||
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
|
while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) {
|
||||||
if (strlen(line) > MINLINE) {
|
if (strlen(line) > MINLINE) {
|
||||||
char *name = strtok(line, " ");
|
char *name = strtok(line, " ");
|
||||||
char *port = strtok(NULL, " ");
|
char *port = strtok(NULL, " ");
|
||||||
@ -189,8 +196,8 @@ static int serverlist_load(const char *filename)
|
|||||||
if (name == NULL || port == NULL || key_ascii == NULL)
|
if (name == NULL || port == NULL || key_ascii == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
strncpy(servers[linecnt], name, SERVERLEN);
|
strncpy(nodes[linecnt], name, NODELEN);
|
||||||
servers[linecnt][SERVERLEN - 1] = 0;
|
nodes[linecnt][NODELEN - 1] = 0;
|
||||||
ports[linecnt] = htons(atoi(port));
|
ports[linecnt] = htons(atoi(port));
|
||||||
|
|
||||||
uint8_t *key_binary = hex_string_to_bin(key_ascii);
|
uint8_t *key_binary = hex_string_to_bin(key_ascii);
|
||||||
@ -212,17 +219,17 @@ static int serverlist_load(const char *filename)
|
|||||||
|
|
||||||
int init_connection_helper(Tox *m, int line)
|
int init_connection_helper(Tox *m, int line)
|
||||||
{
|
{
|
||||||
return tox_bootstrap_from_address(m, servers[line], TOX_ENABLE_IPV6_DEFAULT,
|
return tox_bootstrap_from_address(m, nodes[line], TOX_ENABLE_IPV6_DEFAULT,
|
||||||
ports[line], keys[line]);
|
ports[line], keys[line]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Connects to a random DHT server listed in the DHTservers file
|
/* Connects to a random DHT node listed in the DHTnodes file
|
||||||
*
|
*
|
||||||
* return codes:
|
* return codes:
|
||||||
* 1: failed to open server file
|
* 1: failed to open node file
|
||||||
* 2: no line of sufficient length in server file
|
* 2: no line of sufficient length in node file
|
||||||
* 3: failed to resolve name to IP
|
* 3: failed to resolve name to IP
|
||||||
* 4: serverlist file contains no acceptable line
|
* 4: nodelist file contains no acceptable line
|
||||||
*/
|
*/
|
||||||
static bool srvlist_loaded = false;
|
static bool srvlist_loaded = false;
|
||||||
|
|
||||||
@ -230,28 +237,19 @@ static bool srvlist_loaded = false;
|
|||||||
|
|
||||||
int init_connection(Tox *m)
|
int init_connection(Tox *m)
|
||||||
{
|
{
|
||||||
if (linecnt > 0) /* already loaded serverlist */
|
if (linecnt > 0) /* already loaded nodelist */
|
||||||
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
|
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
|
||||||
|
|
||||||
/* only once:
|
/* only once:
|
||||||
* - load the serverlist
|
* - load the nodelist
|
||||||
* - connect to "everyone" inside
|
* - connect to "everyone" inside
|
||||||
*/
|
*/
|
||||||
if (!srvlist_loaded) {
|
if (!srvlist_loaded) {
|
||||||
srvlist_loaded = true;
|
srvlist_loaded = true;
|
||||||
int res = serverlist_load(SRVLIST_FILE);
|
int res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
|
||||||
|
|
||||||
if (res) {
|
if (linecnt < 1)
|
||||||
/* Fallback on the provided DHTServers in /usr/share or /usr/local/share,
|
return res;
|
||||||
so new starts of toxic will connect to the DHT. */
|
|
||||||
res = serverlist_load(PACKAGE_DATADIR "/DHTservers");
|
|
||||||
|
|
||||||
if (res)
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!linecnt)
|
|
||||||
return 2;
|
|
||||||
|
|
||||||
res = 3;
|
res = 3;
|
||||||
int i;
|
int i;
|
||||||
@ -264,7 +262,7 @@ int init_connection(Tox *m)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* empty serverlist file */
|
/* empty nodelist file */
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -277,18 +275,18 @@ static void do_connection(Tox *m, ToxWindow *prompt)
|
|||||||
bool is_connected = tox_isconnected(m);
|
bool is_connected = tox_isconnected(m);
|
||||||
|
|
||||||
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
|
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
|
||||||
prep_prompt_win();
|
|
||||||
if (!conn_err) {
|
if (!conn_err) {
|
||||||
wprintw(prompt->window, "Establishing connection...\n");
|
if ((conn_err = init_connection(m))) {
|
||||||
if ((conn_err = init_connection(m)))
|
prep_prompt_win();
|
||||||
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (!dht_on && is_connected) {
|
} else if (!dht_on && is_connected) {
|
||||||
dht_on = true;
|
dht_on = true;
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
prompt_update_connectionstatus(prompt, dht_on);
|
||||||
|
|
||||||
prep_prompt_win();
|
prep_prompt_win();
|
||||||
wprintw(prompt->window, "\nDHT connected.\n");
|
wprintw(prompt->window, "DHT connected.\n");
|
||||||
} else if (dht_on && !is_connected) {
|
} else if (dht_on && !is_connected) {
|
||||||
dht_on = false;
|
dht_on = false;
|
||||||
prompt_update_connectionstatus(prompt, dht_on);
|
prompt_update_connectionstatus(prompt, dht_on);
|
||||||
@ -298,6 +296,15 @@ static void do_connection(Tox *m, ToxWindow *prompt)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void load_friendlist(Tox *m)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
uint32_t numfriends = tox_count_friendlist(m);
|
||||||
|
|
||||||
|
for (i = 0; i < numfriends; ++i)
|
||||||
|
friendlist_onFriendAdded(NULL, m, i, false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store Messenger to given location
|
* Store Messenger to given location
|
||||||
* Return 0 stored successfully
|
* Return 0 stored successfully
|
||||||
@ -376,12 +383,7 @@ static void load_data(Tox *m, char *path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tox_load(m, buf, len);
|
tox_load(m, buf, len);
|
||||||
|
load_friendlist(m);
|
||||||
uint32_t i = 0;
|
|
||||||
uint8_t name[TOX_MAX_NAME_LENGTH];
|
|
||||||
|
|
||||||
while (tox_get_name(m, i, name) != -1)
|
|
||||||
on_friendadded(m, i++, false);
|
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
@ -396,93 +398,20 @@ static void load_data(Tox *m, char *path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_file_sender(int i)
|
|
||||||
{
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
|
||||||
|
|
||||||
int j;
|
|
||||||
|
|
||||||
for (j = max_file_senders_index; j > 0; --j) {
|
|
||||||
if (file_senders[j-1].active)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
max_file_senders_index = j;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void do_file_senders(Tox *m)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (!file_senders[i].active)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
uint8_t *pathname = file_senders[i].pathname;
|
|
||||||
uint8_t filenum = file_senders[i].filenum;
|
|
||||||
int friendnum = file_senders[i].friendnum;
|
|
||||||
FILE *fp = file_senders[i].file;
|
|
||||||
uint64_t current_time = (uint64_t) time(NULL);
|
|
||||||
|
|
||||||
/* If file transfer has timed out kill transfer and send kill control */
|
|
||||||
if (timed_out(file_senders[i].timestamp, current_time, TIMEOUT_FILESENDER)) {
|
|
||||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
|
||||||
|
|
||||||
if (ctx != NULL) {
|
|
||||||
wprintw(ctx->history, "File transfer for '%s' timed out.\n", pathname);
|
|
||||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
|
||||||
close_file_sender(i);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (tox_file_send_data(m, friendnum, filenum, file_senders[i].nextpiece,
|
|
||||||
file_senders[i].piecelen) == -1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
file_senders[i].timestamp = current_time;
|
|
||||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
|
||||||
tox_file_data_size(m, friendnum), fp);
|
|
||||||
|
|
||||||
if (file_senders[i].piecelen == 0) {
|
|
||||||
ChatContext *ctx = file_senders[i].toxwin->chatwin;
|
|
||||||
|
|
||||||
if (ctx != NULL) {
|
|
||||||
wprintw(ctx->history, "File '%s' successfuly sent.\n", pathname);
|
|
||||||
alert_window(file_senders[i].toxwin, WINDOW_ALERT_2, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
|
||||||
close_file_sender(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void exit_toxic(Tox *m)
|
void exit_toxic(Tox *m)
|
||||||
{
|
{
|
||||||
store_data(m, DATA_FILE);
|
store_data(m, DATA_FILE);
|
||||||
|
close_all_file_senders();
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < max_file_senders_index; ++i) {
|
|
||||||
if (file_senders[i].active)
|
|
||||||
fclose(file_senders[i].file);
|
|
||||||
}
|
|
||||||
|
|
||||||
kill_all_windows();
|
kill_all_windows();
|
||||||
free(DATA_FILE);
|
|
||||||
free(SRVLIST_FILE);
|
|
||||||
free(prompt->stb);
|
|
||||||
log_disable(prompt->promptbuf->log);
|
log_disable(prompt->promptbuf->log);
|
||||||
|
free(DATA_FILE);
|
||||||
|
free(prompt->stb);
|
||||||
free(prompt->promptbuf->log);
|
free(prompt->promptbuf->log);
|
||||||
free(prompt->promptbuf);
|
free(prompt->promptbuf);
|
||||||
tox_kill(m);
|
tox_kill(m);
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
terminate_audio(prompt, av);
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
endwin();
|
endwin();
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
@ -536,7 +465,7 @@ int main(int argc, char *argv[])
|
|||||||
if (DATA_FILE != NULL) {
|
if (DATA_FILE != NULL) {
|
||||||
strcpy(DATA_FILE, user_config_dir);
|
strcpy(DATA_FILE, user_config_dir);
|
||||||
strcat(DATA_FILE, CONFIGDIR);
|
strcat(DATA_FILE, CONFIGDIR);
|
||||||
strcat(DATA_FILE, "data");
|
strcat(DATA_FILE, "data");
|
||||||
} else {
|
} else {
|
||||||
endwin();
|
endwin();
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
fprintf(stderr, "malloc() failed. Aborting...\n");
|
||||||
@ -545,17 +474,6 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
|
|
||||||
if (SRVLIST_FILE != NULL) {
|
|
||||||
strcpy(SRVLIST_FILE, user_config_dir);
|
|
||||||
strcat(SRVLIST_FILE, CONFIGDIR);
|
|
||||||
strcat(SRVLIST_FILE, "DHTservers");
|
|
||||||
} else {
|
|
||||||
endwin();
|
|
||||||
fprintf(stderr, "malloc() failed. Aborting...\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(user_config_dir);
|
free(user_config_dir);
|
||||||
|
|
||||||
init_term();
|
init_term();
|
||||||
@ -569,6 +487,22 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
prompt = init_windows(m);
|
prompt = init_windows(m);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
attron(COLOR_PAIR(RED) | A_BOLD);
|
||||||
|
wprintw(prompt->window, "Starting audio...\n");
|
||||||
|
attroff(COLOR_PAIR(RED) | A_BOLD);
|
||||||
|
|
||||||
|
av = init_audio(prompt, m);
|
||||||
|
|
||||||
|
if ( errors() == NoError )
|
||||||
|
wprintw(prompt->window, "Audio started with no problems.\n");
|
||||||
|
else /* Get error code and stuff */
|
||||||
|
wprintw(prompt->window, "Error starting audio!\n");
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
if (f_loadfromfile)
|
if (f_loadfromfile)
|
||||||
load_data(m, DATA_FILE);
|
load_data(m, DATA_FILE);
|
||||||
|
|
||||||
@ -589,8 +523,8 @@ int main(int argc, char *argv[])
|
|||||||
prompt_init_statusbar(prompt, m);
|
prompt_init_statusbar(prompt, m);
|
||||||
sort_friendlist_index(m);
|
sort_friendlist_index(m);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
do_toxic(m, prompt);
|
do_toxic(m, prompt);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@ -29,7 +33,7 @@
|
|||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
// XXX: FIX
|
/* XXX: FIX */
|
||||||
unsigned char *hex_string_to_bin(char hex_string[])
|
unsigned char *hex_string_to_bin(char hex_string[])
|
||||||
{
|
{
|
||||||
size_t len = strlen(hex_string);
|
size_t len = strlen(hex_string);
|
||||||
@ -66,12 +70,12 @@ void print_time(WINDOW *window)
|
|||||||
struct tm *timeinfo = get_time();
|
struct tm *timeinfo = get_time();
|
||||||
|
|
||||||
wattron(window, COLOR_PAIR(BLUE));
|
wattron(window, COLOR_PAIR(BLUE));
|
||||||
wprintw(window, "[%02d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
wprintw(window, "[%d:%02d:%02d] ", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec);
|
||||||
wattroff(window,COLOR_PAIR(BLUE));
|
wattroff(window,COLOR_PAIR(BLUE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if the string is empty, false otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
bool string_is_empty(char *string)
|
int string_is_empty(char *string)
|
||||||
{
|
{
|
||||||
return string[0] == '\0';
|
return string[0] == '\0';
|
||||||
}
|
}
|
||||||
@ -105,7 +109,7 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
|
|||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert wide characters to multibyte string: string returned must be free'd */
|
/* convert wide characters to multibyte string: string returned must be freed */
|
||||||
uint8_t *wcs_to_mbs(wchar_t *string)
|
uint8_t *wcs_to_mbs(wchar_t *string)
|
||||||
{
|
{
|
||||||
uint8_t *ret = NULL;
|
uint8_t *ret = NULL;
|
||||||
@ -152,8 +156,8 @@ char *wc_to_char(wchar_t ch)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if connection has timed out, false otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
|
||||||
{
|
{
|
||||||
return timestamp + timeout <= curtime;
|
return timestamp + timeout <= curtime;
|
||||||
}
|
}
|
||||||
@ -185,23 +189,23 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *nick2)
|
|||||||
return strcasecmp((const char *) nick1, (const char *) nick2);
|
return strcasecmp((const char *) nick1, (const char *) nick2);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns true if nick is valid. A valid toxic nick:
|
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||||
- cannot be empty
|
- cannot be empty
|
||||||
- cannot start with a space
|
- cannot start with a space
|
||||||
- must not contain contiguous spaces */
|
- must not contain contiguous spaces */
|
||||||
bool valid_nick(uint8_t *nick)
|
int valid_nick(uint8_t *nick)
|
||||||
{
|
{
|
||||||
if (!nick[0] || nick[0] == ' ')
|
if (!nick[0] || nick[0] == ' ')
|
||||||
return false;
|
return 0;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; nick[i]; ++i) {
|
for (i = 0; nick[i]; ++i) {
|
||||||
if (nick[i] == ' ' && nick[i+1] == ' ')
|
if (nick[i] == ' ' && nick[i+1] == ' ')
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Moves cursor to the end of the line in given window */
|
/* Moves cursor to the end of the line in given window */
|
||||||
@ -220,13 +224,13 @@ void get_file_name(uint8_t *pathname, uint8_t *namebuf)
|
|||||||
while (idx >= 0 && pathname[idx] == '/')
|
while (idx >= 0 && pathname[idx] == '/')
|
||||||
pathname[idx--] = '\0';
|
pathname[idx--] = '\0';
|
||||||
|
|
||||||
uint8_t *filename = strrchr(pathname, '/'); // Try unix style paths
|
uint8_t *filename = strrchr(pathname, '/'); /* Try unix style paths */
|
||||||
|
|
||||||
if (filename != NULL) {
|
if (filename != NULL) {
|
||||||
if (!strlen(++filename))
|
if (!strlen(++filename))
|
||||||
filename = pathname;
|
filename = pathname;
|
||||||
} else {
|
} else {
|
||||||
filename = strrchr(pathname, '\\'); // Try windows style paths
|
filename = strrchr(pathname, '\\'); /* Try windows style paths */
|
||||||
|
|
||||||
if (filename == NULL)
|
if (filename == NULL)
|
||||||
filename = pathname;
|
filename = pathname;
|
||||||
|
@ -32,8 +32,8 @@ struct tm *get_time(void);
|
|||||||
/* Prints the time to given window */
|
/* Prints the time to given window */
|
||||||
void print_time(WINDOW *window);
|
void print_time(WINDOW *window);
|
||||||
|
|
||||||
/* Returns true if the string is empty, false otherwise */
|
/* Returns 1 if the string is empty, 0 otherwise */
|
||||||
bool string_is_empty(char *string);
|
int string_is_empty(char *string);
|
||||||
|
|
||||||
/* convert a multibyte string to a wide character string (must provide buffer) */
|
/* convert a multibyte string to a wide character string (must provide buffer) */
|
||||||
int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n);
|
int char_to_wcs_buf(wchar_t *buf, const uint8_t *string, size_t n);
|
||||||
@ -48,8 +48,8 @@ uint8_t *wcs_to_mbs(wchar_t *string);
|
|||||||
/* convert a wide char to multibyte char */
|
/* convert a wide char to multibyte char */
|
||||||
char *wc_to_char(wchar_t ch);
|
char *wc_to_char(wchar_t ch);
|
||||||
|
|
||||||
/* Returns true if connection has timed out, false otherwise */
|
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||||
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
int timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
|
||||||
|
|
||||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
/* 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);
|
||||||
|
25
src/prompt.c
25
src/prompt.c
@ -54,6 +54,13 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
|||||||
{ "/note" },
|
{ "/note" },
|
||||||
{ "/quit" },
|
{ "/quit" },
|
||||||
{ "/status" },
|
{ "/status" },
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
{ "/lsdev" },
|
||||||
|
{ "/sdev" },
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* prevents input string from eating system messages: call this prior to printing a prompt message
|
/* prevents input string from eating system messages: call this prior to printing a prompt message
|
||||||
@ -398,32 +405,32 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u
|
|||||||
uint8_t *msg;
|
uint8_t *msg;
|
||||||
|
|
||||||
if (status == 1) {
|
if (status == 1) {
|
||||||
msg = "has come online\n";
|
msg = "has come online";
|
||||||
wattron(self->window, COLOR_PAIR(GREEN));
|
wattron(self->window, COLOR_PAIR(GREEN));
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "* %s ", nick);
|
wprintw(self->window, "* %s ", nick);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "%s", msg);
|
wprintw(self->window, "%s\n", msg);
|
||||||
wattroff(self->window, COLOR_PAIR(GREEN));
|
wattroff(self->window, COLOR_PAIR(GREEN));
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, prt->log, true);
|
write_to_log(msg, nick, prt->log, true);
|
||||||
alert_window(self, WINDOW_ALERT_2, false);
|
alert_window(self, WINDOW_ALERT_2, false);
|
||||||
} else {
|
} else {
|
||||||
msg = "has gone offline\n";
|
msg = "has gone offline";
|
||||||
wattron(self->window, COLOR_PAIR(RED));
|
wattron(self->window, COLOR_PAIR(RED));
|
||||||
wattron(self->window, A_BOLD);
|
wattron(self->window, A_BOLD);
|
||||||
wprintw(self->window, "* %s ", nick);
|
wprintw(self->window, "* %s ", nick);
|
||||||
wattroff(self->window, A_BOLD);
|
wattroff(self->window, A_BOLD);
|
||||||
wprintw(self->window, "%s", msg);
|
wprintw(self->window, "%s\n", msg);
|
||||||
wattroff(self->window, COLOR_PAIR(RED));
|
wattroff(self->window, COLOR_PAIR(RED));
|
||||||
|
|
||||||
add_to_log_buf(msg, nick, prt->log, true);
|
write_to_log(msg, nick, prt->log, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
|
static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data, uint16_t length)
|
||||||
{
|
{
|
||||||
// make sure message data is null-terminated
|
/* make sure message data is null-terminated */
|
||||||
data[length - 1] = 0;
|
data[length - 1] = 0;
|
||||||
PromptBuf *prt = self->promptbuf;
|
PromptBuf *prt = self->promptbuf;
|
||||||
prep_prompt_win();
|
prep_prompt_win();
|
||||||
@ -434,14 +441,14 @@ static void prompt_onFriendRequest(ToxWindow *self, uint8_t *key, uint8_t *data,
|
|||||||
uint8_t msg[MAX_STR_SIZE];
|
uint8_t msg[MAX_STR_SIZE];
|
||||||
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
|
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
|
||||||
wprintw(self->window, "%s", msg);
|
wprintw(self->window, "%s", msg);
|
||||||
add_to_log_buf(msg, "", prt->log, true);
|
write_to_log(msg, "", prt->log, true);
|
||||||
|
|
||||||
int n = add_friend_request(key);
|
int n = add_friend_request(key);
|
||||||
|
|
||||||
if (n == -1) {
|
if (n == -1) {
|
||||||
uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
|
uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
|
||||||
wprintw(self->window, "%s", errmsg);
|
wprintw(self->window, "%s", errmsg);
|
||||||
add_to_log_buf(errmsg, "", prt->log, true);
|
write_to_log(errmsg, "", prt->log, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,12 @@
|
|||||||
|
|
||||||
#define X_OFST 2 /* offset to account for prompt char */
|
#define X_OFST 2 /* offset to account for prompt char */
|
||||||
|
|
||||||
#define AC_NUM_GLOB_COMMANDS 15
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#define AC_NUM_GLOB_COMMANDS 17
|
||||||
|
#else
|
||||||
|
#define AC_NUM_GLOB_COMMANDS 15
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
ToxWindow new_prompt(void);
|
ToxWindow new_prompt(void);
|
||||||
void prep_prompt_win(void);
|
void prep_prompt_win(void);
|
||||||
|
@ -20,6 +20,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -33,7 +33,11 @@
|
|||||||
|
|
||||||
#include <tox/tox.h>
|
#include <tox/tox.h>
|
||||||
|
|
||||||
#define UNKNOWN_NAME "Unknown"
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
#include <tox/toxav.h>
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
|
#define UNKNOWN_NAME "Anonymous"
|
||||||
|
|
||||||
#define MAX_WINDOWS_NUM 32
|
#define MAX_WINDOWS_NUM 32
|
||||||
#define MAX_FRIENDS_NUM 100
|
#define MAX_FRIENDS_NUM 100
|
||||||
@ -78,7 +82,7 @@ enum {
|
|||||||
|
|
||||||
/* Fixes text color problem on some terminals.
|
/* Fixes text color problem on some terminals.
|
||||||
Uncomment if necessary */
|
Uncomment if necessary */
|
||||||
//#define URXVT_FIX
|
/* #define URXVT_FIX */
|
||||||
|
|
||||||
typedef struct ToxWindow ToxWindow;
|
typedef struct ToxWindow ToxWindow;
|
||||||
typedef struct StatusBar StatusBar;
|
typedef struct StatusBar StatusBar;
|
||||||
@ -106,6 +110,21 @@ struct ToxWindow {
|
|||||||
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
|
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
|
||||||
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
|
void(*onTypingChange)(ToxWindow *, Tox *, int, int);
|
||||||
|
|
||||||
|
#ifdef _SUPPORT_AUDIO
|
||||||
|
|
||||||
|
void(*onInvite)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onRinging)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onStarting)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onEnding)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onError)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onStart)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onCancel)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onReject)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onEnd)(ToxWindow *, ToxAv *);
|
||||||
|
void(*onTimeout)(ToxWindow *, ToxAv *);
|
||||||
|
|
||||||
|
#endif /* _SUPPORT_AUDIO */
|
||||||
|
|
||||||
char name[TOX_MAX_NAME_LENGTH];
|
char name[TOX_MAX_NAME_LENGTH];
|
||||||
int num;
|
int num;
|
||||||
bool active;
|
bool active;
|
||||||
@ -124,6 +143,7 @@ struct ToxWindow {
|
|||||||
PromptBuf *promptbuf;
|
PromptBuf *promptbuf;
|
||||||
StatusBar *stb;
|
StatusBar *stb;
|
||||||
|
|
||||||
|
WINDOW *popup;
|
||||||
WINDOW *window;
|
WINDOW *window;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -138,12 +158,11 @@ struct StatusBar {
|
|||||||
bool is_online;
|
bool is_online;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_LOG_BUF_LINES 10 /* write log_buf contents to log file after this many lines */
|
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||||
#define MAX_LOG_LINE_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* extra room for timestamp */
|
|
||||||
|
|
||||||
struct chatlog {
|
struct chatlog {
|
||||||
uint8_t log_path[MAX_STR_SIZE];
|
FILE *file;
|
||||||
uint8_t log_buf[MAX_LOG_BUF_LINES][MAX_LOG_LINE_SIZE];
|
uint64_t lastwrite;
|
||||||
int pos;
|
int pos;
|
||||||
bool log_on; /* specific to current chat window */
|
bool log_on; /* specific to current chat window */
|
||||||
};
|
};
|
||||||
@ -207,6 +226,7 @@ typedef struct {
|
|||||||
|
|
||||||
struct FileReceiver {
|
struct FileReceiver {
|
||||||
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
|
||||||
|
FILE *files[MAX_FILES];
|
||||||
bool pending[MAX_FILES];
|
bool pending[MAX_FILES];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -234,8 +254,8 @@ void draw_active_window(Tox *m);
|
|||||||
int add_window(Tox *m, ToxWindow w);
|
int add_window(Tox *m, ToxWindow w);
|
||||||
void del_window(ToxWindow *w);
|
void del_window(ToxWindow *w);
|
||||||
void set_active_window(int ch);
|
void set_active_window(int ch);
|
||||||
int num_active_windows(void);
|
int get_num_active_windows(void);
|
||||||
|
|
||||||
/* closes all chat and groupchat windows (should only be called on shutdown) */
|
/* cleans up all chat and groupchat windows (should only be called on shutdown) */
|
||||||
void kill_all_windows(void);
|
void kill_all_windows(void);
|
||||||
#endif
|
#endif
|
||||||
|
@ -39,12 +39,14 @@ static ToxWindow *active_window;
|
|||||||
|
|
||||||
extern ToxWindow *prompt;
|
extern ToxWindow *prompt;
|
||||||
|
|
||||||
|
static int num_active_windows;
|
||||||
|
|
||||||
/* CALLBACKS START */
|
/* CALLBACKS START */
|
||||||
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
|
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onFriendRequest != NULL)
|
if (windows[i].onFriendRequest != NULL)
|
||||||
windows[i].onFriendRequest(&windows[i], public_key, data, length);
|
windows[i].onFriendRequest(&windows[i], public_key, data, length);
|
||||||
}
|
}
|
||||||
@ -54,7 +56,7 @@ void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdat
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onConnectionChange != NULL)
|
if (windows[i].onConnectionChange != NULL)
|
||||||
windows[i].onConnectionChange(&windows[i], m, friendnumber, status);
|
windows[i].onConnectionChange(&windows[i], m, friendnumber, status);
|
||||||
}
|
}
|
||||||
@ -64,7 +66,7 @@ void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onTypingChange != NULL)
|
if (windows[i].onTypingChange != NULL)
|
||||||
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
|
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
|
||||||
}
|
}
|
||||||
@ -74,7 +76,7 @@ void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onMessage != NULL)
|
if (windows[i].onMessage != NULL)
|
||||||
windows[i].onMessage(&windows[i], m, friendnumber, string, length);
|
windows[i].onMessage(&windows[i], m, friendnumber, string, length);
|
||||||
}
|
}
|
||||||
@ -84,7 +86,7 @@ void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onAction != NULL)
|
if (windows[i].onAction != NULL)
|
||||||
windows[i].onAction(&windows[i], m, friendnumber, string, length);
|
windows[i].onAction(&windows[i], m, friendnumber, string, length);
|
||||||
}
|
}
|
||||||
@ -97,7 +99,7 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v
|
|||||||
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onNickChange != NULL)
|
if (windows[i].onNickChange != NULL)
|
||||||
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
|
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
|
||||||
}
|
}
|
||||||
@ -110,7 +112,7 @@ void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onStatusMessageChange != NULL)
|
if (windows[i].onStatusMessageChange != NULL)
|
||||||
windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length);
|
windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length);
|
||||||
}
|
}
|
||||||
@ -120,7 +122,7 @@ void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *user
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onStatusChange != NULL)
|
if (windows[i].onStatusChange != NULL)
|
||||||
windows[i].onStatusChange(&windows[i], m, friendnumber, status);
|
windows[i].onStatusChange(&windows[i], m, friendnumber, status);
|
||||||
}
|
}
|
||||||
@ -130,7 +132,7 @@ void on_friendadded(Tox *m, int friendnumber, bool sort)
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onFriendAdded != NULL)
|
if (windows[i].onFriendAdded != NULL)
|
||||||
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
|
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
|
||||||
}
|
}
|
||||||
@ -144,7 +146,7 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message,
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onGroupMessage != NULL)
|
if (windows[i].onGroupMessage != NULL)
|
||||||
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length);
|
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length);
|
||||||
}
|
}
|
||||||
@ -155,7 +157,7 @@ void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, ui
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onGroupAction != NULL)
|
if (windows[i].onGroupAction != NULL)
|
||||||
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length);
|
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length);
|
||||||
}
|
}
|
||||||
@ -165,7 +167,7 @@ void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *user
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onGroupInvite != NULL)
|
if (windows[i].onGroupInvite != NULL)
|
||||||
windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key);
|
windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key);
|
||||||
}
|
}
|
||||||
@ -175,7 +177,7 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onGroupNamelistChange != NULL)
|
if (windows[i].onGroupNamelistChange != NULL)
|
||||||
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
|
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
|
||||||
}
|
}
|
||||||
@ -186,7 +188,7 @@ void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onFileSendRequest != NULL)
|
if (windows[i].onFileSendRequest != NULL)
|
||||||
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
|
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
|
||||||
filename, filename_length);
|
filename, filename_length);
|
||||||
@ -198,7 +200,7 @@ void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t fi
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onFileControl != NULL)
|
if (windows[i].onFileControl != NULL)
|
||||||
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
|
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
|
||||||
control_type, data, length);
|
control_type, data, length);
|
||||||
@ -210,7 +212,7 @@ void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, u
|
|||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
for (i = 0; i < num_active_windows; ++i) {
|
||||||
if (windows[i].onFileData != NULL)
|
if (windows[i].onFileData != NULL)
|
||||||
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length);
|
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length);
|
||||||
}
|
}
|
||||||
@ -240,6 +242,8 @@ int add_window(Tox *m, ToxWindow w)
|
|||||||
windows[i] = w;
|
windows[i] = w;
|
||||||
w.onInit(&w, m);
|
w.onInit(&w, m);
|
||||||
|
|
||||||
|
++num_active_windows;
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,13 +253,14 @@ int add_window(Tox *m, ToxWindow w)
|
|||||||
/* Deletes window w and cleans up */
|
/* Deletes window w and cleans up */
|
||||||
void del_window(ToxWindow *w)
|
void del_window(ToxWindow *w)
|
||||||
{
|
{
|
||||||
active_window = windows; // Go to prompt screen
|
active_window = windows; /* Go to prompt screen */
|
||||||
|
|
||||||
delwin(w->window);
|
delwin(w->window);
|
||||||
memset(w, 0, sizeof(ToxWindow));
|
memset(w, 0, sizeof(ToxWindow));
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
refresh();
|
refresh();
|
||||||
|
--num_active_windows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Shows next window when tab or back-tab is pressed */
|
/* Shows next window when tab or back-tab is pressed */
|
||||||
@ -274,7 +279,7 @@ void set_next_window(int ch)
|
|||||||
if (active_window->window)
|
if (active_window->window)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (active_window == inf) { // infinite loop check
|
if (active_window == inf) { /* infinite loop check */
|
||||||
endwin();
|
endwin();
|
||||||
fprintf(stderr, "set_next_window() failed. Aborting...\n");
|
fprintf(stderr, "set_next_window() failed. Aborting...\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@ -398,17 +403,9 @@ void draw_active_window(Tox *m)
|
|||||||
a->onKey(a, m, ch);
|
a->onKey(a, m, ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
int num_active_windows(void)
|
int get_num_active_windows(void)
|
||||||
{
|
{
|
||||||
int count = 0;
|
return num_active_windows;
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
|
||||||
if (windows[i].active)
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
||||||
|
Reference in New Issue
Block a user