1
0
mirror of https://github.com/Tha14/toxic.git synced 2025-06-26 22:56:46 +02:00

Compare commits

...

81 Commits

Author SHA1 Message Date
5e377639c8 Merge pull request #94 from lehitoskin/spellcheck
SPELLING IS FOR FOOLS
2014-03-11 22:57:33 -04:00
93fb9611f7 SPELLING IS FOR FOOLS 2014-03-11 19:54:09 -07:00
cc3513968e I don't even 2014-03-11 19:30:23 -07:00
dd697d7af1 Merge branch 'master' of https://github.com/Tox/toxic 2014-03-11 20:00:12 -04:00
a32d76ed16 fix 2014-03-11 20:00:03 -04:00
e6d307f65a Merge pull request #92 from mannol1/master
Fixed segfault
2014-03-12 00:41:43 +01:00
b210068c1d Fixed segfault 2014-03-12 00:25:13 +01:00
0151b9b49f rm 'connecting' message and a few small fixes 2014-03-11 18:57:32 -04:00
ab0da36cb7 Merge pull request #91 from mannol1/master
This should fix segfault and remove one-line comments
2014-03-11 23:26:51 +01:00
ed3e9b476d This should fix segfault and remove one-line comments 2014-03-11 23:22:27 +01:00
7c63bd80d6 Merge pull request #90 from Jman012/master
Fixed another clang issue with bools that broek file sending.
2014-03-11 13:22:07 -07:00
9f06331a0b Fixed another clang issue with bools that broek file sending. 2014-03-11 13:11:22 -07:00
a63cba645f Issue #89 workaround for a toxav_get_peer_id issue 2014-03-11 12:59:12 -07:00
9d50d52216 What a waste of a commit
Note to self, fixup rebase this before g finds it
2014-03-11 10:24:21 -07:00
6cb36e71fe I like using entire commits for tiny things 2014-03-11 10:21:35 -07:00
0b52de3773 Update groupchat.c 2014-03-11 10:19:14 -07:00
22ac65c4a9 Change John Doe to Anonymous 2014-03-11 10:09:20 -07:00
3b90c3495f Forgot al 2014-03-10 23:25:59 -07:00
f4e4fbbef1 Update configure.ac 2014-03-10 21:08:02 -07:00
6aee8c136b Merge pull request #88 from mannol1/master
Toxic audio support
2014-03-10 21:05:46 -07:00
2a0740821c Allow AV in Travis 2014-03-10 21:00:53 -07:00
e6f285adc7 Update with latest core 2014-03-11 01:04:53 +01:00
a80da2b58f Merge remote-tracking branch 'upstream/master' 2014-03-11 00:58:18 +01:00
da924f07a9 Updated to latest core 2014-03-11 00:34:18 +01:00
e8cd1417b7 Fixed clang error, disabling the execute module. 2014-03-08 23:42:37 -08:00
d08feb2cc5 simplify popup drawing 2014-03-09 01:02:54 -05:00
fe0641e981 add popup alert on friend delete 2014-03-08 23:57:21 -05:00
1fd07837ea Fixed build problems 2014-03-08 16:36:42 +01:00
6c2ae4ad24 Don't allow changing device while transmission is active 2014-03-08 02:09:11 +01:00
c678d41709 Now supporting device selection 2014-03-08 01:12:51 +01:00
63745afe09 Toxic now supports audio calls 2014-03-07 03:27:48 +01:00
416419a6e7 Toxic now supports audio calls 2014-03-07 03:14:04 +01:00
33e16fe870 small optimization 2014-03-06 19:39:57 -05:00
d712d6c898 Merge pull request #85 from micrictor/master
Fixing fall-back from IPv6 to IPv4
2014-03-05 18:11:55 +01:00
2ae478d546 Fix fall-back from IPv6 to IPv4
Professionalism edits
2014-03-05 07:17:57 -08:00
4b8de0d16d speed up friendlist loading on startup 2014-03-05 08:01:12 -05:00
2fcfa954ab move file sender stuff to its own files 2014-03-05 05:06:21 -05:00
675c8fa89f fix 2014-03-05 03:37:07 -05:00
d1153f96ca small refactor for incoming file transfers 2014-03-05 03:31:12 -05:00
2f473300cd Fixing fallback from IPv6 to IPv4 2014-03-05 06:01:10 +00:00
92d5b2fefc Merge branch 'master' of https://github.com/Tox/toxic 2014-03-04 03:18:59 -05:00
70f8b103de Merge branch 'master' of https://github.com/JFreegman/toxic 2014-03-04 03:18:01 -05:00
41292c1ded old server list wasn't being installed; default to DHTnodes in /usr/local/share/toxic 2014-03-04 03:17:48 -05:00
90393f1dba Update README.md 2014-03-04 03:14:25 -05:00
87bd0f9b34 old server list wasn't being installed; t in DHTnodes in /usr/local/share/toxic 2014-03-04 03:10:07 -05:00
13e67f4ce3 Update README.md 2014-03-04 03:08:04 -05:00
6c9dbfe3bc Merge branch 'master' of https://github.com/Tox/toxic 2014-03-03 19:23:17 -05:00
24b763bce6 simplify logging 2014-03-03 19:21:52 -05:00
e41008bd4e Minor fixup 2014-03-03 07:55:10 -08:00
7f38c3c6e7 add prompt logging support 2014-03-01 18:06:35 -05:00
7109b8fa18 refactor logging functions to only handle chatlog pointers 2014-03-01 07:10:44 -05:00
1e503b1080 fix 2014-02-27 23:38:15 -05:00
4fb82cceaa save logging preference for friend chats and improve log command message 2014-02-27 23:33:00 -05:00
46b046a209 make C-e and C-aa work like they do in bash and fix/format help messages 2014-02-27 18:55:18 -05:00
6ee1f1ed0f fix 2014-02-26 21:30:27 -05:00
044b731089 Fix bug 2014-02-26 21:00:35 -05:00
d83ef1d8be update help messages 2014-02-26 19:45:11 -05:00
459eb64dc4 Merge branch 'master' of https://github.com/Tox/toxic 2014-02-26 19:03:15 -05:00
af5627b050 update to 0.2.7 2014-02-26 19:02:22 -05:00
9b57c05648 add command to turn logs on/off 2014-02-26 19:00:13 -05:00
817f763589 give groupchat logs unique names 2014-02-26 17:15:34 -05:00
8e4db369bc log events 2014-02-26 06:35:19 -05:00
a61f5f6a6d properly close windows on exit 2014-02-26 05:23:11 -05:00
5ff7065744 basic logging for groupchats 2014-02-26 03:51:26 -05:00
831d8e5f24 implement chat logging 2014-02-26 01:51:06 -05:00
6896292c49 Update README.md 2014-02-25 03:07:03 -05:00
b6613a015f add license info to source files 2014-02-25 02:28:24 -05:00
2d9f4facf7 connect to limited number of nodes on init instead of all of them 2014-02-24 20:08:51 -05:00
e7920d1da7 fix connection error codes 2014-02-24 19:41:02 -05:00
eb09fceed7 fix bug and some cleanup 2014-02-24 19:18:43 -05:00
b308e19e6b Merge pull request #80 from viric/dhtservers
Fallback to loading /usr/share/toxic/DHTservers.
2014-02-24 02:17:14 -05:00
5867f1a672 Merge pull request #82 from kl4ng/master
down arrow returns empty string if at end of history
2014-02-23 12:49:20 -08:00
5187861b69 down arrow returns empty string if at end of history 2014-02-23 14:22:45 -05:00
0013dae552 Merge branch 'master' of https://github.com/Tox/toxic 2014-02-23 05:40:21 -05:00
b018aa384e small fix and don't show typing alert for /commands 2014-02-23 05:38:44 -05:00
ad8f99dae4 Revert that 2014-02-23 02:19:22 -08:00
5b9d3f6f62 fix segfault 2014-02-23 05:15:48 -05:00
93bcecde70 Update README.md 2014-02-23 02:11:51 -08:00
6be1c907d9 Fallback to loading /usr/share/toxic/DHTservers.
First try ~/.config/tox/DHTservers, and fallback to the /usr/share
one if the former fails.

