mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-26 22:46:44 +02:00
Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
9ee7a48910 | |||
daf4614ba6 | |||
919d36369c | |||
618704df76 | |||
9a70dd9651 | |||
e2d310b10f | |||
a1015a366a | |||
913ec7b3fe | |||
4a52b06954 | |||
89f9c07b9e | |||
388d78d11e | |||
a2a23b3932 | |||
f405ae8b42 | |||
cf3f6750eb | |||
4de22d067a | |||
51e274ea38 | |||
0a6ce62363 | |||
13c5de5531 | |||
21f8e7f398 | |||
bcf4a5af90 | |||
09f90d095b | |||
416ebc9ab8 | |||
3ca22aa714 | |||
8dd25e1f0b | |||
5b9bd603ea | |||
3c2c1f15ce | |||
6876df4a45 | |||
1ff97161fb | |||
667410e879 | |||
a862874740 | |||
79bde4e5bf | |||
833b724e9f | |||
96b68058bb | |||
e823233149 | |||
3ac22fafe4 | |||
71f2ac170c | |||
0ef888eea3 | |||
a9b0028a15 | |||
b18e6cff5a | |||
424a1c94d9 | |||
009095af24 | |||
2ce42ab057 | |||
934459dea8 | |||
52bc874675 | |||
511907fbc5 | |||
155e194174 | |||
b1c7e21ca9 | |||
7edcf6cb45 | |||
9581940cfa | |||
f2aa57c4fa | |||
8bf4405fd0 | |||
21ef1788ca | |||
68f1dffba7 | |||
092df2c0e4 | |||
691f94c75c | |||
d6d4476e85 | |||
924e8e0860 | |||
53193e933f | |||
328587ad9c | |||
1a8fdb1b99 | |||
690f0221b5 | |||
e117bd3985 | |||
15cc87bffd | |||
97d4c97c52 | |||
c8b22d7e8a | |||
f48ec4f49b | |||
d4ce697bd9 | |||
bd20513493 | |||
fd3f4eb724 | |||
75e8486061 | |||
bd216709fc | |||
a3a8f7608a | |||
affc88d0a8 | |||
eca4882ce2 | |||
58b0a04019 | |||
b870679f2c | |||
ae83725cb6 | |||
595e42b587 | |||
f5401df2c7 | |||
3e79a5ca8b | |||
327081945e | |||
68ec484a58 | |||
1d6ccf56a8 | |||
67f637a1e1 | |||
5e175d5319 | |||
778db0fece | |||
72010dd2e1 | |||
39556b36f3 | |||
dc9ffa6e56 | |||
1e92bb3c2b | |||
edbdf2966a | |||
3f6fd734d3 | |||
919a06d282 | |||
35cc815cdb | |||
a318bdb034 | |||
d6aaa95b25 | |||
5718ad52db | |||
0f4cffbacc | |||
d9a861331f | |||
2f12a8d429 | |||
e75cf4f3ad | |||
bb85f31bb2 | |||
396d08f0d2 | |||
6ab184e7ce | |||
fd65fbfd0c | |||
637ea0ed55 |
@ -1,7 +1,7 @@
|
||||
language: c
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
# - clang # Fix me
|
||||
|
||||
before_script:
|
||||
# Installing yasm (needed for compiling vpx) and openal
|
||||
@ -9,7 +9,7 @@ before_script:
|
||||
# Installing libsodium, needed for toxcore
|
||||
- git clone https://github.com/jedisct1/libsodium.git libsodium
|
||||
- cd libsodium
|
||||
- git checkout tags/0.4.2 > /dev/null
|
||||
- git checkout tags/0.7.0 > /dev/null
|
||||
- ./autogen.sh > /dev/null
|
||||
- ./configure > /dev/null
|
||||
- make check -j2 || make check || exit 1 > /dev/null
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Toxic [](https://travis-ci.org/Tox/toxic)
|
||||
Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
|
||||
|
||||
.
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
@ -22,6 +22,11 @@ Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly
|
||||
##### Desktop notifications
|
||||
* [libnotify](https://developer.gnome.org/libnotify) (for Debian based systems, 'libnotify-dev')
|
||||
|
||||
##### Documentation
|
||||
* [Asciidoc](http://asciidoc.org/index.html) (only required for regenerating manpages)
|
||||
* Run `make doc` after editing the asciidoc files to regenerate the manpages.
|
||||
* **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.
|
||||
|
||||
### Compiling
|
||||
1. `cd build/`
|
||||
2. `make PREFIX="/where/to/install"`
|
||||
@ -31,6 +36,7 @@ Toxic is a [Tox](https://tox.im)-based instant messenging client which formerly
|
||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
|
||||
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
|
||||
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
|
||||
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
|
||||
* `DISABLE_AV=1` → build toxic without audio call support
|
||||
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
|
||||
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
|
||||
|
@ -1,25 +1,19 @@
|
||||
TOXIC_VERSION = 0.4.7
|
||||
REV = $(shell git rev-list HEAD --count)
|
||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||
BASE_DIR = $(shell cd .. && pwd -P)
|
||||
CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
CFG_DIR = ../cfg
|
||||
SRC_DIR = ../src
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DATADIR = $(PREFIX)/share/toxic
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
-include $(CFG_DIR)/global_vars.mk
|
||||
|
||||
LIBS = libtoxcore ncursesw libconfig
|
||||
|
||||
CFLAGS = -std=gnu99 -pthread -Wall -g
|
||||
CFLAGS += -DTOXICVER="\"$(VERSION)\"" -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
|
||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED
|
||||
CFLAGS += -DPACKAGE_DATADIR="\"$(abspath $(DATADIR))\""
|
||||
CFLAGS += $(USER_CFLAGS)
|
||||
LDFLAGS = $(USER_LDFLAGS)
|
||||
|
||||
OBJ = chat.o chat_commands.o configdir.o dns.o execute.o file_senders.o notify.o
|
||||
OBJ += friendlist.o global_commands.o groupchat.o line_info.o input.o help.o autocomplete.o
|
||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o
|
||||
OBJ += log.o misc_tools.o prompt.o settings.o toxic.o toxic_strings.o windows.o message_queue.o
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
@ -49,10 +43,10 @@ ifneq ($(filter arm%, $(UNAME_M)),)
|
||||
endif
|
||||
|
||||
# Include all needed checks
|
||||
-include $(CFG_DIR)/check_features.mk
|
||||
-include $(CFG_DIR)/checks/check_features.mk
|
||||
|
||||
# Targets
|
||||
all: toxic
|
||||
all: toxic doc
|
||||
|
||||
toxic: $(OBJ)
|
||||
@echo " LD $@"
|
||||
@ -68,7 +62,6 @@ clean:
|
||||
|
||||
-include $(OBJ:.o=.d)
|
||||
|
||||
-include $(CFG_DIR)/install.mk
|
||||
-include $(CFG_DIR)/help.mk
|
||||
-include $(CFG_DIR)/targets/*.mk
|
||||
|
||||
.PHONY: clean all
|
||||
|
@ -8,14 +8,14 @@ else
|
||||
endif
|
||||
|
||||
# Check if we can build audio support
|
||||
CHECK_AUDIO_LIBS = $(shell pkg-config $(AUDIO_LIBS) || echo -n "error")
|
||||
CHECK_AUDIO_LIBS = $(shell pkg-config --exists $(AUDIO_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||
LIBS += $(AUDIO_LIBS)
|
||||
CFLAGS += $(AUDIO_CFLAGS)
|
||||
OBJ += $(AUDIO_OBJ)
|
||||
else
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||
$(warning WARNING -- You need these libraries for audio support)
|
||||
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
@ -1,36 +1,37 @@
|
||||
# Check if we can use X11
|
||||
CHECK_X11_LIBS = $(shell pkg-config x11 || echo -n "error")
|
||||
ifneq ($(CHECK_X11_LIBS), error)
|
||||
LIBS += x11
|
||||
CFLAGS += -D_X11
|
||||
CHECKS_DIR = $(CFG_DIR)/checks
|
||||
|
||||
# Check if we want build X11 support
|
||||
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(X11), disabled)
|
||||
-include $(CHECKS_DIR)/x11.mk
|
||||
endif
|
||||
|
||||
# Check if we want build audio support
|
||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(AUDIO), disabled)
|
||||
-include $(CFG_DIR)/av.mk
|
||||
-include $(CHECKS_DIR)/av.mk
|
||||
endif
|
||||
|
||||
# Check if we want build sound notifications support
|
||||
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(SND_NOTIFY), disabled)
|
||||
-include $(CFG_DIR)/sound_notifications.mk
|
||||
-include $(CHECKS_DIR)/sound_notifications.mk
|
||||
endif
|
||||
|
||||
# Check if we want build desktop notifications support
|
||||
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(DESK_NOTIFY), disabled)
|
||||
-include $(CFG_DIR)/desktop_notifications.mk
|
||||
-include $(CHECKS_DIR)/desktop_notifications.mk
|
||||
endif
|
||||
|
||||
# Check if we can build Toxic
|
||||
CHECK_LIBS = $(shell pkg-config $(LIBS) || echo -n "error")
|
||||
CHECK_LIBS = $(shell pkg-config --exists $(LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_LIBS), error)
|
||||
CFLAGS += $(shell pkg-config --cflags $(LIBS))
|
||||
LDFLAGS += $(shell pkg-config --libs $(LIBS))
|
||||
else
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning ERROR -- Cannot compile Toxic)
|
||||
$(warning ERROR -- You need these libraries)
|
||||
$(warning ERROR -- $(MISSING_LIBS))
|
@ -3,13 +3,13 @@ DESK_NOTIFY_LIBS = libnotify
|
||||
DESK_NOTIFY_CFLAGS = -D_BOX_NOTIFY
|
||||
|
||||
# Check if we can build desktop notifications support
|
||||
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||
CHECK_DESK_NOTIFY_LIBS = $(shell pkg-config --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
|
||||
LIBS += $(DESK_NOTIFY_LIBS)
|
||||
CFLAGS += $(DESK_NOTIFY_CFLAGS)
|
||||
else
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
|
||||
$(warning WARNING -- You need these libraries for desktop notifications support)
|
||||
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
|
@ -8,14 +8,14 @@ else
|
||||
endif
|
||||
|
||||
# Check if we can build sound notifications support
|
||||
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config $(SND_NOTIFY_LIBS) || echo -n "error")
|
||||
CHECK_SND_NOTIFY_LIBS = $(shell pkg-config --exists $(SND_NOTIFY_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_SND_NOTIFY_LIBS), error)
|
||||
LIBS += $(SND_NOTIFY_LIBS)
|
||||
CFLAGS += $(SND_NOTIFY_CFLAGS)
|
||||
OBJ += $(SND_NOTIFY_OBJ)
|
||||
else
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_SND_NOTIFY_LIBS = $(shell for lib in $(SND_NOTIFY_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without sound notifications support)
|
||||
$(warning WARNING -- You need these libraries for sound notifications support)
|
||||
$(warning WARNING -- $(MISSING_SND_NOTIFY_LIBS))
|
17
cfg/checks/x11.mk
Normal file
17
cfg/checks/x11.mk
Normal file
@ -0,0 +1,17 @@
|
||||
# Variables for X11 support
|
||||
X11_LIBS = x11
|
||||
X11_CFLAGS = -D_X11
|
||||
|
||||
# Check if we can build X11 support
|
||||
CHECK_X11_LIBS = $(shell pkg-config --exists $(X11_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_X11_LIBS), error)
|
||||
LIBS += $(X11_LIBS)
|
||||
CFLAGS += $(X11_CFLAGS)
|
||||
else
|
||||
ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_X11_LIBS = $(shell for lib in $(X11_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without x11 support (needed for focus tracking))
|
||||
$(warning WARNING -- You need these libraries for x11 support)
|
||||
$(warning WARNING -- $(MISSING_X11_LIBS))
|
||||
endif
|
||||
endif
|
23
cfg/global_vars.mk
Normal file
23
cfg/global_vars.mk
Normal file
@ -0,0 +1,23 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.5.0
|
||||
REV = $(shell git rev-list HEAD --count)
|
||||
VERSION = $(TOXIC_VERSION)_r$(REV)
|
||||
|
||||
# Project directories
|
||||
DOC_DIR = $(BASE_DIR)/doc
|
||||
SRC_DIR = $(BASE_DIR)/src
|
||||
SND_DIR = $(BASE_DIR)/sounds
|
||||
MISC_DIR = $(BASE_DIR)/misc
|
||||
|
||||
# Project files
|
||||
MANFILES = toxic.1 toxic.conf.5
|
||||
DATAFILES = DHTnodes DNSservers toxic.conf.example
|
||||
SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav
|
||||
SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav
|
||||
SNDFILES += TransferComplete.wav TransferPending.wav
|
||||
|
||||
# Install directories
|
||||
PREFIX = /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DATADIR = $(PREFIX)/share/toxic
|
||||
MANDIR = $(PREFIX)/share/man
|
@ -1,3 +1,4 @@
|
||||
# Specials options for freebsd systems
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw
|
||||
MANDIR = $(PREFIX)/man
|
||||
|
@ -1,3 +1,4 @@
|
||||
# Specials options for linux systems
|
||||
CFLAGS +=
|
||||
LDFLAGS += -ldl -lresolv
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
|
10
cfg/targets/doc.mk
Normal file
10
cfg/targets/doc.mk
Normal file
@ -0,0 +1,10 @@
|
||||
# Doc target
|
||||
doc: $(MANFILES:%=$(DOC_DIR)/%)
|
||||
|
||||
$(DOC_DIR)/%: $(DOC_DIR)/%.asc
|
||||
@echo " MAN $(@F)"
|
||||
@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \
|
||||
-a manmanual="Toxic Manual" -a mansource=toxic \
|
||||
-a manversion=__VERSION__ -a datadir=__DATADIR__ $<
|
||||
|
||||
.PHONY: doc
|
@ -1,13 +1,15 @@
|
||||
# Help target
|
||||
help:
|
||||
@echo "-- Targets --"
|
||||
@echo " all: Build toxic [DEFAULT]"
|
||||
@echo " all: Build toxic and documentation [DEFAULT]"
|
||||
@echo " toxic: Build toxic"
|
||||
@echo " doc: Build documentation"
|
||||
@echo " install: Build toxic and install it in PREFIX (default PREFIX is \"$(abspath $(PREFIX))\")"
|
||||
@echo " clean: Remove built files"
|
||||
@echo " help: This help"
|
||||
@echo
|
||||
@echo "-- Variables --"
|
||||
@echo " DISABLE_X11: Set to \"1\" to force building without X11 support"
|
||||
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
|
||||
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
|
||||
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
|
@ -1,19 +1,13 @@
|
||||
MISC_DIR = ../misc
|
||||
DOC_DIR = ../doc
|
||||
SND_DIR = ../sounds
|
||||
DATAFILES = DHTnodes toxic.conf.example
|
||||
MANFILES = toxic.1 toxic.conf.5
|
||||
SNDFILES = ContactLogsIn.wav ContactLogsOut.wav Error.wav IncomingCall.wav
|
||||
SNDFILES += LogIn.wav LogOut.wav NewMessage.wav OutgoingCall.wav
|
||||
SNDFILES += TransferComplete.wav TransferPending.wav
|
||||
|
||||
install: toxic
|
||||
# Install target
|
||||
install: toxic doc
|
||||
mkdir -p $(abspath $(DESTDIR)/$(BINDIR))
|
||||
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))
|
||||
mkdir -p $(abspath $(DESTDIR)/$(DATADIR))/sounds
|
||||
mkdir -p $(abspath $(DESTDIR)/$(MANDIR))
|
||||
|
||||
@echo "Installing toxic executable"
|
||||
@install -m 0755 toxic $(abspath $(DESTDIR)/$(BINDIR))
|
||||
|
||||
@echo "Installing data files"
|
||||
@for f in $(DATAFILES) ; do \
|
||||
install -m 0644 $(MISC_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR)) ;\
|
||||
@ -23,6 +17,7 @@ install: toxic
|
||||
@for f in $(SNDFILES) ; do \
|
||||
install -m 0644 $(SND_DIR)/$$f $(abspath $(DESTDIR)/$(DATADIR))/sounds ;\
|
||||
done
|
||||
|
||||
@echo "Installing man pages"
|
||||
@for f in $(MANFILES) ; do \
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
206
doc/toxic.1
206
doc/toxic.1
@ -1,63 +1,153 @@
|
||||
.TH TOXIC 1 "June 2014" "Toxic v__VERSION__" "User Manual"
|
||||
.SH NAME
|
||||
Toxic \- CLI client for Tox
|
||||
.SH SYNOPSYS
|
||||
.B toxic [\-f
|
||||
.I data\-file
|
||||
.B ] [\-x] [\-4] [\-c
|
||||
.I config\-file
|
||||
.B ] [\-n
|
||||
.I nodes\-file
|
||||
.B ] [\-h]
|
||||
.SH DESCRIPTION
|
||||
Toxic is an ncurses-based instant messaging client for Tox which formerly
|
||||
resided in the Tox core repository, and is now available as a standalone
|
||||
application.
|
||||
.SH OPTIONS
|
||||
.IP "\-f, \-\-file data\-file"
|
||||
Use specified
|
||||
.I data\-file
|
||||
instead of
|
||||
.IR ~/.config/tox/data
|
||||
.IP "\-x, \-\-nodata"
|
||||
Ignore data file
|
||||
.IP "\-4, \-\-ipv4"
|
||||
'\" t
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2014-09-19
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2014\-09\-19" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.\" http://bugs.debian.org/507673
|
||||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * set default formatting
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" disable hyphenation
|
||||
.nh
|
||||
.\" disable justification (adjust text to left margin only)
|
||||
.ad l
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * MAIN CONTENT STARTS HERE *
|
||||
.\" -----------------------------------------------------------------
|
||||
.SH "NAME"
|
||||
toxic \- CLI client for Tox
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
\fBtoxic\fR [\-f \fIdata\-file\fR] [\-x] [\-4] [\-c \fIconfig\-file\fR] [\-n \fInodes\-file\fR] [\-h]
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
toxic is an ncurses\-based instant messaging client for Tox which formerly resided in the Tox core repository, and is now available as a standalone application\&.
|
||||
.SH "OPTIONS"
|
||||
.PP
|
||||
\-4, \-\-ipv4
|
||||
.RS 4
|
||||
Force IPv4 connection
|
||||
.IP "\-d, \-\-default_locale
|
||||
Use default locale
|
||||
.IP "\-c, \-\-config config\-file"
|
||||
.RE
|
||||
.PP
|
||||
\-b, \-\-debug
|
||||
.RS 4
|
||||
Enable stderr for debugging\&. Redirect output to avoid breaking the curses interface and better capture messages\&.
|
||||
.RE
|
||||
.PP
|
||||
\-c, \-\-config config\-file
|
||||
.RS 4
|
||||
Use specified
|
||||
.I config\-file
|
||||
\fIconfig\-file\fR
|
||||
instead of
|
||||
.IR ~/.config/tox/toxic.conf
|
||||
.IP "\-n, \-\-nodes nodes\-file"
|
||||
\fI~/\&.config/tox/toxic\&.conf\fR
|
||||
.RE
|
||||
.PP
|
||||
\-d, \-\-default\-locale
|
||||
.RS 4
|
||||
Use default locale
|
||||
.RE
|
||||
.PP
|
||||
\-e, \-\-encrypt\-data
|
||||
.RS 4
|
||||
Encrypt an unencrypted data file\&. An error will occur if this option is used with an encrypted data file\&.
|
||||
.RE
|
||||
.PP
|
||||
\-f, \-\-file data\-file
|
||||
.RS 4
|
||||
Use specified
|
||||
.I nodes\-file
|
||||
for DHT bootstrap nodes, instead of
|
||||
.IR __DATADIR__/DHTnodes
|
||||
.IP "\-h, \-\-help"
|
||||
\fIdata\-file\fR
|
||||
instead of
|
||||
\fI~/\&.config/tox/data\fR
|
||||
.RE
|
||||
.PP
|
||||
\-h, \-\-help
|
||||
.RS 4
|
||||
Show help message
|
||||
.SH FILES
|
||||
.IP __DATADIR__/DHTnodes
|
||||
Default list of DHT bootstrap nodes.
|
||||
.IP ~/.config/tox/data
|
||||
Savestate which contains your personal info (nickname, Tox ID,...) and
|
||||
your contacts list.
|
||||
.IP ~/.config/tox/toxic.conf
|
||||
Configuration file. See
|
||||
.BR toxic.conf (5)
|
||||
for more details.
|
||||
.IP __DATADIR__/toxic.conf.example
|
||||
Configuration example.
|
||||
.SH BUGS
|
||||
Unicode characters with a width larger than 1 column may cause
|
||||
strange behaviour. Expect more bugs and bad
|
||||
behaviour: this software is in a pre\-alpha stage.
|
||||
.SH AUTHORS
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
.SH SEE ALSO
|
||||
.BR toxic.conf (5)
|
||||
.SH LINKS
|
||||
Project page on github: https://github.com/Tox/toxic
|
||||
.br
|
||||
IRC channel on Freenode: chat.freenode.net#tox
|
||||
.RE
|
||||
.PP
|
||||
\-n, \-\-nodes nodes\-file
|
||||
.RS 4
|
||||
Use specified
|
||||
\fInodes\-file\fR
|
||||
for DHT bootstrap nodes, instead of
|
||||
\fI__DATADIR__/DHTnodes\fR
|
||||
.RE
|
||||
.PP
|
||||
\-o, \-\-noconnect
|
||||
.RS 4
|
||||
Do not connect to the DHT network
|
||||
.RE
|
||||
.PP
|
||||
\-p, \-\-proxy
|
||||
.RS 4
|
||||
Use proxy: Requires [IP] [port]
|
||||
.RE
|
||||
.PP
|
||||
\-r, \-\-dnslist
|
||||
.RS 4
|
||||
Use specified DNSservers file
|
||||
.RE
|
||||
.PP
|
||||
\-t, \-\-force\-tcp
|
||||
.RS 4
|
||||
Force TCP connection (use this with proxies)
|
||||
.RE
|
||||
.PP
|
||||
\-u, \-\-unencrypt\-data
|
||||
.RS 4
|
||||
Unencrypt a data file\&. A warning will appear if this option is used with a data file that is already unencrypted\&.
|
||||
.RE
|
||||
.PP
|
||||
\-x, \-\-nodata
|
||||
.RS 4
|
||||
Ignore data file
|
||||
.RE
|
||||
.SH "FILES"
|
||||
.PP
|
||||
__DATADIR__/DHTnodes
|
||||
.RS 4
|
||||
Default list of DHT bootstrap nodes\&.
|
||||
.RE
|
||||
.PP
|
||||
~/\&.config/tox/data
|
||||
.RS 4
|
||||
Savestate which contains your personal info (nickname, Tox ID, contacts, etc)
|
||||
.RE
|
||||
.PP
|
||||
~/\&.config/tox/toxic\&.conf
|
||||
.RS 4
|
||||
Configuration file\&. See
|
||||
\fBtoxic\&.conf\fR(5) for more details\&.
|
||||
.RE
|
||||
.PP
|
||||
__DATADIR__/toxic\&.conf\&.example
|
||||
.RS 4
|
||||
Configuration example\&.
|
||||
.RE
|
||||
.SH "BUGS"
|
||||
.sp
|
||||
Unicode characters with a width larger than 1 column may cause strange behaviour\&. Expect more bugs and bad behaviour: this software is in a pre\-alpha stage\&.
|
||||
.SH "AUTHORS"
|
||||
.sp
|
||||
JFreegman <JFreegman@gmail\&.com>
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\&.conf\fR(5)
|
||||
.SH "LINKS"
|
||||
.sp
|
||||
Project page: https://github\&.com/Tox/toxic
|
||||
.sp
|
||||
IRC channel: chat\&.freenode\&.net#tox
|
||||
|
99
doc/toxic.1.asc
Normal file
99
doc/toxic.1.asc
Normal file
@ -0,0 +1,99 @@
|
||||
toxic(1)
|
||||
========
|
||||
|
||||
NAME
|
||||
----
|
||||
toxic - CLI client for Tox
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
*toxic* [-f 'data-file'] [-x] [-4] [-c 'config-file'] [-n 'nodes-file'] [-h]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
toxic is an ncurses-based instant messaging client for Tox which formerly
|
||||
resided in the Tox core repository, and is now available as a standalone
|
||||
application.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-4, --ipv4::
|
||||
Force IPv4 connection
|
||||
|
||||
-b, --debug::
|
||||
Enable stderr for debugging. Redirect output to
|
||||
avoid breaking the curses interface and better capture messages.
|
||||
|
||||
-c, --config config-file::
|
||||
Use specified 'config-file' instead of '~/.config/tox/toxic.conf'
|
||||
|
||||
-d, --default-locale::
|
||||
Use default locale
|
||||
|
||||
-e, --encrypt-data::
|
||||
Encrypt an unencrypted data file. An error will occur if this option
|
||||
is used with an encrypted data file.
|
||||
|
||||
-f, --file data-file::
|
||||
Use specified 'data-file' instead of '~/.config/tox/data'
|
||||
|
||||
-h, --help::
|
||||
Show help message
|
||||
|
||||
-n, --nodes nodes-file::
|
||||
Use specified 'nodes-file' for DHT bootstrap nodes, instead of
|
||||
'{datadir}/DHTnodes'
|
||||
|
||||
-o, --noconnect::
|
||||
Do not connect to the DHT network
|
||||
|
||||
-p, --proxy::
|
||||
Use proxy: Requires [IP] [port]
|
||||
|
||||
-r, --dnslist::
|
||||
Use specified DNSservers file
|
||||
|
||||
-t, --force-tcp::
|
||||
Force TCP connection (use this with proxies)
|
||||
|
||||
-u, --unencrypt-data::
|
||||
Unencrypt a data file. A warning will appear if this option is used
|
||||
with a data file that is already unencrypted.
|
||||
|
||||
-x, --nodata::
|
||||
Ignore data file
|
||||
|
||||
FILES
|
||||
-----
|
||||
{datadir}/DHTnodes::
|
||||
Default list of DHT bootstrap nodes.
|
||||
|
||||
~/.config/tox/data::
|
||||
Savestate which contains your personal info (nickname, Tox ID, contacts,
|
||||
etc)
|
||||
|
||||
~/.config/tox/toxic.conf::
|
||||
Configuration file. See *toxic.conf*(5) for more details.
|
||||
|
||||
{datadir}/toxic.conf.example::
|
||||
Configuration example.
|
||||
|
||||
BUGS
|
||||
----
|
||||
Unicode characters with a width larger than 1 column may cause strange
|
||||
behaviour. Expect more bugs and bad behaviour: this software is in a
|
||||
pre-alpha stage.
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*toxic.conf*(5)
|
||||
|
||||
LINKS
|
||||
-----
|
||||
Project page: <https://github.com/Tox/toxic>
|
||||
|
||||
IRC channel: chat.freenode.net#tox
|
671
doc/toxic.conf.5
671
doc/toxic.conf.5
@ -1,413 +1,276 @@
|
||||
.TH TOXIC.CONF 5 "June 2014" "Toxic v__VERSION__" "User Manual"
|
||||
.SH NAME
|
||||
toxic.conf \- Configuration file for toxic(1)
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.I toxic.conf
|
||||
file is the main configuration file for
|
||||
.BR toxic (1)
|
||||
client.
|
||||
.SH SYNTAX
|
||||
.I <SECTION>
|
||||
.B = {
|
||||
.PP
|
||||
.IB <KEY1> = <VALUE1> ;
|
||||
.br
|
||||
.IB <KEY2> = <VALUE2> ;
|
||||
.br
|
||||
...
|
||||
.PP
|
||||
.B };
|
||||
.PP
|
||||
Uses syntax accepted by libconfig.
|
||||
.br
|
||||
Lines starting with "//" are comments and will be ignored.
|
||||
.PP
|
||||
Sections:
|
||||
.PP
|
||||
.B ui
|
||||
.RS
|
||||
Configurations related to user interface elements.
|
||||
.PP
|
||||
Keys:
|
||||
.br
|
||||
.B timestamps
|
||||
.RS
|
||||
Enable or disable timestamps.
|
||||
.br
|
||||
Values: 'true' to enable, 'false' to disable
|
||||
.RE
|
||||
.PP
|
||||
.B alerts
|
||||
.RS
|
||||
Enable or disable terminal alerts on events.
|
||||
.br
|
||||
Values: 'true' to enable, 'false' to disable
|
||||
.RE
|
||||
.PP
|
||||
.B native_colors
|
||||
.RS
|
||||
Select between native terminal colors and toxic color theme.
|
||||
.br
|
||||
Values: 'true' for terminal colours, 'false' for toxic colours
|
||||
.RE
|
||||
.PP
|
||||
.B autolog
|
||||
.RS
|
||||
Enable or disable autologging.
|
||||
.br
|
||||
Values: 'true' to enable, 'false' to disable
|
||||
.RE
|
||||
.PP
|
||||
.B time_format
|
||||
.RS
|
||||
Select between 24 and 12 hour time.
|
||||
.br
|
||||
Values: 24, 12
|
||||
.RE
|
||||
.PP
|
||||
.B show_typing_other
|
||||
.RS
|
||||
Show you when others are typing in a 1-on-1 chat
|
||||
.br
|
||||
Values: 'true' to enable, 'false' to disable
|
||||
.RE
|
||||
.PP
|
||||
.B show_typing_self
|
||||
.RS
|
||||
Show others when you're typing in a 1-on-1 chat
|
||||
.br
|
||||
Values: 'true' to enable, 'false' to disable
|
||||
.RE
|
||||
.PP
|
||||
.B history_size
|
||||
.RS
|
||||
Maximum lines for chat window history.
|
||||
.br
|
||||
Values: <INTEGER> (for example: 700)
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
.B audio
|
||||
.RS
|
||||
Configurations related to audio devices.
|
||||
.PP
|
||||
Keys:
|
||||
.br
|
||||
.B input_device
|
||||
.RS
|
||||
Audio input device.
|
||||
.br
|
||||
Values: <INTEGER> (number correspond to "/lsdev in")
|
||||
.RE
|
||||
.PP
|
||||
.B output_device
|
||||
.RS
|
||||
Audio output device.
|
||||
.br
|
||||
Values: <INTEGER> (number correspond to "/lsdev out")
|
||||
.RE
|
||||
.PP
|
||||
.B VAD_treshold
|
||||
.RS
|
||||
Voice Activity Detection treshold.
|
||||
.br
|
||||
Values: <FLOAT> (recommended values are around 40.0)
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
.B tox
|
||||
.RS
|
||||
Configurations related to file transfer.
|
||||
.PP
|
||||
Keys:
|
||||
.br
|
||||
.B download_path
|
||||
.RS
|
||||
Default path for downloads.
|
||||
.br
|
||||
Values: <STRING> (absolute path where to store downloaded files)
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
.B sounds
|
||||
.RS
|
||||
Configurations related to notification sounds.
|
||||
.br
|
||||
(Special value "silent" can be used to disable a specific notification)
|
||||
.PP
|
||||
Keys:
|
||||
.br
|
||||
.B error
|
||||
.RS
|
||||
Sound to play when an error occurs.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B self_log_in
|
||||
.RS
|
||||
Sound to play when you log in.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B self_log_out
|
||||
.RS
|
||||
Sound to play when you log out.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B user_log_in
|
||||
.RS
|
||||
Sound to play when a contact become online.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B user_log_out
|
||||
.RS
|
||||
Sound to play when a contact become offline.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B call_incoming
|
||||
.RS
|
||||
Sound to play when you receive an incoming call.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B call_outgoing
|
||||
.RS
|
||||
Sound to play when you start a call.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B generic_message
|
||||
.RS
|
||||
Sound to play when an event occurs.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B transfer_pending
|
||||
.RS
|
||||
Sound to play when you receive a file transfer request.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.PP
|
||||
.B transfer_completed
|
||||
.RS
|
||||
Sound to play when a file transfer is completed.
|
||||
.br
|
||||
Values: <STRING> (sound file absolute path)
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
.B keys
|
||||
.RS
|
||||
Configurations related to user interface interaction.
|
||||
.br
|
||||
(Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive))
|
||||
.PP
|
||||
Keys:
|
||||
.br
|
||||
.B next_tab
|
||||
.RS
|
||||
Key combination to switch next tab.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B prev_tab
|
||||
.RS
|
||||
Key combination to switch previous tab.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B scroll_line_up
|
||||
.RS
|
||||
Key combination to scroll one line up.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B scroll_line_down
|
||||
.RS
|
||||
Key combination to scroll one line down.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B half_page_up
|
||||
.RS
|
||||
Key combination to scroll half page up.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B half_page_down
|
||||
.RS
|
||||
Key combination to scroll half page down.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B page_bottom
|
||||
.RS
|
||||
Key combination to scroll to page bottom.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B peer_list_up
|
||||
.RS
|
||||
Key combination to scroll contacts list up.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.PP
|
||||
.B peer_list_down
|
||||
.RS
|
||||
Key combination to scroll contacts list down.
|
||||
.br
|
||||
Values: <STRING> (key combination)
|
||||
.RE
|
||||
.RE
|
||||
.SH EXAMPLES
|
||||
Default settings from __DATADIR__/toxic.conf.exmaple:
|
||||
.PP
|
||||
// SAMPLE TOXIC CONFIGURATION
|
||||
.br
|
||||
// USES LIBCONFIG-ACCEPTED SYNTAX
|
||||
.br
|
||||
'\" t
|
||||
.\" Title: toxic.conf
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2014-08-26
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2014\-08\-26" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.\" http://bugs.debian.org/507673
|
||||
.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
|
||||
.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
.ie \n(.g .ds Aq \(aq
|
||||
.el .ds Aq '
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * set default formatting
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" disable hyphenation
|
||||
.nh
|
||||
.\" disable justification (adjust text to left margin only)
|
||||
.ad l
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * MAIN CONTENT STARTS HERE *
|
||||
.\" -----------------------------------------------------------------
|
||||
.SH "NAME"
|
||||
toxic.conf \- Configuration file for toxic
|
||||
.SH "SYNOPSIS"
|
||||
.sp
|
||||
~/\&.config/tox/toxic\&.conf
|
||||
.SH "DESCRIPTION"
|
||||
.sp
|
||||
The \fItoxic\&.conf\fR file is the main configuration file for \fBtoxic\fR(1) client\&. It uses syntax accepted by \fBlibconfig\fR\&. Lines starting with "//" are comments and will be ignored\&.
|
||||
.SH "EXAMPLE"
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
.\}
|
||||
.nf
|
||||
// Configuration for interface
|
||||
ui = {
|
||||
.RS
|
||||
// true to enable timestamps, false to disable
|
||||
.br
|
||||
timestamps=true;
|
||||
.br
|
||||
// true to enable terminal alerts on messages, false to disable
|
||||
.br
|
||||
alerts=true;
|
||||
.br
|
||||
// true to use native terminal colours, false to use toxic default colour theme
|
||||
.br
|
||||
native_colors=false;
|
||||
.br
|
||||
// true to enable autologging, false to disable
|
||||
.br
|
||||
autolog=false;
|
||||
.br
|
||||
// 24 or 12 hour time
|
||||
.br
|
||||
time_format=24;
|
||||
.br
|
||||
// true to show you when others are typing a message in 1-on-1 chats
|
||||
.br
|
||||
show_typing_other=true;
|
||||
.br
|
||||
// true to show others when you're typing a message in 1-on-1 chats
|
||||
.br
|
||||
show_typing_self=true;
|
||||
.br
|
||||
// maximum lines for chat window history
|
||||
.br
|
||||
history_size=700;
|
||||
.RE
|
||||
timestamps = true;
|
||||
alerts = false;
|
||||
};
|
||||
.PP
|
||||
|
||||
// Configuration for audio
|
||||
audio = {
|
||||
.RS
|
||||
// preferred audio input device; numbers correspond to /lsdev in
|
||||
.br
|
||||
input_device=2;
|
||||
.br
|
||||
// preferred audio output device; numbers correspond to /lsdev out
|
||||
.br
|
||||
output_device=0;
|
||||
.br
|
||||
// default VAD treshold; float (recommended values are around 40)
|
||||
.br
|
||||
VAD_treshold=40.0;
|
||||
.RE
|
||||
input_device = 1;
|
||||
};
|
||||
.fi
|
||||
.if n \{\
|
||||
.RE
|
||||
.\}
|
||||
.SH "OPTIONS"
|
||||
.PP
|
||||
tox = {
|
||||
.RS
|
||||
// where to store received files
|
||||
.br
|
||||
//download_path="/home/USERNAME/Downloads/";
|
||||
.RE
|
||||
};
|
||||
\fBui\fR
|
||||
.RS 4
|
||||
Configuration related to interface elements\&.
|
||||
.PP
|
||||
// To disable a sound set the path to "silent"
|
||||
.br
|
||||
sounds = {
|
||||
.RS
|
||||
error="__DATADIR__/sounds/Error.wav";
|
||||
.br
|
||||
self_log_in="__DATADIR__/sounds/LogIn.wav";
|
||||
.br
|
||||
self_log_out="__DATADIR__/sounds/LogOut.wav";
|
||||
.br
|
||||
user_log_in="__DATADIR__/sounds/ContactLogsIn.wav";
|
||||
.br
|
||||
user_log_out="__DATADIR__/sounds/ContactLogsOut.wav";
|
||||
.br
|
||||
call_incoming="__DATADIR__/sounds/IncomingCall.wav";
|
||||
.br
|
||||
call_outgoing="__DATADIR__/sounds/OutgoingCall.wav";
|
||||
.br
|
||||
generic_message="__DATADIR__/sounds/NewMessage.wav";
|
||||
.br
|
||||
transfer_pending="__DATADIR__/sounds/TransferPending.wav";
|
||||
.br
|
||||
transfer_completed="__DATADIR__/sounds/TransferComplete.wav";
|
||||
\fBtimestamps\fR
|
||||
.RS 4
|
||||
Enable or disable timestamps\&. true or false
|
||||
.RE
|
||||
};
|
||||
.PP
|
||||
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
|
||||
.br
|
||||
// Note: All printable keys register as input
|
||||
.br
|
||||
keys = {
|
||||
.RS
|
||||
next_tab="Ctrl+P";
|
||||
.br
|
||||
prev_tab="Ctrl+O";
|
||||
.br
|
||||
scroll_line_up="PAGEUP";
|
||||
.br
|
||||
scroll_line_down="PAGEDOWN";
|
||||
.br
|
||||
half_page_up="Ctrl+F";
|
||||
.br
|
||||
half_page_down="Ctrl+V";
|
||||
.br
|
||||
page_bottom="Ctrl+H";
|
||||
.br
|
||||
peer_list_up="Ctrl+[";
|
||||
.br
|
||||
peer_list_down="Ctrl+]";
|
||||
\fBalerts\fR
|
||||
.RS 4
|
||||
Enable or disable terminal alerts on events\&. true or false
|
||||
.RE
|
||||
};
|
||||
.SH FILES
|
||||
.IP ~/.config/tox/toxic.conf
|
||||
Main configuration file.
|
||||
.IP __DATADIR__/toxic.conf.example
|
||||
Configuration example.
|
||||
.SH AUTHORS
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
.SH SEE ALSO
|
||||
.BR toxic (1)
|
||||
.SH LINKS
|
||||
Project page on github: https://github.com/Tox/toxic
|
||||
.br
|
||||
IRC channel on Freenode: chat.freenode.net#tox
|
||||
.PP
|
||||
\fBnative_colors\fR
|
||||
.RS 4
|
||||
Select between native terminal colors and toxic color theme\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBautolog\fR
|
||||
.RS 4
|
||||
Enable or disable autologging\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBtime_format\fR
|
||||
.RS 4
|
||||
Select between 24 and 12 hour time\&. Specify 24 or 12
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_typing_other\fR
|
||||
.RS 4
|
||||
Show when others are typing in a 1\-on\-1 chat\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_typing_self\fR
|
||||
.RS 4
|
||||
Show others when you\(cqre typing in a 1\-on\-1 chat\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_welcome_msg\fR
|
||||
.RS 4
|
||||
Show welcome message on startup\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBhistory_size\fR
|
||||
.RS 4
|
||||
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\fBaudio\fR
|
||||
.RS 4
|
||||
Configuration related to audio devices\&.
|
||||
.PP
|
||||
\fBinput_device\fR
|
||||
.RS 4
|
||||
Audio input device\&. Integer value\&. Number corresponds to
|
||||
/lsdev in
|
||||
.RE
|
||||
.PP
|
||||
\fBoutput_device\fR
|
||||
.RS 4
|
||||
Audio output device\&. Integer value\&. Number corresponds to
|
||||
/lsdev out
|
||||
.RE
|
||||
.PP
|
||||
\fBVAD_treshold\fR
|
||||
.RS 4
|
||||
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\fBtox\fR
|
||||
.RS 4
|
||||
Configuration related to paths\&.
|
||||
.PP
|
||||
\fBdownload_path\fR
|
||||
.RS 4
|
||||
Default path for downloads\&. String value\&. Absolute path for downloaded files\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBchatlogs_path\fR
|
||||
.RS 4
|
||||
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\fBsounds\fR
|
||||
.RS 4
|
||||
Configuration related to notification sounds\&. Special value "silent" can be used to disable a specific notification\&.
|
||||
|
||||
Each value is a string which corresponds to the absolute path of a wav sound file\&.
|
||||
.PP
|
||||
\fBerror\fR
|
||||
.RS 4
|
||||
Sound to play when an error occurs\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBself_log_in\fR
|
||||
.RS 4
|
||||
Sound to play when you log in\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBself_log_out\fR
|
||||
.RS 4
|
||||
Sound to play when you log out\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBuser_log_in\fR
|
||||
.RS 4
|
||||
Sound to play when a contact become online\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBuser_log_out\fR
|
||||
.RS 4
|
||||
Sound to play when a contact become offline\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBcall_incoming\fR
|
||||
.RS 4
|
||||
Sound to play when you receive an incoming call\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBcall_outgoing\fR
|
||||
.RS 4
|
||||
Sound to play when you start a call\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBgeneric_message\fR
|
||||
.RS 4
|
||||
Sound to play when an event occurs\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtransfer_pending\fR
|
||||
.RS 4
|
||||
Sound to play when you receive a file transfer request\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtransfer_completed\fR
|
||||
.RS 4
|
||||
Sound to play when a file transfer is completed\&.
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
\fBkeys\fR
|
||||
.RS 4
|
||||
Configuration related to user interface interaction\&. Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN\&.
|
||||
|
||||
Each value is a string which corresponds to a key combination\&.
|
||||
.PP
|
||||
\fBnext_tab\fR
|
||||
.RS 4
|
||||
Key combination to switch next tab\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBprev_tab\fR
|
||||
.RS 4
|
||||
Key combination to switch previous tab\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBscroll_line_up\fR
|
||||
.RS 4
|
||||
Key combination to scroll one line up\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBscroll_line_down\fR
|
||||
.RS 4
|
||||
Key combination to scroll one line down\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBhalf_page_up\fR
|
||||
.RS 4
|
||||
Key combination to scroll half page up\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBhalf_page_down\fR
|
||||
.RS 4
|
||||
Key combination to scroll half page down\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpage_bottom\fR
|
||||
.RS 4
|
||||
Key combination to scroll to page bottom\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpeer_list_up\fR
|
||||
.RS 4
|
||||
Key combination to scroll contacts list up\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpeer_list_down\fR
|
||||
.RS 4
|
||||
Key combination to scroll contacts list down\&.
|
||||
.RE
|
||||
.RE
|
||||
.SH "FILES"
|
||||
.PP
|
||||
~/\&.config/tox/toxic\&.conf
|
||||
.RS 4
|
||||
Main configuration file\&.
|
||||
.RE
|
||||
.PP
|
||||
__DATADIR__/toxic\&.conf\&.example
|
||||
.RS 4
|
||||
Configuration example\&.
|
||||
.RE
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\fR(1)
|
||||
.SH "RESOURCES"
|
||||
.sp
|
||||
Project page: https://github\&.com/Tox/toxic
|
||||
.sp
|
||||
IRC channel: chat\&.freenode\&.net#tox
|
||||
.SH "AUTHORS"
|
||||
.sp
|
||||
JFreegman <JFreegman@gmail\&.com>
|
||||
|
185
doc/toxic.conf.5.asc
Normal file
185
doc/toxic.conf.5.asc
Normal file
@ -0,0 +1,185 @@
|
||||
toxic.conf(5)
|
||||
=============
|
||||
|
||||
NAME
|
||||
----
|
||||
toxic.conf - Configuration file for toxic
|
||||
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
~/.config/tox/toxic.conf
|
||||
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
The 'toxic.conf' file is the main configuration file for *toxic*(1) client.
|
||||
It uses syntax accepted by *libconfig*.
|
||||
Lines starting with "//" are comments and will be ignored.
|
||||
|
||||
|
||||
EXAMPLE
|
||||
-------
|
||||
----
|
||||
// Configuration for interface
|
||||
ui = {
|
||||
timestamps = true;
|
||||
alerts = false;
|
||||
};
|
||||
|
||||
// Configuration for audio
|
||||
audio = {
|
||||
input_device = 1;
|
||||
};
|
||||
----
|
||||
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
*ui*::
|
||||
Configuration related to interface elements.
|
||||
|
||||
*timestamps*;;
|
||||
Enable or disable timestamps. true or false
|
||||
|
||||
*alerts*;;
|
||||
Enable or disable terminal alerts on events. true or false
|
||||
|
||||
*native_colors*;;
|
||||
Select between native terminal colors and toxic color theme. true or false
|
||||
|
||||
*autolog*;;
|
||||
Enable or disable autologging. true or false
|
||||
|
||||
*time_format*;;
|
||||
Select between 24 and 12 hour time. Specify 24 or 12
|
||||
|
||||
*show_typing_other*;;
|
||||
Show when others are typing in a 1-on-1 chat. true or false
|
||||
|
||||
*show_typing_self*;;
|
||||
Show others when you're typing in a 1-on-1 chat. true or false
|
||||
|
||||
*show_welcome_msg*;;
|
||||
Show welcome message on startup. true or false
|
||||
|
||||
*history_size*;;
|
||||
Maximum lines for chat window history. Integer value. (for example: 700)
|
||||
|
||||
|
||||
*audio*::
|
||||
Configuration related to audio devices.
|
||||
|
||||
*input_device*;;
|
||||
Audio input device. Integer value. Number corresponds to `/lsdev in`
|
||||
|
||||
*output_device*;;
|
||||
Audio output device. Integer value. Number corresponds to `/lsdev out`
|
||||
|
||||
*VAD_treshold*;;
|
||||
Voice Activity Detection treshold. Float value. Recommended values are
|
||||
around 40.0
|
||||
|
||||
*tox*::
|
||||
Configuration related to paths.
|
||||
|
||||
*download_path*;;
|
||||
Default path for downloads. String value. Absolute path for downloaded
|
||||
files.
|
||||
|
||||
*chatlogs_path*;;
|
||||
Default path for chatlogs. String value. Absolute path for chatlog files.
|
||||
|
||||
*sounds*::
|
||||
Configuration related to notification sounds.
|
||||
Special value "silent" can be used to disable a specific notification. +
|
||||
Each value is a string which corresponds to the absolute path of a wav
|
||||
sound file.
|
||||
|
||||
*error*;;
|
||||
Sound to play when an error occurs.
|
||||
|
||||
*self_log_in*;;
|
||||
Sound to play when you log in.
|
||||
|
||||
*self_log_out*;;
|
||||
Sound to play when you log out.
|
||||
|
||||
*user_log_in*;;
|
||||
Sound to play when a contact become online.
|
||||
|
||||
*user_log_out*;;
|
||||
Sound to play when a contact become offline.
|
||||
|
||||
*call_incoming*;;
|
||||
Sound to play when you receive an incoming call.
|
||||
|
||||
*call_outgoing*;;
|
||||
Sound to play when you start a call.
|
||||
|
||||
*generic_message*;;
|
||||
Sound to play when an event occurs.
|
||||
|
||||
*transfer_pending*;;
|
||||
Sound to play when you receive a file transfer request.
|
||||
|
||||
*transfer_completed*;;
|
||||
Sound to play when a file transfer is completed.
|
||||
|
||||
*keys*::
|
||||
Configuration related to user interface interaction.
|
||||
Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN. +
|
||||
Each value is a string which corresponds to a key combination.
|
||||
|
||||
*next_tab*;;
|
||||
Key combination to switch next tab.
|
||||
|
||||
*prev_tab*;;
|
||||
Key combination to switch previous tab.
|
||||
|
||||
*scroll_line_up*;;
|
||||
Key combination to scroll one line up.
|
||||
|
||||
*scroll_line_down*;;
|
||||
Key combination to scroll one line down.
|
||||
|
||||
*half_page_up*;;
|
||||
Key combination to scroll half page up.
|
||||
|
||||
*half_page_down*;;
|
||||
Key combination to scroll half page down.
|
||||
|
||||
*page_bottom*;;
|
||||
Key combination to scroll to page bottom.
|
||||
|
||||
*peer_list_up*;;
|
||||
Key combination to scroll contacts list up.
|
||||
|
||||
*peer_list_down*;;
|
||||
Key combination to scroll contacts list down.
|
||||
|
||||
|
||||
FILES
|
||||
-----
|
||||
~/.config/tox/toxic.conf::
|
||||
Main configuration file.
|
||||
|
||||
{datadir}/toxic.conf.example::
|
||||
Configuration example.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*toxic*(1)
|
||||
|
||||
|
||||
RESOURCES
|
||||
---------
|
||||
Project page: <https://github.com/Tox/toxic>
|
||||
|
||||
IRC channel: chat.freenode.net#tox
|
||||
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
@ -2,7 +2,10 @@
|
||||
37.187.46.132 33445 A9D98212B3F972BD11DA52BEB0658C326FCCC1BFD49F347F9C2D3D8B61E1B927
|
||||
144.76.60.215 33445 04119E835DF3E78BACF0F84235B300546AF8B936F035185E2A8E9E0A67C8924F
|
||||
23.226.230.47 33445 A09162D68618E742FFBCA1C2C70385E6679604B2D80EA6E84AD0996A1AC8A074
|
||||
37.187.20.216 33445 4FD54CFD426A338399767E56FD0F44F5E35FA8C38C8E87C8DC3FEAC0160F8E17
|
||||
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
109.169.46.133 33445 7F31BFC93B8E4016A902144D0B110C3EA97CB7D43F1C4D21BCAE998A7C838821
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
54.199.139.199 33445 7F9C31FE850E97CEFD4C4591DF93FC757C7C12549DDD55F8EEAECC34FE76C029
|
||||
192.210.149.121 33445 F404ABAA1C99A9D37D61AB54898F56793E1DEF8BD46B1038B9D822E8460FAB67
|
||||
37.59.102.176 33445 B98A2CEAA6C6A2FADC2C3632D284318B60FE5375CCB41EFA081AB67F500C1B0B
|
||||
178.21.112.187 33445 4B2C19E924972CB9B57732FB172F8A8604DE13EEDA2A6234E348983344B23057
|
||||
107.161.17.51 33445 7BE3951B97CA4B9ECDDA768E8C52BA19E9E2690AB584787BF4C90E04DBB75111
|
||||
31.7.57.236 443 2A4B50D1D525DA2E669592A20C327B5FAD6C7E5962DC69296F9FEC77C4436E4E
|
||||
63.165.243.15 443 8CD087E31C67568103E8C2A28653337E90E6B8EDA0D765D57C6B5172B4F1F04C
|
||||
|
2
misc/DNSservers
Normal file
2
misc/DNSservers
Normal file
@ -0,0 +1,2 @@
|
||||
utox.org d3154f65d28a5b41a05d4ac7e4b39c6b1c233cc857fb365c56e8392737462a12
|
||||
toxme.se 5d72c517df6aec54f1e977a6b6f25914ea4cf7277a85027cd9f5196df17e0b13
|
@ -23,6 +23,9 @@ ui = {
|
||||
// true to show others when you're typing a message in 1-on-1 chats
|
||||
show_typing_self=true;
|
||||
|
||||
// true to show the welcome message on startup
|
||||
show_welcome_msg=true;
|
||||
|
||||
// maximum lines for chat window history
|
||||
history_size=700;
|
||||
};
|
||||
@ -39,8 +42,11 @@ audio = {
|
||||
};
|
||||
|
||||
tox = {
|
||||
// where to store received files
|
||||
//download_path="/home/USERNAME/Downloads/";
|
||||
// Path for downloaded files
|
||||
// download_path="/home/USERNAME/Downloads/";
|
||||
|
||||
// Path for chatlogs
|
||||
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
|
||||
};
|
||||
|
||||
// To disable a sound set the path to "silent"
|
||||
@ -60,14 +66,14 @@ sounds = {
|
||||
// Currently supported: Ctrl modified keys, Tab, PAGEUP and PAGEDOWN (case insensitive)
|
||||
// Note: All printable keys register as input
|
||||
keys = {
|
||||
next_tab="Ctrl+P";
|
||||
prev_tab="Ctrl+O";
|
||||
scroll_line_up="PAGEUP";
|
||||
scroll_line_down="PAGEDOWN";
|
||||
half_page_up="Ctrl+F";
|
||||
half_page_down="Ctrl+V";
|
||||
page_bottom="Ctrl+H";
|
||||
peer_list_up="Ctrl+[";
|
||||
peer_list_down="Ctrl+]";
|
||||
next_tab="Ctrl+P";
|
||||
prev_tab="Ctrl+O";
|
||||
scroll_line_up="PAGEUP";
|
||||
scroll_line_down="PAGEDOWN";
|
||||
half_page_up="Ctrl+F";
|
||||
half_page_down="Ctrl+V";
|
||||
page_bottom="Ctrl+H";
|
||||
peer_list_up="Ctrl+[";
|
||||
peer_list_down="Ctrl+]";
|
||||
};
|
||||
|
||||
|
@ -244,7 +244,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line)
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
|
||||
return -1;
|
||||
|
||||
if (!strncmp(b_path, "~/", 2))
|
||||
if (b_path[0] == '~')
|
||||
complt_home_dir(self, b_path, sizeof(b_path));
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
|
241
src/chat.c
241
src/chat.c
@ -43,6 +43,7 @@
|
||||
#include "help.h"
|
||||
#include "autocomplete.h"
|
||||
#include "notify.h"
|
||||
#include "message_queue.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#include "audio_call.h"
|
||||
@ -52,7 +53,7 @@
|
||||
extern char *DATA_FILE;
|
||||
|
||||
extern FileSender file_senders[MAX_FILES];
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
extern _Friends Friends;
|
||||
|
||||
extern struct _Winthread Winthread;
|
||||
extern struct user_settings *user_settings_;
|
||||
@ -124,13 +125,17 @@ static void chat_set_window_name(ToxWindow *self, char *nick, int len)
|
||||
snprintf(self->name, sizeof(self->name), "%s", nick);
|
||||
}
|
||||
|
||||
void kill_chat_window(ToxWindow *self)
|
||||
static void close_all_file_receivers(Tox *m, int friendnum);
|
||||
|
||||
void kill_chat_window(ToxWindow *self, Tox *m)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
close_all_file_receivers(m, self->num);
|
||||
log_disable(ctx->log);
|
||||
line_info_cleanup(ctx->hst);
|
||||
cqueue_cleanup(ctx->cqueue);
|
||||
|
||||
#ifdef _AUDIO
|
||||
stop_current_call(self);
|
||||
@ -141,7 +146,6 @@ void kill_chat_window(ToxWindow *self)
|
||||
delwin(statusbar->topline);
|
||||
|
||||
free(ctx->log);
|
||||
free(ctx->hst);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
free(statusbar);
|
||||
@ -173,24 +177,45 @@ static void chat_onMessage(ToxWindow *self, Tox *m, int32_t num, const char *msg
|
||||
|
||||
}
|
||||
|
||||
static void chat_resume_file_transfers(Tox *m, int fnum);
|
||||
static void chat_stop_file_senders(int fnum);
|
||||
|
||||
static void chat_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
|
||||
{
|
||||
if (self->num != num)
|
||||
return;
|
||||
|
||||
StatusBar *statusbar = self->stb;
|
||||
ChatContext *ctx = self->chatwin;
|
||||
const char *msg;
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
|
||||
if (status == 1) { /* Friend goes online */
|
||||
statusbar->is_online = true;
|
||||
friends[num].is_typing = user_settings_->show_typing_other == SHOW_TYPING_ON
|
||||
Friends.list[num].is_typing = user_settings_->show_typing_other == SHOW_TYPING_ON
|
||||
? tox_get_is_typing(m, num) : 0;
|
||||
|
||||
chat_resume_file_transfers(m, num);
|
||||
|
||||
msg = "has come online";
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
} else { /* Friend goes offline */
|
||||
statusbar->is_online = false;
|
||||
friends[num].is_typing = 0;
|
||||
Friends.list[num].is_typing = 0;
|
||||
|
||||
if (self->chatwin->self_is_typing)
|
||||
set_self_typingstatus(self, m, 0);
|
||||
|
||||
chat_stop_file_senders(num);
|
||||
|
||||
msg = "has gone offline";
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, RED, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +224,7 @@ static void chat_onTypingChange(ToxWindow *self, Tox *m, int32_t num, uint8_t is
|
||||
if (self->num != num)
|
||||
return;
|
||||
|
||||
friends[num].is_typing = is_typing;
|
||||
Friends.list[num].is_typing = is_typing;
|
||||
}
|
||||
|
||||
static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, const char *action, uint16_t len)
|
||||
@ -215,13 +240,13 @@ static void chat_onAction(ToxWindow *self, Tox *m, int32_t num, const char *acti
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
|
||||
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
|
||||
write_to_log(action, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
|
||||
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
|
||||
else
|
||||
box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action );
|
||||
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action );
|
||||
}
|
||||
|
||||
static void chat_onNickChange(ToxWindow *self, Tox *m, int32_t num, const char *nick, uint16_t len)
|
||||
@ -263,27 +288,31 @@ static void chat_onStatusMessageChange(ToxWindow *self, int32_t num, const char
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
}
|
||||
|
||||
static void chat_onReadReceipt(ToxWindow *self, Tox *m, int32_t num, uint32_t receipt)
|
||||
{
|
||||
cqueue_remove(self, m, receipt);
|
||||
}
|
||||
|
||||
static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
|
||||
uint64_t filesize, const char *pathname, uint16_t path_len)
|
||||
{
|
||||
if (self->num != num)
|
||||
return;
|
||||
|
||||
const char *errmsg;
|
||||
|
||||
/* holds the filename appended to the user specified path */
|
||||
char filename_path[MAX_STR_SIZE] = {0};
|
||||
|
||||
/* holds the lone filename */
|
||||
char filename_nopath[MAX_STR_SIZE];
|
||||
get_file_name(filename_nopath, sizeof(filename_nopath), pathname);
|
||||
char sizestr[32];
|
||||
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||
int len = strlen(filename_nopath);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%llu bytes).",
|
||||
filename_nopath, (long long unsigned int) filesize);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)",
|
||||
filename_nopath, sizestr);
|
||||
|
||||
if (filenum >= MAX_FILES) {
|
||||
errmsg = "Too many pending file requests; discarding.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Too many pending file requests; discarding.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -293,9 +322,8 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t
|
||||
len += strlen(user_settings_->download_path);
|
||||
}
|
||||
|
||||
if (len >= sizeof(friends[num].file_receiver.filenames[filenum])) {
|
||||
errmsg = "File name too long; discarding.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
if (len >= sizeof(Friends.list[num].file_receiver[filenum].filename)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File name too long; discarding.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -325,26 +353,56 @@ static void chat_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t
|
||||
filename[len + d_len] = '\0';
|
||||
|
||||
if (count > 999) {
|
||||
errmsg = "Error saving file to disk.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error saving file to disk.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %d' to accept the file transfer.", filenum);
|
||||
|
||||
friends[num].file_receiver.pending[filenum] = true;
|
||||
friends[num].file_receiver.size[filenum] = filesize;
|
||||
strcpy(friends[num].file_receiver.filenames[filenum], filename);
|
||||
Friends.list[num].file_receiver[filenum].pending = true;
|
||||
Friends.list[num].file_receiver[filenum].size = filesize;
|
||||
Friends.list[num].file_receiver[filenum].filenum = filenum;
|
||||
strcpy(Friends.list[num].file_receiver[filenum].filename, filename);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, transfer_pending, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box,
|
||||
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
|
||||
"Incoming file: %s", filename );
|
||||
else
|
||||
box_notify(self, transfer_pending, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, self->name,
|
||||
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
|
||||
"Incoming file: %s", filename );
|
||||
}
|
||||
|
||||
/* Stops active file senders for this friend. Call when a friend goes offline */
|
||||
static void chat_stop_file_senders(int fnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (file_senders[i].active && file_senders[i].friendnum == fnum)
|
||||
file_senders[i].noconnection = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tries to resume broken file transfers. Call when a friend comes online */
|
||||
static void chat_resume_file_transfers(Tox *m, int fnum)
|
||||
{
|
||||
if (Friends.list[fnum].active_file_receivers == 0)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (Friends.list[fnum].file_receiver[i].active) {
|
||||
uint8_t bytes_recv[sizeof(uint64_t)];
|
||||
memcpy(bytes_recv, &Friends.list[fnum].file_receiver[i].bytes_recv, sizeof(uint64_t));
|
||||
net_to_host(bytes_recv, sizeof(uint64_t));
|
||||
int filenum = Friends.list[fnum].file_receiver[i].filenum;
|
||||
tox_file_send_control(m, fnum, 1, filenum, TOX_FILECONTROL_RESUME_BROKEN, bytes_recv, sizeof(uint64_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* set CTRL to -1 if we don't want to send a control signal.
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL)
|
||||
@ -352,13 +410,22 @@ void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL)
|
||||
if (CTRL > 0)
|
||||
tox_file_send_control(m, friendnum, 1, filenum, CTRL, 0, 0);
|
||||
|
||||
friends[friendnum].file_receiver.active[filenum] = false;
|
||||
friends[friendnum].file_receiver.pending[filenum] = false;
|
||||
FILE *file = friends[friendnum].file_receiver.files[filenum];
|
||||
FILE *file = Friends.list[friendnum].file_receiver[filenum].file;
|
||||
|
||||
if (file != NULL) {
|
||||
if (file != NULL)
|
||||
fclose(file);
|
||||
friends[friendnum].file_receiver.files[filenum] = NULL;
|
||||
|
||||
memset(&Friends.list[friendnum].file_receiver[filenum], 0, sizeof(struct FileReceiver));
|
||||
--Friends.list[friendnum].active_file_receivers;
|
||||
}
|
||||
|
||||
static void close_all_file_receivers(Tox *m, int friendnum)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (Friends.list[friendnum].file_receiver[i].active)
|
||||
chat_close_file_receiver(m, i, friendnum, TOX_FILECONTROL_KILL);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,17 +437,27 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
|
||||
|
||||
const char *filename;
|
||||
char msg[MAX_STR_SIZE] = {0};
|
||||
int i = 0; /* file_sender index */
|
||||
int send_idx = 0; /* file sender index */
|
||||
|
||||
if (receive_send == 0) {
|
||||
filename = friends[num].file_receiver.filenames[filenum];
|
||||
if (!Friends.list[num].file_receiver[filenum].active)
|
||||
return;
|
||||
|
||||
filename = Friends.list[num].file_receiver[filenum].filename;
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
send_idx = i;
|
||||
|
||||
if (file_senders[i].active && file_senders[i].filenum == filenum)
|
||||
break;
|
||||
}
|
||||
|
||||
filename = file_senders[i].filename;
|
||||
if (!file_senders[send_idx].active)
|
||||
return;
|
||||
|
||||
filename = file_senders[send_idx].filename;
|
||||
}
|
||||
|
||||
switch (control_type) {
|
||||
@ -392,7 +469,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
|
||||
char progline[MAX_STR_SIZE];
|
||||
prep_prog_line(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
file_senders[i].line_id = self->chatwin->hst->line_end->id + 2;
|
||||
file_senders[send_idx].line_id = self->chatwin->hst->line_end->id + 2;
|
||||
sound_notify(self, silent, NT_NOFOCUS | NT_BEEP | NT_WNDALERT_2, NULL);
|
||||
}
|
||||
|
||||
@ -411,29 +488,50 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, int32_t num, uint8_t rec
|
||||
if (receive_send == 0)
|
||||
chat_close_file_receiver(m, filenum, num, -1);
|
||||
else
|
||||
close_file_sender(self, m, i, NULL, -1, filenum, num);
|
||||
close_file_sender(self, m, send_idx, NULL, -1, filenum, num);
|
||||
|
||||
break;
|
||||
|
||||
case TOX_FILECONTROL_FINISHED:
|
||||
if (receive_send == 0) {
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' complete.", filename);
|
||||
print_progress_bar(self, filenum, num, 100.0);
|
||||
|
||||
char filename_nopath[MAX_STR_SIZE];
|
||||
get_file_name(filename_nopath, sizeof(filename_nopath), filename);
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' complete.", filename_nopath);
|
||||
chat_close_file_receiver(m, filenum, num, TOX_FILECONTROL_FINISHED);
|
||||
} else {
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File '%s' successfuly sent.", filename);
|
||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_FINISHED, filenum, num);
|
||||
return;
|
||||
close_file_sender(self, m, send_idx, NULL, TOX_FILECONTROL_FINISHED, filenum, num);
|
||||
}
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2,
|
||||
self->active_box, "%s", msg);
|
||||
box_notify2(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
||||
else
|
||||
box_notify(self, transfer_completed, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box,
|
||||
self->name, "%s", msg);
|
||||
|
||||
break;
|
||||
|
||||
case TOX_FILECONTROL_RESUME_BROKEN:
|
||||
if (receive_send == 0)
|
||||
break;
|
||||
|
||||
FILE *fp = file_senders[send_idx].file;
|
||||
|
||||
if (fp == NULL)
|
||||
break;
|
||||
|
||||
uint8_t tmp[sizeof(uint64_t)];
|
||||
memcpy(tmp, &data, sizeof(uint64_t));
|
||||
net_to_host(tmp, sizeof(uint64_t));
|
||||
uint64_t datapos;
|
||||
memcpy(&datapos, tmp, sizeof(uint64_t));
|
||||
|
||||
fseek(fp, datapos, SEEK_SET);
|
||||
tox_file_send_control(m, num, 0, filenum, TOX_FILECONTROL_ACCEPT, 0, 0);
|
||||
file_senders[send_idx].noconnection = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (msg[0])
|
||||
@ -446,7 +544,7 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenu
|
||||
if (self->num != num)
|
||||
return;
|
||||
|
||||
FILE *fp = friends[num].file_receiver.files[filenum];
|
||||
FILE *fp = Friends.list[num].file_receiver[filenum].file;
|
||||
|
||||
if (fp) {
|
||||
if (fwrite(data, length, 1, fp) != 1) {
|
||||
@ -455,18 +553,8 @@ static void chat_onFileData(ToxWindow *self, Tox *m, int32_t num, uint8_t filenu
|
||||
}
|
||||
}
|
||||
|
||||
friends[num].file_receiver.bps[filenum] += length;
|
||||
double remain = (double) tox_file_data_remaining(m, num, filenum, 1);
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
/* refresh line with percentage complete and transfer speed (must be called once per second) */
|
||||
if (!remain || timed_out(friends[num].file_receiver.last_progress[filenum], curtime, 1)) {
|
||||
friends[num].file_receiver.last_progress[filenum] = curtime;
|
||||
uint64_t size = friends[num].file_receiver.size[filenum];
|
||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
||||
print_progress_bar(self, filenum, num, pct_done);
|
||||
friends[num].file_receiver.bps[filenum] = 0;
|
||||
}
|
||||
Friends.list[num].file_receiver[filenum].bps += length;
|
||||
Friends.list[num].file_receiver[filenum].bytes_recv += length;
|
||||
}
|
||||
|
||||
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, const char *group_pub_key)
|
||||
@ -480,9 +568,9 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, co
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
|
||||
|
||||
memcpy(friends[friendnumber].groupchat_key, group_pub_key,
|
||||
sizeof(friends[friendnumber].groupchat_key));
|
||||
friends[friendnumber].groupchat_pending = true;
|
||||
memcpy(Friends.list[friendnumber].groupchat_key, group_pub_key,
|
||||
sizeof(Friends.list[friendnumber].groupchat_key));
|
||||
Friends.list[friendnumber].groupchat_pending = true;
|
||||
|
||||
|
||||
sound_notify(self, generic_message, NT_WNDALERT_2, NULL);
|
||||
@ -748,13 +836,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, selfname, NULL, ACTION, 0, 0, "%s", action);
|
||||
|
||||
if (tox_send_action(m, self->num, (uint8_t *) action, strlen(action)) == 0) {
|
||||
line_info_add(self, NULL, selfname, NULL, SYS_MSG, 0, RED, " * Failed to send action.");
|
||||
} else {
|
||||
write_to_log(action, selfname, ctx->log, true);
|
||||
}
|
||||
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
|
||||
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1);
|
||||
}
|
||||
|
||||
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
@ -819,7 +902,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
if (line[0] == '/') {
|
||||
if (strcmp(line, "/close") == 0) {
|
||||
kill_chat_window(self);
|
||||
kill_chat_window(self, m);
|
||||
return;
|
||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
||||
send_action(self, ctx, m, line + strlen("/me "));
|
||||
@ -835,12 +918,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
||||
|
||||
if (!statusbar->is_online || tox_send_message(m, self->num, (uint8_t *) line, strlen(line)) == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message.");
|
||||
} else {
|
||||
write_to_log(line, selfname, ctx->log, false);
|
||||
}
|
||||
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
|
||||
}
|
||||
|
||||
wclear(ctx->linewin);
|
||||
@ -899,14 +977,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
||||
wprintw(statusbar->topline, " %s", ONLINE_CHAR);
|
||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
|
||||
if (friends[self->num].is_typing)
|
||||
if (Friends.list[self->num].is_typing)
|
||||
wattron(statusbar->topline, COLOR_PAIR(YELLOW));
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wprintw(statusbar->topline, " %s ", statusbar->nick);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
|
||||
if (friends[self->num].is_typing)
|
||||
if (Friends.list[self->num].is_typing)
|
||||
wattroff(statusbar->topline, COLOR_PAIR(YELLOW));
|
||||
} else {
|
||||
wprintw(statusbar->topline, " %s", OFFLINE_CHAR);
|
||||
@ -930,10 +1008,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
||||
self->x = x2;
|
||||
|
||||
/* Truncate note if it doesn't fit in statusbar */
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - (KEY_IDENT_DIGITS * 2) - 7;
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - (KEY_IDENT_DIGITS * 2) - 6;
|
||||
|
||||
if (statusbar->statusmsg_len > maxlen) {
|
||||
statusbar->statusmsg[maxlen] = '\0';
|
||||
statusbar->statusmsg[maxlen - 3] = '\0';
|
||||
strcat(statusbar->statusmsg, "...");
|
||||
statusbar->statusmsg_len = maxlen;
|
||||
}
|
||||
|
||||
@ -947,7 +1026,7 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < KEY_IDENT_DIGITS; ++i)
|
||||
wprintw(statusbar->topline, "%02X", friends[self->num].pub_key[i] & 0xff);
|
||||
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
|
||||
|
||||
wprintw(statusbar->topline, "}\n");
|
||||
|
||||
@ -1005,14 +1084,15 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
||||
|
||||
ctx->hst = calloc(1, sizeof(struct history));
|
||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||
ctx->cqueue = calloc(1, sizeof(struct chat_queue));
|
||||
|
||||
if (ctx->log == NULL || ctx->hst == NULL)
|
||||
if (ctx->log == NULL || ctx->hst == NULL || ctx->cqueue == NULL)
|
||||
exit_toxic_err("failed in chat_onInit", FATALERR_MEMORY);
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (friends[self->num].logging_on)
|
||||
log_enable(nick, friends[self->num].pub_key, ctx->log);
|
||||
if (Friends.list[self->num].logging_on)
|
||||
log_enable(nick, Friends.list[self->num].pub_key, ctx->log);
|
||||
|
||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||
|
||||
@ -1042,6 +1122,7 @@ ToxWindow new_chat(Tox *m, int32_t friendnum)
|
||||
ret.onFileSendRequest = &chat_onFileSendRequest;
|
||||
ret.onFileControl = &chat_onFileControl;
|
||||
ret.onFileData = &chat_onFileData;
|
||||
ret.onReadReceipt = &chat_onReadReceipt;
|
||||
|
||||
#ifdef _AUDIO
|
||||
ret.onInvite = &chat_onInvite;
|
||||
|
@ -29,7 +29,7 @@
|
||||
/* set CTRL to -1 if we don't want to send a control signal.
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
|
||||
void kill_chat_window(ToxWindow *self);
|
||||
void kill_chat_window(ToxWindow *self, Tox *m);
|
||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||
|
||||
#endif /* end of include guard: CHAT_H_6489PZ13 */
|
||||
|
@ -34,8 +34,7 @@
|
||||
#include "file_senders.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
extern _Friends Friends;
|
||||
|
||||
extern FileSender file_senders[MAX_FILES];
|
||||
extern uint8_t max_file_senders_index;
|
||||
@ -57,12 +56,12 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
}
|
||||
|
||||
if (strcasecmp(inoutstr, "in") == 0) { /* cancel an incoming file transfer */
|
||||
if (!friends[self->num].file_receiver.active[filenum]) {
|
||||
if (!Friends.list[self->num].file_receiver[filenum].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *filepath = friends[self->num].file_receiver.filenames[filenum];
|
||||
const char *filepath = Friends.list[self->num].file_receiver[filenum].filename;
|
||||
char name[MAX_STR_SIZE];
|
||||
get_file_name(name, sizeof(name), filepath);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer for '%s' canceled.", name);
|
||||
@ -124,9 +123,9 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
const char *groupkey = friends[self->num].groupchat_key;
|
||||
const char *groupkey = Friends.list[self->num].groupchat_key;
|
||||
|
||||
if (!friends[self->num].groupchat_pending) {
|
||||
if (!Friends.list[self->num].groupchat_pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||
return;
|
||||
}
|
||||
@ -159,12 +158,12 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
return;
|
||||
}
|
||||
|
||||
if (!friends[self->num].file_receiver.pending[filenum]) {
|
||||
if (!Friends.list[self->num].file_receiver[filenum].pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *filename = friends[self->num].file_receiver.filenames[filenum];
|
||||
const char *filename = Friends.list[self->num].file_receiver[filenum].filename;
|
||||
|
||||
if (tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_ACCEPT, 0, 0) == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", filenum, filename);
|
||||
@ -173,18 +172,20 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
char progline[MAX_STR_SIZE];
|
||||
prep_prog_line(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
friends[self->num].file_receiver.line_id[filenum] = self->chatwin->hst->line_end->id + 2;
|
||||
Friends.list[self->num].file_receiver[filenum].line_id = self->chatwin->hst->line_end->id + 2;
|
||||
|
||||
if ((friends[self->num].file_receiver.files[filenum] = fopen(filename, "a")) == NULL) {
|
||||
if ((Friends.list[self->num].file_receiver[filenum].file = fopen(filename, "a")) == NULL) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Error writing to file.");
|
||||
tox_file_send_control(m, self->num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||
} else {
|
||||
Friends.list[self->num].file_receiver[filenum].active = true;
|
||||
++Friends.list[self->num].active_file_receivers;
|
||||
}
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed.");
|
||||
}
|
||||
|
||||
friends[self->num].file_receiver.pending[filenum] = false;
|
||||
friends[self->num].file_receiver.active[filenum] = true;
|
||||
Friends.list[self->num].file_receiver[filenum].pending = false;
|
||||
}
|
||||
|
||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -241,7 +242,6 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
if (!file_senders[i].active) {
|
||||
file_senders[i].queue_pos = num_active_file_senders;
|
||||
memcpy(file_senders[i].filename, filename, namelen + 1);
|
||||
file_senders[i].active = true;
|
||||
file_senders[i].toxwin = self;
|
||||
@ -253,13 +253,17 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, self->num), file_to_send);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s'", filenum, filename);
|
||||
char sizestr[32];
|
||||
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Sending file [%d]: '%s' (%s)", filenum, filename, sizestr);
|
||||
|
||||
++num_active_file_senders;
|
||||
|
||||
if (i == max_file_senders_index)
|
||||
++max_file_senders_index;
|
||||
|
||||
reset_file_sender_queue();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
156
src/dns.c
156
src/dns.c
@ -40,19 +40,23 @@
|
||||
#include "dns.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
|
||||
#define MAX_DNS_REQST_SIZE 256
|
||||
#define NUM_DNS3_SERVERS 2 /* must correspond to number of items in dns3_servers array */
|
||||
#define DNS3_KEY_SIZE 32
|
||||
#define MAX_DNS_REQST_SIZE 255
|
||||
#define TOX_DNS3_TXT_PREFIX "v=tox3;id="
|
||||
#define DNS3_KEY_SZ 32
|
||||
|
||||
extern struct _Winthread Winthread;
|
||||
extern struct _dns3_servers dns3_servers;
|
||||
extern struct arg_opts arg_opts;
|
||||
|
||||
/* TODO: process keys from key file instead of hard-coding like a noob */
|
||||
static struct dns3_server {
|
||||
#define NUM_DNS3_BACKUP_SERVERS 2
|
||||
|
||||
/* Hardcoded backup in case domain list is not loaded */
|
||||
static struct dns3_server_backup {
|
||||
const char *name;
|
||||
char key[DNS3_KEY_SZ];
|
||||
} dns3_servers[] = {
|
||||
char key[DNS3_KEY_SIZE];
|
||||
} dns3_servers_backup[] = {
|
||||
{
|
||||
"utox.org",
|
||||
{
|
||||
@ -84,6 +88,61 @@ static struct _dns_thread {
|
||||
} dns_thread;
|
||||
|
||||
|
||||
#define MAX_DNS_SERVERS 50
|
||||
#define MAX_DOMAIN_SIZE 32
|
||||
#define MAX_DNS_LINE MAX_DOMAIN_SIZE + (DNS3_KEY_SIZE * 2) + 3
|
||||
|
||||
struct _dns3_servers {
|
||||
bool loaded;
|
||||
int lines;
|
||||
char names[MAX_DNS_SERVERS][MAX_DOMAIN_SIZE];
|
||||
char keys[MAX_DNS_SERVERS][DNS3_KEY_SIZE];
|
||||
} dns3_servers;
|
||||
|
||||
static int load_dns_domainlist(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "r");
|
||||
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
char line[MAX_DNS_LINE];
|
||||
|
||||
while (fgets(line, sizeof(line), fp) && dns3_servers.lines < MAX_DNS_SERVERS) {
|
||||
int linelen = strlen(line);
|
||||
|
||||
if (linelen < DNS3_KEY_SIZE * 2 + 5)
|
||||
continue;
|
||||
|
||||
if (line[linelen - 1] == '\n')
|
||||
line[--linelen] = '\0';
|
||||
|
||||
const char *name = strtok(line, " ");
|
||||
const char *keystr = strtok(NULL, " ");
|
||||
|
||||
if (name == NULL || keystr == NULL)
|
||||
continue;
|
||||
|
||||
if (strlen(keystr) != DNS3_KEY_SIZE * 2)
|
||||
continue;
|
||||
|
||||
snprintf(dns3_servers.names[dns3_servers.lines], sizeof(dns3_servers.names[dns3_servers.lines]), "%s", name);
|
||||
int res = hex_string_to_bytes(dns3_servers.keys[dns3_servers.lines], DNS3_KEY_SIZE, keystr);
|
||||
|
||||
if (res == -1)
|
||||
continue;
|
||||
|
||||
++dns3_servers.lines;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (dns3_servers.lines < 1)
|
||||
return -2;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dns_error(ToxWindow *self, const char *errmsg)
|
||||
{
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
@ -161,6 +220,9 @@ static int parse_dns_response(ToxWindow *self, u_char *answer, int ans_len, char
|
||||
if (!size || txt_len >= size || !txt_len)
|
||||
return dns_error(self, "No record found.");
|
||||
|
||||
if (txt_len > MAX_DNS_REQST_SIZE)
|
||||
return dns_error(self, "Invalid DNS response.");
|
||||
|
||||
ans_pt++;
|
||||
ans_pt[txt_len] = '\0';
|
||||
memcpy(buf, ans_pt, txt_len + 1);
|
||||
@ -191,35 +253,61 @@ static int parse_addr(const char *addr, char *namebuf, char *dombuf)
|
||||
return strlen(namebuf);
|
||||
}
|
||||
|
||||
/* matches input domain name with domains in list and obtains key. Return 0 on success, -1 on failure */
|
||||
static int get_domain_match(char *pubkey, char *domain, const char *inputdomain)
|
||||
{
|
||||
/* check server list first */
|
||||
int i;
|
||||
bool match = false;
|
||||
|
||||
for (i = 0; i < dns3_servers.lines; ++i) {
|
||||
if (strcmp(dns3_servers.names[i], inputdomain) == 0) {
|
||||
memcpy(pubkey, dns3_servers.keys[i], DNS3_KEY_SIZE);
|
||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers.names[i]);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* fall back to hard-coded domains on server list failure */
|
||||
if (!match) {
|
||||
for (i = 0; i < NUM_DNS3_BACKUP_SERVERS; ++i) {
|
||||
if (strcmp(dns3_servers_backup[i].name, inputdomain) == 0) {
|
||||
memcpy(pubkey, dns3_servers_backup[i].key, DNS3_KEY_SIZE);
|
||||
snprintf(domain, MAX_DOMAIN_SIZE, "%s", dns3_servers_backup[i].name);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!match)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Does DNS lookup for addr and puts resulting tox id in id_bin. */
|
||||
void *dns3_lookup_thread(void *data)
|
||||
{
|
||||
ToxWindow *self = t_data.self;
|
||||
|
||||
char domain[MAX_STR_SIZE];
|
||||
char inputdomain[MAX_STR_SIZE];
|
||||
char name[MAX_STR_SIZE];
|
||||
|
||||
int namelen = parse_addr(t_data.addr, name, domain);
|
||||
int namelen = parse_addr(t_data.addr, name, inputdomain);
|
||||
|
||||
if (namelen == -1) {
|
||||
dns_error(self, "Must be a Tox ID or an address in the form username@domain");
|
||||
kill_dns_thread(NULL);
|
||||
}
|
||||
|
||||
/* get domain name/pub key */
|
||||
const char *DNS_pubkey = NULL;
|
||||
const char *domname = NULL;
|
||||
int i;
|
||||
char DNS_pubkey[DNS3_KEY_SIZE];
|
||||
char domain[MAX_DOMAIN_SIZE];
|
||||
|
||||
for (i = 0; i < NUM_DNS3_SERVERS; ++i) {
|
||||
if (strcmp(dns3_servers[i].name, domain) == 0) {
|
||||
DNS_pubkey = dns3_servers[i].key;
|
||||
domname = dns3_servers[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int match = get_domain_match(DNS_pubkey, domain, inputdomain);
|
||||
|
||||
if (domname == NULL) {
|
||||
if (match == -1) {
|
||||
dns_error(self, "Domain not found.");
|
||||
kill_dns_thread(NULL);
|
||||
}
|
||||
@ -231,7 +319,7 @@ void *dns3_lookup_thread(void *data)
|
||||
kill_dns_thread(NULL);
|
||||
}
|
||||
|
||||
char string[MAX_DNS_REQST_SIZE];
|
||||
char string[MAX_DNS_REQST_SIZE + 1];
|
||||
uint32_t request_id;
|
||||
|
||||
int str_len = tox_generate_dns3_string(dns_obj, (uint8_t *) string, sizeof(string), &request_id,
|
||||
@ -245,10 +333,10 @@ void *dns3_lookup_thread(void *data)
|
||||
string[str_len] = '\0';
|
||||
|
||||
u_char answer[PACKETSZ];
|
||||
char d_string[MAX_DNS_REQST_SIZE];
|
||||
char d_string[MAX_DOMAIN_SIZE + MAX_DNS_REQST_SIZE + 10];
|
||||
|
||||
/* format string and create dns query */
|
||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domname);
|
||||
snprintf(d_string, sizeof(d_string), "_%s._tox.%s", string, domain);
|
||||
int ans_len = res_query(d_string, C_IN, T_TXT, answer, sizeof(answer));
|
||||
|
||||
if (ans_len <= 0) {
|
||||
@ -256,13 +344,13 @@ void *dns3_lookup_thread(void *data)
|
||||
kill_dns_thread(dns_obj);
|
||||
}
|
||||
|
||||
char ans_id[MAX_DNS_REQST_SIZE];
|
||||
char ans_id[MAX_DNS_REQST_SIZE + 1];
|
||||
|
||||
/* extract TXT from DNS response */
|
||||
if (parse_dns_response(self, answer, ans_len, ans_id) == -1)
|
||||
kill_dns_thread(dns_obj);
|
||||
|
||||
char encrypted_id[MAX_DNS_REQST_SIZE];
|
||||
char encrypted_id[MAX_DNS_REQST_SIZE + 1];
|
||||
int prfx_len = strlen(TOX_DNS3_TXT_PREFIX);
|
||||
|
||||
/* extract the encrypted ID from TXT response */
|
||||
@ -290,12 +378,28 @@ void *dns3_lookup_thread(void *data)
|
||||
/* creates new thread for dns3 lookup. Only allows one lookup at a time. */
|
||||
void dns3_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *msg)
|
||||
{
|
||||
if (arg_opts.use_proxy && arg_opts.force_tcp) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "DNS lookups are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (t_data.busy) {
|
||||
const char *err = "Please wait for previous user lookup to finish.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dns3_servers.loaded) {
|
||||
const char *path = arg_opts.dns_path[0] ? arg_opts.dns_path : PACKAGE_DATADIR "/DNSservers";
|
||||
dns3_servers.loaded = true;
|
||||
int ret = load_dns_domainlist(path);
|
||||
|
||||
if (ret < 0) {
|
||||
const char *errmsg = "DNS server list failed to load with error code %d. Falling back to hard-coded list.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg, ret);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(t_data.id_bin, sizeof(t_data.id_bin), "%s", id_bin);
|
||||
snprintf(t_data.addr, sizeof(t_data.addr), "%s", addr);
|
||||
snprintf(t_data.msg, sizeof(t_data.msg), "%s", msg);
|
||||
|
@ -43,6 +43,7 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/add", cmd_add },
|
||||
{ "/clear", cmd_clear },
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/groupchat", cmd_groupchat },
|
||||
{ "/help", cmd_prompt_help },
|
||||
@ -52,12 +53,13 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/note", cmd_note },
|
||||
{ "/q", cmd_quit },
|
||||
{ "/quit", cmd_quit },
|
||||
{ "/requests", cmd_requests },
|
||||
{ "/status", cmd_status },
|
||||
|
||||
#ifdef _AUDIO
|
||||
{ "/lsdev", cmd_list_devices },
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* _AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
@ -66,7 +68,6 @@ static struct cmd_func chat_commands[] = {
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
|
||||
#ifdef _AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
@ -76,6 +77,7 @@ static struct cmd_func chat_commands[] = {
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
#endif /* _AUDIO */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
@ -99,7 +101,7 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
|
||||
i = char_find(1, cmd, '\"');
|
||||
|
||||
if (cmd[i] == '\0') {
|
||||
char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
free(cmd);
|
||||
return -1;
|
||||
@ -116,7 +118,7 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
|
||||
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
|
||||
strcpy(cmd, tmp);
|
||||
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
@ -124,12 +126,12 @@ static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*a
|
||||
}
|
||||
|
||||
/* Matches command to respective function. Returns 0 on match, 1 on no match */
|
||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, int num_cmds,
|
||||
struct cmd_func *commands, char (*args)[MAX_STR_SIZE])
|
||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
|
||||
char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_cmds; ++i) {
|
||||
for (i = 0; commands[i].name != NULL; ++i) {
|
||||
if (strcmp(args[0], commands[i].name) == 0) {
|
||||
(commands[i].func)(w, self, m, num_args - 1, args);
|
||||
return 0;
|
||||
@ -156,7 +158,7 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
Note: Global commands must come last in case of duplicate command names */
|
||||
switch (mode) {
|
||||
case CHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, CHAT_NUM_COMMANDS, chat_commands, args) == 0)
|
||||
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
|
||||
return;
|
||||
|
||||
break;
|
||||
@ -165,12 +167,8 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_command(w, self, m, num_args, GLOBAL_NUM_COMMANDS, global_commands, args) == 0)
|
||||
if (do_command(w, self, m, num_args, global_commands, args) == 0)
|
||||
return;
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
sound_notify(self, error, 0, NULL);
|
||||
#else
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
#endif
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
@ -28,14 +28,6 @@
|
||||
|
||||
#define MAX_NUM_ARGS 4 /* Includes command */
|
||||
|
||||
#ifdef _AUDIO
|
||||
#define GLOBAL_NUM_COMMANDS 16
|
||||
#define CHAT_NUM_COMMANDS 12
|
||||
#else
|
||||
#define GLOBAL_NUM_COMMANDS 14
|
||||
#define CHAT_NUM_COMMANDS 5
|
||||
#endif /* _AUDIO */
|
||||
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
CHAT_COMMAND_MODE,
|
||||
|
@ -36,11 +36,7 @@
|
||||
FileSender file_senders[MAX_FILES];
|
||||
uint8_t max_file_senders_index;
|
||||
uint8_t num_active_file_senders;
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024 ^ 2 */
|
||||
#define GiB 1073741824 /* 1024 ^ 3 */
|
||||
extern _Friends Friends;
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline is of size MAX_STR_SIZE */
|
||||
@ -66,35 +62,20 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done
|
||||
bps = file_senders[idx].bps;
|
||||
line_id = file_senders[idx].line_id;
|
||||
} else {
|
||||
bps = friends[friendnum].file_receiver.bps[idx];
|
||||
line_id = friends[friendnum].file_receiver.line_id[idx];
|
||||
}
|
||||
|
||||
const char *unit;
|
||||
|
||||
if (bps < KiB) {
|
||||
unit = "B/s";
|
||||
} else if (bps < MiB) {
|
||||
unit = "KiB/s";
|
||||
bps /= (double) KiB;
|
||||
} else if (bps < GiB) {
|
||||
unit = "MiB/s";
|
||||
bps /= (double) MiB;
|
||||
} else {
|
||||
unit = "GiB/s";
|
||||
bps /= (double) GiB;
|
||||
bps = Friends.list[friendnum].file_receiver[idx].bps;
|
||||
line_id = Friends.list[friendnum].file_receiver[idx].line_id;
|
||||
}
|
||||
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "%.1f %s [", bps, unit);
|
||||
bytes_convert_str(msg, sizeof(msg), bps);
|
||||
strcat(msg, "/s [");
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i;
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
strcat(msg, "#");
|
||||
|
||||
int j;
|
||||
|
||||
for (j = i; j < NUM_PROG_MARKS; ++j)
|
||||
strcat(msg, "-");
|
||||
|
||||
@ -108,6 +89,64 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_done
|
||||
line_info_set(self, line_id, msg);
|
||||
}
|
||||
|
||||
/* refreshes active file receiver status bars */
|
||||
static void refresh_recv_prog(Tox *m)
|
||||
{
|
||||
int i;
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
ToxWindow *toxwin = get_window_ptr(i);
|
||||
|
||||
if (toxwin == NULL || !toxwin->is_chat)
|
||||
continue;
|
||||
|
||||
int fnum = toxwin->num;
|
||||
int j;
|
||||
|
||||
for (j = 0; j < MAX_FILES; ++j) {
|
||||
if (!Friends.list[fnum].file_receiver[j].active)
|
||||
continue;
|
||||
|
||||
int filenum = Friends.list[fnum].file_receiver[j].filenum;
|
||||
double remain = (double) tox_file_data_remaining(m, fnum, filenum, 1);
|
||||
|
||||
/* must be called once per second */
|
||||
if (timed_out(Friends.list[fnum].file_receiver[filenum].last_progress, curtime, 1)) {
|
||||
Friends.list[fnum].file_receiver[filenum].last_progress = curtime;
|
||||
uint64_t size = Friends.list[fnum].file_receiver[filenum].size;
|
||||
double pct_done = remain > 0 ? (1 - (remain / size)) * 100 : 100;
|
||||
print_progress_bar(toxwin, filenum, fnum, pct_done);
|
||||
Friends.list[fnum].file_receiver[filenum].bps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* refreshes active file sender status bars */
|
||||
static void refresh_sender_prog(Tox *m)
|
||||
{
|
||||
int i;
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (!file_senders[i].active || file_senders[i].finished)
|
||||
continue;
|
||||
|
||||
int filenum = file_senders[i].filenum;
|
||||
int32_t friendnum = file_senders[i].friendnum;
|
||||
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
||||
|
||||
/* must be called once per second */
|
||||
if (timed_out(file_senders[i].last_progress, curtime, 1)) {
|
||||
file_senders[i].last_progress = curtime;
|
||||
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
|
||||
print_progress_bar(file_senders[i].toxwin, i, -1, pct_done);
|
||||
file_senders[i].bps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void set_max_file_senders_index(void)
|
||||
{
|
||||
int j;
|
||||
@ -120,6 +159,18 @@ static void set_max_file_senders_index(void)
|
||||
max_file_senders_index = j;
|
||||
}
|
||||
|
||||
/* called whenever a file sender is opened or closed */
|
||||
void reset_file_sender_queue(void)
|
||||
{
|
||||
int i;
|
||||
int pos = 0;
|
||||
|
||||
for (i = 0; i < max_file_senders_index; ++i) {
|
||||
if (file_senders[i].active)
|
||||
file_senders[i].queue_pos = pos++;
|
||||
}
|
||||
}
|
||||
|
||||
/* set CTRL to -1 if we don't want to send a control signal.
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum)
|
||||
@ -133,6 +184,7 @@ void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL
|
||||
fclose(file_senders[i].file);
|
||||
memset(&file_senders[i], 0, sizeof(FileSender));
|
||||
set_max_file_senders_index();
|
||||
reset_file_sender_queue();
|
||||
--num_active_file_senders;
|
||||
}
|
||||
|
||||
@ -161,27 +213,30 @@ static void send_file_data(ToxWindow *self, Tox *m, int i, int32_t friendnum, in
|
||||
file_senders[i].piecelen) == -1)
|
||||
return;
|
||||
|
||||
uint64_t curtime = get_unix_time();
|
||||
file_senders[i].timestamp = curtime;
|
||||
file_senders[i].timestamp = get_unix_time();
|
||||
file_senders[i].bps += file_senders[i].piecelen;
|
||||
file_senders[i].piecelen = fread(file_senders[i].nextpiece, 1,
|
||||
tox_file_data_size(m, friendnum), fp);
|
||||
|
||||
double remain = (double) tox_file_data_remaining(m, friendnum, filenum, 0);
|
||||
/* note: file sender is closed in chat_onFileControl callback after receiving reply */
|
||||
if (file_senders[i].piecelen == 0) {
|
||||
if (feof(fp) != 0) { /* make sure we're really at eof */
|
||||
print_progress_bar(self, i, -1, 100.0);
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||
file_senders[i].finished = true;
|
||||
} else {
|
||||
char msg[MAX_STR_SIZE];
|
||||
snprintf(msg, sizeof(msg), "File transfer for '%s' failed: Read error.", file_senders[i].filename);
|
||||
close_file_sender(self, m, i, msg, TOX_FILECONTROL_KILL, filenum, friendnum);
|
||||
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
||||
else
|
||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
||||
}
|
||||
|
||||
/* refresh line with percentage complete and transfer speed (must be called once per second) */
|
||||
if ( (self->chatwin != NULL && timed_out(file_senders[i].last_progress, curtime, 1))
|
||||
|| (!remain && !file_senders[i].finished) ) {
|
||||
file_senders[i].last_progress = curtime;
|
||||
double pct_done = remain > 0 ? (1 - (remain / file_senders[i].size)) * 100 : 100;
|
||||
print_progress_bar(self, i, -1, pct_done);
|
||||
file_senders[i].bps = 0;
|
||||
}
|
||||
|
||||
/* file sender is closed in chat_onFileControl callback after receiving reply */
|
||||
if (file_senders[i].piecelen == 0 && !file_senders[i].finished) {
|
||||
tox_file_send_control(m, friendnum, 0, filenum, TOX_FILECONTROL_FINISHED, 0, 0);
|
||||
file_senders[i].finished = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,15 +273,19 @@ void do_file_senders(Tox *m)
|
||||
sound_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, NULL);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2,
|
||||
self->active_box, "File transfer for '%s' timed out.", filename );
|
||||
box_notify2(self, error, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", msg);
|
||||
else
|
||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box,
|
||||
self->name, "File transfer for '%s' timed out.", filename );
|
||||
box_notify(self, error, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", msg);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!file_senders[i].noconnection && !file_senders[i].finished)
|
||||
send_file_data(self, m, i, friendnum, filenum, filename);
|
||||
|
||||
file_senders[i].queue_pos = num_active_file_senders - 1;
|
||||
send_file_data(self, m, i, friendnum, filenum, filename);
|
||||
}
|
||||
|
||||
refresh_sender_prog(m);
|
||||
refresh_recv_prog(m);
|
||||
}
|
||||
|
@ -26,8 +26,12 @@
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024 ^ 2 */
|
||||
#define GiB 1073741824 /* 1024 ^ 3 */
|
||||
|
||||
#define FILE_PIECE_SIZE 2048 /* must be >= (MAX_CRYPTO_DATA_SIZE - 2) in toxcore/net_crypto.h */
|
||||
#define MAX_FILES 255
|
||||
#define MAX_FILES 32
|
||||
#define TIMEOUT_FILESENDER 120
|
||||
#define NUM_PROG_MARKS 50 /* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
|
||||
@ -36,6 +40,7 @@ typedef struct {
|
||||
ToxWindow *toxwin;
|
||||
int32_t friendnum;
|
||||
bool active;
|
||||
bool noconnection;
|
||||
bool finished;
|
||||
int filenum;
|
||||
char nextpiece[FILE_PIECE_SIZE];
|
||||
@ -61,6 +66,9 @@ void print_progress_bar(ToxWindow *self, int idx, int friendnum, double pct_rema
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void close_file_sender(ToxWindow *self, Tox *m, int i, const char *msg, int CTRL, int filenum, int32_t friendnum);
|
||||
|
||||
/* called whenever a file sender is opened or closed */
|
||||
void reset_file_sender_queue(void);
|
||||
|
||||
void close_all_file_senders(Tox *m);
|
||||
void do_file_senders(Tox *m);
|
||||
|
||||
|
476
src/friendlist.c
476
src/friendlist.c
@ -51,20 +51,17 @@ extern struct user_settings *user_settings_;
|
||||
extern struct arg_opts arg_opts;
|
||||
|
||||
static uint8_t blocklist_view = 0; /* 0 if we're in friendlist view, 1 if we're in blocklist view */
|
||||
static int num_selected = 0;
|
||||
static int max_friends_index = 0; /* 1 + the index of the last friend in friends array */
|
||||
static int num_friends = 0;
|
||||
|
||||
ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
static int friendlist_index[MAX_FRIENDS_NUM] = {0};
|
||||
_Friends Friends;
|
||||
|
||||
static struct _Blocked_Contacts {
|
||||
static struct _Blocked {
|
||||
int num_selected;
|
||||
int max_index;
|
||||
int max_idx;
|
||||
int num_blocked;
|
||||
BlockedFriend list[MAX_FRIENDS_NUM];
|
||||
int index[MAX_FRIENDS_NUM];
|
||||
} Blocked_Contacts;
|
||||
|
||||
int *index;
|
||||
BlockedFriend *list;
|
||||
} Blocked;
|
||||
|
||||
static struct _pendingDel {
|
||||
int num;
|
||||
@ -72,6 +69,52 @@ static struct _pendingDel {
|
||||
WINDOW *popup;
|
||||
} pendingdelete;
|
||||
|
||||
static void realloc_friends(int n)
|
||||
{
|
||||
if (n <= 0) {
|
||||
free(Friends.list);
|
||||
free(Friends.index);
|
||||
Friends.list = NULL;
|
||||
Friends.index = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ToxicFriend *f = realloc(Friends.list, n * sizeof(ToxicFriend));
|
||||
int *f_idx = realloc(Friends.index, n * sizeof(int));
|
||||
|
||||
if (f == NULL || f_idx == NULL)
|
||||
exit_toxic_err("failed in realloc_friends", FATALERR_MEMORY);
|
||||
|
||||
Friends.list = f;
|
||||
Friends.index = f_idx;
|
||||
}
|
||||
|
||||
static void realloc_blocklist(int n)
|
||||
{
|
||||
if (n <= 0) {
|
||||
free(Blocked.list);
|
||||
free(Blocked.index);
|
||||
Blocked.list = NULL;
|
||||
Blocked.index = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
BlockedFriend *b = realloc(Blocked.list, n * sizeof(BlockedFriend));
|
||||
int *b_idx = realloc(Blocked.index, n * sizeof(int));
|
||||
|
||||
if (b == NULL || b_idx == NULL)
|
||||
exit_toxic_err("failed in realloc_blocklist", FATALERR_MEMORY);
|
||||
|
||||
Blocked.list = b;
|
||||
Blocked.index = b_idx;
|
||||
}
|
||||
|
||||
void kill_friendlist(void)
|
||||
{
|
||||
realloc_blocklist(0);
|
||||
realloc_friends(0);
|
||||
}
|
||||
|
||||
static int save_blocklist(char *path)
|
||||
{
|
||||
if (arg_opts.ignore_data_file)
|
||||
@ -80,29 +123,30 @@ static int save_blocklist(char *path)
|
||||
if (path == NULL)
|
||||
return -1;
|
||||
|
||||
int len = sizeof(BlockedFriend) * Blocked_Contacts.num_blocked;
|
||||
int len = sizeof(BlockedFriend) * Blocked.num_blocked;
|
||||
char *data = malloc(len);
|
||||
|
||||
if (data == NULL)
|
||||
exit_toxic_err("Failed in save_blocklist", FATALERR_MEMORY);
|
||||
|
||||
int i;
|
||||
int ret = -1;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < Blocked_Contacts.max_index; ++i) {
|
||||
if (count > Blocked_Contacts.num_blocked)
|
||||
return -1;
|
||||
for (i = 0; i < Blocked.max_idx; ++i) {
|
||||
if (count > Blocked.num_blocked)
|
||||
goto on_error;
|
||||
|
||||
if (Blocked_Contacts.list[i].active) {
|
||||
if (Blocked.list[i].active) {
|
||||
BlockedFriend tmp;
|
||||
memset(&tmp, 0, sizeof(BlockedFriend));
|
||||
tmp.namelength = htons(Blocked_Contacts.list[i].namelength);
|
||||
memcpy(tmp.name, Blocked_Contacts.list[i].name, Blocked_Contacts.list[i].namelength + 1);
|
||||
memcpy(tmp.pub_key, Blocked_Contacts.list[i].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
tmp.namelength = htons(Blocked.list[i].namelength);
|
||||
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);
|
||||
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
|
||||
uint8_t lastonline[sizeof(uint64_t)];
|
||||
memcpy(lastonline, &Blocked_Contacts.list[i].last_on, sizeof(uint64_t));
|
||||
host_to_net(lastonline, sizeof(uint64_t));
|
||||
memcpy(lastonline, &Blocked.list[i].last_on, sizeof(uint64_t));
|
||||
hst_to_net(lastonline, sizeof(uint64_t));
|
||||
memcpy(&tmp.last_on, lastonline, sizeof(uint64_t));
|
||||
|
||||
memcpy(data + count * sizeof(BlockedFriend), &tmp, sizeof(BlockedFriend));
|
||||
@ -112,17 +156,14 @@ static int save_blocklist(char *path)
|
||||
|
||||
FILE *fp = fopen(path, "wb");
|
||||
|
||||
if (fp == NULL) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
if (fp == NULL)
|
||||
goto on_error;
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if (fwrite(data, len, 1, fp) != 1)
|
||||
ret = -1;
|
||||
if (fwrite(data, len, 1, fp) == 1)
|
||||
ret = 0;
|
||||
|
||||
fclose(fp);
|
||||
on_error:
|
||||
free(data);
|
||||
return ret;
|
||||
}
|
||||
@ -163,27 +204,30 @@ int load_blocklist(char *path)
|
||||
}
|
||||
|
||||
int num = len / sizeof(BlockedFriend);
|
||||
Blocked.max_idx = num;
|
||||
realloc_blocklist(num);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
|
||||
|
||||
BlockedFriend tmp;
|
||||
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
|
||||
Blocked_Contacts.list[i].active = true;
|
||||
Blocked_Contacts.list[i].num = i;
|
||||
Blocked_Contacts.list[i].namelength = ntohs(tmp.namelength);
|
||||
memcpy(Blocked_Contacts.list[i].name, tmp.name, Blocked_Contacts.list[i].namelength + 1);
|
||||
memcpy(Blocked_Contacts.list[i].pub_key, tmp.pub_key, TOX_CLIENT_ID_SIZE);
|
||||
Blocked.list[i].active = true;
|
||||
Blocked.list[i].num = i;
|
||||
Blocked.list[i].namelength = ntohs(tmp.namelength);
|
||||
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
|
||||
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_CLIENT_ID_SIZE);
|
||||
|
||||
uint8_t lastonline[sizeof(uint64_t)];
|
||||
memcpy(lastonline, &tmp.last_on, sizeof(uint64_t));
|
||||
net_to_host(lastonline, sizeof(uint64_t));
|
||||
memcpy(&Blocked_Contacts.list[i].last_on, lastonline, sizeof(uint64_t));
|
||||
memcpy(&Blocked.list[i].last_on, lastonline, sizeof(uint64_t));
|
||||
|
||||
++Blocked_Contacts.num_blocked;
|
||||
++Blocked.num_blocked;
|
||||
}
|
||||
|
||||
Blocked_Contacts.max_index = i + 1;
|
||||
|
||||
free(data);
|
||||
fclose(fp);
|
||||
sort_blocklist_index();
|
||||
@ -194,32 +238,32 @@ int load_blocklist(char *path)
|
||||
#define S_WEIGHT 100000
|
||||
static int index_name_cmp(const void *n1, const void *n2)
|
||||
{
|
||||
int res = qsort_strcasecmp_hlpr(friends[*(int *) n1].name, friends[*(int *) n2].name);
|
||||
int res = qsort_strcasecmp_hlpr(Friends.list[*(int *) n1].name, Friends.list[*(int *) n2].name);
|
||||
|
||||
/* Use weight to make qsort always put online friends before offline */
|
||||
res = friends[*(int *) n1].online ? (res - S_WEIGHT) : (res + S_WEIGHT);
|
||||
res = friends[*(int *) n2].online ? (res + S_WEIGHT) : (res - S_WEIGHT);
|
||||
res = Friends.list[*(int *) n1].online ? (res - S_WEIGHT) : (res + S_WEIGHT);
|
||||
res = Friends.list[*(int *) n2].online ? (res + S_WEIGHT) : (res - S_WEIGHT);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
/* sorts Friends.index first by connection status then alphabetically */
|
||||
void sort_friendlist_index(void)
|
||||
{
|
||||
int i;
|
||||
int n = 0;
|
||||
|
||||
for (i = 0; i < max_friends_index; ++i) {
|
||||
if (friends[i].active)
|
||||
friendlist_index[n++] = friends[i].num;
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].active)
|
||||
Friends.index[n++] = Friends.list[i].num;
|
||||
}
|
||||
|
||||
qsort(friendlist_index, num_friends, sizeof(int), index_name_cmp);
|
||||
qsort(Friends.index, Friends.num_friends, sizeof(int), index_name_cmp);
|
||||
}
|
||||
|
||||
static int index_name_cmp_block(const void *n1, const void *n2)
|
||||
{
|
||||
return qsort_strcasecmp_hlpr(Blocked_Contacts.list[*(int *) n1].name, Blocked_Contacts.list[*(int *) n2].name);
|
||||
return qsort_strcasecmp_hlpr(Blocked.list[*(int *) n1].name, Blocked.list[*(int *) n2].name);
|
||||
}
|
||||
|
||||
static void sort_blocklist_index(void)
|
||||
@ -227,33 +271,33 @@ static void sort_blocklist_index(void)
|
||||
int i;
|
||||
int n = 0;
|
||||
|
||||
for (i = 0; i < Blocked_Contacts.max_index; ++i) {
|
||||
if (Blocked_Contacts.list[i].active)
|
||||
Blocked_Contacts.index[n++] = Blocked_Contacts.list[i].num;
|
||||
for (i = 0; i < Blocked.max_idx; ++i) {
|
||||
if (Blocked.list[i].active)
|
||||
Blocked.index[n++] = Blocked.list[i].num;
|
||||
}
|
||||
|
||||
qsort(Blocked_Contacts.index, Blocked_Contacts.num_blocked, sizeof(int), index_name_cmp_block);
|
||||
qsort(Blocked.index, Blocked.num_blocked, sizeof(int), index_name_cmp_block);
|
||||
}
|
||||
|
||||
static void update_friend_last_online(int32_t num, uint64_t timestamp)
|
||||
{
|
||||
friends[num].last_online.last_on = timestamp;
|
||||
friends[num].last_online.tm = *localtime((const time_t*)×tamp);
|
||||
Friends.list[num].last_online.last_on = timestamp;
|
||||
Friends.list[num].last_online.tm = *localtime((const time_t*)×tamp);
|
||||
|
||||
/* if the format changes make sure TIME_STR_SIZE is the correct size */
|
||||
const char *t = user_settings_->time == TIME_12 ? "%I:%M %p" : "%H:%M";
|
||||
strftime(friends[num].last_online.hour_min_str, TIME_STR_SIZE, t,
|
||||
&friends[num].last_online.tm);
|
||||
strftime(Friends.list[num].last_online.hour_min_str, TIME_STR_SIZE, t,
|
||||
&Friends.list[num].last_online.tm);
|
||||
}
|
||||
|
||||
static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, const char *str, uint16_t len)
|
||||
{
|
||||
if (num >= max_friends_index)
|
||||
if (num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
if (friends[num].chatwin == -1) {
|
||||
if (Friends.list[num].chatwin == -1) {
|
||||
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
|
||||
} else {
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
@ -272,10 +316,10 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, int32_t num, const cha
|
||||
|
||||
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
|
||||
{
|
||||
if (num >= max_friends_index)
|
||||
if (num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
friends[num].online = status;
|
||||
Friends.list[num].online = status;
|
||||
update_friend_last_online(num, get_unix_time());
|
||||
store_data(m, DATA_FILE);
|
||||
sort_friendlist_index();
|
||||
@ -283,106 +327,110 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, int32_t num,
|
||||
|
||||
static void friendlist_onNickChange(ToxWindow *self, Tox *m, int32_t num, const char *nick, uint16_t len)
|
||||
{
|
||||
if (len > TOX_MAX_NAME_LENGTH || num >= max_friends_index)
|
||||
if (len > TOX_MAX_NAME_LENGTH || num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
char tempname[TOX_MAX_NAME_LENGTH];
|
||||
strcpy(tempname, nick);
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
tempname[len] = '\0';
|
||||
snprintf(friends[num].name, sizeof(friends[num].name), "%s", tempname);
|
||||
friends[num].namelength = len;
|
||||
snprintf(Friends.list[num].name, sizeof(Friends.list[num].name), "%s", tempname);
|
||||
Friends.list[num].namelength = len;
|
||||
sort_friendlist_index();
|
||||
}
|
||||
|
||||
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, int32_t num, uint8_t status)
|
||||
{
|
||||
if (num >= max_friends_index)
|
||||
if (num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
friends[num].status = status;
|
||||
Friends.list[num].status = status;
|
||||
}
|
||||
|
||||
static void friendlist_onStatusMessageChange(ToxWindow *self, int32_t num, const char *status, uint16_t len)
|
||||
{
|
||||
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= max_friends_index)
|
||||
if (len > TOX_MAX_STATUSMESSAGE_LENGTH || num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
snprintf(friends[num].statusmsg, sizeof(friends[num].statusmsg), "%s", status);
|
||||
friends[num].statusmsg_len = strlen(friends[num].statusmsg);
|
||||
snprintf(Friends.list[num].statusmsg, sizeof(Friends.list[num].statusmsg), "%s", status);
|
||||
Friends.list[num].statusmsg_len = strlen(Friends.list[num].statusmsg);
|
||||
}
|
||||
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort)
|
||||
{
|
||||
if (max_friends_index < 0 || max_friends_index >= MAX_FRIENDS_NUM)
|
||||
if (Friends.max_idx < 0)
|
||||
return;
|
||||
|
||||
|
||||
Friends.num_friends = tox_count_friendlist(m);
|
||||
realloc_friends(Friends.max_idx + 1);
|
||||
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= max_friends_index; ++i) {
|
||||
if (!friends[i].active) {
|
||||
friends[i].num = num;
|
||||
friends[i].active = true;
|
||||
friends[i].chatwin = -1;
|
||||
friends[i].online = false;
|
||||
friends[i].status = TOX_USERSTATUS_NONE;
|
||||
friends[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
|
||||
tox_get_client_id(m, num, (uint8_t *) friends[i].pub_key);
|
||||
update_friend_last_online(i, tox_get_last_online(m, i));
|
||||
for (i = 0; i <= Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].active)
|
||||
continue;
|
||||
|
||||
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
||||
int len = get_nick_truncate(m, tempname, num);
|
||||
Friends.list[i].num = num;
|
||||
Friends.list[i].active = true;
|
||||
Friends.list[i].chatwin = -1;
|
||||
Friends.list[i].online = false;
|
||||
Friends.list[i].status = TOX_USERSTATUS_NONE;
|
||||
Friends.list[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
|
||||
tox_get_client_id(m, num, (uint8_t *) Friends.list[i].pub_key);
|
||||
update_friend_last_online(i, tox_get_last_online(m, i));
|
||||
|
||||
if (len == -1 || tempname[0] == '\0') {
|
||||
strcpy(friends[i].name, UNKNOWN_NAME);
|
||||
friends[i].namelength = strlen(UNKNOWN_NAME);
|
||||
} else { /* Enforce toxic's maximum name length */
|
||||
friends[i].namelength = len;
|
||||
snprintf(friends[i].name, sizeof(friends[i].name), "%s", tempname);
|
||||
}
|
||||
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
||||
int len = get_nick_truncate(m, tempname, num);
|
||||
|
||||
num_friends = tox_count_friendlist(m);
|
||||
|
||||
if (i == max_friends_index)
|
||||
++max_friends_index;
|
||||
|
||||
if (sort)
|
||||
sort_friendlist_index();
|
||||
|
||||
return;
|
||||
if (len == -1 || tempname[0] == '\0') {
|
||||
strcpy(Friends.list[i].name, UNKNOWN_NAME);
|
||||
Friends.list[i].namelength = strlen(UNKNOWN_NAME);
|
||||
} else { /* Enforce toxic's maximum name length */
|
||||
Friends.list[i].namelength = len;
|
||||
snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname);
|
||||
}
|
||||
|
||||
if (i == Friends.max_idx)
|
||||
++Friends.max_idx;
|
||||
|
||||
if (sort)
|
||||
sort_friendlist_index();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* puts blocked friend back in friendlist. fnum is new friend number, bnum is blocked number */
|
||||
static void friendlist_add_blocked(Tox *m, int32_t fnum, int32_t bnum)
|
||||
{
|
||||
if (max_friends_index >= MAX_FRIENDS_NUM)
|
||||
return;
|
||||
Friends.num_friends = tox_count_friendlist(m);
|
||||
realloc_friends(Friends.max_idx + 1);
|
||||
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= max_friends_index; ++i) {
|
||||
if (friends[i].active)
|
||||
for (i = 0; i <= Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].active)
|
||||
continue;
|
||||
|
||||
friends[i].num = fnum;
|
||||
friends[i].active = true;
|
||||
friends[i].chatwin = -1;
|
||||
friends[i].status = TOX_USERSTATUS_NONE;
|
||||
friends[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
|
||||
friends[i].namelength = Blocked_Contacts.list[bnum].namelength;
|
||||
update_friend_last_online(i, Blocked_Contacts.list[bnum].last_on);
|
||||
memcpy(friends[i].name, Blocked_Contacts.list[bnum].name, friends[i].namelength + 1);
|
||||
memcpy(friends[i].pub_key, Blocked_Contacts.list[bnum].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
Friends.list[i].num = fnum;
|
||||
Friends.list[i].active = true;
|
||||
Friends.list[i].chatwin = -1;
|
||||
Friends.list[i].status = TOX_USERSTATUS_NONE;
|
||||
Friends.list[i].logging_on = (bool) user_settings_->autolog == AUTOLOG_ON;
|
||||
Friends.list[i].namelength = Blocked.list[bnum].namelength;
|
||||
update_friend_last_online(i, Blocked.list[bnum].last_on);
|
||||
memcpy(Friends.list[i].name, Blocked.list[bnum].name, Friends.list[i].namelength + 1);
|
||||
memcpy(Friends.list[i].pub_key, Blocked.list[bnum].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
|
||||
num_friends = tox_count_friendlist(m);
|
||||
|
||||
if (i == max_friends_index)
|
||||
++max_friends_index;
|
||||
if (i == Friends.max_idx)
|
||||
++Friends.max_idx;
|
||||
|
||||
sort_blocklist_index();
|
||||
sort_friendlist_index();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -390,12 +438,12 @@ static void friendlist_add_blocked(Tox *m, int32_t fnum, int32_t bnum)
|
||||
static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, uint8_t filenum,
|
||||
uint64_t filesize, const char *filename, uint16_t filename_len)
|
||||
{
|
||||
if (num >= max_friends_index)
|
||||
if (num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
if (friends[num].chatwin == -1) {
|
||||
if (Friends.list[num].chatwin == -1) {
|
||||
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
|
||||
} else {
|
||||
tox_file_send_control(m, num, 1, filenum, TOX_FILECONTROL_KILL, 0, 0);
|
||||
|
||||
@ -412,20 +460,12 @@ static void friendlist_onFileSendRequest(ToxWindow *self, Tox *m, int32_t num, u
|
||||
|
||||
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, const char *group_pub_key)
|
||||
{
|
||||
if (num >= max_friends_index)
|
||||
if (num >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
if (friends[num].chatwin == -1) {
|
||||
if (Friends.list[num].chatwin == -1) {
|
||||
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||
friends[num].chatwin = add_window(m, new_chat(m, friends[num].num));
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_notify2(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, self->active_box,
|
||||
"You are invited to join group" );
|
||||
else
|
||||
box_notify(self, generic_message, NT_WNDALERT_0 | NT_NOFOCUS, &self->active_box, self->name,
|
||||
"You are invited to join group" );
|
||||
|
||||
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
|
||||
} else {
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
@ -452,31 +492,32 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
|
||||
|
||||
static void delete_friend(Tox *m, int32_t f_num)
|
||||
{
|
||||
if (friends[f_num].chatwin >= 0) {
|
||||
ToxWindow *toxwin = get_window_ptr(friends[f_num].chatwin);
|
||||
if (Friends.list[f_num].chatwin >= 0) {
|
||||
ToxWindow *toxwin = get_window_ptr(Friends.list[f_num].chatwin);
|
||||
|
||||
if (toxwin != NULL) {
|
||||
kill_chat_window(toxwin);
|
||||
kill_chat_window(toxwin, m);
|
||||
set_active_window(1); /* keep friendlist focused */
|
||||
}
|
||||
}
|
||||
|
||||
tox_del_friend(m, f_num);
|
||||
memset(&friends[f_num], 0, sizeof(ToxicFriend));
|
||||
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = max_friends_index; i > 0; --i) {
|
||||
if (friends[i - 1].active)
|
||||
for (i = Friends.max_idx; i > 0; --i) {
|
||||
if (Friends.list[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
max_friends_index = i;
|
||||
num_friends = tox_count_friendlist(m);
|
||||
Friends.max_idx = i;
|
||||
Friends.num_friends = tox_count_friendlist(m);
|
||||
realloc_friends(i);
|
||||
|
||||
/* make sure num_selected stays within num_friends range */
|
||||
if (num_friends && num_selected == num_friends)
|
||||
--num_selected;
|
||||
/* make sure num_selected stays within Friends.num_friends range */
|
||||
if (Friends.num_friends && Friends.num_selected == Friends.num_friends)
|
||||
--Friends.num_selected;
|
||||
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
@ -524,9 +565,9 @@ static void draw_del_popup(void)
|
||||
wattron(pendingdelete.popup, A_BOLD);
|
||||
|
||||
if (blocklist_view == 0)
|
||||
wprintw(pendingdelete.popup, "%s", friends[pendingdelete.num].name);
|
||||
wprintw(pendingdelete.popup, "%s", Friends.list[pendingdelete.num].name);
|
||||
else
|
||||
wprintw(pendingdelete.popup, "%s", Blocked_Contacts.list[pendingdelete.num].name);
|
||||
wprintw(pendingdelete.popup, "%s", Blocked.list[pendingdelete.num].name);
|
||||
|
||||
wattroff(pendingdelete.popup, A_BOLD);
|
||||
wprintw(pendingdelete.popup, "? y/n");
|
||||
@ -537,46 +578,50 @@ static void draw_del_popup(void)
|
||||
/* deletes contact from blocked list */
|
||||
static void delete_blocked_friend(int32_t bnum)
|
||||
{
|
||||
memset(&Blocked_Contacts.list[bnum], 0, sizeof(BlockedFriend));
|
||||
memset(&Blocked.list[bnum], 0, sizeof(BlockedFriend));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = Blocked_Contacts.max_index; i > 0; --i) {
|
||||
if (Blocked_Contacts.list[i - 1].active)
|
||||
for (i = Blocked.max_idx; i > 0; --i) {
|
||||
if (Blocked.list[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
--Blocked_Contacts.num_blocked;
|
||||
Blocked_Contacts.max_index = i;
|
||||
--Blocked.num_blocked;
|
||||
Blocked.max_idx = i;
|
||||
realloc_blocklist(i);
|
||||
save_blocklist(BLOCK_FILE);
|
||||
|
||||
if (Blocked_Contacts.num_blocked && Blocked_Contacts.num_selected == Blocked_Contacts.num_blocked)
|
||||
--Blocked_Contacts.num_selected;
|
||||
if (Blocked.num_blocked && Blocked.num_selected == Blocked.num_blocked)
|
||||
--Blocked.num_selected;
|
||||
}
|
||||
|
||||
/* deletes contact from friendlist and puts in blocklist */
|
||||
void block_friend(Tox *m, int32_t fnum)
|
||||
{
|
||||
if (Blocked_Contacts.max_index >= MAX_FRIENDS_NUM || num_friends <= 0)
|
||||
if (Friends.num_friends <= 0)
|
||||
return;
|
||||
|
||||
realloc_blocklist(Blocked.max_idx + 1);
|
||||
memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= Blocked_Contacts.max_index; ++i) {
|
||||
if (Blocked_Contacts.list[i].active)
|
||||
for (i = 0; i <= Blocked.max_idx; ++i) {
|
||||
if (Blocked.list[i].active)
|
||||
continue;
|
||||
|
||||
Blocked_Contacts.list[i].active = true;
|
||||
Blocked_Contacts.list[i].num = i;
|
||||
Blocked_Contacts.list[i].namelength = friends[fnum].namelength;
|
||||
Blocked_Contacts.list[i].last_on = friends[fnum].last_online.last_on;
|
||||
memcpy(Blocked_Contacts.list[i].pub_key, friends[fnum].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
memcpy(Blocked_Contacts.list[i].name, friends[fnum].name, friends[fnum].namelength + 1);
|
||||
Blocked.list[i].active = true;
|
||||
Blocked.list[i].num = i;
|
||||
Blocked.list[i].namelength = Friends.list[fnum].namelength;
|
||||
Blocked.list[i].last_on = Friends.list[fnum].last_online.last_on;
|
||||
memcpy(Blocked.list[i].pub_key, Friends.list[fnum].pub_key, TOX_CLIENT_ID_SIZE);
|
||||
memcpy(Blocked.list[i].name, Friends.list[fnum].name, Friends.list[fnum].namelength + 1);
|
||||
|
||||
++Blocked_Contacts.num_blocked;
|
||||
++Blocked.num_blocked;
|
||||
|
||||
if (i == Blocked_Contacts.max_index)
|
||||
++Blocked_Contacts.max_index;
|
||||
if (i == Blocked.max_idx)
|
||||
++Blocked.max_idx;
|
||||
|
||||
delete_friend(m, fnum);
|
||||
save_blocklist(BLOCK_FILE);
|
||||
@ -590,10 +635,10 @@ void block_friend(Tox *m, int32_t fnum)
|
||||
/* removes friend from blocklist, puts back in friendlist */
|
||||
static void unblock_friend(Tox *m, int32_t bnum)
|
||||
{
|
||||
if (Blocked_Contacts.num_blocked <= 0)
|
||||
if (Blocked.num_blocked <= 0)
|
||||
return;
|
||||
|
||||
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) Blocked_Contacts.list[bnum].pub_key);
|
||||
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key);
|
||||
|
||||
if (friendnum == -1) {
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend");
|
||||
@ -619,14 +664,18 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blocklist_view && !num_friends && (key != KEY_RIGHT && key != KEY_LEFT))
|
||||
if (!blocklist_view && !Friends.num_friends && (key != KEY_RIGHT && key != KEY_LEFT))
|
||||
return;
|
||||
|
||||
if (blocklist_view && !Blocked_Contacts.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT))
|
||||
if (blocklist_view && !Blocked.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT))
|
||||
return;
|
||||
|
||||
int f = blocklist_view == 1 ? Blocked_Contacts.index[Blocked_Contacts.num_selected]
|
||||
: friendlist_index[num_selected];
|
||||
int f = 0;
|
||||
|
||||
if (blocklist_view == 1 && Blocked.num_blocked)
|
||||
f = Blocked.index[Blocked.num_selected];
|
||||
else if (Friends.num_friends)
|
||||
f = Friends.index[Friends.num_selected];
|
||||
|
||||
/* lock screen and force decision on deletion popup */
|
||||
if (pendingdelete.active) {
|
||||
@ -645,11 +694,11 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
break;
|
||||
|
||||
/* Jump to chat window if already open */
|
||||
if (friends[f].chatwin != -1) {
|
||||
set_active_window(friends[f].chatwin);
|
||||
if (Friends.list[f].chatwin != -1) {
|
||||
set_active_window(Friends.list[f].chatwin);
|
||||
} 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);
|
||||
Friends.list[f].chatwin = add_window(m, new_chat(m, Friends.list[f].num));
|
||||
set_active_window(Friends.list[f].chatwin);
|
||||
} else {
|
||||
const char *msg = "* Warning: Too many windows are open.";
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, msg);
|
||||
@ -676,9 +725,9 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
default:
|
||||
if (blocklist_view == 0)
|
||||
select_friend(self, key, &num_selected, num_friends);
|
||||
select_friend(self, key, &Friends.num_selected, Friends.num_friends);
|
||||
else
|
||||
select_friend(self, key, &Blocked_Contacts.num_selected, Blocked_Contacts.num_blocked);
|
||||
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -690,7 +739,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " Blocked: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
wprintw(self->window, "%d\n\n", Blocked_Contacts.num_blocked);
|
||||
wprintw(self->window, "%d\n\n", Blocked.num_blocked);
|
||||
|
||||
if ((y2 - FLIST_OFST) <= 0)
|
||||
return;
|
||||
@ -698,17 +747,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
int selected_num = 0;
|
||||
|
||||
/* Determine which portion of friendlist to draw based on current position */
|
||||
int page = Blocked_Contacts.num_selected / (y2 - FLIST_OFST);
|
||||
int page = Blocked.num_selected / (y2 - FLIST_OFST);
|
||||
int start = (y2 - FLIST_OFST) * page;
|
||||
int end = y2 - FLIST_OFST + start;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = start; i < Blocked_Contacts.num_blocked && i < end; ++i) {
|
||||
int f = Blocked_Contacts.index[i];
|
||||
for (i = start; i < Blocked.num_blocked && i < end; ++i) {
|
||||
int f = Blocked.index[i];
|
||||
bool f_selected = false;
|
||||
|
||||
if (i == Blocked_Contacts.num_selected) {
|
||||
if (i == Blocked.num_selected) {
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " > ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
@ -726,7 +775,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
wattron(self->window, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " %s\n", Blocked_Contacts.list[f].name);
|
||||
wprintw(self->window, " %s\n", Blocked.list[f].name);
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
if (f_selected)
|
||||
@ -736,17 +785,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
wprintw(self->window, "\n");
|
||||
self->x = x2;
|
||||
|
||||
if (Blocked_Contacts.num_blocked) {
|
||||
if (Blocked.num_blocked) {
|
||||
wmove(self->window, y2 - 1, 1);
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "ID: ");
|
||||
wprintw(self->window, "Key: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
|
||||
wprintw(self->window, "%02X", Blocked_Contacts.list[selected_num].pub_key[i] & 0xff);
|
||||
wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
|
||||
}
|
||||
|
||||
wrefresh(self->window);
|
||||
@ -768,7 +817,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wattron(self->window, COLOR_PAIR(CYAN));
|
||||
wprintw(self->window, " Press the");
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " H ");
|
||||
wprintw(self->window, " h ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
wprintw(self->window, "key for help\n\n");
|
||||
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||
@ -788,7 +837,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " Online: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
wprintw(self->window, "%d/%d \n\n", nf, num_friends);
|
||||
wprintw(self->window, "%d/%d \n\n", nf, Friends.num_friends);
|
||||
|
||||
if ((y2 - FLIST_OFST) <= 0)
|
||||
return;
|
||||
@ -796,18 +845,18 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
int selected_num = 0;
|
||||
|
||||
/* Determine which portion of friendlist to draw based on current position */
|
||||
int page = num_selected / (y2 - FLIST_OFST);
|
||||
int page = Friends.num_selected / (y2 - FLIST_OFST);
|
||||
int start = (y2 - FLIST_OFST) * page;
|
||||
int end = y2 - FLIST_OFST + start;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = start; i < num_friends && i < end; ++i) {
|
||||
int f = friendlist_index[i];
|
||||
for (i = start; i < Friends.num_friends && i < end; ++i) {
|
||||
int f = Friends.index[i];
|
||||
bool f_selected = false;
|
||||
|
||||
if (friends[f].active) {
|
||||
if (i == num_selected) {
|
||||
if (Friends.list[f].active) {
|
||||
if (i == Friends.num_selected) {
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " > ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
@ -817,8 +866,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wprintw(self->window, " ");
|
||||
}
|
||||
|
||||
if (friends[f].online) {
|
||||
uint8_t status = friends[f].status;
|
||||
if (Friends.list[f].online) {
|
||||
uint8_t status = Friends.list[f].status;
|
||||
int colour = WHITE;
|
||||
|
||||
switch (status) {
|
||||
@ -847,36 +896,36 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wattron(self->window, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "%s", friends[f].name);
|
||||
wprintw(self->window, "%s", Friends.list[f].name);
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
if (f_selected)
|
||||
wattroff(self->window, COLOR_PAIR(BLUE));
|
||||
|
||||
/* Reset friends[f].statusmsg on window resize */
|
||||
/* Reset Friends.list[f].statusmsg on window resize */
|
||||
if (fix_statuses) {
|
||||
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH];
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
tox_get_status_message(m, friends[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||
tox_get_status_message(m, Friends.list[f].num, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
snprintf(friends[f].statusmsg, sizeof(friends[f].statusmsg), "%s", statusmsg);
|
||||
friends[f].statusmsg_len = strlen(friends[f].statusmsg);
|
||||
snprintf(Friends.list[f].statusmsg, sizeof(Friends.list[f].statusmsg), "%s", statusmsg);
|
||||
Friends.list[f].statusmsg_len = strlen(Friends.list[f].statusmsg);
|
||||
}
|
||||
|
||||
/* Truncate note if it doesn't fit on one line */
|
||||
uint16_t maxlen = x2 - getcurx(self->window) - 2;
|
||||
|
||||
if (friends[f].statusmsg_len > maxlen) {
|
||||
friends[f].statusmsg[maxlen - 3] = '\0';
|
||||
strcat(friends[f].statusmsg, "...");
|
||||
friends[f].statusmsg[maxlen] = '\0';
|
||||
friends[f].statusmsg_len = maxlen;
|
||||
if (Friends.list[f].statusmsg_len > maxlen) {
|
||||
Friends.list[f].statusmsg[maxlen - 3] = '\0';
|
||||
strcat(Friends.list[f].statusmsg, "...");
|
||||
Friends.list[f].statusmsg[maxlen] = '\0';
|
||||
Friends.list[f].statusmsg_len = maxlen;
|
||||
}
|
||||
|
||||
if (friends[f].statusmsg[0])
|
||||
wprintw(self->window, " %s", friends[f].statusmsg);
|
||||
if (Friends.list[f].statusmsg[0])
|
||||
wprintw(self->window, " %s", Friends.list[f].statusmsg);
|
||||
|
||||
wprintw(self->window, "\n");
|
||||
} else {
|
||||
@ -886,17 +935,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wattron(self->window, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "%s", friends[f].name);
|
||||
wprintw(self->window, "%s", Friends.list[f].name);
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
if (f_selected)
|
||||
wattroff(self->window, COLOR_PAIR(BLUE));
|
||||
|
||||
uint64_t last_seen = friends[f].last_online.last_on;
|
||||
uint64_t last_seen = Friends.list[f].last_online.last_on;
|
||||
|
||||
if (last_seen != 0) {
|
||||
int day_dist = (cur_loc_tm.tm_yday - friends[f].last_online.tm.tm_yday) % 365;
|
||||
const char *hourmin = friends[f].last_online.hour_min_str;
|
||||
int day_dist = (cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday) % 365;
|
||||
const char *hourmin = Friends.list[f].last_online.hour_min_str;
|
||||
|
||||
switch (day_dist) {
|
||||
case 0:
|
||||
@ -920,17 +969,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
|
||||
self->x = x2;
|
||||
|
||||
if (num_friends) {
|
||||
if (Friends.num_friends) {
|
||||
wmove(self->window, y2 - 1, 1);
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "ID: ");
|
||||
wprintw(self->window, "Key: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < TOX_CLIENT_ID_SIZE; ++i)
|
||||
wprintw(self->window, "%02X", friends[selected_num].pub_key[i] & 0xff);
|
||||
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
|
||||
}
|
||||
|
||||
wrefresh(self->window);
|
||||
@ -942,7 +991,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
|
||||
void disable_chatwin(int32_t f_num)
|
||||
{
|
||||
friends[f_num].chatwin = -1;
|
||||
Friends.list[f_num].chatwin = -1;
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
@ -950,20 +999,19 @@ static void friendlist_onAv(ToxWindow *self, ToxAv *av, int call_index)
|
||||
{
|
||||
int id = toxav_get_peer_id(av, call_index, 0);
|
||||
|
||||
/*id++;*/
|
||||
if ( id != ErrorInternal && id >= max_friends_index)
|
||||
if ( id != ErrorInternal && id >= Friends.max_idx)
|
||||
return;
|
||||
|
||||
Tox *m = toxav_get_tox(av);
|
||||
|
||||
if (friends[id].chatwin == -1) {
|
||||
if (Friends.list[id].chatwin == -1) {
|
||||
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
|
||||
if (toxav_get_call_state(av, call_index) == av_CallStarting) { /* Only open windows when call is incoming */
|
||||
friends[id].chatwin = add_window(m, new_chat(m, friends[id].num));
|
||||
Friends.list[id].chatwin = add_window(m, new_chat(m, Friends.list[id].num));
|
||||
}
|
||||
} else {
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, friends[id].num);
|
||||
get_nick_truncate(m, nick, Friends.list[id].num);
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
|
||||
|
||||
const char *errmsg = "* Warning: Too many windows are open.";
|
||||
|
@ -30,15 +30,16 @@
|
||||
#include "file_senders.h"
|
||||
|
||||
struct FileReceiver {
|
||||
char filenames[MAX_FILES][MAX_STR_SIZE];
|
||||
FILE *files[MAX_FILES];
|
||||
bool pending[MAX_FILES];
|
||||
bool active[MAX_FILES];
|
||||
uint64_t size[MAX_FILES];
|
||||
double bps[MAX_FILES];
|
||||
uint64_t last_progress[MAX_FILES];
|
||||
uint32_t line_id[MAX_FILES];
|
||||
int filenums[MAX_FILES];
|
||||
char filename[MAX_STR_SIZE];
|
||||
int filenum;
|
||||
FILE *file;
|
||||
bool pending;
|
||||
bool active;
|
||||
uint64_t size;
|
||||
uint64_t bytes_recv;
|
||||
double bps;
|
||||
uint64_t last_progress; /* unix-time when we last updated progress */
|
||||
uint32_t line_id;
|
||||
};
|
||||
|
||||
struct LastOnline {
|
||||
@ -60,10 +61,11 @@ typedef struct {
|
||||
bool active;
|
||||
bool online;
|
||||
uint8_t is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of chat windows */
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
struct LastOnline last_online;
|
||||
struct FileReceiver file_receiver;
|
||||
struct FileReceiver file_receiver[MAX_FILES];
|
||||
uint8_t active_file_receivers;
|
||||
} ToxicFriend;
|
||||
|
||||
typedef struct {
|
||||
@ -75,11 +77,19 @@ typedef struct {
|
||||
uint64_t last_on;
|
||||
} BlockedFriend;
|
||||
|
||||
typedef struct {
|
||||
int num_selected;
|
||||
int max_idx; /* 1 + the index of the last friend in list */
|
||||
int num_friends;
|
||||
int *index;
|
||||
ToxicFriend *list;
|
||||
} _Friends;
|
||||
|
||||
ToxWindow new_friendlist(void);
|
||||
void disable_chatwin(int32_t f_num);
|
||||
int get_friendnum(uint8_t *name);
|
||||
int load_blocklist(char *data);
|
||||
|
||||
void kill_friendlist(void);
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, int32_t num, bool sort);
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
|
@ -37,11 +37,8 @@
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern ToxWindow *prompt;
|
||||
|
||||
extern ToxicFriend friends[MAX_FRIENDS_NUM];
|
||||
|
||||
extern char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||
extern uint8_t num_frnd_requests;
|
||||
extern _Friends Friends;
|
||||
extern _FriendRequests FriendRequests;
|
||||
|
||||
/* command functions */
|
||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -53,18 +50,18 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req >= MAX_FRIENDS_NUM) {
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strlen(pending_frnd_requests[req])) {
|
||||
if (!FriendRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *msg;
|
||||
int32_t friendnum = tox_add_friend_norequest(m, (uint8_t *) pending_frnd_requests[req]);
|
||||
int32_t friendnum = tox_add_friend_norequest(m, FriendRequests.request[req].key);
|
||||
|
||||
if (friendnum == -1)
|
||||
msg = "Failed to add friend.";
|
||||
@ -73,16 +70,17 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
on_friendadded(m, friendnum, true);
|
||||
}
|
||||
|
||||
memset(&pending_frnd_requests[req], 0, TOX_CLIENT_ID_SIZE);
|
||||
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = num_frnd_requests; i > 0; --i) {
|
||||
if (!strlen(pending_frnd_requests[i - 1]))
|
||||
for (i = FriendRequests.max_idx; i > 0; --i) {
|
||||
if (FriendRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
num_frnd_requests = i;
|
||||
FriendRequests.max_idx = i;
|
||||
--FriendRequests.num_requests;
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
||||
}
|
||||
|
||||
@ -211,10 +209,42 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
}
|
||||
|
||||
char *binary_string = hex_string_to_bin(key);
|
||||
tox_bootstrap_from_address(m, ip, TOX_ENABLE_IPV6_DEFAULT, htons(atoi(port)), (uint8_t *) binary_string);
|
||||
tox_bootstrap_from_address(m, ip, atoi(port), (uint8_t *) binary_string);
|
||||
free(binary_string);
|
||||
}
|
||||
|
||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
int req = atoi(argv[1]);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req > MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FriendRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&FriendRequests.request[req], 0, sizeof(struct _friend_request));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = FriendRequests.max_idx; i > 0; --i) {
|
||||
if (FriendRequests.request[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
FriendRequests.max_idx = i;
|
||||
--FriendRequests.num_requests;
|
||||
}
|
||||
|
||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
@ -258,8 +288,8 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
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, log);
|
||||
Friends.list[self->num].logging_on = true;
|
||||
log_enable(self->name, Friends.list[self->num].pub_key, log);
|
||||
} else if (self->is_prompt) {
|
||||
char myid[TOX_FRIEND_ADDRESS_SIZE];
|
||||
tox_get_address(m, (uint8_t *) myid);
|
||||
@ -273,7 +303,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
return;
|
||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||
if (self->is_chat)
|
||||
friends[self->num].logging_on = false;
|
||||
Friends.list[self->num].logging_on = false;
|
||||
|
||||
log_disable(log);
|
||||
|
||||
@ -354,8 +384,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
int len = strlen(msg) - 1;
|
||||
msg[len] = '\0';
|
||||
|
||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
||||
prompt_update_statusmessage(prompt, msg);
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
|
||||
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -368,6 +397,36 @@ void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
exit_toxic_success(m);
|
||||
}
|
||||
|
||||
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (FriendRequests.num_requests == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
return;
|
||||
}
|
||||
|
||||
int i, j;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < FriendRequests.max_idx; ++i) {
|
||||
if (!FriendRequests.request[i].active)
|
||||
continue;
|
||||
|
||||
char id[TOX_CLIENT_ID_SIZE * 2 + 1] = {0};
|
||||
|
||||
for (j = 0; j < TOX_CLIENT_ID_SIZE; ++j) {
|
||||
char d[3];
|
||||
snprintf(d, sizeof(d), "%02X", FriendRequests.request[i].key[j] & 0xff);
|
||||
strcat(id, d);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FriendRequests.request[i].msg);
|
||||
|
||||
if (++count < FriendRequests.num_requests)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
bool have_note = false;
|
||||
@ -414,7 +473,6 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
int len = strlen(msg) - 1;
|
||||
msg[len] = '\0';
|
||||
|
||||
tox_set_status_message(m, (uint8_t *) msg, (uint16_t) len);
|
||||
prompt_update_statusmessage(prompt, msg);
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
|
||||
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_decline(WINDOW *window, ToxWindow *self, Tox *m, 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]);
|
||||
@ -37,6 +38,7 @@ 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_requests(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_status(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, char *id_bin, char *msg);
|
||||
|
@ -97,7 +97,6 @@ void kill_groupchat_window(ToxWindow *self)
|
||||
delwin(ctx->history);
|
||||
delwin(ctx->sidebar);
|
||||
free(ctx->log);
|
||||
free(ctx->hst);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
del_window(self);
|
||||
@ -198,7 +197,7 @@ static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int p
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, ACTION, 0, 0, "%s", action);
|
||||
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
|
||||
write_to_log(action, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
|
20
src/help.c
20
src/help.c
@ -77,26 +77,26 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " G");
|
||||
wprintw(win, " g");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "lobal commands\n");
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " C");
|
||||
wprintw(win, " c");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "hat commands\n");
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " F");
|
||||
wprintw(win, " f");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "riendlist controls\n");
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " K");
|
||||
wprintw(win, " k");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "ey bindings\n");
|
||||
|
||||
wprintw(win, " E");
|
||||
wprintw(win, " e");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "x");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
@ -115,11 +115,11 @@ static void help_draw_bottom_menu(WINDOW *win)
|
||||
wmove(win, y2 - 2, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " M");
|
||||
wprintw(win, " m");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "ain menu |");
|
||||
|
||||
wprintw(win, " E");
|
||||
wprintw(win, " e");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "x");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
@ -138,6 +138,8 @@ static void help_draw_global(ToxWindow *self)
|
||||
|
||||
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||
wprintw(win, " /decline <id> : Decline friend request\n");
|
||||
wprintw(win, " /requests : List pending friend requests\n");
|
||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
||||
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||
@ -264,9 +266,9 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
|
||||
case 'g':
|
||||
#ifdef _AUDIO
|
||||
help_init_window(self, 21, 80);
|
||||
help_init_window(self, 23, 80);
|
||||
#else
|
||||
help_init_window(self, 17, 80);
|
||||
help_init_window(self, 19, 80);
|
||||
#endif
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
@ -31,6 +31,8 @@
|
||||
#include "groupchat.h"
|
||||
#include "settings.h"
|
||||
#include "notify.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
|
||||
@ -89,6 +91,8 @@ void line_info_cleanup(struct history *hst)
|
||||
if (hst->queue[i])
|
||||
free(hst->queue[i]);
|
||||
}
|
||||
|
||||
free(hst);
|
||||
}
|
||||
|
||||
/* moves root forward and frees previous root */
|
||||
@ -107,15 +111,6 @@ static void line_info_root_fwd(struct history *hst)
|
||||
hst->line_root = tmp;
|
||||
}
|
||||
|
||||
/* adds a line_info line to queue */
|
||||
static void line_info_add_queue(struct history *hst, struct line_info *line)
|
||||
{
|
||||
if (hst->queue_sz >= MAX_QUEUE)
|
||||
return;
|
||||
|
||||
hst->queue[hst->queue_sz++] = line;
|
||||
}
|
||||
|
||||
/* returns ptr to queue item 0 and removes it from queue */
|
||||
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
{
|
||||
@ -134,12 +129,15 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
|
||||
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
|
||||
uint8_t colour, const char *msg, ...)
|
||||
{
|
||||
struct history *hst = self->chatwin->hst;
|
||||
|
||||
if (hst->queue_sz >= MAX_QUEUE)
|
||||
return;
|
||||
|
||||
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
||||
|
||||
if (new_line == NULL)
|
||||
@ -156,7 +154,16 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
|
||||
|
||||
/* for type-specific formatting in print function */
|
||||
switch (type) {
|
||||
case ACTION:
|
||||
case IN_ACTION:
|
||||
case OUT_ACTION:
|
||||
len += 3;
|
||||
break;
|
||||
|
||||
case IN_MSG:
|
||||
case OUT_MSG:
|
||||
len += 2;
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
len += 3;
|
||||
break;
|
||||
@ -185,9 +192,9 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
|
||||
}
|
||||
}
|
||||
|
||||
if (tmstmp) {
|
||||
snprintf(new_line->timestamp, sizeof(new_line->timestamp), "%s", tmstmp);
|
||||
len += strlen(new_line->timestamp);
|
||||
if (timestr) {
|
||||
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
|
||||
len += strlen(new_line->timestr);
|
||||
}
|
||||
|
||||
if (name1) {
|
||||
@ -204,8 +211,10 @@ void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint
|
||||
new_line->type = type;
|
||||
new_line->bold = bold;
|
||||
new_line->colour = colour;
|
||||
new_line->noread_flag = false;
|
||||
new_line->timestamp = get_unix_time();
|
||||
|
||||
line_info_add_queue(hst, new_line);
|
||||
hst->queue[hst->queue_sz++] = new_line;
|
||||
}
|
||||
|
||||
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
||||
@ -247,6 +256,8 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
}
|
||||
}
|
||||
|
||||
#define NOREAD_FLAG_TIMEOUT 5 /* seconds before a sent message with no read receipt is flagged as unread */
|
||||
|
||||
void line_info_print(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
@ -280,9 +291,10 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
switch (type) {
|
||||
case OUT_MSG:
|
||||
case OUT_MSG_READ:
|
||||
case IN_MSG:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
int nameclr = GREEN;
|
||||
@ -299,28 +311,54 @@ void line_info_print(ToxWindow *self)
|
||||
if (line->msg[0] == '>')
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
wprintw(win, "%s", line->msg);
|
||||
|
||||
if (line->msg[0] == '>')
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
|
||||
if (type == OUT_MSG && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x", line->msg);
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
break;
|
||||
|
||||
case ACTION:
|
||||
case OUT_ACTION_READ:
|
||||
case OUT_ACTION:
|
||||
case IN_ACTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(YELLOW));
|
||||
wprintw(win, "* %s %s\n", line->name1, line->msg);
|
||||
wprintw(win, "* %s %s", line->name1, line->msg);
|
||||
wattroff(win, COLOR_PAIR(YELLOW));
|
||||
|
||||
if (type == OUT_ACTION && timed_out(line->timestamp, get_unix_time(), NOREAD_FLAG_TIMEOUT)) {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x", line->msg);
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
if (line->timestamp[0]) {
|
||||
if (line->timestr[0]) {
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
}
|
||||
|
||||
@ -353,7 +391,7 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
case CONNECTION:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
@ -367,7 +405,7 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
case NAME_CHANGE:
|
||||
wattron(win, COLOR_PAIR(BLUE));
|
||||
wprintw(win, "%s", line->timestamp);
|
||||
wprintw(win, "%s", line->timestr);
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(MAGENTA));
|
||||
@ -393,6 +431,7 @@ void line_info_print(ToxWindow *self)
|
||||
line_info_print(self);
|
||||
}
|
||||
|
||||
/* puts msg in specified line_info msg buffer */
|
||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||
{
|
||||
struct line_info *line = self->chatwin->hst->line_end;
|
||||
|
@ -34,22 +34,27 @@ enum {
|
||||
SYS_MSG,
|
||||
IN_MSG,
|
||||
OUT_MSG,
|
||||
OUT_MSG_READ, /* for sent messages that have received a read reply. don't set this with line_info_add */
|
||||
IN_ACTION,
|
||||
OUT_ACTION,
|
||||
OUT_ACTION_READ, /* same as OUT_MSG_READ but for actions */
|
||||
PROMPT,
|
||||
ACTION,
|
||||
CONNECTION,
|
||||
NAME_CHANGE,
|
||||
} LINE_TYPE;
|
||||
|
||||
struct line_info {
|
||||
char timestamp[TIME_STR_SIZE];
|
||||
char timestr[TIME_STR_SIZE];
|
||||
char name1[TOXIC_MAX_NAME_LENGTH];
|
||||
char name2[TOXIC_MAX_NAME_LENGTH];
|
||||
char msg[TOX_MAX_MESSAGE_LENGTH];
|
||||
uint64_t timestamp;
|
||||
uint8_t type;
|
||||
uint8_t bold;
|
||||
uint8_t colour;
|
||||
uint8_t noread_flag; /* true if a line should be flagged as unread */
|
||||
uint32_t id;
|
||||
uint16_t len; /* combined len of all strings */
|
||||
uint16_t len; /* combined len of entire line */
|
||||
uint8_t newlines;
|
||||
|
||||
struct line_info *prev;
|
||||
@ -67,9 +72,8 @@ struct history {
|
||||
int queue_sz;
|
||||
};
|
||||
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
SYS_MSG lines may contain an arbitrary number of arguments for string formatting */
|
||||
void line_info_add(ToxWindow *self, char *tmstmp, char *name1, char *name2, uint8_t type, uint8_t bold,
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, char *timestr, char *name1, char *name2, uint8_t type, uint8_t bold,
|
||||
uint8_t colour, const char *msg, ...);
|
||||
|
||||
/* Prints a section of history starting at line_start */
|
||||
|
14
src/log.c
14
src/log.c
@ -42,15 +42,17 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
|
||||
if (!valid_nick(name))
|
||||
name = UNKNOWN_NAME;
|
||||
|
||||
const char *set_path = user_settings_->chatlogs_path;
|
||||
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int path_len = strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
|
||||
int path_len = strlen(set_path) + strlen(name) ? *set_path
|
||||
: strlen(user_config_dir) + strlen(LOGDIR) + strlen(name);
|
||||
|
||||
/* use first 4 digits of key as log ident. If no key use a timestamp */
|
||||
char ident[32];
|
||||
|
||||
if (key != NULL) {
|
||||
path_len += (KEY_IDENT_DIGITS * 2 + 5);
|
||||
|
||||
sprintf(&ident[0], "%02X", key[0] & 0xff);
|
||||
sprintf(&ident[2], "%02X", key[1] & 0xff);
|
||||
ident[KEY_IDENT_DIGITS * 2 + 1] = '\0';
|
||||
@ -66,7 +68,11 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
|
||||
}
|
||||
|
||||
char log_path[MAX_STR_SIZE];
|
||||
snprintf(log_path, MAX_STR_SIZE, "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
|
||||
|
||||
if (*set_path)
|
||||
snprintf(log_path, sizeof(log_path), "%s%s-%s.log", set_path, name, ident);
|
||||
else
|
||||
snprintf(log_path, sizeof(log_path), "%s%s%s-%s.log", user_config_dir, LOGDIR, name, ident);
|
||||
|
||||
free(user_config_dir);
|
||||
|
||||
@ -80,6 +86,8 @@ void init_logging_session(char *name, const char *key, struct chatlog *log)
|
||||
fprintf(log->file, "\n*** NEW SESSION ***\n\n");
|
||||
}
|
||||
|
||||
#define LOG_FLUSH_LIMIT 1 /* limits calls to fflush to a max of one per LOG_FLUSH_LIMIT seconds */
|
||||
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
||||
{
|
||||
if (!log->log_on)
|
||||
|
@ -23,12 +23,9 @@
|
||||
#ifndef _log_h
|
||||
#define _log_h
|
||||
|
||||
#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 */
|
||||
};
|
||||
|
||||
|
156
src/message_queue.c
Normal file
156
src/message_queue.c
Normal file
@ -0,0 +1,156 @@
|
||||
/* message_queue.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
|
||||
void cqueue_cleanup(struct chat_queue *q)
|
||||
{
|
||||
struct cqueue_msg *tmp1 = q->root;
|
||||
|
||||
while (tmp1) {
|
||||
struct cqueue_msg *tmp2 = tmp1->next;
|
||||
free(tmp1);
|
||||
tmp1 = tmp2;
|
||||
}
|
||||
|
||||
free(q);
|
||||
}
|
||||
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id)
|
||||
{
|
||||
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
|
||||
|
||||
if (new_m == NULL)
|
||||
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
|
||||
|
||||
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
|
||||
new_m->len = len;
|
||||
new_m->type = type;
|
||||
new_m->line_id = line_id;
|
||||
new_m->last_send_try = 0;
|
||||
new_m->receipt = 0;
|
||||
new_m->next = NULL;
|
||||
|
||||
if (q->root == NULL) {
|
||||
new_m->prev = NULL;
|
||||
q->root = new_m;
|
||||
} else {
|
||||
new_m->prev = q->end;
|
||||
q->end->next = new_m;
|
||||
}
|
||||
|
||||
q->end = new_m;
|
||||
}
|
||||
|
||||
/* update line to show receipt was received after queue removal */
|
||||
static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
||||
{
|
||||
struct line_info *line = self->chatwin->hst->line_end;
|
||||
|
||||
while (line) {
|
||||
if (line->id != msg->line_id) {
|
||||
line = line->prev;
|
||||
continue;
|
||||
}
|
||||
|
||||
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||
|
||||
if (line->noread_flag == true) {
|
||||
line->len -= 2;
|
||||
line->noread_flag = false;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
|
||||
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
|
||||
{
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
|
||||
while (msg) {
|
||||
if (msg->receipt != receipt) {
|
||||
msg = msg->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
uint16_t len = tox_get_self_name(m, (uint8_t *) selfname);
|
||||
selfname[len] = '\0';
|
||||
|
||||
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
|
||||
cqueue_mark_read(self, msg);
|
||||
|
||||
struct cqueue_msg *next = msg->next;
|
||||
|
||||
if (msg->prev == NULL) { /* root */
|
||||
if (next)
|
||||
next->prev = NULL;
|
||||
|
||||
free(msg);
|
||||
q->root = next;
|
||||
} else {
|
||||
struct cqueue_msg *prev = msg->prev;
|
||||
free(msg);
|
||||
prev->next = next;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define CQUEUE_TRY_SEND_INTERVAL 10
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
void cqueue_try_send(ToxWindow *self, Tox *m)
|
||||
{
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
uint64_t curtime = get_unix_time();
|
||||
|
||||
while (msg) {
|
||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, curtime, CQUEUE_TRY_SEND_INTERVAL)) {
|
||||
msg = msg->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t receipt = 0;
|
||||
|
||||
if (msg->type == OUT_MSG)
|
||||
receipt = tox_send_message(m, self->num, (uint8_t *) msg->message, msg->len);
|
||||
else
|
||||
receipt = tox_send_action(m, self->num, (uint8_t *) msg->message, msg->len);
|
||||
|
||||
msg->last_send_try = curtime;
|
||||
msg->receipt = receipt;
|
||||
return;
|
||||
}
|
||||
}
|
46
src/message_queue.h
Normal file
46
src/message_queue.h
Normal file
@ -0,0 +1,46 @@
|
||||
/* message_queue.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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
struct cqueue_msg {
|
||||
char message[MAX_STR_SIZE];
|
||||
int len;
|
||||
int line_id;
|
||||
uint8_t type;
|
||||
uint32_t receipt;
|
||||
uint64_t last_send_try;
|
||||
struct cqueue_msg *next;
|
||||
struct cqueue_msg *prev;
|
||||
};
|
||||
|
||||
struct chat_queue {
|
||||
struct cqueue_msg *root;
|
||||
struct cqueue_msg *end;
|
||||
};
|
||||
|
||||
void cqueue_cleanup(struct chat_queue *q);
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, int len, uint8_t type, uint32_t line_id);
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
void cqueue_try_send(ToxWindow *self, Tox *m);
|
||||
|
||||
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
|
||||
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
|
@ -26,18 +26,20 @@
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "file_senders.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings_;
|
||||
|
||||
static uint64_t current_unix_time;
|
||||
|
||||
void host_to_net(uint8_t *num, uint16_t numbytes)
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||
{
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
uint32_t i;
|
||||
@ -123,6 +125,25 @@ char *hex_string_to_bin(const char *hex_string)
|
||||
return val;
|
||||
}
|
||||
|
||||
int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||
{
|
||||
if (size % 2 != 0)
|
||||
return -1;
|
||||
|
||||
int i, res;
|
||||
const char *pos = keystr;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
res = sscanf(pos, "%2hhx", &buf[i]);
|
||||
pos += 2;
|
||||
|
||||
if (res == EOF || res < 1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(const char *string)
|
||||
{
|
||||
@ -261,3 +282,32 @@ int char_rfind(const char *s, char ch, int len)
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/* Converts bytes to appropriate unit and puts in buf as a string */
|
||||
void bytes_convert_str(char *buf, int size, uint64_t bytes)
|
||||
{
|
||||
double conv = bytes;
|
||||
const char *unit;
|
||||
|
||||
if (conv < KiB) {
|
||||
unit = "Bytes";
|
||||
} else if (conv < MiB) {
|
||||
unit = "KiB";
|
||||
conv /= (double) KiB;
|
||||
} else if (conv < GiB) {
|
||||
unit = "MiB";
|
||||
conv /= (double) MiB;
|
||||
} else {
|
||||
unit = "GiB";
|
||||
conv /= (double) GiB;
|
||||
}
|
||||
|
||||
snprintf(buf, size, "%.1f %s", conv, unit);
|
||||
}
|
||||
|
||||
/* checks if a file exists. Returns true or false */
|
||||
bool file_exists(const char *path)
|
||||
{
|
||||
struct stat s;
|
||||
return stat(path, &s) == 0;
|
||||
}
|
||||
|
@ -34,18 +34,21 @@
|
||||
#endif
|
||||
|
||||
#ifndef net_to_host
|
||||
#define net_to_host(x, y) host_to_net(x, y)
|
||||
#define net_to_host(x, y) hst_to_net(x, y)
|
||||
#endif
|
||||
|
||||
void host_to_net(uint8_t *num, uint16_t numbytes);
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes);
|
||||
|
||||
/* convert a hex string to binary */
|
||||
char *hex_string_to_bin(const char *hex_string);
|
||||
|
||||
/* convert a hex string to bytes. returns 0 on success, -1 on failure */
|
||||
int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||
|
||||
/* get the current unix time */
|
||||
uint64_t get_unix_time(void);
|
||||
|
||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
/* Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
void get_time_str(char *buf, int bufsize);
|
||||
|
||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||
@ -103,4 +106,10 @@ int char_find(int idx, const char *s, char ch);
|
||||
returns 0 if char not found */
|
||||
int char_rfind(const char *s, char ch, int len);
|
||||
|
||||
/* Converts bytes to appropriate unit and puts in buf as a string */
|
||||
void bytes_convert_str(char *buf, int size, uint64_t bytes);
|
||||
|
||||
/* checks if a file exists. Returns true or false */
|
||||
bool file_exists(const char *path);
|
||||
|
||||
#endif /* #define _misc_tools_h */
|
||||
|
227
src/notify.c
227
src/notify.c
@ -24,10 +24,12 @@
|
||||
#include "device.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
@ -59,6 +61,7 @@
|
||||
#include <libnotify/notify.h>
|
||||
#endif
|
||||
|
||||
#define MAX_BOX_MSG_LEN 127
|
||||
#define SOUNDS_SIZE 10
|
||||
#define ACTIVE_NOTIFS_MAX 50
|
||||
|
||||
@ -67,14 +70,14 @@ extern struct user_settings *user_settings_;
|
||||
struct _Control {
|
||||
time_t cooldown;
|
||||
time_t notif_timeout;
|
||||
unsigned long this_window;
|
||||
#ifdef _X11
|
||||
Display *display;
|
||||
unsigned long this_window;
|
||||
#endif /* _X11 */
|
||||
|
||||
#if defined(_SOUND_NOTIFY) || defined(_BOX_NOTIFY)
|
||||
pthread_mutex_t poll_mutex[1];
|
||||
_Bool poll_active;
|
||||
bool poll_active;
|
||||
#endif
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
@ -87,13 +90,13 @@ struct _ActiveNotifications {
|
||||
#ifdef _SOUND_NOTIFY
|
||||
uint32_t source;
|
||||
uint32_t buffer;
|
||||
_Bool looping;
|
||||
bool looping;
|
||||
#endif
|
||||
_Bool active;
|
||||
bool active;
|
||||
int *id_indicator;
|
||||
#ifdef _BOX_NOTIFY
|
||||
NotifyNotification* box;
|
||||
char messages[128][128];
|
||||
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
|
||||
char title[24];
|
||||
size_t size;
|
||||
time_t n_timeout;
|
||||
@ -105,18 +108,40 @@ struct _ActiveNotifications {
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
|
||||
/* coloured tab notifications: primary notification type */
|
||||
static void tab_notify(ToxWindow *self, uint64_t flags)
|
||||
{
|
||||
if (self == NULL)
|
||||
return;
|
||||
|
||||
if (flags & NT_WNDALERT_0)
|
||||
self->alert = WINDOW_ALERT_0;
|
||||
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) )
|
||||
self->alert = WINDOW_ALERT_1;
|
||||
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) )
|
||||
self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
#ifdef _X11
|
||||
long unsigned int get_focused_window_id()
|
||||
{
|
||||
#ifdef _X11
|
||||
if (!Control.display) return 0;
|
||||
|
||||
Window focus;
|
||||
int revert;
|
||||
XGetInputFocus(Control.display, &focus, &revert);
|
||||
return focus;
|
||||
#else
|
||||
return 0;
|
||||
}
|
||||
#endif /* _X11 */
|
||||
|
||||
static bool notifications_are_disabled(uint64_t flags)
|
||||
{
|
||||
bool res = flags & NT_RESTOL && Control.cooldown > get_unix_time();
|
||||
#ifdef _X11
|
||||
return res || (flags & NT_NOFOCUS && Control.this_window == get_focused_window_id());
|
||||
#else
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void control_lock()
|
||||
@ -134,14 +159,14 @@ static void control_unlock()
|
||||
}
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
_Bool is_playing(int source)
|
||||
bool is_playing(int source)
|
||||
{
|
||||
int ready;
|
||||
alGetSourcei(source, AL_SOURCE_STATE, &ready);
|
||||
return ready == AL_PLAYING;
|
||||
}
|
||||
|
||||
/* Terminate all sounds but wait them to finish first */
|
||||
/* Terminate all sounds but wait for them to finish first */
|
||||
void graceful_clear()
|
||||
{
|
||||
int i;
|
||||
@ -167,7 +192,6 @@ void graceful_clear()
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (i == ACTIVE_NOTIFS_MAX) {
|
||||
@ -224,7 +248,7 @@ void* do_playing(void* _p)
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
int play_source(uint32_t source, uint32_t buffer, _Bool looping)
|
||||
int play_source(uint32_t source, uint32_t buffer, bool looping)
|
||||
{
|
||||
int i = 0;
|
||||
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
|
||||
@ -317,8 +341,6 @@ int init_notify(int login_cooldown, int notification_timeout)
|
||||
#ifdef _X11
|
||||
Control.display = XOpenDisplay(NULL);
|
||||
Control.this_window = get_focused_window_id();
|
||||
#else
|
||||
Control.this_window = 1;
|
||||
#endif /* _X11 */
|
||||
|
||||
|
||||
@ -365,7 +387,7 @@ int set_sound(Notification sound, const char* value)
|
||||
return stat(value, &buf) == 0;
|
||||
}
|
||||
|
||||
int play_sound_internal(Notification what, _Bool loop)
|
||||
int play_sound_internal(Notification what, bool loop)
|
||||
{
|
||||
uint32_t source;
|
||||
uint32_t buffer;
|
||||
@ -442,15 +464,9 @@ void m_notify_action(NotifyNotification *box, char *action, void* data)
|
||||
|
||||
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
|
||||
{
|
||||
/* Consider colored notify as primary */
|
||||
if (self && self->alert == WINDOW_ALERT_NONE) {
|
||||
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
|
||||
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
|
||||
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
int id = -1;
|
||||
@ -486,15 +502,9 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
|
||||
|
||||
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
|
||||
{
|
||||
/* Consider colored notify as primary */
|
||||
if (self && self->alert == WINDOW_ALERT_NONE) {
|
||||
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
|
||||
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
|
||||
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
tab_notify(self, flags);
|
||||
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
|
||||
@ -532,41 +542,42 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
|
||||
|
||||
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, char* title, const char* format, ...)
|
||||
{
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
if (notifications_are_disabled(flags)) {
|
||||
tab_notify(self, flags);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
#ifdef _BOX_NOTIFY
|
||||
|
||||
|
||||
int id = sound_notify(self, notif, flags, id_indicator);
|
||||
|
||||
|
||||
control_lock();
|
||||
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
if (id == -1) { /* Could not play */
|
||||
|
||||
|
||||
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
|
||||
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||
control_unlock();
|
||||
return -1; /* Full */
|
||||
}
|
||||
|
||||
|
||||
actives[id].active = 1;
|
||||
actives[id].id_indicator = id_indicator;
|
||||
if (id_indicator) *id_indicator = id;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
strncpy(actives[id].title, title, 24);
|
||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
|
||||
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[0]) > 124)
|
||||
strcpy(actives[id].messages[0] + 124, "...");
|
||||
|
||||
|
||||
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
|
||||
@ -575,7 +586,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
|
||||
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
|
||||
control_unlock();
|
||||
return id;
|
||||
#else
|
||||
@ -585,48 +596,48 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
|
||||
|
||||
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
|
||||
{
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
if (notifications_are_disabled(flags)) {
|
||||
tab_notify(self, flags);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
#ifdef _BOX_NOTIFY
|
||||
|
||||
|
||||
if (sound_notify2(self, notif, flags, id) == -1)
|
||||
return -1;
|
||||
|
||||
|
||||
control_lock();
|
||||
|
||||
if (!actives[id].box || actives[id].size >= 128 ) {
|
||||
|
||||
if (!actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
|
||||
control_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
|
||||
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > 124)
|
||||
strcpy(actives[id].messages[actives[id].size] + 124, "...");
|
||||
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
|
||||
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
|
||||
|
||||
int i = 0;
|
||||
for (; i <actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
}
|
||||
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
|
||||
control_unlock();
|
||||
|
||||
|
||||
return id;
|
||||
#else
|
||||
return sound_notify2(self, notif, flags, id);
|
||||
@ -635,53 +646,47 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
|
||||
|
||||
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
|
||||
{
|
||||
/* Always do colored notify */
|
||||
if (self && self->alert == WINDOW_ALERT_NONE) {
|
||||
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
|
||||
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
|
||||
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
|
||||
#ifdef _BOX_NOTIFY
|
||||
|
||||
|
||||
control_lock();
|
||||
|
||||
|
||||
int id;
|
||||
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
|
||||
if ( id == ACTIVE_NOTIFS_MAX ) {
|
||||
control_unlock();
|
||||
return -1; /* Full */
|
||||
}
|
||||
|
||||
|
||||
if (id_indicator) {
|
||||
actives[id].id_indicator = id_indicator;
|
||||
*id_indicator = id;
|
||||
}
|
||||
|
||||
|
||||
strncpy(actives[id].title, title, 24);
|
||||
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
|
||||
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[0], 127, format, __ARGS__);
|
||||
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[0]) > 124)
|
||||
strcpy(actives[id].messages[0] + 124, "...");
|
||||
|
||||
|
||||
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].active = 1;
|
||||
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
|
||||
|
||||
|
||||
notify_notification_set_timeout(actives[id].box, Control.notif_timeout);
|
||||
notify_notification_set_app_name(actives[id].box, "toxic");
|
||||
/*notify_notification_add_action(actives[id].box, "lel", "default", m_notify_action, self, NULL);*/
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
|
||||
control_unlock();
|
||||
return id;
|
||||
#else
|
||||
@ -691,51 +696,45 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
|
||||
|
||||
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
|
||||
{
|
||||
/* Always do colored notify */
|
||||
if (self && self->alert == WINDOW_ALERT_NONE) {
|
||||
if (flags & NT_WNDALERT_0) self->alert = WINDOW_ALERT_0;
|
||||
else if (flags & NT_WNDALERT_1) self->alert = WINDOW_ALERT_1;
|
||||
else if (flags & NT_WNDALERT_2) self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
if ((flags & NT_RESTOL && Control.cooldown > time(NULL)) ||
|
||||
((flags & NT_NOFOCUS && Control.this_window == get_focused_window_id()) ))
|
||||
tab_notify(self, flags);
|
||||
|
||||
if (notifications_are_disabled(flags))
|
||||
return -1;
|
||||
|
||||
|
||||
#ifdef _BOX_NOTIFY
|
||||
control_lock();
|
||||
|
||||
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= 128 ) {
|
||||
|
||||
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) {
|
||||
control_unlock();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
va_list __ARGS__; va_start (__ARGS__, format);
|
||||
vsnprintf (actives[id].messages[actives[id].size], 127, format, __ARGS__);
|
||||
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
|
||||
va_end (__ARGS__);
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > 124)
|
||||
strcpy(actives[id].messages[actives[id].size] + 124, "...");
|
||||
|
||||
|
||||
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3)
|
||||
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
|
||||
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = time(NULL) + Control.notif_timeout / 1000;
|
||||
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
|
||||
|
||||
int i = 0;
|
||||
for (; i <actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
}
|
||||
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
|
||||
control_unlock();
|
||||
|
||||
|
||||
return id;
|
||||
#else
|
||||
return -1;
|
||||
|
75
src/prompt.c
75
src/prompt.c
@ -42,12 +42,11 @@
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
|
||||
char pending_frnd_requests[MAX_FRIENDS_NUM][TOX_CLIENT_ID_SIZE];
|
||||
uint16_t num_frnd_requests = 0;
|
||||
extern ToxWindow *prompt;
|
||||
struct _Winthread Winthread;
|
||||
|
||||
extern struct user_settings *user_settings_;
|
||||
extern struct _Winthread Winthread;
|
||||
|
||||
_FriendRequests FriendRequests;
|
||||
|
||||
/* Array of global command names used for tab completion. */
|
||||
const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
@ -56,6 +55,7 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/clear" },
|
||||
{ "/close" }, /* rm /close when groupchats gets its own list */
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/groupchat" },
|
||||
{ "/help" },
|
||||
@ -64,6 +64,7 @@ const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
|
||||
#ifdef _AUDIO
|
||||
@ -87,7 +88,6 @@ void kill_prompt_window(ToxWindow *self)
|
||||
delwin(statusbar->topline);
|
||||
|
||||
free(ctx->log);
|
||||
free(ctx->hst);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
free(statusbar);
|
||||
@ -103,12 +103,14 @@ void prompt_update_nick(ToxWindow *prompt, const char *nick)
|
||||
statusbar->nick_len = strlen(statusbar->nick);
|
||||
}
|
||||
|
||||
/* Updates own statusmessage in prompt statusbar */
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, const char *statusmsg)
|
||||
/* Updates own statusmessage */
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
int len = strlen(statusbar->statusmsg);
|
||||
statusbar->statusmsg_len = len;
|
||||
tox_set_status_message(m, (uint8_t *) statusmsg, (uint64_t) len);
|
||||
}
|
||||
|
||||
/* Updates own status in prompt statusbar */
|
||||
@ -126,20 +128,24 @@ void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected)
|
||||
}
|
||||
|
||||
/* Adds friend request to pending friend requests.
|
||||
Returns request number on success, -1 if queue is full or other error. */
|
||||
static int add_friend_request(const char *public_key)
|
||||
Returns request number on success, -1 if queue is full. */
|
||||
static int add_friend_request(const char *public_key, const char *data)
|
||||
{
|
||||
if (num_frnd_requests >= MAX_FRIENDS_NUM)
|
||||
if (FriendRequests.max_idx >= MAX_FRIEND_REQUESTS)
|
||||
return -1;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= num_frnd_requests; ++i) {
|
||||
if (!strlen(pending_frnd_requests[i])) {
|
||||
memcpy(pending_frnd_requests[i], public_key, TOX_CLIENT_ID_SIZE);
|
||||
for (i = 0; i <= FriendRequests.max_idx; ++i) {
|
||||
if (!FriendRequests.request[i].active) {
|
||||
FriendRequests.request[i].active = true;
|
||||
memcpy(FriendRequests.request[i].key, public_key, TOX_CLIENT_ID_SIZE);
|
||||
snprintf(FriendRequests.request[i].msg, sizeof(FriendRequests.request[i].msg), "%s", data);
|
||||
|
||||
if (i == num_frnd_requests)
|
||||
++num_frnd_requests;
|
||||
if (i == FriendRequests.max_idx)
|
||||
++FriendRequests.max_idx;
|
||||
|
||||
++FriendRequests.num_requests;
|
||||
|
||||
return i;
|
||||
}
|
||||
@ -269,6 +275,29 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
}
|
||||
|
||||
/* Reset statusbar->statusmsg on window resize */
|
||||
if (x2 != self->x) {
|
||||
char statusmsg[TOX_MAX_STATUSMESSAGE_LENGTH] = {0};
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
tox_get_self_status_message(m, (uint8_t *) statusmsg, TOX_MAX_STATUSMESSAGE_LENGTH);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
|
||||
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
|
||||
}
|
||||
|
||||
self->x = x2;
|
||||
|
||||
/* Truncate note if it doesn't fit in statusbar */
|
||||
uint16_t maxlen = x2 - getcurx(statusbar->topline) - 3;
|
||||
|
||||
if (statusbar->statusmsg_len > maxlen) {
|
||||
statusbar->statusmsg[maxlen - 3] = '\0';
|
||||
strcat(statusbar->statusmsg, "...");
|
||||
statusbar->statusmsg_len = maxlen;
|
||||
}
|
||||
|
||||
if (statusbar->statusmsg[0])
|
||||
wprintw(statusbar->topline, " - %s", statusbar->statusmsg);
|
||||
|
||||
@ -340,16 +369,15 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
|
||||
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
|
||||
write_to_log("Friend request with the message '%s'", "", ctx->log, true);
|
||||
|
||||
int n = add_friend_request(key);
|
||||
int n = add_friend_request(key, data);
|
||||
|
||||
if (n == -1) {
|
||||
const char *errmsg = "Friend request queue is full. Discarding request.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
write_to_log(errmsg, "", ctx->log, true);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" to accept it.", n);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
|
||||
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
|
||||
}
|
||||
|
||||
@ -379,13 +407,13 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
|
||||
strcpy(ver, TOXICVER);
|
||||
const char *toxic_ver = strtok(ver, "_");
|
||||
|
||||
if ( (!statusmsg[0] || !strncmp("Toxing on Toxic", statusmsg, 15)) && toxic_ver != NULL) {
|
||||
snprintf(statusmsg, MAX_STR_SIZE, "Toxing on Toxic v.%s", toxic_ver);
|
||||
if ( (s_len <= 0 || !strncmp("Toxing on Toxic", statusmsg, strlen("Toxing on Toxic"))) && toxic_ver != NULL) {
|
||||
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic v.%s", toxic_ver);
|
||||
s_len = strlen(statusmsg);
|
||||
statusmsg[s_len] = '\0';
|
||||
}
|
||||
|
||||
prompt_update_statusmessage(prompt, statusmsg);
|
||||
prompt_update_statusmessage(prompt, m, statusmsg);
|
||||
prompt_update_status(prompt, status);
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
@ -436,7 +464,8 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
scrollok(ctx->history, 0);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
|
||||
print_welcome_msg(self);
|
||||
if (user_settings_->show_welcome_msg == SHOW_WELCOME_MSG_ON)
|
||||
print_welcome_msg(self);
|
||||
}
|
||||
|
||||
ToxWindow new_prompt(void)
|
||||
|
20
src/prompt.h
20
src/prompt.h
@ -27,16 +27,30 @@
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 16
|
||||
#define AC_NUM_GLOB_COMMANDS 18
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 14
|
||||
#define AC_NUM_GLOB_COMMANDS 16
|
||||
#endif /* _AUDIO */
|
||||
|
||||
#define MAX_FRIEND_REQUESTS 32
|
||||
|
||||
struct _friend_request {
|
||||
bool active;
|
||||
char msg[MAX_STR_SIZE];
|
||||
uint8_t key[TOX_CLIENT_ID_SIZE];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
int max_idx;
|
||||
int num_requests;
|
||||
struct _friend_request request[MAX_FRIEND_REQUESTS];
|
||||
} _FriendRequests;
|
||||
|
||||
ToxWindow new_prompt(void);
|
||||
void prep_prompt_win(void);
|
||||
void prompt_init_statusbar(ToxWindow *self, Tox *m);
|
||||
void prompt_update_nick(ToxWindow *prompt, const char *nick);
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, const char *statusmsg);
|
||||
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
|
||||
void prompt_update_status(ToxWindow *prompt, uint8_t status);
|
||||
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
|
||||
void kill_prompt_window(ToxWindow *self);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "windows.h"
|
||||
#include "configdir.h"
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#include "device.h"
|
||||
@ -41,7 +42,9 @@
|
||||
#define PACKAGE_DATADIR "."
|
||||
#endif
|
||||
|
||||
const struct _ui_strings {
|
||||
#define NO_SOUND "silent"
|
||||
|
||||
static struct _ui_strings {
|
||||
const char* self;
|
||||
const char* timestamps;
|
||||
const char* alerts;
|
||||
@ -51,6 +54,7 @@ const struct _ui_strings {
|
||||
const char* history_size;
|
||||
const char* show_typing_self;
|
||||
const char* show_typing_other;
|
||||
const char* show_welcome_msg;
|
||||
} ui_strings = {
|
||||
"ui",
|
||||
"timestamps",
|
||||
@ -61,6 +65,7 @@ const struct _ui_strings {
|
||||
"history_size",
|
||||
"show_typing_self",
|
||||
"show_typing_other",
|
||||
"show_welcome_msg",
|
||||
};
|
||||
|
||||
static void ui_defaults(struct user_settings* settings)
|
||||
@ -73,9 +78,10 @@ static void ui_defaults(struct user_settings* settings)
|
||||
settings->history_size = 700;
|
||||
settings->show_typing_self = SHOW_TYPING_ON;
|
||||
settings->show_typing_other = SHOW_TYPING_ON;
|
||||
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
|
||||
}
|
||||
|
||||
const struct _keys_strings {
|
||||
static const struct _keys_strings {
|
||||
const char* self;
|
||||
const char* next_tab;
|
||||
const char* prev_tab;
|
||||
@ -113,21 +119,24 @@ static void key_defaults(struct user_settings* settings)
|
||||
settings->key_peer_list_down = T_KEY_C_RB;
|
||||
}
|
||||
|
||||
const struct _tox_strings {
|
||||
static const struct _tox_strings {
|
||||
const char* self;
|
||||
const char* download_path;
|
||||
const char* chatlogs_path;
|
||||
} tox_strings = {
|
||||
"tox",
|
||||
"download_path",
|
||||
"chatlogs_path",
|
||||
};
|
||||
|
||||
static void tox_defaults(struct user_settings* settings)
|
||||
{
|
||||
strcpy(settings->download_path, ""); /* explicitly set default to pwd */
|
||||
strcpy(settings->download_path, "");
|
||||
strcpy(settings->chatlogs_path, "");
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
const struct _audio_strings {
|
||||
static const struct _audio_strings {
|
||||
const char* self;
|
||||
const char* input_device;
|
||||
const char* output_device;
|
||||
@ -148,7 +157,7 @@ static void audio_defaults(struct user_settings* settings)
|
||||
#endif
|
||||
|
||||
#ifdef _SOUND_NOTIFY
|
||||
const struct _sound_strings {
|
||||
static const struct _sound_strings {
|
||||
const char* self;
|
||||
const char* error;
|
||||
const char* self_log_in;
|
||||
@ -196,12 +205,13 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
{
|
||||
config_t cfg[1];
|
||||
config_setting_t *setting;
|
||||
const char *str;
|
||||
const char *str = NULL;
|
||||
|
||||
/* Load default settings */
|
||||
ui_defaults(s);
|
||||
tox_defaults(s);
|
||||
key_defaults(s);
|
||||
|
||||
#ifdef _AUDIO
|
||||
audio_defaults(s);
|
||||
#endif
|
||||
@ -217,13 +227,14 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
free(user_config_dir);
|
||||
|
||||
/* make sure path exists or is created on first time running */
|
||||
FILE *fp = fopen(path, "r");
|
||||
if (fp == NULL) {
|
||||
if ((fp = fopen(path, "w")) == NULL)
|
||||
return -1;
|
||||
}
|
||||
if (!file_exists(path)) {
|
||||
FILE *fp = fopen(path, "w");
|
||||
|
||||
fclose(fp);
|
||||
if (fp == NULL)
|
||||
return -1;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
} else {
|
||||
snprintf(path, sizeof(path), "%s", patharg);
|
||||
}
|
||||
@ -242,28 +253,47 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_typing_self, &s->show_typing_self);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_typing_other, &s->show_typing_other);
|
||||
config_setting_lookup_bool(setting, ui_strings.show_welcome_msg, &s->show_welcome_msg);
|
||||
config_setting_lookup_int(setting, ui_strings.time_format, &s->time);
|
||||
s->time = s->time == TIME_24 || s->time == TIME_12 ? s->time : TIME_24; /* Check defaults */
|
||||
}
|
||||
|
||||
/* paths */
|
||||
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
|
||||
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) {
|
||||
strcpy(s->download_path, str);
|
||||
snprintf(s->download_path, sizeof(s->download_path), "%s", str);
|
||||
int len = strlen(s->download_path);
|
||||
|
||||
/* make sure path ends with a '/' */
|
||||
if (len >= sizeof(s->download_path) - 2)
|
||||
s->download_path[0] = '\0';
|
||||
else if (s->download_path[len - 1] != '/')
|
||||
strcat(&s->download_path[len - 1], "/");
|
||||
}
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) {
|
||||
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
|
||||
int len = strlen(s->chatlogs_path);
|
||||
|
||||
if (len >= sizeof(s->chatlogs_path) - 2)
|
||||
s->chatlogs_path[0] = '\0';
|
||||
else if (s->chatlogs_path[len - 1] != '/')
|
||||
strcat(&s->chatlogs_path[len - 1], "/");
|
||||
}
|
||||
}
|
||||
|
||||
/* keys */
|
||||
if((setting = config_lookup(cfg, key_strings.self)) != NULL) {
|
||||
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
|
||||
const char* tmp = NULL;
|
||||
if(config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) s->key_next_tab = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) s->key_prev_tab = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) s->key_scroll_line_up = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) s->key_scroll_line_down= key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) s->key_half_page_up = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) s->key_half_page_down = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) s->key_page_bottom = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) s->key_peer_list_up = key_parse(&tmp);
|
||||
if(config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) s->key_peer_list_down = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) s->key_next_tab = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) s->key_prev_tab = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) s->key_scroll_line_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) s->key_scroll_line_down= key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) s->key_half_page_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) s->key_half_page_down = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) s->key_page_bottom = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) s->key_peer_list_up = key_parse(&tmp);
|
||||
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp)) s->key_peer_list_down = key_parse(&tmp);
|
||||
}
|
||||
|
||||
#ifdef _AUDIO
|
||||
@ -282,61 +312,61 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
|
||||
if ( (config_setting_lookup_string(setting, sound_strings.error, &str) != CONFIG_TRUE) ||
|
||||
!set_sound(error, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(error, PACKAGE_DATADIR "/sounds/Error.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
|
||||
!set_sound(user_log_in, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ContactLogsIn.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.self_log_in, &str) ||
|
||||
!set_sound(self_log_in, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(self_log_in, PACKAGE_DATADIR "/sounds/LogIn.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
|
||||
!set_sound(user_log_out, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ContactLogsOut.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.self_log_out, &str) ||
|
||||
!set_sound(self_log_out, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(self_log_out, PACKAGE_DATADIR "/sounds/LogOut.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
|
||||
!set_sound(call_incoming, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/IncomingCall.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
|
||||
!set_sound(call_outgoing, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/OutgoingCall.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
|
||||
!set_sound(generic_message, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(generic_message, PACKAGE_DATADIR "/sounds/NewMessage.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
|
||||
!set_sound(transfer_pending, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/TransferPending.wav");
|
||||
}
|
||||
|
||||
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
|
||||
!set_sound(transfer_completed, str) ) {
|
||||
if (strcasecmp(str, NO_SOUND) != 0)
|
||||
if (str && strcasecmp(str, NO_SOUND) != 0)
|
||||
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/TransferComplete.wav");
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@
|
||||
#ifndef _settings_h
|
||||
#define _settings_h
|
||||
|
||||
#define NO_SOUND "silent"
|
||||
#include <limits.h>
|
||||
|
||||
/* holds user setting values */
|
||||
struct user_settings {
|
||||
@ -35,8 +35,10 @@ struct user_settings {
|
||||
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
|
||||
int show_typing_self; /* boolean */
|
||||
int show_typing_other; /* boolean */
|
||||
int show_welcome_msg; /* boolean */
|
||||
|
||||
char download_path[MAX_STR_SIZE];
|
||||
char download_path[PATH_MAX];
|
||||
char chatlogs_path[PATH_MAX];
|
||||
|
||||
int key_next_tab; /* character code */
|
||||
int key_prev_tab; /* character code */
|
||||
@ -73,6 +75,9 @@ enum {
|
||||
SHOW_TYPING_OFF = 0,
|
||||
SHOW_TYPING_ON = 1,
|
||||
|
||||
SHOW_WELCOME_MSG_OFF = 0,
|
||||
SHOW_WELCOME_MSG_ON = 1,
|
||||
|
||||
DFLT_HST_SIZE = 700,
|
||||
} settings_values;
|
||||
|
||||
|
540
src/toxic.c
540
src/toxic.c
@ -37,8 +37,11 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "toxic.h"
|
||||
@ -52,6 +55,7 @@
|
||||
#include "log.h"
|
||||
#include "notify.h"
|
||||
#include "device.h"
|
||||
#include "message_queue.h"
|
||||
|
||||
#ifdef _AUDIO
|
||||
#include "audio_call.h"
|
||||
@ -73,9 +77,19 @@ ToxWindow *prompt = NULL;
|
||||
#define AUTOSAVE_FREQ 60
|
||||
|
||||
struct _Winthread Winthread;
|
||||
struct _cqueue_thread cqueue_thread;
|
||||
struct arg_opts arg_opts;
|
||||
struct user_settings *user_settings_ = NULL;
|
||||
|
||||
#define MIN_PASSWORD_LEN 6
|
||||
#define MAX_PASSWORD_LEN 64
|
||||
|
||||
static struct _user_password {
|
||||
bool data_is_encrypted;
|
||||
char pass[MAX_PASSWORD_LEN + 1];
|
||||
int len;
|
||||
} user_password;
|
||||
|
||||
static void catch_SIGINT(int sig)
|
||||
{
|
||||
Winthread.sig_exit_toxic = true;
|
||||
@ -104,8 +118,9 @@ static void init_signal_catchers(void)
|
||||
void exit_toxic_success(Tox *m)
|
||||
{
|
||||
store_data(m, DATA_FILE);
|
||||
memset(&user_password, 0, sizeof(struct _user_password));
|
||||
close_all_file_senders(m);
|
||||
kill_all_windows();
|
||||
kill_all_windows(m);
|
||||
|
||||
free(DATA_FILE);
|
||||
free(BLOCK_FILE);
|
||||
@ -175,23 +190,100 @@ static void init_term(void)
|
||||
refresh();
|
||||
}
|
||||
|
||||
static Tox *init_tox(int ipv4)
|
||||
{
|
||||
/* Init core */
|
||||
int ipv6 = !ipv4;
|
||||
Tox *m = tox_new(ipv6);
|
||||
static struct _init_messages {
|
||||
char **msgs;
|
||||
int num;
|
||||
} init_messages;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
/* One-time queue for messages created during init. Do not use after program init. */
|
||||
static void queue_init_message(const char *msg, ...)
|
||||
{
|
||||
char frmt_msg[MAX_STR_SIZE] = {0};
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
vsnprintf(frmt_msg, sizeof(frmt_msg), msg, args);
|
||||
va_end(args);
|
||||
|
||||
int i = init_messages.num;
|
||||
++init_messages.num;
|
||||
|
||||
char **new_msgs = realloc(init_messages.msgs, sizeof(char *) * init_messages.num);
|
||||
|
||||
if (new_msgs == NULL)
|
||||
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
|
||||
|
||||
new_msgs[i] = malloc(MAX_STR_SIZE);
|
||||
|
||||
if (new_msgs[i] == NULL)
|
||||
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
|
||||
|
||||
snprintf(new_msgs[i], MAX_STR_SIZE, "%s", frmt_msg);
|
||||
init_messages.msgs = new_msgs;
|
||||
}
|
||||
|
||||
/* called after messages have been printed to console and are no longer needed */
|
||||
static void cleanup_init_messages(void)
|
||||
{
|
||||
if (init_messages.num <= 0)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < init_messages.num; ++i)
|
||||
free(init_messages.msgs[i]);
|
||||
|
||||
free(init_messages.msgs);
|
||||
}
|
||||
|
||||
static void print_init_messages(ToxWindow *toxwin)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < init_messages.num; ++i)
|
||||
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
|
||||
}
|
||||
|
||||
static Tox *init_tox(void)
|
||||
{
|
||||
Tox_Options tox_opts;
|
||||
tox_opts.ipv6enabled = !arg_opts.use_ipv4;
|
||||
tox_opts.udp_disabled = arg_opts.force_tcp;
|
||||
tox_opts.proxy_enabled = arg_opts.use_proxy;
|
||||
|
||||
if (tox_opts.proxy_enabled) {
|
||||
tox_opts.proxy_port = arg_opts.proxy_port;
|
||||
snprintf(tox_opts.proxy_address, sizeof(tox_opts.proxy_address), "%s", arg_opts.proxy_address);
|
||||
|
||||
char tmp[48];
|
||||
snprintf(tmp, sizeof(tmp), "Using proxy %s : %d",
|
||||
arg_opts.proxy_address, arg_opts.proxy_port);
|
||||
queue_init_message("%s", tmp);
|
||||
}
|
||||
|
||||
if (ipv4)
|
||||
fprintf(stderr, "Forcing IPv4 connection\n");
|
||||
if (tox_opts.udp_disabled) {
|
||||
queue_init_message("UDP disabled");
|
||||
} else if (tox_opts.proxy_enabled) {
|
||||
const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address.";
|
||||
queue_init_message("%s", msg);
|
||||
msg = "Use the -t option to disable UDP.";
|
||||
queue_init_message("%s", msg);
|
||||
}
|
||||
|
||||
/* Init core */
|
||||
Tox *m = tox_new(&tox_opts);
|
||||
|
||||
if (tox_opts.ipv6enabled && m == NULL) {
|
||||
queue_init_message("IPv6 failed to initialize");
|
||||
tox_opts.ipv6enabled = 0;
|
||||
m = tox_new(&tox_opts);
|
||||
}
|
||||
|
||||
if (!tox_opts.ipv6enabled)
|
||||
queue_init_message("Forcing IPv4 connection");
|
||||
|
||||
if (tox_opts.proxy_enabled && m == NULL)
|
||||
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||
|
||||
if (m == NULL)
|
||||
return NULL;
|
||||
@ -212,6 +304,7 @@ static Tox *init_tox(int ipv4)
|
||||
tox_callback_file_send_request(m, on_file_sendrequest, NULL);
|
||||
tox_callback_file_control(m, on_file_control, NULL);
|
||||
tox_callback_file_data(m, on_file_data, NULL);
|
||||
tox_callback_read_receipt(m, on_read_receipt, NULL);
|
||||
|
||||
#ifdef __linux__
|
||||
tox_set_name(m, (uint8_t *) "Cool dude", strlen("Cool dude"));
|
||||
@ -226,10 +319,10 @@ static Tox *init_tox(int ipv4)
|
||||
return m;
|
||||
}
|
||||
|
||||
#define MINLINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
||||
#define MAXLINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||
#define MIN_NODE_LINE 50 /* IP: 7 + port: 5 + key: 38 + spaces: 2 = 70. ! (& e.g. tox.im = 6) */
|
||||
#define MAX_NODE_LINE 256 /* Approx max number of chars in a sever line (name + port + key) */
|
||||
#define MAXNODES 50
|
||||
#define NODELEN (MAXLINE - TOX_CLIENT_ID_SIZE - 7)
|
||||
#define NODELEN (MAX_NODE_LINE - TOX_CLIENT_ID_SIZE - 7)
|
||||
|
||||
static struct _toxNodes {
|
||||
int lines;
|
||||
@ -238,7 +331,7 @@ static struct _toxNodes {
|
||||
char keys[MAXNODES][TOX_CLIENT_ID_SIZE];
|
||||
} toxNodes;
|
||||
|
||||
static int nodelist_load(const char *filename)
|
||||
static int load_nodelist(const char *filename)
|
||||
{
|
||||
if (!filename)
|
||||
return 1;
|
||||
@ -248,10 +341,10 @@ static int nodelist_load(const char *filename)
|
||||
if (fp == NULL)
|
||||
return 1;
|
||||
|
||||
char line[MAXLINE];
|
||||
char line[MAX_NODE_LINE];
|
||||
|
||||
while (fgets(line, sizeof(line), fp) && toxNodes.lines < MAXNODES) {
|
||||
if (strlen(line) > MINLINE) {
|
||||
if (strlen(line) > MIN_NODE_LINE) {
|
||||
const char *name = strtok(line, " ");
|
||||
const char *port = strtok(NULL, " ");
|
||||
const char *key_ascii = strtok(NULL, " ");
|
||||
@ -262,7 +355,7 @@ static int nodelist_load(const char *filename)
|
||||
|
||||
snprintf(toxNodes.nodes[toxNodes.lines], sizeof(toxNodes.nodes[toxNodes.lines]), "%s", name);
|
||||
toxNodes.nodes[toxNodes.lines][NODELEN - 1] = 0;
|
||||
toxNodes.ports[toxNodes.lines] = htons(atoi(port));
|
||||
toxNodes.ports[toxNodes.lines] = atoi(port);
|
||||
|
||||
char *key_binary = hex_string_to_bin(key_ascii);
|
||||
memcpy(toxNodes.keys[toxNodes.lines], key_binary, TOX_CLIENT_ID_SIZE);
|
||||
@ -272,19 +365,17 @@ static int nodelist_load(const char *filename)
|
||||
}
|
||||
}
|
||||
|
||||
if (toxNodes.lines < 1) {
|
||||
fclose(fp);
|
||||
return 2;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (toxNodes.lines < 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int init_connection_helper(Tox *m, int line)
|
||||
{
|
||||
return tox_bootstrap_from_address(m, toxNodes.nodes[line], TOX_ENABLE_IPV6_DEFAULT,
|
||||
toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
|
||||
return tox_bootstrap_from_address(m, toxNodes.nodes[line], toxNodes.ports[line], (uint8_t *) toxNodes.keys[line]);
|
||||
}
|
||||
|
||||
/* Connects to a random DHT node listed in the DHTnodes file
|
||||
@ -313,11 +404,11 @@ int init_connection(Tox *m)
|
||||
int res;
|
||||
|
||||
if (!arg_opts.nodes_path[0])
|
||||
res = nodelist_load(PACKAGE_DATADIR "/DHTnodes");
|
||||
res = load_nodelist(PACKAGE_DATADIR "/DHTnodes");
|
||||
else
|
||||
res = nodelist_load(arg_opts.nodes_path);
|
||||
res = load_nodelist(arg_opts.nodes_path);
|
||||
|
||||
if (toxNodes.lines < 1)
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
res = 3;
|
||||
@ -340,6 +431,9 @@ int init_connection(Tox *m)
|
||||
|
||||
static void do_connection(Tox *m, ToxWindow *prompt)
|
||||
{
|
||||
if (arg_opts.no_connect == 1)
|
||||
return;
|
||||
|
||||
char msg[MAX_STR_SIZE] = {0};
|
||||
|
||||
static int conn_err = 0;
|
||||
@ -354,7 +448,7 @@ static void do_connection(Tox *m, ToxWindow *prompt)
|
||||
if (!was_connected && is_connected) {
|
||||
was_connected = true;
|
||||
prompt_update_connectionstatus(prompt, was_connected);
|
||||
snprintf(msg, sizeof(msg), "DHT connected.");
|
||||
snprintf(msg, sizeof(msg), "DHT connected");
|
||||
} else if (was_connected && !is_connected) {
|
||||
was_connected = false;
|
||||
prompt_update_connectionstatus(prompt, was_connected);
|
||||
@ -384,6 +478,104 @@ static void load_friendlist(Tox *m)
|
||||
sort_friendlist_index();
|
||||
}
|
||||
|
||||
/* return length of password on success, 0 on failure */
|
||||
static int password_prompt(char *buf, int size)
|
||||
{
|
||||
buf[0] = '\0';
|
||||
|
||||
/* disable terminal echo */
|
||||
struct termios oflags, nflags;
|
||||
tcgetattr(fileno(stdin), &oflags);
|
||||
nflags = oflags;
|
||||
nflags.c_lflag &= ~ECHO;
|
||||
nflags.c_lflag |= ECHONL;
|
||||
|
||||
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0)
|
||||
return 0;
|
||||
|
||||
const char *p = fgets(buf, size, stdin);
|
||||
int len = strlen(buf);
|
||||
|
||||
/* re-enable terminal echo */
|
||||
tcsetattr(fileno(stdin), TCSANOW, &oflags);
|
||||
|
||||
if (p == NULL || len <= 1)
|
||||
return 0;
|
||||
|
||||
/* eat overflowed stdin and return error */
|
||||
if (buf[--len] != '\n') {
|
||||
int ch;
|
||||
while ((ch = getchar()) != '\n' && ch > 0)
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Ask user if they would like to encrypt the data file and set password */
|
||||
static void first_time_encrypt(const char *msg)
|
||||
{
|
||||
char ch[256] = {0};
|
||||
|
||||
do {
|
||||
system("clear");
|
||||
printf("%s ", msg);
|
||||
|
||||
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|
||||
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
|
||||
break;
|
||||
|
||||
} while (fgets(ch, sizeof(ch), stdin));
|
||||
|
||||
printf("\n");
|
||||
|
||||
if (ch[0] == 'q' || ch[0] == 'Q')
|
||||
exit(0);
|
||||
|
||||
if (ch[0] == 'y' || ch[0] == 'Y') {
|
||||
int len = 0;
|
||||
bool valid_password = false;
|
||||
char passconfirm[MAX_PASSWORD_LEN + 1] = {0};
|
||||
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
|
||||
|
||||
while (valid_password == false) {
|
||||
len = password_prompt(user_password.pass, sizeof(user_password.pass));
|
||||
user_password.len = len;
|
||||
|
||||
if (strcasecmp(user_password.pass, "q") == 0)
|
||||
exit(0);
|
||||
|
||||
if (string_is_empty(passconfirm) && (len < MIN_PASSWORD_LEN || len > MAX_PASSWORD_LEN)) {
|
||||
printf("Password must be between %d and %d characters long. ", MIN_PASSWORD_LEN, MAX_PASSWORD_LEN);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (string_is_empty(passconfirm)) {
|
||||
printf("Enter password again ");
|
||||
snprintf(passconfirm, sizeof(passconfirm), "%s", user_password.pass);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(user_password.pass, passconfirm) != 0) {
|
||||
memset(passconfirm, 0, sizeof(passconfirm));
|
||||
memset(user_password.pass, 0, sizeof(user_password.pass));
|
||||
printf("Passwords don't match. Try again. ");
|
||||
continue;
|
||||
}
|
||||
|
||||
valid_password = true;
|
||||
}
|
||||
|
||||
queue_init_message("Data file '%s' has been encrypted", DATA_FILE);
|
||||
memset(passconfirm, 0, sizeof(passconfirm));
|
||||
user_password.data_is_encrypted = true;
|
||||
}
|
||||
|
||||
system("clear");
|
||||
}
|
||||
|
||||
/*
|
||||
* Store Messenger to given location
|
||||
* Return 0 stored successfully or ignoring data file
|
||||
@ -392,7 +584,7 @@ static void load_friendlist(Tox *m)
|
||||
* Return -3 opening path failed
|
||||
* Return -4 fwrite failed
|
||||
*/
|
||||
int store_data(Tox *m, char *path)
|
||||
int store_data(Tox *m, const char *path)
|
||||
{
|
||||
if (arg_opts.ignore_data_file)
|
||||
return 0;
|
||||
@ -400,13 +592,16 @@ int store_data(Tox *m, char *path)
|
||||
if (path == NULL)
|
||||
return -1;
|
||||
|
||||
int len = tox_size(m);
|
||||
int len = user_password.data_is_encrypted ? tox_encrypted_size(m) : tox_size(m);
|
||||
char *buf = malloc(len);
|
||||
|
||||
if (buf == NULL)
|
||||
return -2;
|
||||
|
||||
tox_save(m, (uint8_t *) buf);
|
||||
if (user_password.data_is_encrypted && !arg_opts.unencrypt_data)
|
||||
tox_encrypted_save(m, (uint8_t *) buf, (uint8_t *) user_password.pass, user_password.len);
|
||||
else
|
||||
tox_save(m, (uint8_t *) buf);
|
||||
|
||||
FILE *fd = fopen(path, "wb");
|
||||
|
||||
@ -451,7 +646,51 @@ static void load_data(Tox *m, char *path)
|
||||
exit_toxic_err("failed in load_data", FATALERR_FREAD);
|
||||
}
|
||||
|
||||
tox_load(m, (uint8_t *) buf, len);
|
||||
bool is_encrypted = tox_is_data_encrypted((uint8_t *) buf);
|
||||
|
||||
/* attempt to encrypt an already encrypted data file */
|
||||
if (arg_opts.encrypt_data && is_encrypted)
|
||||
exit_toxic_err("failed in load_data", FATALERR_ENCRYPT);
|
||||
|
||||
if (arg_opts.unencrypt_data && is_encrypted)
|
||||
queue_init_message("Data file '%s' has been unencrypted", path);
|
||||
else if (arg_opts.unencrypt_data)
|
||||
queue_init_message("Warning: passed --unencrypt-data option with unencrypted data file '%s'", path);
|
||||
|
||||
if (is_encrypted) {
|
||||
if (!arg_opts.unencrypt_data)
|
||||
user_password.data_is_encrypted = true;
|
||||
|
||||
int pwlen = 0;
|
||||
system("clear");
|
||||
printf("Enter password (q to quit) ");
|
||||
|
||||
while (true) {
|
||||
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
|
||||
user_password.len = pwlen;
|
||||
|
||||
if (strcasecmp(user_password.pass, "q") == 0)
|
||||
exit(0);
|
||||
|
||||
if (pwlen < MIN_PASSWORD_LEN) {
|
||||
system("clear");
|
||||
sleep(1);
|
||||
printf("Invalid password. Try again. ");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tox_encrypted_load(m, (uint8_t *) buf, len, (uint8_t *) user_password.pass, pwlen) == 0) {
|
||||
break;
|
||||
} else {
|
||||
system("clear");
|
||||
sleep(1);
|
||||
printf("Invalid password. Try again. ");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tox_load(m, (uint8_t *) buf, len);
|
||||
}
|
||||
|
||||
load_friendlist(m);
|
||||
load_blocklist(BLOCK_FILE);
|
||||
|
||||
@ -468,7 +707,10 @@ static void do_toxic(Tox *m, ToxWindow *prompt)
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
do_connection(m, prompt);
|
||||
do_file_senders(m);
|
||||
tox_do(m); /* main tox-core loop */
|
||||
|
||||
if (arg_opts.no_connect == 0)
|
||||
tox_do(m); /* main tox-core loop */
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
@ -498,24 +740,50 @@ void *thread_winref(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
void *thread_cqueue(void *data)
|
||||
{
|
||||
Tox *m = (Tox *) data;
|
||||
|
||||
while (true) {
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
int i;
|
||||
|
||||
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
ToxWindow *toxwin = get_window_ptr(i);
|
||||
|
||||
if (toxwin != NULL && toxwin->is_chat && tox_get_friend_connection_status(m, toxwin->num) == 1)
|
||||
cqueue_try_send(toxwin, m);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
usleep(7000);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: toxic [OPTION] [FILE ...]\n");
|
||||
fprintf(stderr, " -f, --file Use specified data file\n");
|
||||
fprintf(stderr, " -x, --nodata Ignore data file\n");
|
||||
fprintf(stderr, " -4, --ipv4 Force IPv4 connection\n");
|
||||
fprintf(stderr, " -d, --default_locale Use default locale\n");
|
||||
fprintf(stderr, " -b, --debug Enable stderr for debugging\n");
|
||||
fprintf(stderr, " -c, --config Use specified config file\n");
|
||||
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
|
||||
fprintf(stderr, " -d, --default-locale Use default POSIX locale\n");
|
||||
fprintf(stderr, " -e, --encrypt-data Encrypt an unencrypted data file\n");
|
||||
fprintf(stderr, " -f, --file Use specified data file\n");
|
||||
fprintf(stderr, " -h, --help Show this message and exit\n");
|
||||
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
|
||||
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
|
||||
fprintf(stderr, " -p, --proxy Use proxy: Requires [IP] [port]\n");
|
||||
fprintf(stderr, " -r, --dnslist Use specified DNSservers file\n");
|
||||
fprintf(stderr, " -t, --force-tcp Force TCP connection (use this with proxies)\n");
|
||||
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
|
||||
fprintf(stderr, " -x, --nodata Ignore data file\n");
|
||||
}
|
||||
|
||||
static void set_default_opts(void)
|
||||
{
|
||||
arg_opts.use_ipv4 = 0;
|
||||
arg_opts.ignore_data_file = 0;
|
||||
arg_opts.default_locale = 0;
|
||||
arg_opts.use_custom_data = 0;
|
||||
memset(&arg_opts, 0, sizeof(struct arg_opts));
|
||||
|
||||
/* set any non-zero defaults here*/
|
||||
}
|
||||
|
||||
static void parse_args(int argc, char *argv[])
|
||||
@ -526,17 +794,51 @@ static void parse_args(int argc, char *argv[])
|
||||
{"file", required_argument, 0, 'f'},
|
||||
{"nodata", no_argument, 0, 'x'},
|
||||
{"ipv4", no_argument, 0, '4'},
|
||||
{"default_locale", no_argument, 0, 'd'},
|
||||
{"debug", no_argument, 0, 'b'},
|
||||
{"default-locale", no_argument, 0, 'd'},
|
||||
{"config", required_argument, 0, 'c'},
|
||||
{"encrypt-data", no_argument, 0, 'e'},
|
||||
{"nodes", required_argument, 0, 'n'},
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"noconnect", no_argument, 0, 'o'},
|
||||
{"dnslist", required_argument, 0, 'r'},
|
||||
{"force-tcp", no_argument, 0, 't'},
|
||||
{"proxy", required_argument, 0, 'p'},
|
||||
{"unencrypt-data", no_argument, 0, 'u'},
|
||||
{NULL, no_argument, NULL, 0},
|
||||
};
|
||||
|
||||
const char *opts_str = "4xdf:c:n:h";
|
||||
const char *opts_str = "4bdehotuxc:f:n:r:p:";
|
||||
int opt, indexptr;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, opts_str, long_opts, &indexptr)) != -1) {
|
||||
switch (opt) {
|
||||
case '4':
|
||||
arg_opts.use_ipv4 = 1;
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
arg_opts.debug = 1;
|
||||
queue_init_message("stderr enabled");
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
|
||||
|
||||
if (!file_exists(arg_opts.config_path))
|
||||
queue_init_message("Config file not found");
|
||||
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
arg_opts.default_locale = 1;
|
||||
queue_init_message("Using default POSIX locale");
|
||||
break;
|
||||
|
||||
case 'e':
|
||||
arg_opts.encrypt_data = 1;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
arg_opts.use_custom_data = 1;
|
||||
DATA_FILE = strdup(optarg);
|
||||
@ -547,26 +849,53 @@ static void parse_args(int argc, char *argv[])
|
||||
|
||||
strcpy(BLOCK_FILE, optarg);
|
||||
strcat(BLOCK_FILE, "-blocklist");
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
arg_opts.ignore_data_file = 1;
|
||||
break;
|
||||
queue_init_message("Using '%s' data file", DATA_FILE);
|
||||
|
||||
case '4':
|
||||
arg_opts.use_ipv4 = 1;
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
|
||||
|
||||
if (!file_exists(arg_opts.nodes_path))
|
||||
queue_init_message("DHTnodes file not found");
|
||||
|
||||
break;
|
||||
|
||||
case 'd':
|
||||
arg_opts.default_locale = 1;
|
||||
case 'o':
|
||||
arg_opts.no_connect = 1;
|
||||
queue_init_message("DHT disabled");
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
arg_opts.use_proxy = 1;
|
||||
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
|
||||
|
||||
if (++optind > argc || argv[optind-1][0] == '-')
|
||||
exit_toxic_err("Proxy error", FATALERR_PROXY);
|
||||
|
||||
arg_opts.proxy_port = (uint16_t) atoi(argv[optind-1]);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
snprintf(arg_opts.dns_path, sizeof(arg_opts.dns_path), "%s", optarg);
|
||||
|
||||
if (!file_exists(arg_opts.dns_path))
|
||||
queue_init_message("DNSservers file not found");
|
||||
|
||||
break;
|
||||
|
||||
case 't':
|
||||
arg_opts.force_tcp = 1;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
arg_opts.unencrypt_data = 1;
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
arg_opts.ignore_data_file = 1;
|
||||
queue_init_message("Ignoring data file");
|
||||
break;
|
||||
|
||||
case 'h':
|
||||
@ -579,7 +908,7 @@ static void parse_args(int argc, char *argv[])
|
||||
|
||||
#define DATANAME "data"
|
||||
#define BLOCKNAME "data-blocklist"
|
||||
static int init_data_files(void)
|
||||
static int init_default_data_files(void)
|
||||
{
|
||||
if (arg_opts.use_custom_data)
|
||||
return 0;
|
||||
@ -587,28 +916,26 @@ static int init_data_files(void)
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int config_err = create_user_config_dirs(user_config_dir);
|
||||
|
||||
if (DATA_FILE == NULL ) {
|
||||
if (config_err) {
|
||||
DATA_FILE = strdup(DATANAME);
|
||||
BLOCK_FILE = strdup(BLOCKNAME);
|
||||
if (config_err) {
|
||||
DATA_FILE = strdup(DATANAME);
|
||||
BLOCK_FILE = strdup(BLOCKNAME);
|
||||
|
||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
} else {
|
||||
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
|
||||
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
|
||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
} else {
|
||||
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
|
||||
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
|
||||
|
||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
|
||||
strcpy(DATA_FILE, user_config_dir);
|
||||
strcat(DATA_FILE, CONFIGDIR);
|
||||
strcat(DATA_FILE, DATANAME);
|
||||
strcpy(DATA_FILE, user_config_dir);
|
||||
strcat(DATA_FILE, CONFIGDIR);
|
||||
strcat(DATA_FILE, DATANAME);
|
||||
|
||||
strcpy(BLOCK_FILE, user_config_dir);
|
||||
strcat(BLOCK_FILE, CONFIGDIR);
|
||||
strcat(BLOCK_FILE, BLOCKNAME);
|
||||
}
|
||||
strcpy(BLOCK_FILE, user_config_dir);
|
||||
strcat(BLOCK_FILE, CONFIGDIR);
|
||||
strcat(BLOCK_FILE, BLOCKNAME);
|
||||
}
|
||||
|
||||
free(user_config_dir);
|
||||
@ -639,9 +966,28 @@ int main(int argc, char *argv[])
|
||||
init_signal_catchers();
|
||||
parse_args(argc, argv);
|
||||
|
||||
if (arg_opts.encrypt_data && arg_opts.unencrypt_data) {
|
||||
arg_opts.encrypt_data = 0;
|
||||
arg_opts.unencrypt_data = 0;
|
||||
queue_init_message("Warning: Using --unencrypt-data and --encrypt-data simultaneously has no effect");
|
||||
}
|
||||
|
||||
/* Use the -b flag to enable stderr */
|
||||
if (!arg_opts.debug)
|
||||
freopen("/dev/null", "w", stderr);
|
||||
|
||||
/* Make sure all written files are read/writeable only by the current user. */
|
||||
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
|
||||
int config_err = init_data_files();
|
||||
|
||||
int config_err = init_default_data_files();
|
||||
bool datafile_exists = file_exists(DATA_FILE);
|
||||
|
||||
if (!arg_opts.ignore_data_file) {
|
||||
if (!datafile_exists && !arg_opts.unencrypt_data)
|
||||
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
|
||||
else if (arg_opts.encrypt_data)
|
||||
first_time_encrypt("Encrypt existing data file? Y/n (q to quit)");
|
||||
}
|
||||
|
||||
/* init user_settings struct and load settings from conf file */
|
||||
user_settings_ = calloc(1, sizeof(struct user_settings));
|
||||
@ -649,18 +995,23 @@ int main(int argc, char *argv[])
|
||||
if (user_settings_ == NULL)
|
||||
exit_toxic_err("failed in main", FATALERR_MEMORY);
|
||||
|
||||
char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
|
||||
int settings_err = settings_load(user_settings_, p);
|
||||
|
||||
Tox *m = init_tox(arg_opts.use_ipv4);
|
||||
init_term();
|
||||
Tox *m = init_tox();
|
||||
|
||||
if (m == NULL)
|
||||
exit_toxic_err("failed in main", FATALERR_NETWORKINIT);
|
||||
|
||||
if (!arg_opts.ignore_data_file)
|
||||
if (!arg_opts.ignore_data_file) {
|
||||
if (arg_opts.encrypt_data && !datafile_exists)
|
||||
arg_opts.encrypt_data = 0;
|
||||
|
||||
load_data(m, DATA_FILE);
|
||||
|
||||
}
|
||||
|
||||
init_term();
|
||||
prompt = init_windows(m);
|
||||
prompt_init_statusbar(prompt, m);
|
||||
|
||||
@ -671,16 +1022,20 @@ int main(int argc, char *argv[])
|
||||
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0)
|
||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||
|
||||
/* thread for message queue */
|
||||
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0)
|
||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||
|
||||
#ifdef _AUDIO
|
||||
|
||||
av = init_audio(prompt, m);
|
||||
|
||||
|
||||
set_primary_device(input, user_settings_->audio_in_dev);
|
||||
set_primary_device(output, user_settings_->audio_out_dev);
|
||||
|
||||
#elif _SOUND_NOTIFY
|
||||
if ( init_devices() == de_InternalError )
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
|
||||
queue_init_message("Failed to init audio devices");
|
||||
|
||||
#endif /* _AUDIO */
|
||||
|
||||
@ -694,17 +1049,14 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (config_err) {
|
||||
msg = "Unable to determine configuration directory. Defaulting to 'data' for data file...";
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
queue_init_message("%s", msg);
|
||||
}
|
||||
|
||||
if (settings_err == -1) {
|
||||
msg = "Failed to load user settings";
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
}
|
||||
if (settings_err == -1)
|
||||
queue_init_message("Failed to load user settings");
|
||||
|
||||
/* Redirect stderr to /dev/null
|
||||
NOTE: Might not be best solution. Comment out for debugging. */
|
||||
freopen("/dev/null", "w", stderr);
|
||||
print_init_messages(prompt);
|
||||
cleanup_init_messages();
|
||||
|
||||
uint64_t last_save = (uint64_t) time(NULL);
|
||||
uint64_t looptimer = last_save;
|
||||
|
@ -42,7 +42,6 @@
|
||||
|
||||
#define UNKNOWN_NAME "Anonymous"
|
||||
|
||||
#define MAX_FRIENDS_NUM 999
|
||||
#define MAX_STR_SIZE TOX_MAX_MESSAGE_LENGTH
|
||||
#define MAX_CMDNAME_SIZE 64
|
||||
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
||||
@ -79,6 +78,8 @@ typedef enum _FATAL_ERRS {
|
||||
FATALERR_NETWORKINIT = -8, /* Tox network failed to init */
|
||||
FATALERR_INFLOOP = -9, /* infinite loop detected */
|
||||
FATALERR_WININIT = -10, /* window init failed */
|
||||
FATALERR_PROXY = -11, /* Tox network failed to init using a proxy */
|
||||
FATALERR_ENCRYPT = -12, /* Data file encryption failure */
|
||||
} FATAL_ERRS;
|
||||
|
||||
/* Fixes text color problem on some terminals.
|
||||
@ -88,7 +89,7 @@ typedef enum _FATAL_ERRS {
|
||||
void exit_toxic_success(Tox *m);
|
||||
void exit_toxic_err(const char *errmsg, int errcode);
|
||||
|
||||
int store_data(Tox *m, char *path);
|
||||
int store_data(Tox *m, const char *path);
|
||||
|
||||
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, uint16_t length, void *userdata);
|
||||
void on_connectionchange(Tox *m, int32_t friendnumber, uint8_t status, void *userdata);
|
||||
@ -108,5 +109,6 @@ void on_file_control(Tox *m, int32_t friendnumber, uint8_t receive_send, uint8_t
|
||||
const uint8_t *data, uint16_t length, void *userdata);
|
||||
void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_t *data, uint16_t length, void *userdata);
|
||||
void on_typing_change(Tox *m, int32_t friendnumber, uint8_t is_typing, void *userdata);
|
||||
void on_read_receipt(Tox *m, int32_t, uint32_t, void *userdata);
|
||||
|
||||
#endif /* #define _toxic_h */
|
||||
|
@ -100,9 +100,6 @@ void on_action(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t len
|
||||
|
||||
void on_nickchange(Tox *m, int32_t friendnumber, const uint8_t *string, uint16_t length, void *userdata)
|
||||
{
|
||||
if (friendnumber < 0 || friendnumber > MAX_FRIENDS_NUM)
|
||||
return;
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
@ -222,6 +219,15 @@ void on_file_data(Tox *m, int32_t friendnumber, uint8_t filenumber, const uint8_
|
||||
}
|
||||
}
|
||||
|
||||
void on_read_receipt(Tox *m, int32_t friendnumber, uint32_t receipt, void *userdata)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].onReadReceipt != NULL)
|
||||
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
|
||||
}
|
||||
}
|
||||
/* CALLBACKS END */
|
||||
|
||||
int add_window(Tox *m, ToxWindow w)
|
||||
@ -371,13 +377,12 @@ void on_window_resize(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_window_tab(ToxWindow toxwin)
|
||||
static void draw_window_tab(ToxWindow *toxwin)
|
||||
{
|
||||
if (toxwin.alert) attron(COLOR_PAIR(toxwin.alert));
|
||||
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
|
||||
clrtoeol();
|
||||
printw(" [%s]", toxwin.name);
|
||||
|
||||
if (toxwin.alert) attroff(COLOR_PAIR(toxwin.alert));
|
||||
printw(" [%s]", toxwin->name);
|
||||
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
|
||||
}
|
||||
|
||||
static void draw_bar(void)
|
||||
@ -407,7 +412,7 @@ static void draw_bar(void)
|
||||
|
||||
attron(A_BOLD);
|
||||
|
||||
draw_window_tab(windows[i]);
|
||||
draw_window_tab(&windows[i]);
|
||||
|
||||
if (windows + i == active_window)
|
||||
|
||||
@ -485,7 +490,7 @@ ToxWindow *get_window_ptr(int i)
|
||||
{
|
||||
ToxWindow *toxwin = NULL;
|
||||
|
||||
if (windows[i].active)
|
||||
if (i >= 0 && i <= MAX_WINDOWS_NUM && windows[i].active)
|
||||
toxwin = &windows[i];
|
||||
|
||||
return toxwin;
|
||||
@ -497,16 +502,17 @@ int get_num_active_windows(void)
|
||||
}
|
||||
|
||||
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
||||
void kill_all_windows(void)
|
||||
void kill_all_windows(Tox *m)
|
||||
{
|
||||
kill_prompt_window(prompt);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].is_chat)
|
||||
kill_chat_window(&windows[i]);
|
||||
kill_chat_window(&windows[i], m);
|
||||
else if (windows[i].is_groupchat)
|
||||
kill_groupchat_window(&windows[i]);
|
||||
}
|
||||
|
||||
kill_prompt_window(prompt);
|
||||
kill_friendlist();
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ enum {
|
||||
BLACK,
|
||||
} C_COLOURS;
|
||||
|
||||
/* tab alert types: lower types take priority */
|
||||
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
|
||||
typedef enum {
|
||||
WINDOW_ALERT_NONE = 0,
|
||||
WINDOW_ALERT_0 = GREEN,
|
||||
@ -71,13 +71,27 @@ struct _Winthread {
|
||||
bool flag_resize;
|
||||
};
|
||||
|
||||
struct _cqueue_thread {
|
||||
pthread_t tid;
|
||||
};
|
||||
|
||||
struct arg_opts {
|
||||
int ignore_data_file;
|
||||
int use_ipv4;
|
||||
int force_tcp;
|
||||
int debug;
|
||||
int default_locale;
|
||||
int use_custom_data;
|
||||
int no_connect;
|
||||
int encrypt_data;
|
||||
int unencrypt_data;
|
||||
char dns_path[MAX_STR_SIZE];
|
||||
char config_path[MAX_STR_SIZE];
|
||||
char nodes_path[MAX_STR_SIZE];
|
||||
|
||||
int use_proxy;
|
||||
char proxy_address[256];
|
||||
uint16_t proxy_port;
|
||||
};
|
||||
|
||||
typedef struct ToxWindow ToxWindow;
|
||||
@ -106,6 +120,7 @@ struct ToxWindow {
|
||||
void(*onFileControl)(ToxWindow *, Tox *, int32_t, uint8_t, uint8_t, uint8_t, const char *, uint16_t);
|
||||
void(*onFileData)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||
void(*onTypingChange)(ToxWindow *, Tox *, int32_t, uint8_t);
|
||||
void(*onReadReceipt)(ToxWindow *, Tox *, int32_t, uint32_t);
|
||||
|
||||
#ifdef _AUDIO
|
||||
|
||||
@ -199,6 +214,7 @@ struct ChatContext {
|
||||
|
||||
struct history *hst;
|
||||
struct chatlog *log;
|
||||
struct chat_queue *cqueue;
|
||||
|
||||
#ifdef _AUDIO
|
||||
struct infobox infobox;
|
||||
@ -223,7 +239,7 @@ int add_window(Tox *m, ToxWindow w);
|
||||
void del_window(ToxWindow *w);
|
||||
void set_active_window(int ch);
|
||||
int get_num_active_windows(void);
|
||||
void kill_all_windows(void); /* should only be called on shutdown */
|
||||
void kill_all_windows(Tox *m); /* should only be called on shutdown */
|
||||
void on_window_resize(void);
|
||||
ToxWindow *get_window_ptr(int i);
|
||||
|
||||
|
Reference in New Issue
Block a user