This should allow people just starting toxic for the first time to
connect to the DHT.
2014-02-23 09:52:07 +00:00
fd86f01fd0 Started with audio 2014-02-23 00:00:34 +01:00
e775c51a06 Merge upsteam/master 2014-02-22 23:58:36 +01:00
35 changed files with 2364 additions and 404 deletions

View File

@ -4,23 +4,52 @@ compiler:
- clang
before_script:
# installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git
#installing libsodium, needed for Core
- git clone git://github.com/jedisct1/libsodium.git > /dev/null
- cd libsodium
- git checkout tags/0.4.2
- ./autogen.sh
- ./configure && make -j3
- sudo make install
- git checkout tags/0.4.2 > /dev/null
- ./autogen.sh > /dev/null
- ./configure > /dev/null
- make check -j3 > /dev/null
- sudo make install >/dev/null
- cd ..
#installing yasm, needed for compiling vpx
- sudo apt-get install yasm > /dev/null
#installing libconfig, needed for DHT_bootstrap_daemon
- wget http://www.hyperrealm.com/libconfig/libconfig-1.4.9.tar.gz > /dev/null
- tar -xvzf libconfig-1.4.9.tar.gz > /dev/null
- cd libconfig-1.4.9
- ./configure > /dev/null
- make -j3 > /dev/null
- 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
- sudo ldconfig
- git clone https://github.com/irungentoo/ProjectTox-Core.git toxcore
- cd toxcore
- autoreconf -i
- ./configure --disable-tests --disable-ntox --disable-dht-bootstrap-daemon
- ./configure --disable-tests --disable-ntox --disable-daemon --enable-av
- make -j2
- sudo make install
- cd ..
- sudo apt-get install libopenal-dev -yq
script:
- autoreconf -i
- ./configure

View File

@ -1,20 +1,21 @@
## Toxic - console client for [Tox](http://tox.im)
# Toxic
The client formerly resided in the [Tox core repository](https://github.com/irungentoo/ProjectTox-Core) and is now available as a standalone program. It looks like [this](http://wiki.tox.im/images/b/b6/Ncursesclient1.png).
Toxic is 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.):
```
./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/
* Compile and install the program with ```make && sudo make install```
```
*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:
#### Notes
If your default prefix is /usr/local and you get the error: "error while loading shared libraries: libtoxcore.so.0: cannot open shared object file: No such file or directory", then you can try running ```sudo ldconfig```. If that doesn't fix it, run:
```
echo '/usr/local/lib/' | sudo tee -a /etc/ld.so.conf.d/locallib.conf
sudo ldconfig
```
If you dont already have them, you might want to install the ncurses libraries, on Debian:
If you dont already have them, you may need to install the ncurses libraries. For Debian based systems:
```
sudo apt-get install libncurses5-dev libncursesw5-dev
```

View File

@ -25,7 +25,11 @@ toxic_SOURCES = $(top_srcdir)/src/main.c \
$(top_srcdir)/src/misc_tools.c \
$(top_srcdir)/src/misc_tools.h \
$(top_srcdir)/src/toxic_strings.c \
$(top_srcdir)/src/toxic_strings.h
$(top_srcdir)/src/toxic_strings.h \
$(top_srcdir)/src/log.c \
$(top_srcdir)/src/log.h \
$(top_srcdir)/src/file_senders.c \
$(top_srcdir)/src/file_senders.h
toxic_CFLAGS = -I$(top_srcdir) \
$(NCURSES_CFLAGS) \
@ -41,3 +45,16 @@ toxic_LDADD = $(LIBTOXCORE_LDFLAGS) \
$(LIBSODIUM_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

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
AC_INIT([toxic], [0.2.6], [https://tox.im/])
AC_INIT([toxic], [0.3.0], [https://tox.im/])
AC_CONFIG_AUX_DIR(configure_aux)
AC_CONFIG_SRCDIR([src/main.c])
AC_CONFIG_HEADERS([config.h])
@ -381,6 +381,73 @@ if test "x$LIBTOXCORE_FOUND" = "xno"; then
AC_SUBST(LIBTOXCORE_LDFLAGS)
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"
AC_PATH_PROG([GIT], [git], [no])
if test "x$GIT" != "xno"; then

View File

@ -1 +1 @@
dist_pkgdata_DATA = DHTservers
dist_pkgdata_DATA = DHTnodes

600
src/audio_call.c Normal file
View 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
View 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 */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* chat.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
@ -15,6 +33,11 @@
#include "misc_tools.h"
#include "friendlist.h"
#include "toxic_strings.h"
#include "log.h"
#ifdef _SUPPORT_AUDIO
#include "audio_call.h"
#endif /* _SUPPORT_AUDIO */
extern char *DATA_FILE;
extern int store_data(Tox *m, char *path);
@ -22,7 +45,12 @@ extern int store_data(Tox *m, char *path);
extern FileSender file_senders[MAX_FILES];
extern ToxicFriend friends[MAX_FRIENDS_NUM];
#define AC_NUM_CHAT_COMMANDS 17
#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. */
static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -36,6 +64,7 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" },
{ "/invite" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
@ -43,6 +72,15 @@ static const uint8_t chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/savefile" },
{ "/sendfile" },
{ "/status" },
#ifdef _SUPPORT_AUDIO
{ "/call" },
{ "/cancel" },
{ "/answer" },
{ "/hangup" },
#endif /* _SUPPORT_AUDIO */
};
static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
@ -53,6 +91,25 @@ static void set_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
ctx->self_is_typing = is_typing;
}
void kill_chat_window(ToxWindow *self)
{
set_active_window(0);
ChatContext *ctx = self->chatwin;
StatusBar *statusbar = self->stb;
log_disable(ctx->log);
int f_num = self->num;
delwin(ctx->linewin);
delwin(statusbar->topline);
del_window(self);
disable_chatwin(f_num);
free(ctx->log);
free(ctx);
free(statusbar);
}
static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint16_t len)
{
if (self->num != num)
@ -76,6 +133,7 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *msg, uint1
} else
wprintw(ctx->history, "%s\n", msg);
write_to_log(msg, nick, ctx->log, false);
alert_window(self, WINDOW_ALERT_1, true);
}
@ -119,6 +177,7 @@ static void chat_onAction(ToxWindow *self, Tox *m, int num, uint8_t *action, uin
wprintw(ctx->history, "* %s %s\n", nick, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW));
write_to_log(action, nick, ctx->log, true);
alert_window(self, WINDOW_ALERT_1, true);
}
@ -182,7 +241,7 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
strcat(filename, d);
filename[len + strlen(d)] = '\0';
if (count > 999999) {
if (count > 999) {
wprintw(ctx->history, "Error saving file to disk.\n");
return;
}
@ -196,6 +255,15 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8_t fil
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,
uint8_t filenum, uint8_t control_type, uint8_t *data, uint16_t length)
{
@ -214,20 +282,20 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int num, uint8_t receive
case TOX_FILECONTROL_ACCEPT:
wprintw(ctx->history, "File transfer for '%s' accepted.\n", filename);
break;
case TOX_FILECONTROL_PAUSE:
// wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
break;
/*case TOX_FILECONTROL_PAUSE:
wprintw(ctx->history, "File transfer for '%s' paused.\n", filename);
break; */
case TOX_FILECONTROL_KILL:
wprintw(ctx->history, "File transfer for '%s' failed.\n", filename);
if (receive_send == 0)
friends[num].file_receiver.pending[filenum] = false;
chat_close_file_receiver(num, filenum);
else
close_file_sender(filenum);
chat_close_file_receiver(num, filenum);
break;
case TOX_FILECONTROL_FINISHED:
wprintw(ctx->history, "File transfer for '%s' complete.\n", filename);
chat_close_file_receiver(num, filenum);
break;
}
@ -242,26 +310,14 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int num, uint8_t filenum, u
ChatContext *ctx = self->chatwin;
uint8_t *filename = friends[num].file_receiver.filenames[filenum];
FILE *file_to_save = fopen(filename, "a");
// we have a problem here, but don't let it segfault
if (file_to_save == NULL) {
if (fwrite(data, length, 1, friends[num].file_receiver.files[filenum]) != 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);
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);
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)
@ -282,6 +338,123 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int friendnumber, uint8_
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, "Incoming 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) {
if (action == NULL) {
wprintw(ctx->history, "Invalid syntax.\n");
@ -300,6 +473,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t *acti
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send action\n");
wattroff(ctx->history, COLOR_PAIR(RED));
} else {
write_to_log(action, selfname, ctx->log, true);
}
}
@ -350,14 +525,14 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
beep();
}
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END) { /* END key: move cursor to end of line */
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
@ -440,7 +615,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
wmove(self->window, y, x + MAX(1, wcwidth(key)));
}
if (!ctx->self_is_typing)
if (!ctx->self_is_typing && ctx->line[0] != '/')
set_typingstatus(self, m, true);
}
/* RETURN key: Execute command or print line */
@ -453,22 +628,22 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
wclrtobot(self->window);
bool close_win = false;
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
if (line[0] == '/') {
if (close_win = !strcmp(line, "/close")) {
int f_num = self->num;
delwin(ctx->linewin);
delwin(statusbar->topline);
del_window(self);
disable_chatwin(f_num);
} else if (strncmp(line, "/me ", strlen("/me ")) == 0)
if (strcmp(line, "/close") == 0) {
if (ctx->self_is_typing)
set_typingstatus(self, m, false);
kill_chat_window(self);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_action(self, ctx, m, line + strlen("/me "));
else
} else {
execute(ctx->history, self, m, line, CHAT_COMMAND_MODE);
}
} else if (!string_is_empty(line)) {
uint8_t selfname[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
@ -489,15 +664,12 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key)
wattron(ctx->history, COLOR_PAIR(RED));
wprintw(ctx->history, " * Failed to send message.\n");
wattroff(ctx->history, COLOR_PAIR(RED));
} else {
write_to_log(line, selfname, ctx->log, false);
}
}
if (close_win) {
free(ctx);
free(statusbar);
} else {
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
if (ctx->len <= 0 && ctx->self_is_typing)
@ -630,8 +802,24 @@ static void chat_onInit(ToxWindow *self, Tox *m)
ctx->history = subwin(self->window, y2-CHATBOX_HEIGHT+1, x2, 0, 0);
scrollok(ctx->history, 1);
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2-CHATBOX_HEIGHT, 0);
ctx->log = malloc(sizeof(struct chatlog));
if (ctx->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
if (friends[self->num].logging_on)
log_enable(self->name, friends[self->num].pub_key, ctx->log);
wprintw(ctx->history, "\n\n");
execute(ctx->history, self, m, "/help", CHAT_COMMAND_MODE);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
@ -641,6 +829,7 @@ ToxWindow new_chat(Tox *m, int friendnum)
memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_chat = true;
ret.onKey = &chat_onKey;
ret.onDraw = &chat_onDraw;
@ -656,6 +845,19 @@ ToxWindow new_chat(Tox *m, int friendnum)
ret.onFileSendRequest = &chat_onFileSendRequest;
ret.onFileControl = &chat_onFileControl;
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'};
uint16_t len = tox_get_name(m, friendnum, name);

View File

@ -1,8 +1,31 @@
/* chat.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef CHAT_H_6489PZ13
#define CHAT_H_6489PZ13
#include "toxic_windows.h"
void kill_chat_window(ToxWindow *self);
ToxWindow new_chat(Tox *m, int friendnum);
#endif /* end of include guard: CHAT_H_6489PZ13 */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* chat_commands.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
@ -12,6 +30,7 @@
#include "toxic_windows.h"
#include "misc_tools.h"
#include "friendlist.h"
#include "execute.h"
extern ToxWindow *prompt;
@ -22,23 +41,34 @@ extern uint8_t max_file_senders_index;
void cmd_chat_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc == 1) {
if (!strcmp(argv[1], "global")) {
execute(window, self, m, "/help", GLOBAL_COMMAND_MODE);
return;
}
}
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, "Chat commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
wprintw(window, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /invite <n> : Invite friend to a group chat\n");
wprintw(window, " /me <action> : Do an action\n");
wprintw(window, " /myid : Print your ID\n");
wprintw(window, " /join : Join a pending group chat\n");
wprintw(window, " /clear : Clear the screen\n");
wprintw(window, " /close : Close the current chat window\n");
wprintw(window, " /sendfile <filepath> : Send a file\n");
wprintw(window, " /savefile <n> : Receive a file\n");
wprintw(window, " /quit or /exit : Exit Toxic\n");
wprintw(window, " /help : Print this message again\n");
#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, " /join : Join a pending group chat\n");
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /sendfile <filepath> : Send a file\n");
wprintw(window, " /savefile <n> : Receive a file\n");
wprintw(window, " /close : Close the current chat window\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /help global : Show a list of global commands\n");
wattron(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " * Argument messages must be enclosed in quotation marks.\n\n");
@ -69,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])
{
if (num_active_windows() >= MAX_WINDOWS_NUM) {
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
@ -118,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];
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);
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");
}
friends[self->num].file_receiver.pending[filenum] = false;
}

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* chat_commands.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
void cmd_chat_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -7,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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef _SUPPORT_AUDIO
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 */

View File

@ -1,20 +1,22 @@
/*
* Copyright (C) 2013 Tox project All Rights Reserved.
/* configdir.c
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/

View File

@ -1,20 +1,22 @@
/*
* Copyright (C) 2013 Tox project All Rights Reserved.
/* configdir.h
*
* This file is part of Tox.
*
* Tox is free software: you can redistribute it and/or modify
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tox is distributed in the hope that it will be useful,
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tox. If not, see <http://www.gnu.org/licenses/>.
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/

View File

@ -1,9 +1,32 @@
/*
* Toxic -- Tox Curses Client
/* execute.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "toxic_windows.h"
#include "execute.h"
@ -16,19 +39,24 @@ struct cmd_func {
};
static struct cmd_func global_commands[] = {
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/myid", cmd_myid },
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/status", cmd_status },
{ "/accept", cmd_accept },
{ "/add", cmd_add },
{ "/clear", cmd_clear },
{ "/connect", cmd_connect },
{ "/exit", cmd_quit },
{ "/groupchat", cmd_groupchat },
{ "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/q", cmd_quit },
{ "/quit", cmd_quit },
{ "/status", cmd_status },
#ifdef _SUPPORT_AUDIO
{ "/lsdev", cmd_list_devices },
{ "/sdev", cmd_change_device },
#endif /* _SUPPORT_AUDIO */
};
static struct cmd_func chat_commands[] = {
@ -37,6 +65,13 @@ static struct cmd_func chat_commands[] = {
{ "/join", cmd_join_group },
{ "/savefile", cmd_savefile },
{ "/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.
@ -44,8 +79,8 @@ static struct cmd_func chat_commands[] = {
static int parse_command(WINDOW *w, char *cmd, char (*args)[MAX_STR_SIZE])
{
int num_args = 0;
bool cmd_end = false; // flags when we get to the end of cmd
char *end; // points to the end of the current arg
bool cmd_end = false; /* flags when we get to the end of cmd */
char *end; /* points to the end of the current arg */
/* characters wrapped in double quotes count as one arg */
while (!cmd_end && num_args < MAX_NUM_ARGS) {

View File

@ -1,10 +1,34 @@
/*
* Toxic -- Tox Curses Client
/* execute.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#define MAX_NUM_ARGS 4 /* Includes command */
#define GLOBAL_NUM_COMMANDS 13
#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 {
GLOBAL_COMMAND_MODE,

113
src/file_senders.c Normal file
View 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
View 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);

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* friendlist.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
@ -15,6 +33,7 @@
#include "chat.h"
#include "friendlist.h"
#include "misc_tools.h"
#include "audio_call.h"
extern char *DATA_FILE;
extern ToxWindow *prompt;
@ -26,6 +45,8 @@ static int num_friends = 0;
ToxicFriend friends[MAX_FRIENDS_NUM];
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
static PendingDel pendingdelete;
#define S_WEIGHT 100
static int index_name_cmp(const void *n1, const void *n2)
@ -59,7 +80,7 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, int num, uint8_t *str,
return;
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));
} else {
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
@ -115,7 +136,7 @@ static void friendlist_onStatusMessageChange(ToxWindow *self, int num, uint8_t *
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)
return;
@ -160,7 +181,7 @@ static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int num, uint8
return;
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));
} else {
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
@ -185,7 +206,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int num, uint8_t *
return;
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));
} else {
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
@ -212,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);
memset(&friends[f_num], 0, sizeof(ToxicFriend));
@ -235,6 +256,49 @@ static void delete_friend(Tox *m, ToxWindow *self, int f_num, wint_t key)
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)
{
if (num_friends == 0)
@ -242,11 +306,19 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
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') {
/* Jump to chat window if already open */
if (friends[f].chatwin != -1) {
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));
set_active_window(friends[f].chatwin);
} else {
@ -258,7 +330,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key)
alert_window(prompt, WINDOW_ALERT_1, true);
}
} else if (key == KEY_DC) {
delete_friend(m, self, f, key);
del_friend_activate(self, m, f);
} else {
select_friend(self, m, key);
}
@ -288,7 +360,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wattroff(self->window, COLOR_PAIR(CYAN));
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);
if ((y2 - FLIST_OFST) <= 0) /* don't allow division by zero */
@ -383,6 +455,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
self->x = x2;
wrefresh(self->window);
draw_popup(self, m);
}
void disable_chatwin(int f_num)
@ -395,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 ret;
@ -408,12 +511,25 @@ ToxWindow new_friendlist(void)
ret.onFriendAdded = &friendlist_onFriendAdded;
ret.onMessage = &friendlist_onMessage;
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.onStatusChange = &friendlist_onStatusChange;
ret.onStatusMessageChange = &friendlist_onStatusMessageChange;
ret.onFileSendRequest = &friendlist_onFileSendRequest;
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");
return ret;

View File

@ -1,3 +1,25 @@
/* friendlist.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef FRIENDLIST_H_53I41IM
#define FRIENDLIST_H_53I41IM
@ -15,14 +37,22 @@ typedef struct {
bool active;
bool online;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of chat windows */
TOX_USERSTATUS status;
struct FileReceiver file_receiver;
} ToxicFriend;
typedef struct {
int num;
bool active;
} PendingDel;
ToxWindow new_friendlist(void);
void disable_chatwin(int f_num);
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 */
void sort_friendlist_index(Tox *m);

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* global_commands.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
@ -179,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])
{
if (num_active_windows() >= MAX_WINDOWS_NUM) {
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
wattron(window, COLOR_PAIR(RED));
wprintw(window, " * Warning: Too many windows are open.\n");
wattron(window, COLOR_PAIR(RED));
@ -202,6 +220,73 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
wprintw(window, "Group chat created as %d.\n", groupnum);
}
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
if (argc == 0) {
bool on;
if (self->is_chat || self->is_groupchat)
on = self->chatwin->log->log_on;
else if (self->is_prompt)
on = self->promptbuf->log->log_on;
if (on) {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, ". Type \"/log off\" to disable.\n");
} else {
wprintw(window, "Logging for this window is ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, ". Type \"/log on\" to enable.\n");
}
return;
}
uint8_t *swch = argv[1];
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
if (self->is_chat) {
friends[self->num].logging_on = true;
log_enable(self->name, friends[self->num].pub_key, self->chatwin->log);
} else if (self->is_prompt) {
uint8_t myid[TOX_FRIEND_ADDRESS_SIZE];
tox_get_address(m, myid);
log_enable(self->name, &myid, self->promptbuf->log);
} else if (self->is_groupchat) {
log_enable(self->name, NULL, self->chatwin->log);
}
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(GREEN) | A_BOLD);
wprintw(window, "[on]\n");
wattroff(window, COLOR_PAIR(GREEN) | A_BOLD);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat) {
friends[self->num].logging_on = false;
log_disable(self->chatwin->log);
} else if (self->is_prompt) {
log_disable(self->promptbuf->log);
} else if (self->is_groupchat) {
log_disable(self->chatwin->log);
}
wprintw(window, "Logging ");
wattron(window, COLOR_PAIR(RED) | A_BOLD);
wprintw(window, "[off]\n");
wattroff(window, COLOR_PAIR(RED) | A_BOLD);
return;
}
wprintw(window, "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.\n");
}
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id[TOX_FRIEND_ADDRESS_SIZE * 2 + 1] = {0};
@ -280,18 +365,24 @@ void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
wprintw(window, "\n\nGlobal commands:\n");
wattroff(window, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
wprintw(window, " /accept <n> : Accept friend request\n");
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT server\n");
wprintw(window, " /status <type> <msg> : Set your status with optional note\n");
wprintw(window, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /groupchat : Create a group chat\n");
wprintw(window, " /myid : Print your ID\n");
wprintw(window, " /quit or /exit : Exit Toxic\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /clear : Clear the window\n");
wprintw(window, " /add <id> <msg> : Add friend with optional message\n");
wprintw(window, " /accept <n> : Accept friend request\n");
wprintw(window, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
wprintw(window, " /status <type> <msg> : Set status with optional note\n");
wprintw(window, " /note <msg> : Set a personal note\n");
wprintw(window, " /nick <nick> : Set your nickname\n");
wprintw(window, " /log <on> or <off> : Enable/disable logging\n");
wprintw(window, " /groupchat : Create a group chat\n");
wprintw(window, " /myid : Print your ID\n");
wprintw(window, " /help : Print this message again\n");
wprintw(window, " /clear : Clear the window\n");
wprintw(window, " /quit or /exit : Exit Toxic\n");
#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);
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");

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* global_commands.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
@ -7,9 +25,15 @@ void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE])
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_prompt_help(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_quit(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_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 */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* groupchat.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
@ -16,6 +34,7 @@
#include "groupchat.h"
#include "prompt.h"
#include "toxic_strings.h"
#include "log.h"
extern char *DATA_FILE;
extern int store_data(Tox *m, char *path);
@ -24,7 +43,7 @@ static GroupChat groupchats[MAX_WINDOWS_NUM];
static int max_groupchat_index = 0;
/* temporary until group chats have unique commands */
extern glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
extern const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE];
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
{
@ -53,8 +72,20 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum)
return -1;
}
static void close_groupchatwin(Tox *m, int groupnum)
void kill_groupchat_window(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
log_disable(ctx->log);
delwin(ctx->linewin);
del_window(self);
free(ctx->log);
free(ctx);
}
static void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
{
set_active_window(0);
tox_del_groupchat(m, groupnum);
free(groupchats[groupnum].peer_names);
@ -69,6 +100,7 @@ static void close_groupchatwin(Tox *m, int groupnum)
}
max_groupchat_index = i;
kill_groupchat_window(self);
}
static void print_groupchat_help(ChatContext *ctx)
@ -77,21 +109,23 @@ static void print_groupchat_help(ChatContext *ctx)
wprintw(ctx->history, "Group chat commands:\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
wprintw(ctx->history, " /groupchat : Create a group chat\n");
wprintw(ctx->history, " /myid : Print your ID\n");
wprintw(ctx->history, " /clear : Clear the screen\n");
wprintw(ctx->history, " /close : Close the current group chat\n");
wprintw(ctx->history, " /quit or /exit : Exit Toxic\n");
wprintw(ctx->history, " /help : Print this message again\n");
wprintw(ctx->history, " /add <id> <msg> : Add friend with optional message\n");
wprintw(ctx->history, " /status <type> <msg>: Set your status with optional note\n");
wprintw(ctx->history, " /note <msg> : Set a personal note\n");
wprintw(ctx->history, " /nick <nick> : Set your nickname\n");
wprintw(ctx->history, " /groupchat : Create a group chat\n");
wprintw(ctx->history, " /log <on> or <off> : Enable/disable logging\n");
wprintw(ctx->history, " /close : Close the current group chat\n");
wprintw(ctx->history, " /help : Print this message again\n");
wprintw(ctx->history, " /help global : Show a list of global commands\n");
wattron(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
wprintw(ctx->history, " * Argument messages must be enclosed in quotation marks.\n");
wprintw(ctx->history, " * Scroll peer list with the Page Up/Page Down keys.\n\n");
wattroff(ctx->history, COLOR_PAIR(CYAN) | A_BOLD);
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,
@ -136,6 +170,8 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
} else {
wprintw(ctx->history, "%s\n", msg);
}
write_to_log(msg, nick, ctx->log, false);
}
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t *action,
@ -170,6 +206,8 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
wattron(ctx->history, COLOR_PAIR(YELLOW));
wprintw(ctx->history, "* %s %s\n", nick, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW));
write_to_log(action, nick, ctx->log, true);
}
/* Puts two copies of peerlist in chat instance */
@ -234,25 +272,36 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
ChatContext *ctx = self->chatwin;
print_time(ctx->history);
uint8_t *event;
switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD:
event = "has joined the room";
wattron(ctx->history, COLOR_PAIR(GREEN));
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", peername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has joined the room\n");
wprintw(ctx->history, " %s\n", event);
wattroff(ctx->history, COLOR_PAIR(GREEN));
write_to_log(event, peername, ctx->log, true);
break;
case TOX_CHAT_CHANGE_PEER_DEL:
event = "has left the room";
wattron(ctx->history, A_BOLD);
wprintw(ctx->history, "* %s", oldpeername);
wattroff(ctx->history, A_BOLD);
wprintw(ctx->history, " has left the room\n");
wprintw(ctx->history, " %s\n", event);
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
write_to_log(event, oldpeername, ctx->log, true);
break;
case TOX_CHAT_CHANGE_PEER_NAME:
wattron(ctx->history, COLOR_PAIR(MAGENTA));
wattron(ctx->history, A_BOLD);
@ -265,6 +314,10 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
wprintw(ctx->history, "%s\n", peername);
wattroff(ctx->history, A_BOLD);
wattroff(ctx->history, COLOR_PAIR(MAGENTA));
uint8_t tmp_event[TOXIC_MAX_NAME_LENGTH + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", peername);
write_to_log(tmp_event, oldpeername, ctx->log, true);
break;
}
@ -277,13 +330,13 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, uint8_t
return;
}
// uint8_t selfname[TOX_MAX_NAME_LENGTH];
// tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
/* uint8_t selfname[TOX_MAX_NAME_LENGTH];
tox_get_self_name(m, selfname, TOX_MAX_NAME_LENGTH);
// print_time(ctx->history);
// wattron(ctx->history, COLOR_PAIR(YELLOW));
// wprintw(ctx->history, "* %s %s\n", selfname, action);
// wattroff(ctx->history, COLOR_PAIR(YELLOW));
print_time(ctx->history);
wattron(ctx->history, COLOR_PAIR(YELLOW));
wprintw(ctx->history, "* %s %s\n", selfname, action);
wattroff(ctx->history, COLOR_PAIR(YELLOW)); */
if (tox_group_action_send(m, self->num, action, strlen(action) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
@ -338,14 +391,14 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
beep();
}
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (ctx->pos > 0) {
ctx->pos = 0;
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
}
else if (key == KEY_END) { /* END key: move cursor to end of line */
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (ctx->pos != ctx->len) {
ctx->pos = ctx->len;
mv_curs_end(self->window, MAX(0, wcswidth(ctx->line, (CHATBOX_HEIGHT-1)*x2)), y2, x2);
@ -458,24 +511,25 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
wclear(ctx->linewin);
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
wclrtobot(self->window);
bool close_win = false;
if (!string_is_empty(line))
add_line_to_hist(ctx->line, ctx->len, ctx->ln_history, &ctx->hst_tot, &ctx->hst_pos);
if (line[0] == '/') {
if (close_win = strcmp(line, "/close") == 0) {
set_active_window(0);
int groupnum = self->num;
delwin(ctx->linewin);
del_window(self);
close_groupchatwin(m, groupnum);
} else if (strcmp(line, "/help") == 0)
print_groupchat_help(ctx);
else if (strncmp(line, "/me ", strlen("/me ")) == 0)
if (strcmp(line, "/close") == 0) {
close_groupchat(self, m, self->num);
return;
} else if (strcmp(line, "/help") == 0) {
if (strcmp(line, "help global") == 0)
execute(ctx->history, self, m, "/help", GLOBAL_COMMAND_MODE);
else
print_groupchat_help(ctx);
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
else
} else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else if (!string_is_empty(line)) {
if (tox_group_message_send(m, self->num, line, strlen(line) + 1) == -1) {
wattron(ctx->history, COLOR_PAIR(RED));
@ -484,10 +538,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key)
}
}
if (close_win)
free(ctx);
else
reset_buf(ctx->line, &ctx->pos, &ctx->len);
reset_buf(ctx->line, &ctx->pos, &ctx->len);
}
}
@ -555,7 +606,19 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x, y-CHATBOX_HEIGHT, 0);
ctx->sidebar = subwin(self->window, y-CHATBOX_HEIGHT+1, SIDEBAR_WIDTH, 0, x-SIDEBAR_WIDTH);
ctx->log = malloc(sizeof(struct chatlog));
if (ctx->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(ctx->log, 0, sizeof(struct chatlog));
print_groupchat_help(ctx);
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
wmove(self->window, y-CURS_Y_OFFSET, 0);
}
@ -565,6 +628,7 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_groupchat = true;
ret.onKey = &groupchat_onKey;
ret.onDraw = &groupchat_onDraw;

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* groupchat.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/>.
*
*/
#define SIDEBAR_WIDTH 16
@ -14,5 +32,6 @@ typedef struct {
uint8_t *oldpeer_names;
} GroupChat;
void kill_groupchat_window(ToxWindow *self);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum);
ToxWindow new_group_chat(Tox *m, int groupnum);

128
src/log.c Normal file
View File

@ -0,0 +1,128 @@
/* log.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "configdir.h"
#include "toxic_windows.h"
#include "misc_tools.h"
/* Creates/fetches log file by appending to the config dir the name and a pseudo-unique identity */
void init_logging_session(uint8_t *name, uint8_t *key, struct chatlog *log)
{
if (!log->log_on)
return;
char *user_config_dir = get_user_config_dir();
int path_len = strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(name);
/* use first 4 digits of key as log ident. If no key use a timestamp */
uint8_t ident[32];
if (key != NULL) {
path_len += (KEY_IDENT_DIGITS * 2 + 5);
sprintf(&ident[0], "%02X", key[0] & 0xff);
sprintf(&ident[2], "%02X", key[2] & 0xff);
ident[KEY_IDENT_DIGITS*2+1] = '\0';
} else {
struct tm *tminfo = get_time();
snprintf(ident, sizeof(ident),
"%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);
path_len += strlen(ident) + 1;
}
if (path_len > MAX_STR_SIZE) {
log->log_on = false;
return;
}
uint8_t log_path[MAX_STR_SIZE];
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log",
user_config_dir, CONFIGDIR, name, ident);
log->file = fopen(log_path, "a");
if (log->file == NULL) {
log->log_on = false;
return;
}
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
free(user_config_dir);
}
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event)
{
if (!log->log_on)
return;
if (log->file == NULL) {
log->log_on = false;
return;
}
uint8_t name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
if (event)
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
struct tm *tminfo = get_time();
fprintf(log->file,"%04d/%02d/%02d [%d:%02d:%02d] %s %s\n", tminfo->tm_year+1900,
tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour, tminfo->tm_min,
tminfo->tm_sec, name_frmt, msg);
uint64_t curtime = (uint64_t) time(NULL);
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)
{
log->log_on = true;
if (log->file == NULL)
init_logging_session(name, key, log);
}
void log_disable(struct chatlog *log)
{
log->log_on = false;
if (log->file != NULL) {
fclose(log->file);
log->file = NULL;
}
}

33
src/log.h Normal file
View File

@ -0,0 +1,33 @@
/* log.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/>.
*
*/
/* 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);
/* formats/writes line to log file */
void write_to_log(uint8_t *msg, uint8_t *name, struct chatlog *log, bool event);
/* enables logging for specified log and creates/fetches file if necessary */
void log_enable(uint8_t *name, uint8_t *key, struct chatlog *log);
/* disables logging for specified log and closes file */
void log_disable(struct chatlog *log);

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* main.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
@ -30,6 +48,7 @@
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#endif
#include <tox/tox.h>
@ -39,18 +58,25 @@
#include "friendlist.h"
#include "prompt.h"
#include "misc_tools.h"
#include "file_senders.h"
#ifdef _SUPPORT_AUDIO
#include "audio_call.h"
#endif /* _SUPPORT_AUDIO */
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
#ifdef _SUPPORT_AUDIO
ToxAv* av;
#endif /* _SUPPORT_AUDIO */
/* Export for use in Callbacks */
char *DATA_FILE = NULL;
char *SRVLIST_FILE = NULL;
ToxWindow *prompt = NULL;
FileSender file_senders[MAX_FILES];
uint8_t max_file_senders_index;
static int f_loadfromfile; /* 1 if we want to load from/save the data file, 0 otherwise */
void on_window_resize(int sig)
{
@ -98,8 +124,12 @@ static Tox *init_tox(int ipv4)
int ipv6 = !ipv4;
Tox *m = tox_new(ipv6);
if (TOX_ENABLE_IPV6_DEFAULT && m == NULL) {
fprintf(stderr, "IPv6 didn't initialize, trying IPv4 only\n");
/*
* 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);
}
@ -128,7 +158,7 @@ static Tox *init_tox(int ipv4)
#elif defined(_WIN32)
tox_set_name(m, (uint8_t *) "I should install GNU/Linux", sizeof("I should install GNU/Linux"));
#elif defined(__APPLE__)
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); //This used to users of other Unixes are hipsters
tox_set_name(m, (uint8_t *) "Hipster", sizeof("Hipster")); /* This used to users of other Unixes are hipsters */
#else
tox_set_name(m, (uint8_t *) "Registered Minix user #4", sizeof("Registered Minix user #4"));
#endif
@ -138,25 +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 MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
#define MAXSERVERS 50
#define SERVERLEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
#define MAXNODES 50
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
static int linecnt = 0;
static char servers[MAXSERVERS][SERVERLEN];
static uint16_t ports[MAXSERVERS];
static uint8_t keys[MAXSERVERS][TOX_CLIENT_ID_SIZE];
static char nodes[MAXNODES][NODELEN];
static uint16_t ports[MAXNODES];
static uint8_t keys[MAXNODES][TOX_CLIENT_ID_SIZE];
int serverlist_load(void)
static int nodelist_load(char *filename)
{
FILE *fp = NULL;
if (!filename)
return 1;
fp = fopen(SRVLIST_FILE, "r");
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return 1;
char line[MAXLINE];
while (fgets(line, sizeof(line), fp) && linecnt < MAXSERVERS) {
while (fgets(line, sizeof(line), fp) && linecnt < MAXNODES) {
if (strlen(line) > MINLINE) {
char *name = strtok(line, " ");
char *port = strtok(NULL, " ");
@ -165,8 +196,8 @@ int serverlist_load(void)
if (name == NULL || port == NULL || key_ascii == NULL)
continue;
strncpy(servers[linecnt], name, SERVERLEN);
servers[linecnt][SERVERLEN - 1] = 0;
strncpy(nodes[linecnt], name, NODELEN);
nodes[linecnt][NODELEN - 1] = 0;
ports[linecnt] = htons(atoi(port));
uint8_t *key_binary = hex_string_to_bin(key_ascii);
@ -186,51 +217,53 @@ int serverlist_load(void)
return 0;
}
int init_connection_helper(Tox *m, int linenumber)
int init_connection_helper(Tox *m, int line)
{
return tox_bootstrap_from_address(m, servers[linenumber], TOX_ENABLE_IPV6_DEFAULT,
ports[linenumber], keys[linenumber]);
return tox_bootstrap_from_address(m, nodes[line], TOX_ENABLE_IPV6_DEFAULT,
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:
* 1: failed to open server file
* 2: no line of sufficient length in server file
* 3: (old, removed) failed to split a selected line in the server file
* 4: failed to resolve name to IP
* 5: serverlist file contains no acceptable line
* 1: failed to open node file
* 2: no line of sufficient length in node file
* 3: failed to resolve name to IP
* 4: nodelist file contains no acceptable line
*/
static int init_connection_serverlist_loaded = 0;
static bool srvlist_loaded = false;
#define NUM_INIT_NODES 5
int init_connection(Tox *m)
{
if (linecnt > 0) /* already loaded serverlist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 4;
if (linecnt > 0) /* already loaded nodelist */
return init_connection_helper(m, rand() % linecnt) ? 0 : 3;
/* only once:
* - load the serverlist
* - load the nodelist
* - connect to "everyone" inside
*/
if (!init_connection_serverlist_loaded) {
init_connection_serverlist_loaded = 1;
int res = serverlist_load();
if (res)
if (!srvlist_loaded) {
srvlist_loaded = true;
int res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
if (linecnt < 1)
return res;
if (!linecnt)
return 4;
res = 3;
int i;
int n = MIN(NUM_INIT_NODES, linecnt);
res = 6;
int linenumber;
for(linenumber = 0; linenumber < linecnt; linenumber++)
if (init_connection_helper(m, linenumber))
for(i = 0; i < n; ++i)
if (init_connection_helper(m, rand() % linecnt))
res = 0;
return res;
}
/* empty serverlist file */
return 5;
/* empty nodelist file */
return 4;
}
static void do_connection(Tox *m, ToxWindow *prompt)
@ -242,18 +275,18 @@ static void do_connection(Tox *m, ToxWindow *prompt)
bool is_connected = tox_isconnected(m);
if (!dht_on && !is_connected && !(conn_try++ % 100)) {
prep_prompt_win();
if (!conn_err) {
wprintw(prompt->window, "Establishing connection...\n");
if ((conn_err = init_connection(m)))
if ((conn_err = init_connection(m))) {
prep_prompt_win();
wprintw(prompt->window, "\nAuto-connect failed with error code %d\n", conn_err);
}
}
} else if (!dht_on && is_connected) {
dht_on = true;
prompt_update_connectionstatus(prompt, dht_on);
prep_prompt_win();
wprintw(prompt->window, "\nDHT connected.\n");
wprintw(prompt->window, "DHT connected.\n");
} else if (dht_on && !is_connected) {
dht_on = false;
prompt_update_connectionstatus(prompt, dht_on);
@ -263,7 +296,14 @@ static void do_connection(Tox *m, ToxWindow *prompt)
}
}
int f_loadfromfile;
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
@ -343,12 +383,7 @@ static void load_data(Tox *m, char *path)
}
tox_load(m, buf, len);
uint32_t i = 0;
uint8_t name[TOX_MAX_NAME_LENGTH];
while (tox_get_name(m, i, name) != -1)
on_friendadded(m, i++, false);
load_friendlist(m);
free(buf);
fclose(fd);
@ -363,90 +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)
{
store_data(m, DATA_FILE);
int i;
for (i = 0; i < max_file_senders_index; ++i) {
if (file_senders[i].active)
fclose(file_senders[i].file);
}
close_all_file_senders();
kill_all_windows();
log_disable(prompt->promptbuf->log);
free(DATA_FILE);
free(SRVLIST_FILE);
free(prompt->stb);
free(prompt->promptbuf->log);
free(prompt->promptbuf);
tox_kill(m);
#ifdef _SUPPORT_AUDIO
terminate_audio(prompt, av);
#endif /* _SUPPORT_AUDIO */
endwin();
exit(EXIT_SUCCESS);
}
@ -471,7 +436,7 @@ int main(int argc, char *argv[])
int i = 0;
int f_use_ipv4 = 0;
// Make sure all written files are read/writeable only by the current user.
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
for (i = 0; i < argc; ++i) {
@ -500,7 +465,7 @@ int main(int argc, char *argv[])
if (DATA_FILE != NULL) {
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
strcat(DATA_FILE, "data");
strcat(DATA_FILE, "data");
} else {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
@ -509,21 +474,6 @@ int main(int argc, char *argv[])
}
}
if (config_err) {
SRVLIST_FILE = strdup(PACKAGE_DATADIR "/DHTservers");
} else {
SRVLIST_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen("DHTservers") + 1);
if (SRVLIST_FILE != NULL) {
strcpy(SRVLIST_FILE, user_config_dir);
strcat(SRVLIST_FILE, CONFIGDIR);
strcat(SRVLIST_FILE, "DHTservers");
} else {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
}
free(user_config_dir);
init_term();
@ -537,6 +487,22 @@ int main(int argc, char *argv[])
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)
load_data(m, DATA_FILE);
@ -557,8 +523,8 @@ int main(int argc, char *argv[])
prompt_init_statusbar(prompt, m);
sort_friendlist_index(m);
while (true)
while (true)
do_toxic(m, prompt);
return 0;
}

View File

@ -1,7 +1,29 @@
/*
* Toxic -- Tox Curses Client
/* misc_tools.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <time.h>
@ -11,7 +33,7 @@
extern ToxWindow *prompt;
// XXX: FIX
/* XXX: FIX */
unsigned char *hex_string_to_bin(char hex_string[])
{
size_t len = strlen(hex_string);
@ -48,7 +70,7 @@ void print_time(WINDOW *window)
struct tm *timeinfo = get_time();
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));
}
@ -87,7 +109,7 @@ int wcs_to_mbs_buf(uint8_t *buf, const wchar_t *string, size_t n)
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 *ret = NULL;
@ -134,8 +156,8 @@ char *wc_to_char(wchar_t ch)
return ret;
}
/* Returns true if connection has timed out, false otherwise */
bool timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
/* Returns 1 if connection has timed out, 0 otherwise */
int timed_out(uint64_t timestamp, uint64_t curtime, uint64_t timeout)
{
return timestamp + timeout <= curtime;
}
@ -167,23 +189,23 @@ int qsort_strcasecmp_hlpr(const void *nick1, const void *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 start with a space
- must not contain contiguous spaces */
bool valid_nick(uint8_t *nick)
int valid_nick(uint8_t *nick)
{
if (!nick[0] || nick[0] == ' ')
return false;
return 0;
int i;
for (i = 0; nick[i]; ++i) {
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 */
@ -202,13 +224,13 @@ void get_file_name(uint8_t *pathname, uint8_t *namebuf)
while (idx >= 0 && pathname[idx] == '/')
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 (!strlen(++filename))
filename = pathname;
} else {
filename = strrchr(pathname, '\\'); // Try windows style paths
filename = strrchr(pathname, '\\'); /* Try windows style paths */
if (filename == NULL)
filename = pathname;

View File

@ -1,9 +1,27 @@
/*
* Toxic -- Tox Curses Client
/* misc_tools.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/>.
*
*/
// #define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
/* convert a hex string to binary */
unsigned char *hex_string_to_bin(char hex_string[]);
@ -30,8 +48,8 @@ uint8_t *wcs_to_mbs(wchar_t *string);
/* convert a wide char to multibyte char */
char *wc_to_char(wchar_t ch);
/* Returns true if connection has timed out, false otherwise */
bool timed_out(uint64_t timestamp, uint64_t timeout, uint64_t curtime);
/* Returns 1 if connection has timed out, 0 otherwise */
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 */
void alert_window(ToxWindow *self, int type, bool is_beep);

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* prompt.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
@ -30,11 +48,19 @@ const uint8_t glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/groupchat" },
{ "/help" },
{ "/join" },
{ "/log" },
{ "/myid" },
{ "/nick" },
{ "/note" },
{ "/quit" },
{ "/status" },
#ifdef _SUPPORT_AUDIO
{ "/lsdev" },
{ "/sdev" },
#endif /* _SUPPORT_AUDIO */
};
/* prevents input string from eating system messages: call this prior to printing a prompt message
@ -154,12 +180,12 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key)
beep();
}
else if (key == KEY_HOME) { /* HOME key: Move cursor to beginning of line */
else if (key == KEY_HOME || key == T_KEY_C_A) { /* HOME/C-a key: Move cursor to start of line */
if (prt->pos != 0)
prt->pos = 0;
}
else if (key == KEY_END) { /* END key: move cursor to end of line */
else if (key == KEY_END || key == T_KEY_C_E) { /* END/C-e key: move cursor to end of line */
if (prt->pos != prt->len)
prt->pos = prt->len;
}
@ -341,6 +367,18 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
static void prompt_onInit(ToxWindow *self, Tox *m)
{
scrollok(self->window, true);
PromptBuf *prt = self->promptbuf;
prt->log = malloc(sizeof(struct chatlog));
if (prt->log == NULL) {
endwin();
fprintf(stderr, "malloc() failed. Aborting...\n");
exit(EXIT_FAILURE);
}
memset(prt->log, 0, sizeof(struct chatlog));
execute(self->window, self, m, "/help", GLOBAL_COMMAND_MODE);
wclrtoeol(self->window);
}
@ -350,6 +388,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u
if (friendnum < 0)
return;
PromptBuf *prt = self->promptbuf;
prep_prompt_win();
uint8_t nick[TOX_MAX_NAME_LENGTH] = {'\0'};
@ -363,39 +402,53 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, int friendnum , u
wprintw(self->window, "\n");
print_time(self->window);
uint8_t *msg;
if (status == 1) {
msg = "has come online";
wattron(self->window, COLOR_PAIR(GREEN));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has come online\n");
wprintw(self->window, "%s\n", msg);
wattroff(self->window, COLOR_PAIR(GREEN));
write_to_log(msg, nick, prt->log, true);
alert_window(self, WINDOW_ALERT_2, false);
} else {
msg = "has gone offline";
wattron(self->window, COLOR_PAIR(RED));
wattron(self->window, A_BOLD);
wprintw(self->window, "* %s ", nick);
wattroff(self->window, A_BOLD);
wprintw(self->window, "has gone offline\n");
wprintw(self->window, "%s\n", msg);
wattroff(self->window, COLOR_PAIR(RED));
write_to_log(msg, nick, prt->log, true);
}
}
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;
PromptBuf *prt = self->promptbuf;
prep_prompt_win();
wprintw(self->window, "\n");
print_time(self->window);
wprintw(self->window, "Friend request with the message: '%s'\n", data);
uint8_t msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "Friend request with the message '%s'\n", data);
wprintw(self->window, "%s", msg);
write_to_log(msg, "", prt->log, true);
int n = add_friend_request(key);
if (n == -1) {
wprintw(self->window, "Friend request queue is full. Discarding request.\n");
uint8_t *errmsg = "Friend request queue is full. Discarding request.\n";
wprintw(self->window, "%s", errmsg);
write_to_log(errmsg, "", prt->log, true);
return;
}
@ -441,6 +494,7 @@ ToxWindow new_prompt(void)
memset(&ret, 0, sizeof(ret));
ret.active = true;
ret.is_prompt = true;
ret.onKey = &prompt_onKey;
ret.onDraw = &prompt_onDraw;

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* prompt.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PROMPT_H_UZYGWFFL
@ -7,7 +25,12 @@
#define X_OFST 2 /* offset to account for prompt char */
#define AC_NUM_GLOB_COMMANDS 14
#ifdef _SUPPORT_AUDIO
#define AC_NUM_GLOB_COMMANDS 17
#else
#define AC_NUM_GLOB_COMMANDS 15
#endif /* _SUPPORT_AUDIO */
ToxWindow new_prompt(void);
void prep_prompt_win(void);

View File

@ -1,7 +1,29 @@
/*
* Toxic -- Tox Curses Client
/* toxic_strings.c
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
@ -120,19 +142,21 @@ void add_line_to_hist(const wchar_t *buf, size_t len, wchar_t (*hst)[MAX_STR_SIZ
}
/* copies history item at hst_pos to buf. Sets pos and len to the len of the history item.
hst_pos is decremented or incremented depending on key_dir. */
hst_pos is decremented or incremented depending on key_dir.
resets buffer if at end of history */
void fetch_hist_item(wchar_t *buf, size_t *pos, size_t *len, wchar_t (*hst)[MAX_STR_SIZE],
int hst_tot, int *hst_pos, int key_dir)
{
if (key_dir == LN_HIST_MV_UP) {
if (--(*hst_pos) < 0) {
++(*hst_pos);
*hst_pos = 0;
beep();
}
} else {
if (++(*hst_pos) >= hst_tot) {
--(*hst_pos);
beep();
*hst_pos = hst_tot;
reset_buf(buf, pos, len);
return;
}
}

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* toxic_strings.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/>.
*
*/
/* Adds char to buffer at pos */

View File

@ -1,5 +1,23 @@
/*
* Toxic -- Tox Curses Client
/* toxic_windows.h
*
*
* Copyright (C) 2014 Toxic All Rights Reserved.
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef _windows_h
@ -15,7 +33,11 @@
#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_FRIENDS_NUM 100
@ -36,6 +58,8 @@
#define T_KEY_DISCARD 0x15 /* ctrl-u */
#define T_KEY_NEXT 0x10 /* ctrl-p */
#define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */
/* Curses foreground colours (background is black) */
enum {
@ -58,7 +82,7 @@ enum {
/* Fixes text color problem on some terminals.
Uncomment if necessary */
//#define URXVT_FIX
/* #define URXVT_FIX */
typedef struct ToxWindow ToxWindow;
typedef struct StatusBar StatusBar;
@ -86,11 +110,31 @@ struct ToxWindow {
void(*onFileData)(ToxWindow *, Tox *, int, uint8_t, uint8_t *, uint16_t);
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];
int num;
bool active;
int x;
/* window type identifiers */
bool is_chat;
bool is_groupchat;
bool is_prompt;
bool alert0;
bool alert1;
bool alert2;
@ -99,6 +143,7 @@ struct ToxWindow {
PromptBuf *promptbuf;
StatusBar *stb;
WINDOW *popup;
WINDOW *window;
};
@ -113,6 +158,15 @@ struct StatusBar {
bool is_online;
};
#define LOG_FLUSH_LIMIT 2 /* limits calls to fflush(logfile) to a max of one per LOG_FLUSH_LIMIT seconds */
struct chatlog {
FILE *file;
uint64_t lastwrite;
int pos;
bool log_on; /* specific to current chat window */
};
#define MAX_LINE_HIST 128
/* chat and groupchat window/buffer holder */
@ -127,6 +181,8 @@ struct ChatContext {
bool self_is_typing;
struct chatlog *log;
WINDOW *history;
WINDOW *linewin;
WINDOW *sidebar;
@ -137,6 +193,7 @@ struct PromptBuf {
wchar_t line[MAX_STR_SIZE];
size_t pos;
size_t len;
bool at_bottom; /* true if line end is at bottom of window */
int orig_y; /* y axis point of line origin */
bool scroll; /* used for prompt window hack to determine when to scroll down */
@ -145,6 +202,7 @@ struct PromptBuf {
int hst_pos;
int hst_tot;
struct chatlog *log;
WINDOW *linewin;
};
@ -168,6 +226,7 @@ typedef struct {
struct FileReceiver {
uint8_t filenames[MAX_FILES][MAX_STR_SIZE];
FILE *files[MAX_FILES];
bool pending[MAX_FILES];
};
@ -195,5 +254,8 @@ void draw_active_window(Tox *m);
int add_window(Tox *m, ToxWindow w);
void del_window(ToxWindow *w);
void set_active_window(int ch);
int num_active_windows(void);
int get_num_active_windows(void);
/* cleans up all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void);
#endif

View File

@ -1,3 +1,25 @@
/* windows.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
@ -8,6 +30,7 @@
#include "friendlist.h"
#include "prompt.h"
#include "toxic_windows.h"
#include "groupchat.h"
extern char *DATA_FILE;
@ -16,12 +39,14 @@ static ToxWindow *active_window;
extern ToxWindow *prompt;
static int num_active_windows;
/* CALLBACKS START */
void on_request(uint8_t *public_key, uint8_t *data, uint16_t length, void *userdata)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onFriendRequest != NULL)
windows[i].onFriendRequest(&windows[i], public_key, data, length);
}
@ -31,7 +56,7 @@ void on_connectionchange(Tox *m, int friendnumber, uint8_t status, void *userdat
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onConnectionChange != NULL)
windows[i].onConnectionChange(&windows[i], m, friendnumber, status);
}
@ -41,7 +66,7 @@ void on_typing_change(Tox *m, int friendnumber, int is_typing, void *userdata)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onTypingChange != NULL)
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
}
@ -51,7 +76,7 @@ void on_message(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onMessage != NULL)
windows[i].onMessage(&windows[i], m, friendnumber, string, length);
}
@ -61,7 +86,7 @@ void on_action(Tox *m, int friendnumber, uint8_t *string, uint16_t length, void
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onAction != NULL)
windows[i].onAction(&windows[i], m, friendnumber, string, length);
}
@ -74,7 +99,7 @@ void on_nickchange(Tox *m, int friendnumber, uint8_t *string, uint16_t length, v
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onNickChange != NULL)
windows[i].onNickChange(&windows[i], m, friendnumber, string, length);
}
@ -87,7 +112,7 @@ void on_statusmessagechange(Tox *m, int friendnumber, uint8_t *string, uint16_t
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onStatusMessageChange != NULL)
windows[i].onStatusMessageChange(&windows[i], friendnumber, string, length);
}
@ -97,7 +122,7 @@ void on_statuschange(Tox *m, int friendnumber, TOX_USERSTATUS status, void *user
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onStatusChange != NULL)
windows[i].onStatusChange(&windows[i], m, friendnumber, status);
}
@ -107,7 +132,7 @@ void on_friendadded(Tox *m, int friendnumber, bool sort)
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onFriendAdded != NULL)
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
}
@ -121,7 +146,7 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, uint8_t *message,
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, message, length);
}
@ -132,7 +157,7 @@ void on_groupaction(Tox *m, int groupnumber, int peernumber, uint8_t *action, ui
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onGroupAction != NULL)
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, action, length);
}
@ -142,7 +167,7 @@ void on_groupinvite(Tox *m, int friendnumber, uint8_t *group_pub_key, void *user
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, group_pub_key);
}
@ -152,7 +177,7 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onGroupNamelistChange != NULL)
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
}
@ -163,7 +188,7 @@ void on_file_sendrequest(Tox *m, int friendnumber, uint8_t filenumber, uint64_t
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onFileSendRequest != NULL)
windows[i].onFileSendRequest(&windows[i], m, friendnumber, filenumber, filesize,
filename, filename_length);
@ -175,7 +200,7 @@ void on_file_control (Tox *m, int friendnumber, uint8_t receive_send, uint8_t fi
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onFileControl != NULL)
windows[i].onFileControl(&windows[i], m, friendnumber, receive_send, filenumber,
control_type, data, length);
@ -187,7 +212,7 @@ void on_file_data(Tox *m, int friendnumber, uint8_t filenumber, uint8_t *data, u
{
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
for (i = 0; i < num_active_windows; ++i) {
if (windows[i].onFileData != NULL)
windows[i].onFileData(&windows[i], m, friendnumber, filenumber, data, length);
}
@ -217,6 +242,8 @@ int add_window(Tox *m, ToxWindow w)
windows[i] = w;
w.onInit(&w, m);
++num_active_windows;
return i;
}
@ -226,13 +253,14 @@ int add_window(Tox *m, ToxWindow w)
/* Deletes window w and cleans up */
void del_window(ToxWindow *w)
{
active_window = windows; // Go to prompt screen
active_window = windows; /* Go to prompt screen */
delwin(w->window);
memset(w, 0, sizeof(ToxWindow));
clear();
refresh();
--num_active_windows;
}
/* Shows next window when tab or back-tab is pressed */
@ -251,7 +279,7 @@ void set_next_window(int ch)
if (active_window->window)
return;
if (active_window == inf) { // infinite loop check
if (active_window == inf) { /* infinite loop check */
endwin();
fprintf(stderr, "set_next_window() failed. Aborting...\n");
exit(EXIT_FAILURE);
@ -375,15 +403,20 @@ void draw_active_window(Tox *m)
a->onKey(a, m, ch);
}
int num_active_windows(void)
int get_num_active_windows(void)
{
return num_active_windows;
}
/* destroys all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(void)
{
int count = 0;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].active)
++count;
if (windows[i].is_chat)
kill_chat_window(&windows[i]);
else if (windows[i].is_groupchat)
kill_groupchat_window(&windows[i]);
}
return count;
}