mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-26 19:36:45 +02:00
Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
a5246bcbc6 | |||
b37e28c2d0 | |||
973e60ef11 | |||
ae94bc593b | |||
81eb58532e | |||
8464ea9a7a | |||
b77bff35a1 | |||
eb964b64c2 | |||
2ff9d29491 | |||
2640919318 | |||
2fcbc4fa1c | |||
4330bf5867 | |||
3f1b7cdd26 | |||
1e985c1456 | |||
61740bda85 | |||
0d8e6d713e | |||
39e4ff8bd6 | |||
0434ac186a | |||
8d9d51640c | |||
c4c0c0d1f4 | |||
3f2826bd66 | |||
7b7ea0e386 | |||
d35a38735b | |||
f0c4906fdc | |||
56ba61e061 | |||
898d89e95a | |||
1fd1e27bdf | |||
8e84ac58d4 | |||
9d65997871 | |||
da2889f3ab | |||
312b38d253 | |||
0554bf0240 | |||
53a7530e8a | |||
41be04a142 | |||
31f36318a2 | |||
f882fdf608 | |||
7e1e410307 | |||
c135c812c2 | |||
6c239193ab | |||
de7db08352 | |||
ba5ded9bc2 | |||
4581dee4fc | |||
d75d6e8b60 | |||
142ce642f0 | |||
7dead5ec96 | |||
ddcf224db2 | |||
daf794c4a2 | |||
dac0124f0f | |||
15b7a30925 | |||
77ab71f26f | |||
68e1ba312d | |||
752fc6d619 | |||
16bcb27ca7 | |||
71d7d355a6 | |||
4188b392cc | |||
811fbfbb1e | |||
32eb7d3040 | |||
42763905d7 | |||
f64300d1d6 | |||
1a723f0e8e | |||
a86884c40e | |||
3f02e119f4 | |||
1bbd50aac7 | |||
e7a0c32a68 | |||
7560bc9547 | |||
2b43340c90 | |||
ff1620c923 | |||
1303053a27 | |||
91f194c821 | |||
478762f76c | |||
4d96d6a753 | |||
3cdcfbf4e5 | |||
4c302da503 | |||
26b5fe8f9d |
@ -52,7 +52,7 @@ jobs:
|
||||
libqrencode
|
||||
libsodium
|
||||
openal-soft
|
||||
- export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.20.1/lib"
|
||||
- export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.21.0/lib"
|
||||
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
|
||||
- test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc))
|
||||
|
||||
|
@ -59,6 +59,8 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
|
||||
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG
|
||||
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
|
||||
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
|
||||
* `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
|
||||
* `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled
|
||||
|
||||
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
|
||||
|
||||
|
23
Makefile
23
Makefile
@ -5,8 +5,7 @@ CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
LIBS = toxcore ncursesw libconfig libcurl
|
||||
|
||||
CFLAGS ?= -g
|
||||
CFLAGS += -std=gnu99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all
|
||||
CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces
|
||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||
CFLAGS += ${USER_CFLAGS}
|
||||
@ -14,10 +13,26 @@ LDFLAGS ?=
|
||||
LDFLAGS += ${USER_LDFLAGS}
|
||||
|
||||
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
|
||||
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
|
||||
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o
|
||||
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
||||
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
|
||||
|
||||
# Check if debug build is enabled
|
||||
RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(RELEASE), enabled)
|
||||
CFLAGS += -O0 -g -DDEBUG
|
||||
LDFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2 -flto
|
||||
LDFLAGS += -O2 -flto
|
||||
endif
|
||||
|
||||
# Check if LLVM Address Sanitizer is enabled
|
||||
ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(ASAN), disabled)
|
||||
CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
@ -70,7 +85,7 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
fi
|
||||
@echo " CC $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c >$(BUILD_DIR)/$*.d
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
|
||||
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
[](https://i.imgur.com/TwYA8L0.png)
|
||||
|
||||
## Installation
|
||||
[See the install instructions](/INSTALL.md)
|
||||
|
@ -55,9 +55,9 @@ author = 'Jakob Kreuze'
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = '0.8.4'
|
||||
version = '0.10.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.8.4'
|
||||
release = '0.10.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -11,6 +11,7 @@ endif
|
||||
CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||
LIBS += $(AUDIO_LIBS)
|
||||
LDFLAGS += -lm
|
||||
CFLAGS += $(AUDIO_CFLAGS)
|
||||
OBJ += $(AUDIO_OBJ)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Variables for X11 support
|
||||
X11_LIBS = x11
|
||||
X11_CFLAGS = -DX11
|
||||
X11_OBJ = xtra.o
|
||||
X11_OBJ = x11focus.o
|
||||
|
||||
# Check if we can build X11 support
|
||||
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.8.4
|
||||
TOXIC_VERSION = 0.10.1
|
||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||
ifneq (, $(findstring error, $(REV)))
|
||||
VERSION = $(TOXIC_VERSION)
|
||||
|
@ -17,6 +17,8 @@ help:
|
||||
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support"
|
||||
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||
@echo " ENABLE_RELEASE: Set to \"1\" to build without debug symbols and with full compiler optimizations"
|
||||
@echo " ENABLE_ASAN: Set to \"1\" to build with LLVM address sanitizer enabled.
|
||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||
|
@ -1,13 +1,13 @@
|
||||
'\" t
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2016-09-20
|
||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2020-05-04
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC" "1" "2020\-05\-04" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
|
@ -2,12 +2,12 @@
|
||||
.\" Title: toxic.conf
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2018-10-27
|
||||
.\" Date: 2020-11-18
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2018\-10\-27" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -93,6 +93,26 @@ Enable or disable acoustic alerts on events\&. true or false
|
||||
Select between native terminal colors and toxic color theme\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_bg\fR
|
||||
.RS 4
|
||||
set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_fg\fR
|
||||
.RS 4
|
||||
set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_accent\fR
|
||||
.RS 4
|
||||
set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_notify\fR
|
||||
.RS 4
|
||||
set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBautolog\fR
|
||||
.RS 4
|
||||
Enable or disable autologging\&. true or false
|
||||
@ -135,7 +155,7 @@ Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
||||
.PP
|
||||
\fBnotification_timeout\fR
|
||||
.RS 4
|
||||
Time in milliseconds to display a notification\&. (for example: 3000)
|
||||
Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000)
|
||||
.RE
|
||||
.PP
|
||||
\fBline_join\fR
|
||||
@ -217,9 +237,24 @@ Audio output device\&. Integer value\&. Number corresponds to
|
||||
/lsdev out
|
||||
.RE
|
||||
.PP
|
||||
\fBVAD_treshold\fR
|
||||
\fBVAD_threshold\fR
|
||||
.RS 4
|
||||
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
|
||||
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
|
||||
.RE
|
||||
.PP
|
||||
\fBconference_audio_channels\fR
|
||||
.RS 4
|
||||
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||
.RE
|
||||
.PP
|
||||
\fBchat_audio_channels\fR
|
||||
.RS 4
|
||||
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||
.RE
|
||||
.PP
|
||||
\fBpush_to_talk\fR
|
||||
.RS 4
|
||||
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
|
@ -60,6 +60,18 @@ OPTIONS
|
||||
*native_colors*;;
|
||||
Select between native terminal colors and toxic color theme. true or false
|
||||
|
||||
*color_bar_bg*;;
|
||||
set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_fg*;;
|
||||
set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_accent*;;
|
||||
set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_notify*;;
|
||||
set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*autolog*;;
|
||||
Enable or disable autologging. true or false
|
||||
|
||||
@ -135,9 +147,18 @@ OPTIONS
|
||||
*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
|
||||
*VAD_threshold*;;
|
||||
Voice Activity Detection threshold. Float value. Recommended values are
|
||||
1.0-40.0
|
||||
|
||||
*conference_audio_channels*;;
|
||||
Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||
|
||||
*chat_audio_channels*;;
|
||||
Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||
|
||||
*push_to_talk*;;
|
||||
Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false
|
||||
|
||||
*tox*::
|
||||
Configuration related to paths.
|
||||
|
@ -9,20 +9,32 @@ ui = {
|
||||
alerts=true;
|
||||
|
||||
// Output a bell when receiving a message (see manpage)
|
||||
bell_on_message=true
|
||||
bell_on_message=true;
|
||||
|
||||
// Output a bell when receiving a filetransfer (see manpage)
|
||||
bell_on_filetrans=true
|
||||
bell_on_filetrans=true;
|
||||
|
||||
// Don't output a bell when a filetransfer was accepted (see manpage)
|
||||
bell_on_filetrans_accept=false
|
||||
bell_on_filetrans_accept=false;
|
||||
|
||||
// Output a bell when receiving a group/call invite (see manpage)
|
||||
bell_on_invite=true
|
||||
bell_on_invite=true;
|
||||
|
||||
// true to use native terminal colours, false to use toxic default colour theme
|
||||
native_colors=false;
|
||||
|
||||
// set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_bg="blue";
|
||||
|
||||
// set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_fg="white";
|
||||
|
||||
// set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_accent="cyan";
|
||||
|
||||
// set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_notify="yellow";
|
||||
|
||||
// true to enable autologging, false to disable
|
||||
autolog=false;
|
||||
|
||||
@ -30,7 +42,7 @@ ui = {
|
||||
time_format=24;
|
||||
|
||||
// Timestamp format string according to date/strftime format. Overrides time_format setting
|
||||
timestamp_format="%H:%M:%S";
|
||||
timestamp_format="%H:%M";
|
||||
|
||||
// true to show you when others are typing a message in 1-on-1 chats
|
||||
show_typing_other=true;
|
||||
@ -54,19 +66,19 @@ ui = {
|
||||
history_size=700;
|
||||
|
||||
// time in milliseconds to display a notification
|
||||
notification_timeout=3000;
|
||||
notification_timeout=6000;
|
||||
|
||||
// Indicator for display when someone connects or joins a group.
|
||||
// Indicator for display when someone connects or joins a group
|
||||
line_join="-->";
|
||||
|
||||
// Indicator for display when someone disconnects or leaves a group.
|
||||
// Indicator for display when someone disconnects or leaves a group
|
||||
line_quit="<--";
|
||||
|
||||
// Indicator for alert messages.
|
||||
line_alert="-!-";
|
||||
|
||||
// Indicator for normal messages.
|
||||
line_normal="---";
|
||||
line_normal="-";
|
||||
|
||||
// true to change status based on screen/tmux attach/detach, false to disable
|
||||
mplex_away=true;
|
||||
@ -82,8 +94,17 @@ audio = {
|
||||
// preferred audio output device; numbers correspond to /lsdev out
|
||||
output_device=0;
|
||||
|
||||
// default VAD treshold; float (recommended values are around 40)
|
||||
VAD_treshold=40.0;
|
||||
// default VAD threshold; float (recommended values are 1.0-40.0)
|
||||
VAD_threshold=5.0;
|
||||
|
||||
// Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo.
|
||||
conference_audio_channels=1;
|
||||
|
||||
// Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo.
|
||||
chat_audio_channels=2;
|
||||
|
||||
// toggle conference push-to-talk
|
||||
push_to_talk=false;
|
||||
};
|
||||
|
||||
tox = {
|
||||
@ -122,6 +143,6 @@ keys = {
|
||||
half_page_up="Ctrl+F";
|
||||
half_page_down="Ctrl+V";
|
||||
page_bottom="Ctrl+H";
|
||||
toggle_peerlist="Ctrl+b";
|
||||
toggle_peerlist="Ctrl+B";
|
||||
toggle_paste_mode="Ctrl+T";
|
||||
};
|
||||
|
297
script/build-minimal-static-toxic.sh
Executable file
297
script/build-minimal-static-toxic.sh
Executable file
@ -0,0 +1,297 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021 Maxim Biro <nurupo.contributions@gmail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# Script for building a minimal statically compiled Toxic. While it doesn't
|
||||
# support X11 integration, video/audio calls, desktop & sound notifications, QR
|
||||
# codes and Python scripting, it is rather portable.
|
||||
#
|
||||
# Run as:
|
||||
#
|
||||
# sudo docker run -it --rm \
|
||||
# -v /tmp/artifact:/artifact \
|
||||
# -v /home/jfreegman/git/toxic:/toxic \
|
||||
# amd64/alpine:latest \
|
||||
# /bin/sh /toxic/script/build-minimal-static-toxic.sh
|
||||
#
|
||||
# that would use Toxic code from /home/jfreegman/git/toxic and place the build
|
||||
# artifact at /tmp/artifact.
|
||||
# You can change between amd64/alpine:latest and i386/alpine:latest, for 64-bit
|
||||
# and 32-bit builds.
|
||||
#
|
||||
# To debug, run:
|
||||
#
|
||||
# sudo docker run -it --rm \
|
||||
# -v /tmp/artifact:/artifact \
|
||||
# -v /home/jfreegman/git/toxic:/toxic \
|
||||
# amd64/alpine:latest \
|
||||
# /bin/sh
|
||||
# # sh /toxic/script/build-minimal-static-toxic.sh
|
||||
|
||||
set -eu
|
||||
|
||||
ARTIFACT_DIR="/artifact"
|
||||
TOXIC_SRC_DIR="/toxic"
|
||||
|
||||
# Make sure we run in the expected environment
|
||||
if ! grep -q 'docker' /proc/1/cgroup
|
||||
then
|
||||
echo "Error: This script should be run inside a disposable Docker container as it might modify system files in ways that would break a real system."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f /etc/os-release ] || ! grep -qi 'Alpine Linux' /etc/os-release
|
||||
then
|
||||
echo "Error: This script expects to be run on Alpine Linux."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ARTIFACT_DIR" ] || [ ! -d "$TOXIC_SRC_DIR" ]
|
||||
then
|
||||
echo "Error: At least one of $ARTIFACT_DIR or $TOXIC_SRC_DIR directories inside the container is missing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" != "0" ]
|
||||
then
|
||||
echo "Error: This script expects to be run as root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
# Use all cores for building
|
||||
MAKEFLAGS=j$(nproc)
|
||||
export MAKEFLAGS
|
||||
|
||||
check_sha256()
|
||||
{
|
||||
if ! ( echo "$1 $2" | sha256sum -cs - )
|
||||
then
|
||||
echo "Error: sha256 of $2 doesn't match the known one."
|
||||
echo "Expected: $1 $2"
|
||||
echo "Got: $(sha256sum "$2")"
|
||||
exit 1
|
||||
else
|
||||
echo "sha256 matches the expected one: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
apk update
|
||||
apk upgrade
|
||||
apk add \
|
||||
brotli-dev \
|
||||
brotli-static \
|
||||
build-base \
|
||||
cmake \
|
||||
git \
|
||||
libconfig-dev \
|
||||
libconfig-static \
|
||||
libsodium-dev \
|
||||
libsodium-static \
|
||||
linux-headers \
|
||||
ncurses-dev \
|
||||
ncurses-static \
|
||||
ncurses-terminfo \
|
||||
ncurses-terminfo-base \
|
||||
nghttp2-dev \
|
||||
nghttp2-static \
|
||||
openssl-dev \
|
||||
openssl-libs-static \
|
||||
pkgconf \
|
||||
wget \
|
||||
xz \
|
||||
zlib-dev \
|
||||
zlib-static
|
||||
|
||||
BUILD_DIR="/tmp/build"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
|
||||
# Build Toxcore
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
TOXCORE_VERSION="0.2.12"
|
||||
TOXCORE_HASH="30ae3263c9b68d3bef06f799ba9d7a67e3fad447030625f0ffa4bb22684228b0"
|
||||
TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
|
||||
|
||||
wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/v$TOXCORE_VERSION.tar.gz"
|
||||
check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
|
||||
tar -o -xf "$TOXCORE_FILENAME"
|
||||
rm "$TOXCORE_FILENAME"
|
||||
cd c-toxcore*
|
||||
|
||||
cmake -B_build -H. \
|
||||
-DENABLE_STATIC=ON \
|
||||
-DENABLE_SHARED=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TOXAV=OFF \
|
||||
-DBOOTSTRAP_DAEMON=OFF \
|
||||
-DDHT_BOOTSTRAP=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="$BUILD_DIR/prefix-toxcore"
|
||||
cmake --build _build --target install
|
||||
|
||||
|
||||
# Build cURL
|
||||
# While Alpine does provide a static cURL build, it's not built with
|
||||
# --with-ca-fallback, which is needed for better cross-distro portability.
|
||||
# Basically, some distros put their ca-certificates in different places, and
|
||||
# with --with-ca-fallback we or the user can provide the cert bundle file
|
||||
# location with SSL_CERT_FILE env variable.
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
CURL_VERSION="7.74.0"
|
||||
CURL_HASH="e56b3921eeb7a2951959c02db0912b5fcd5fdba5aca071da819e1accf338bbd7"
|
||||
CURL_FILENAME="curl-$CURL_VERSION.tar.gz"
|
||||
|
||||
wget --timeout=10 -O "$CURL_FILENAME" "https://curl.haxx.se/download/$CURL_FILENAME"
|
||||
check_sha256 "$CURL_HASH" "$CURL_FILENAME"
|
||||
tar -xf curl*.tar.gz
|
||||
rm curl*.tar.gz
|
||||
cd curl*
|
||||
|
||||
./configure \
|
||||
--prefix="$BUILD_DIR/prefix-curl" \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--without-ca-bundle \
|
||||
--without-ca-path \
|
||||
--with-ca-fallback \
|
||||
--with-nghttp2 \
|
||||
--with-brotli
|
||||
make
|
||||
make install
|
||||
sed -i 's|-lbrotlidec |-lbrotlidec-static -lbrotlicommon-static |g' $BUILD_DIR/prefix-curl/lib/pkgconfig/libcurl.pc
|
||||
|
||||
# Build Toxic
|
||||
cd "$BUILD_DIR"
|
||||
cp -a "$TOXIC_SRC_DIR" toxic
|
||||
cd toxic
|
||||
|
||||
if [ -z "$(git describe --tags --exact-match HEAD)" ]
|
||||
then
|
||||
set +x
|
||||
echo "Didn't find a git tag on the HEAD commit. You seem to be building an in-development release of Toxic rather than a release version." | fold -sw 80
|
||||
printf "Do you wish to proceed? (y/N): "
|
||||
read -r answer
|
||||
if echo "$answer" | grep -v -iq "^y" ; then
|
||||
echo "Exiting."
|
||||
exit 1
|
||||
fi
|
||||
set -x
|
||||
fi
|
||||
|
||||
sed -i 's|pkg-config|pkg-config --static|' cfg/global_vars.mk
|
||||
sed -i 's|<limits.h|<linux/limits.h|' src/*
|
||||
|
||||
CFLAGS="-static" PKG_CONFIG_PATH="$BUILD_DIR/prefix-toxcore/lib64/pkgconfig:$BUILD_DIR/prefix-toxcore/lib/pkgconfig:$BUILD_DIR/prefix-curl/lib/pkgconfig" PREFIX="$BUILD_DIR/prefix-toxic" make \
|
||||
DISABLE_X11=1 \
|
||||
DISABLE_AV=1 \
|
||||
DISABLE_SOUND_NOTIFY=1 \
|
||||
DISABLE_QRCODE=1 \
|
||||
DISABLE_QRPNG=1 \
|
||||
DISABLE_DESKTOP_NOTIFY=1 \
|
||||
ENABLE_PYTHON=0 \
|
||||
ENABLE_RELEASE=1 \
|
||||
ENABLE_ASAN=0 \
|
||||
install
|
||||
|
||||
|
||||
# Prepare the build artifact
|
||||
PREPARE_ARTIFACT_DIR="$BUILD_DIR/artifact"
|
||||
cp -a "$BUILD_DIR/prefix-toxic/bin" "$PREPARE_ARTIFACT_DIR"
|
||||
strip "$PREPARE_ARTIFACT_DIR"/*
|
||||
|
||||
cp -a "$BUILD_DIR/toxic/misc"/* "$PREPARE_ARTIFACT_DIR"
|
||||
mv "$PREPARE_ARTIFACT_DIR/toxic.conf.example" "$PREPARE_ARTIFACT_DIR/toxic.conf"
|
||||
|
||||
cp -aL /usr/share/terminfo "$PREPARE_ARTIFACT_DIR"
|
||||
|
||||
echo "A minimal statically compiled Toxic.
|
||||
Doesn't support X11 integration, video/audio calls, desktop & sound
|
||||
notifications, QR codes and Python scripting.
|
||||
However, it is rather portable.
|
||||
|
||||
Toxic $(git -C "$BUILD_DIR/toxic" describe --tags --exact-match HEAD) ($(git -C "$BUILD_DIR/toxic" rev-parse HEAD))
|
||||
|
||||
Build date time: $(TZ=UTC date +"%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
OS:
|
||||
$(cat /etc/os-release)
|
||||
|
||||
List of self-built software statically compiled into Toxic:
|
||||
libcurl $CURL_VERSION
|
||||
libtoxcore $TOXCORE_VERSION
|
||||
|
||||
List of OS-packaged software statically compiled into Toxic:
|
||||
$(apk list -I | grep 'static' | sort -i)
|
||||
|
||||
List of all packages installed during the build:
|
||||
$(apk list -I | sort -i)" > "$PREPARE_ARTIFACT_DIR/build_info"
|
||||
|
||||
echo '#!/usr/bin/env sh
|
||||
|
||||
DEBIAN_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
RHEL_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
|
||||
OPENSUSE_CERT_FILE=/etc/ssl/ca-bundle.pem
|
||||
|
||||
if [ ! -f "$SSL_CERT_FILE" ] ; then
|
||||
if [ -f "$DEBIAN_SSL_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$DEBIAN_SSL_CERT_FILE"
|
||||
elif [ -f "$RHEL_SSL_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$RHEL_SSL_CERT_FILE"
|
||||
elif [ -f "$OPENSUSE_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$OPENSUSE_CERT_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SSL_CERT_FILE" ] ; then
|
||||
echo "Warning: Couldn'\''t find the SSL CA certificate store file." | fold -sw 80
|
||||
echo
|
||||
echo "Toxic uses HTTPS to download a list of DHT bootstrap nodes in order to connect to the Tox DHT. This functionality is optional, you should be able to use Toxic without it. If you choose to use Toxic without it, you might need to manually enter DHT bootstrap node information using the '\''/connect'\'' command in order to come online." | fold -sw 80
|
||||
echo
|
||||
echo "To fix this issue, install SSL CAs as provided by your Linux distribution, e.g. '\''ca-certificates'\'' package on Debian/Ubuntu. If it'\''s already installed and you still see this message, run this script with SSL_CERT_FILE variable set to point to the SSL CA certificate store file location. The file is usually named '\''ca-certificates.crt'\'' or '\''ca-bundle.pem'\''." | fold -sw 80
|
||||
echo
|
||||
printf "Do you wish to run Toxic without SSL CA certificate store file found? (y/N): "
|
||||
read -r answer
|
||||
if echo "$answer" | grep -v -iq "^y" ; then
|
||||
echo "Exiting."
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$(dirname -- $0)"
|
||||
|
||||
SSL_CERT_FILE="$SSL_CERT_FILE" TERMINFO=./terminfo ./toxic -c toxic.conf' > "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||
chmod a+x "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||
|
||||
|
||||
# Tar it
|
||||
cd "$PREPARE_ARTIFACT_DIR"
|
||||
cd ..
|
||||
ARCH="$(tr '_' '-' < /etc/apk/arch)"
|
||||
ARTIFACT_NAME="toxic-minimal-static-musl_linux_$ARCH"
|
||||
mv "$PREPARE_ARTIFACT_DIR" "$PREPARE_ARTIFACT_DIR/../$ARTIFACT_NAME"
|
||||
tar -cJf "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_NAME"
|
||||
mv "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_DIR"
|
||||
chmod 777 -R "$ARTIFACT_DIR"
|
30
src/api.c
30
src/api.c
@ -99,18 +99,16 @@ void api_send(const char *msg)
|
||||
}
|
||||
|
||||
char *name = api_get_nick();
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
|
||||
if (name == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
self_window = get_active_window();
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
|
||||
add_line_to_hist(self_window->chatwin);
|
||||
int id = line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg);
|
||||
int id = line_info_add(self_window, true, name, NULL, OUT_MSG, 0, 0, "%s", msg);
|
||||
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
|
||||
free(name);
|
||||
}
|
||||
@ -156,7 +154,7 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
error_str = "Only one argument allowed.";
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -165,7 +163,7 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
if (fp == NULL) {
|
||||
error_str = "Path does not exist.";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -175,36 +173,36 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
|
||||
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||
{
|
||||
struct dirent *dir;
|
||||
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
|
||||
size_t path_len;
|
||||
DIR *d;
|
||||
FILE *fp;
|
||||
char abspath_buf[PATH_MAX + 256];
|
||||
char err_buf[PATH_MAX + 128];
|
||||
|
||||
if (user_settings->autorun_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
d = opendir(user_settings->autorun_path);
|
||||
DIR *d = opendir(user_settings->autorun_path);
|
||||
|
||||
if (d == NULL) {
|
||||
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||
snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||
api_display(err_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *dir = NULL;
|
||||
|
||||
cur_window = window;
|
||||
|
||||
self_window = self;
|
||||
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
path_len = strlen(dir->d_name);
|
||||
size_t path_len = strlen(dir->d_name);
|
||||
|
||||
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
|
||||
fp = fopen(abspath_buf, "r");
|
||||
snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name);
|
||||
FILE *fp = fopen(abspath_buf, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
|
||||
snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf);
|
||||
api_display(err_buf);
|
||||
continue;
|
||||
}
|
||||
|
824
src/audio_call.c
824
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,7 @@
|
||||
|
||||
#include "audio_device.h"
|
||||
|
||||
typedef enum _AudioError {
|
||||
typedef enum AudioError {
|
||||
ae_None = 0,
|
||||
ae_StartingCaptureDevice = 1 << 0,
|
||||
ae_StartingOutputDevice = 1 << 1,
|
||||
@ -35,7 +35,7 @@ typedef enum _AudioError {
|
||||
} AudioError;
|
||||
|
||||
#ifdef VIDEO
|
||||
typedef enum _VideoError {
|
||||
typedef enum VideoError {
|
||||
ve_None = 0,
|
||||
ve_StartingCaptureDevice = 1 << 0,
|
||||
ve_StartingOutputDevice = 1 << 1,
|
||||
@ -44,14 +44,27 @@ typedef enum _VideoError {
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
/* Status transitions:
|
||||
* None -> Pending (call invitation made or received);
|
||||
* Pending -> None (invitation rejected or failed);
|
||||
* Pending -> Active (call starts);
|
||||
* Active -> None (call ends).
|
||||
*/
|
||||
typedef enum CallStatus {
|
||||
cs_None = 0,
|
||||
cs_Pending,
|
||||
cs_Active
|
||||
} CallStatus;
|
||||
|
||||
typedef struct Call {
|
||||
pthread_t ttid; /* Transmission thread id */
|
||||
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||
uint32_t in_idx, out_idx; /* Audio Index */
|
||||
#ifdef VIDEO
|
||||
uint32_t vin_idx, vout_idx; /* Video Index */
|
||||
#endif /* VIDEO */
|
||||
pthread_mutex_t mutex;
|
||||
CallStatus status;
|
||||
uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */
|
||||
uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */
|
||||
uint32_t audio_bit_rate; /* Bit rate for sending audio */
|
||||
|
||||
uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */
|
||||
uint32_t video_width, video_height;
|
||||
uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */
|
||||
} Call;
|
||||
|
||||
struct CallControl {
|
||||
@ -66,19 +79,17 @@ struct CallControl {
|
||||
Call *calls;
|
||||
uint32_t max_calls;
|
||||
|
||||
uint32_t call_state;
|
||||
bool pending_call;
|
||||
bool audio_enabled;
|
||||
bool video_enabled;
|
||||
|
||||
uint32_t audio_bit_rate;
|
||||
int32_t audio_frame_duration;
|
||||
uint32_t audio_sample_rate;
|
||||
uint8_t audio_channels;
|
||||
uint32_t default_audio_bit_rate;
|
||||
|
||||
uint32_t video_bit_rate;
|
||||
int32_t video_frame_duration;
|
||||
|
||||
uint32_t default_video_width, default_video_height;
|
||||
uint32_t default_video_bit_rate;
|
||||
};
|
||||
|
||||
extern struct CallControl CallControl;
|
||||
@ -86,9 +97,12 @@ extern struct CallControl CallControl;
|
||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||
void terminate_audio(void);
|
||||
int start_transmission(ToxWindow *self, Call *call);
|
||||
int stop_transmission(Call *call, uint32_t friend_number);
|
||||
|
||||
bool init_call(Call *call);
|
||||
|
||||
void place_call(ToxWindow *self);
|
||||
void stop_current_call(ToxWindow *self);
|
||||
|
||||
void init_friend_AV(uint32_t index);
|
||||
void del_friend_AV(uint32_t index);
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@
|
||||
#define AUDIO_DEVICE_H
|
||||
|
||||
#define OPENAL_BUFS 5
|
||||
#define MAX_OPENAL_DEVICES 32
|
||||
#define MAX_DEVICES 32
|
||||
|
||||
#include "windows.h"
|
||||
@ -55,42 +56,42 @@ typedef enum DeviceError {
|
||||
typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data);
|
||||
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError init_devices(ToxAV *av);
|
||||
#else
|
||||
DeviceError init_devices(void);
|
||||
#endif /* AUDIO */
|
||||
|
||||
void get_devices_names(void);
|
||||
void get_al_device_names(void);
|
||||
DeviceError terminate_devices(void);
|
||||
|
||||
/* Callback handles ready data from INPUT device */
|
||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
|
||||
void *data, bool enable_VAD);
|
||||
void *get_device_callback_data(uint32_t device_idx);
|
||||
|
||||
/* toggle device mute */
|
||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||
#endif
|
||||
bool device_is_muted(DeviceType type, uint32_t device_idx);
|
||||
|
||||
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value);
|
||||
|
||||
float device_get_VAD_threshold(uint32_t device_idx);
|
||||
|
||||
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z);
|
||||
|
||||
DeviceError set_al_device(DeviceType type, int32_t selection);
|
||||
|
||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
||||
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
|
||||
uint8_t channels);
|
||||
/* Start device */
|
||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
|
||||
uint32_t frame_duration, uint8_t channels);
|
||||
DeviceError open_input_device(uint32_t *device_idx,
|
||||
DataHandleCallback cb, void *cb_data, bool enable_VAD,
|
||||
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
DeviceError open_output_device(uint32_t *device_idx,
|
||||
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
|
||||
/* Stop device */
|
||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||
|
||||
/* Write data to device */
|
||||
/* Write data to output device */
|
||||
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
|
||||
uint32_t sample_rate);
|
||||
|
||||
void print_devices(ToxWindow *self, DeviceType type);
|
||||
void get_primary_device_name(DeviceType type, char *buf, int size);
|
||||
/* return current input volume as float in range 0.0-100.0 */
|
||||
float get_input_volume(void);
|
||||
|
||||
void print_al_devices(ToxWindow *self, DeviceType type);
|
||||
|
||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||
#endif /* AUDIO_DEVICE_H */
|
||||
|
@ -20,9 +20,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/types.h>
|
||||
@ -31,27 +31,24 @@
|
||||
#include <dirent.h>
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "execute.h"
|
||||
#include "configdir.h"
|
||||
#include "execute.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size)
|
||||
static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches)
|
||||
{
|
||||
if (m) {
|
||||
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
|
||||
}
|
||||
|
||||
const char *L = (char *) list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
|
||||
for (size_t i = 0; i < n_matches; ++i) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[i]);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
|
||||
/* puts match in match buffer. if more than one match, add first n chars that are identical.
|
||||
@ -59,21 +56,19 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_it
|
||||
*
|
||||
* Returns the length of the match.
|
||||
*/
|
||||
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
|
||||
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, const char **matches, size_t n_items,
|
||||
size_t max_size)
|
||||
{
|
||||
UNUSED_VAR(self);
|
||||
|
||||
if (n == 1) {
|
||||
if (n_items == 1) {
|
||||
return snprintf(match, match_sz, "%s", matches[0]);
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_STR_SIZE; ++i) {
|
||||
for (size_t i = 0; i < max_size; ++i) {
|
||||
char ch1 = matches[0][i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < n; ++j) {
|
||||
for (size_t j = 0; j < n_items; ++j) {
|
||||
char ch2 = matches[j][i];
|
||||
|
||||
if (ch1 != ch2 || !ch1) {
|
||||
@ -92,8 +87,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
|
||||
* then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
*
|
||||
* list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
* in the list, and size is the size of each item in the list.
|
||||
* `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE.
|
||||
*
|
||||
* dir_search should be true if the line being completed is a file path.
|
||||
*
|
||||
@ -102,7 +96,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
|
||||
*
|
||||
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||
*/
|
||||
static int complete_line_helper(ToxWindow *self, const void *list, size_t n_items, size_t size, bool dir_search)
|
||||
static int complete_line_helper(ToxWindow *self, const char **list, const size_t n_items, bool dir_search)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -110,13 +104,12 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) {
|
||||
if (ctx->len >= MAX_STR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *L = (const char *) list;
|
||||
const char *endchrs = " ";
|
||||
char ubuf[MAX_STR_SIZE] = {0};
|
||||
char ubuf[MAX_STR_SIZE];
|
||||
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
|
||||
@ -125,8 +118,8 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[ctx->pos] = '\0';
|
||||
memcpy(tmp, ubuf, ctx->pos);
|
||||
tmp[ctx->pos] = 0;
|
||||
|
||||
const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
|
||||
char *sub = calloc(1, strlen(ubuf) + 1);
|
||||
@ -154,38 +147,43 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item
|
||||
}
|
||||
}
|
||||
|
||||
if (!sub[0]) {
|
||||
if (!sub[0] && !(dir_search && n_items == 1)) {
|
||||
free(sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
size_t n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE);
|
||||
|
||||
if (matches == NULL) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* put all list matches in matches array */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
char str[MAX_CMDNAME_SIZE + 1];
|
||||
snprintf(str, sizeof(str), "%s", &L[i * size]);
|
||||
|
||||
if (strncasecmp(str, sub, s_len) == 0) {
|
||||
strcpy(matches[n_matches++], str);
|
||||
for (size_t i = 0; i < n_items; ++i) {
|
||||
if (strncasecmp(list[i], sub, s_len) == 0) {
|
||||
snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(sub);
|
||||
|
||||
if (!n_matches) {
|
||||
free_ptr_array((void **) matches);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dir_search && n_matches > 1) {
|
||||
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
|
||||
print_ac_matches(self, NULL, matches, n_matches);
|
||||
}
|
||||
|
||||
char match[MAX_STR_SIZE];
|
||||
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
|
||||
size_t match_len = get_str_match(self, match, sizeof(match), (const char **) matches, n_matches, MAX_STR_SIZE);
|
||||
|
||||
free_ptr_array((void **) matches);
|
||||
|
||||
if (match_len == 0) {
|
||||
return 0;
|
||||
@ -261,14 +259,14 @@ static int complete_line_helper(ToxWindow *self, const void *list, size_t n_item
|
||||
return diff;
|
||||
}
|
||||
|
||||
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
|
||||
int complete_line(ToxWindow *self, const char **list, size_t n_items)
|
||||
{
|
||||
return complete_line_helper(self, list, n_items, size, false);
|
||||
return complete_line_helper(self, list, n_items, false);
|
||||
}
|
||||
|
||||
static int complete_path(ToxWindow *self, const void *list, size_t n_items, size_t size)
|
||||
static int complete_path(ToxWindow *self, const char **list, const size_t n_items)
|
||||
{
|
||||
return complete_line_helper(self, list, n_items, size, true);
|
||||
return complete_line_helper(self, list, n_items, true);
|
||||
}
|
||||
|
||||
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
|
||||
@ -279,7 +277,7 @@ static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const c
|
||||
char homedir[MAX_STR_SIZE] = {0};
|
||||
get_home_dir(homedir, sizeof(homedir));
|
||||
|
||||
char newline[MAX_STR_SIZE];
|
||||
char newline[MAX_STR_SIZE + 1];
|
||||
snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
|
||||
|
||||
@ -300,21 +298,33 @@ static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const c
|
||||
ctx->len = ctx->pos;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name.
|
||||
*/
|
||||
static bool is_partial_match(const char *s, const char *p, size_t p_len)
|
||||
{
|
||||
if (s == NULL || p == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0;
|
||||
}
|
||||
|
||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
* If there is only one match the line is auto-completed.
|
||||
*
|
||||
* Returns the diff between old len and new len of ctx->line on success.
|
||||
* Returns -1 if no matches or more than one match.
|
||||
*/
|
||||
#define MAX_DIRS 512
|
||||
#define MAX_DIRS 75
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
{
|
||||
char b_path[MAX_STR_SIZE];
|
||||
char b_name[MAX_STR_SIZE];
|
||||
char b_path[MAX_STR_SIZE + 1];
|
||||
char b_name[MAX_STR_SIZE + 1];
|
||||
char b_cmd[MAX_STR_SIZE];
|
||||
const wchar_t *tmpline = &line[wcslen(cmd) + 1]; /* start after "/command " */
|
||||
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) {
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -323,7 +333,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
}
|
||||
|
||||
if (b_path[0] == '~') {
|
||||
complete_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
|
||||
complete_home_dir(self, b_path, sizeof(b_path) - 1, b_cmd, strlen(b_cmd) + 2);
|
||||
}
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
@ -332,28 +342,33 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
b_path[0] = '.';
|
||||
b_path[1] = '\0';
|
||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||
memmove(b_path + 1, b_path, sizeof(b_path) - 1);
|
||||
b_path[0] = '.';
|
||||
}
|
||||
|
||||
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||
b_path[si + 1] = '\0';
|
||||
int b_name_len = strlen(b_name);
|
||||
size_t b_name_len = strlen(b_name);
|
||||
DIR *dp = opendir(b_path);
|
||||
|
||||
if (dp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dirnames[MAX_DIRS][NAME_MAX + 1];
|
||||
char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1);
|
||||
|
||||
if (dirnames == NULL) {
|
||||
closedir(dp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
|
||||
int dircount = 0;
|
||||
|
||||
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0
|
||||
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
|
||||
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
|
||||
if (is_partial_match(entry->d_name, b_name, b_name_len)) {
|
||||
snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name);
|
||||
++dircount;
|
||||
}
|
||||
}
|
||||
@ -361,13 +376,18 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
closedir(dp);
|
||||
|
||||
if (dircount == 0) {
|
||||
free_ptr_array((void **) dirnames);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dircount > 1) {
|
||||
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
|
||||
print_matches(self, m, dirnames, dircount, NAME_MAX + 1);
|
||||
qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper);
|
||||
print_ac_matches(self, m, dirnames, dircount);
|
||||
}
|
||||
|
||||
return complete_path(self, dirnames, dircount, NAME_MAX + 1);
|
||||
int ret = complete_path(self, (const char **) dirnames, dircount);
|
||||
|
||||
free_ptr_array((void **) dirnames);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -23,18 +23,23 @@
|
||||
#ifndef AUTOCOMPLETE_H
|
||||
#define AUTOCOMPLETE_H
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
/*
|
||||
* Looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
* then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
*
|
||||
* list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
* in the list, and size is the size of each item in the list.
|
||||
* `list` is a pointer to `n_items` strings.
|
||||
*
|
||||
* dir_search should be true if the line being completed is a file path.
|
||||
*
|
||||
* Returns the difference between the old len and new len of line on success.
|
||||
* Returns -1 on error.
|
||||
*
|
||||
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||
*/
|
||||
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
|
||||
int complete_line(ToxWindow *self, const char **list, size_t n_items);
|
||||
|
||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
* If there is only one match the line is auto-completed.
|
||||
|
@ -20,14 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc_tools.h"
|
||||
#include "avatars.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "avatars.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
@ -39,33 +39,66 @@ static struct Avatar {
|
||||
off_t size;
|
||||
} Avatar;
|
||||
|
||||
/* Compares the first size bytes of fp to signature.
|
||||
*
|
||||
* Returns 0 if they are the same
|
||||
* Returns 1 if they differ
|
||||
* Returns -1 on error.
|
||||
*
|
||||
* On success this function will seek back to the beginning of fp.
|
||||
*/
|
||||
static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
|
||||
{
|
||||
char *buf = malloc(size);
|
||||
|
||||
if (buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(buf, size, 1, fp) != 1) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = memcmp(signature, buf, size);
|
||||
|
||||
free(buf);
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
static void avatar_clear(void)
|
||||
{
|
||||
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||
Avatar = (struct Avatar) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
/* Sends avatar to friendnumber.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum)
|
||||
int avatar_send(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
Tox_Err_File_Send err;
|
||||
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
uint32_t filenumber = tox_file_send(m, friendnumber, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
|
||||
if (Avatar.size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %u (error %d)\n", friendnumber, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
|
||||
if (!ft) {
|
||||
return -1;
|
||||
@ -86,9 +119,7 @@ int avatar_send(Tox *m, uint32_t friendnum)
|
||||
/* Sends avatar to all friends */
|
||||
static void avatar_send_all(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) {
|
||||
avatar_send(m, Friends.list[i].num);
|
||||
}
|
||||
@ -149,6 +180,13 @@ void avatar_unset(Tox *m)
|
||||
avatar_send_all(m);
|
||||
}
|
||||
|
||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status)
|
||||
{
|
||||
if (connection_status == TOX_CONNECTION_NONE) {
|
||||
kill_avatar_file_transfers_friend(m, friendnumber);
|
||||
}
|
||||
}
|
||||
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control)
|
||||
{
|
||||
switch (control) {
|
||||
@ -196,21 +234,29 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position,
|
||||
ft->position = position;
|
||||
}
|
||||
|
||||
uint8_t send_data[length];
|
||||
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||
uint8_t *send_data = malloc(length);
|
||||
|
||||
if (send_length != length) {
|
||||
if (send_data == NULL) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t send_length = fread(send_data, 1, length, ft->file);
|
||||
|
||||
if (send_length != length) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
free(send_data);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_File_Send_Chunk err;
|
||||
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||
tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err);
|
||||
|
||||
free(send_data);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
|
||||
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||
}
|
||||
|
||||
ft->position += send_length;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#ifndef AVATARS_H
|
||||
#define AVATARS_H
|
||||
|
||||
#include "file_transfers.h"
|
||||
|
||||
#define MAX_AVATAR_FILE_SIZE 65536
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
@ -48,5 +50,6 @@ void avatar_unset(Tox *m);
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control);
|
||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status);
|
||||
|
||||
#endif /* AVATARS_H */
|
||||
|
@ -20,21 +20,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <limits.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "line_info.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
#include "curl_util.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct arg_opts arg_opts;
|
||||
extern struct user_settings *user_settings;
|
||||
@ -105,6 +108,37 @@ static struct DHT_Nodes {
|
||||
time_t last_updated;
|
||||
} Nodes;
|
||||
|
||||
/* Return true if address appears to be a valid ipv4 address. */
|
||||
static bool is_ip4_address(const char *address)
|
||||
{
|
||||
struct sockaddr_in s_addr;
|
||||
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
|
||||
}
|
||||
|
||||
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||
*
|
||||
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||
* for now the only guarantee is that it won't return true if the
|
||||
* address is a domain or ipv4 address, and should only be used if you're
|
||||
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||
*/
|
||||
static bool is_ip6_address(const char *address)
|
||||
{
|
||||
size_t num_colons = 0;
|
||||
char ch = 0;
|
||||
|
||||
for (size_t i = 0; (ch = address[i]); ++i) {
|
||||
if (ch == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == ':') {
|
||||
++num_colons;
|
||||
}
|
||||
}
|
||||
|
||||
return num_colons > 1 && num_colons < 8;
|
||||
}
|
||||
|
||||
/* Determine if a node is offline by comparing the age of the nodeslist
|
||||
* to the last time the node was successfully pinged.
|
||||
@ -241,6 +275,7 @@ on_exit:
|
||||
* Return -2 if http lookup failed.
|
||||
* Return -3 if http reponse was empty.
|
||||
* Return -4 if data could not be written to disk.
|
||||
* Return -5 if memory allocation fails.
|
||||
*/
|
||||
static int update_DHT_nodeslist(const char *nodes_path)
|
||||
{
|
||||
@ -254,26 +289,34 @@ static int update_DHT_nodeslist(const char *nodes_path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct Recv_Curl_Data recv_data;
|
||||
struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data));
|
||||
|
||||
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
|
||||
if (recv_data == NULL) {
|
||||
fclose(fp);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
|
||||
if (curl_fetch_nodes_JSON(recv_data) == -1) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (recv_data.length == 0) {
|
||||
if (recv_data->length == 0) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) {
|
||||
if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -4;
|
||||
}
|
||||
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
600
src/chat.c
600
src/chat.c
File diff suppressed because it is too large
Load Diff
@ -23,8 +23,8 @@
|
||||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
/* 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 */
|
||||
|
@ -23,15 +23,15 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chat.h"
|
||||
#include "conference.h"
|
||||
#include "execute.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "friendlist.h"
|
||||
#include "execute.h"
|
||||
#include "line_info.h"
|
||||
#include "groupchat.h"
|
||||
#include "chat.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern FriendsList Friends;
|
||||
@ -41,7 +41,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 2) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -50,7 +50,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
long int idx = strtol(argv[2], NULL, 10);
|
||||
|
||||
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -62,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -80,72 +80,96 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
}
|
||||
|
||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int groupnum = strtol(argv[1], NULL, 10);
|
||||
long int conferencenum = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Invite err;
|
||||
|
||||
if (!tox_conference_invite(m, self->num, groupnum, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
|
||||
if (!tox_conference_invite(m, self->num, conferencenum, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum);
|
||||
}
|
||||
|
||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(argc);
|
||||
UNUSED_VAR(argv);
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *groupkey = Friends.list[self->num].group_invite.key;
|
||||
uint16_t length = Friends.list[self->num].group_invite.length;
|
||||
uint8_t type = Friends.list[self->num].group_invite.type;
|
||||
const char *conferencekey = Friends.list[self->num].conference_invite.key;
|
||||
uint16_t length = Friends.list[self->num].conference_invite.length;
|
||||
uint8_t type = Friends.list[self->num].conference_invite.type;
|
||||
|
||||
if (!Friends.list[self->num].group_invite.pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||
if (!Friends.list[self->num].conference_invite.pending) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||
uint32_t conferencenum;
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||
Tox_Err_Conference_Join err;
|
||||
conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
}
|
||||
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
#ifdef AUDIO
|
||||
conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length,
|
||||
audio_conference_callback, NULL);
|
||||
|
||||
if (conferencenum == (uint32_t) -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Join err;
|
||||
|
||||
uint32_t groupnum = tox_conference_join(m, self->num, (const uint8_t *) groupkey, length, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
|
||||
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
return;
|
||||
#ifdef AUDIO
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
if (!init_conference_audio_input(m, conferencenum)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -153,26 +177,26 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int idx = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -183,18 +207,18 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
}
|
||||
|
||||
Tox_Err_File_Control err;
|
||||
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||
tox_file_control(m, self->num, ft->filenumber, TOX_FILE_CONTROL_RESUME, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_CONTROL_OK) {
|
||||
goto on_recv_error;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
init_progress_bar(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
|
||||
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
@ -205,23 +229,23 @@ on_recv_error:
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
return;
|
||||
|
||||
default:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -233,7 +257,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -242,21 +266,21 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
int path_len = strlen(path);
|
||||
|
||||
if (path_len >= MAX_STR_SIZE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file_to_send = fopen(path, "r");
|
||||
|
||||
if (file_to_send == NULL) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
off_t filesize = file_size(path);
|
||||
|
||||
if (filesize == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
fclose(file_to_send);
|
||||
return;
|
||||
}
|
||||
@ -286,7 +310,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
|
||||
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, file_name, sizestr);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||
|
||||
return;
|
||||
|
||||
@ -314,7 +338,7 @@ on_send_error:
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||
fclose(file_to_send);
|
||||
}
|
||||
|
@ -23,12 +23,12 @@
|
||||
#ifndef CHAT_COMMANDS_H
|
||||
#define CHAT_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
@ -45,8 +45,9 @@ void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SI
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* CHAT_COMMANDS_H */
|
||||
|
1418
src/conference.c
Normal file
1418
src/conference.c
Normal file
File diff suppressed because it is too large
Load Diff
118
src/conference.h
Normal file
118
src/conference.h
Normal file
@ -0,0 +1,118 @@
|
||||
/* conference.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CONFERENCE_H
|
||||
#define CONFERENCE_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH
|
||||
#define SIDEBAR_WIDTH 16
|
||||
|
||||
typedef struct ConferencePeer {
|
||||
bool active;
|
||||
|
||||
uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t peernum; /* index in chat->peer_list */
|
||||
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
|
||||
bool sending_audio;
|
||||
uint32_t audio_out_idx;
|
||||
time_t last_audio_time;
|
||||
} ConferencePeer;
|
||||
|
||||
typedef struct AudioInputCallbackData {
|
||||
Tox *tox;
|
||||
uint32_t conferencenum;
|
||||
} AudioInputCallbackData;
|
||||
|
||||
#define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1)
|
||||
typedef struct NameListEntry {
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
char pubkey_str[PUBKEY_STRING_SIZE];
|
||||
uint32_t peernum;
|
||||
} NameListEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
time_t start_time;
|
||||
|
||||
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||
size_t title_length;
|
||||
|
||||
ConferencePeer *peer_list;
|
||||
uint32_t max_idx;
|
||||
|
||||
NameListEntry *name_list;
|
||||
uint32_t num_peers;
|
||||
|
||||
bool push_to_talk_enabled;
|
||||
time_t ptt_last_pushed;
|
||||
|
||||
bool audio_enabled;
|
||||
time_t last_sent_audio;
|
||||
uint32_t audio_in_idx;
|
||||
AudioInputCallbackData audio_input_callback_data;
|
||||
} ConferenceChat;
|
||||
|
||||
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
||||
void free_conference(ToxWindow *self, uint32_t conferencenum);
|
||||
|
||||
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length);
|
||||
|
||||
/* destroys and re-creates conference window with or without the peerlist */
|
||||
void redraw_conference_win(ToxWindow *self);
|
||||
|
||||
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length);
|
||||
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title);
|
||||
int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log);
|
||||
|
||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
||||
* of `maxpeers`.
|
||||
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||
* If `prefix` is exactly the pubkey of a peer, matches only that peer.
|
||||
* return number of entries placed in `entries`.
|
||||
*/
|
||||
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
|
||||
uint32_t maxpeers);
|
||||
|
||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
||||
bool enable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||
bool disable_conference_audio(Tox *tox, uint32_t conferencenum);
|
||||
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled);
|
||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
|
||||
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
||||
sample_rate, void *userdata);
|
||||
|
||||
bool conference_mute_self(uint32_t conferencenum);
|
||||
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum);
|
||||
bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold);
|
||||
float conference_get_VAD_threshold(uint32_t conferencenum);
|
||||
|
||||
#endif /* CONFERENCE_H */
|
210
src/conference_commands.c
Normal file
210
src/conference_commands.c
Normal file
@ -0,0 +1,210 @@
|
||||
/* conference_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "conference.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
static void print_err(ToxWindow *self, const char *error_str)
|
||||
{
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||
}
|
||||
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
Tox_Err_Conference_Title err;
|
||||
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||
|
||||
if (argc < 1) {
|
||||
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
|
||||
print_err(self, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||
print_err(self, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = strlen(argv[1]);
|
||||
|
||||
if (len >= sizeof(title)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(title, sizeof(title), "%s", argv[1]);
|
||||
|
||||
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
conference_rename_log_path(m, self->num, title); // must be called first
|
||||
|
||||
conference_set_title(self, self->num, title, len);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
line_info_add(self, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE + 20];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
bool enable;
|
||||
|
||||
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||
enable = true;
|
||||
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||
enable = false;
|
||||
} else {
|
||||
print_err(self, "Please specify: on | off");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable ? enable_conference_audio(m, self->num) : disable_conference_audio(m, self->num)) {
|
||||
print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk."
|
||||
: "Disabled conference audio");
|
||||
} else {
|
||||
print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
if (conference_mute_self(self->num)) {
|
||||
print_err(self, "Toggled self audio mute status");
|
||||
} else {
|
||||
print_err(self, "No audio input to mute");
|
||||
}
|
||||
} else {
|
||||
NameListEntry *entries[16];
|
||||
uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16);
|
||||
|
||||
if (n == 0) {
|
||||
print_err(self, "No such peer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):");
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
|
||||
} else {
|
||||
print_err(self, "Peer is not on the call");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
if (argc == 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f",
|
||||
(double) conference_get_VAD_threshold(self->num));
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
print_err(self, "Only one argument allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *end;
|
||||
float value = strtof(argv[1], &end);
|
||||
|
||||
if (*end) {
|
||||
print_err(self, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conference_set_VAD_threshold(self->num, value)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value);
|
||||
} else {
|
||||
print_err(self, "Failed to set conference audio input sensitivity.");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
bool enable;
|
||||
|
||||
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||
enable = true;
|
||||
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||
enable = false;
|
||||
} else {
|
||||
print_err(self, "Please specify: on | off");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toggle_conference_push_to_talk(self->num, enable)) {
|
||||
print_err(self, "Failed to toggle push to talk.");
|
||||
return;
|
||||
}
|
||||
|
||||
print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled");
|
||||
}
|
||||
#endif /* AUDIO */
|
@ -1,4 +1,4 @@
|
||||
/* xtra.h
|
||||
/* conference_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
@ -20,21 +20,16 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef XTRA_H
|
||||
#define XTRA_H
|
||||
#ifndef CONFERENCE_COMMANDS_H
|
||||
#define CONFERENCE_COMMANDS_H
|
||||
|
||||
/* NOTE: If no xlib present don't compile */
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
typedef enum {
|
||||
DT_plain,
|
||||
DT_file_list
|
||||
}
|
||||
DropType;
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
typedef void (*drop_callback)(const char *, DropType);
|
||||
|
||||
int init_xtra(drop_callback d);
|
||||
void terminate_xtra(void);
|
||||
int is_focused(void); /* returns bool */
|
||||
|
||||
#endif /* XTRA_H */
|
||||
#endif /* CONFERENCE_COMMANDS_H */
|
@ -20,18 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "configdir.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
|
||||
/* get the user's home directory. */
|
||||
void get_home_dir(char *home, int size)
|
||||
|
@ -23,6 +23,8 @@
|
||||
#ifndef CURL_UTIL_H
|
||||
#define CURL_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||
|
||||
|
@ -20,20 +20,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "execute.h"
|
||||
#include "api.h"
|
||||
#include "chat_commands.h"
|
||||
#include "execute.h"
|
||||
#include "global_commands.h"
|
||||
#include "group_commands.h"
|
||||
#include "conference_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "api.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
struct cmd_func {
|
||||
const char *name;
|
||||
@ -48,7 +48,7 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/group", cmd_groupchat },
|
||||
{ "/conference", cmd_conference },
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/log", cmd_log },
|
||||
{ "/myid", cmd_myid },
|
||||
@ -67,8 +67,8 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev", cmd_change_video_device },
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev", cmd_change_video_device },
|
||||
#endif /* VIDEO */
|
||||
#ifdef PYTHON
|
||||
{ "/run", cmd_run },
|
||||
@ -77,41 +77,45 @@ static struct cmd_func global_commands[] = {
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/invite", cmd_groupinvite },
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/invite", cmd_conference_invite },
|
||||
{ "/join", cmd_conference_join },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
#ifdef AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/bitrate", cmd_bitrate },
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/bitrate", cmd_bitrate },
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/video", cmd_video },
|
||||
{ "/vcall", cmd_vcall },
|
||||
{ "/video", cmd_video },
|
||||
{ "/res", cmd_res },
|
||||
#endif /* VIDEO */
|
||||
{ NULL, NULL },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func group_commands[] = {
|
||||
{ "/title", cmd_set_title },
|
||||
static struct cmd_func conference_commands[] = {
|
||||
{ "/title", cmd_conference_set_title },
|
||||
|
||||
#ifdef AUDIO
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/audio", cmd_enable_audio },
|
||||
{ "/mute", cmd_conference_mute },
|
||||
{ "/ptt", cmd_conference_push_to_talk },
|
||||
{ "/sense", cmd_conference_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
|
||||
#ifdef PYTHON
|
||||
#define SPECIAL_COMMANDS 6
|
||||
#define SPECIAL_COMMANDS 7
|
||||
#else
|
||||
#define SPECIAL_COMMANDS 5
|
||||
#define SPECIAL_COMMANDS 6
|
||||
#endif /* PYTHON */
|
||||
|
||||
/* Special commands are commands that only take one argument even if it contains spaces */
|
||||
@ -124,6 +128,7 @@ static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
#endif /* PYTHON */
|
||||
"/sendfile",
|
||||
"/title",
|
||||
"/mute",
|
||||
};
|
||||
|
||||
/* Returns true if input command is in the special_commands array. */
|
||||
@ -246,8 +251,8 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
|
||||
break;
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, group_commands, args) == 0) {
|
||||
case CONFERENCE_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, conference_commands, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -266,5 +271,5 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
|
||||
#endif
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
CHAT_COMMAND_MODE,
|
||||
GROUPCHAT_COMMAND_MODE,
|
||||
CONFERENCE_COMMAND_MODE,
|
||||
};
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
|
||||
|
@ -20,23 +20,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
#define NUM_PROG_MARKS 50
|
||||
#define STR_BUF_SIZE 30
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline has room for at least MAX_STR_SIZE bytes */
|
||||
@ -59,13 +60,15 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
|
||||
return;
|
||||
}
|
||||
|
||||
char pct_str[24];
|
||||
char pct_str[STR_BUF_SIZE];
|
||||
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
||||
|
||||
char bps_str[24];
|
||||
char bps_str[STR_BUF_SIZE];
|
||||
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
||||
|
||||
char prog_line[NUM_PROG_MARKS + 1] = {0};
|
||||
char prog_line[NUM_PROG_MARKS + 1];
|
||||
prog_line[0] = 0;
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i, j;
|
||||
|
||||
@ -81,10 +84,18 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
|
||||
strcat(prog_line, "-");
|
||||
}
|
||||
|
||||
char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
|
||||
snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||
size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7;
|
||||
char *full_line = malloc(line_buf_size);
|
||||
|
||||
if (full_line == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||
|
||||
line_info_set(self, line_id, full_line);
|
||||
|
||||
free(full_line);
|
||||
}
|
||||
|
||||
static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft)
|
||||
@ -107,33 +118,36 @@ static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft)
|
||||
}
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum)
|
||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i]);
|
||||
refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i]);
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
refresh_progress_helper(self, &Friends.list[friendnumber].file_receiver[i]);
|
||||
refresh_progress_helper(self, &Friends.list[friendnumber].file_sender[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||
static void clear_file_transfer(struct FileTransfer *ft)
|
||||
{
|
||||
size_t i;
|
||||
*ft = (struct FileTransfer) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||
* Returns NULL if filenumber is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) {
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) {
|
||||
return ft_send;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||
struct FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) {
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) {
|
||||
return ft_recv;
|
||||
}
|
||||
}
|
||||
@ -144,19 +158,17 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction)
|
||||
{
|
||||
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||
&Friends.list[friendnum].file_sender[i] :
|
||||
&Friends.list[friendnum].file_receiver[i];
|
||||
&Friends.list[friendnumber].file_sender[i] :
|
||||
&Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
|
||||
return ft;
|
||||
@ -169,23 +181,19 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
|
||||
/* Returns a pointer to an unused file sender.
|
||||
* Returns NULL if all file senders are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
clear_file_transfer(ft);
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->friendnumber = friendnumber;
|
||||
ft->filenumber = filenumber;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_SEND;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
@ -196,23 +204,20 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu
|
||||
/* Returns a pointer to an unused file receiver.
|
||||
* Returns NULL if all file receivers are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
clear_file_transfer(ft);
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->friendnumber = friendnumber;
|
||||
ft->filenumber = filenumber;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_RECV;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
@ -223,15 +228,15 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||
{
|
||||
if (direction == FILE_TRANSFER_RECV) {
|
||||
return new_file_receiver(window, friendnum, filenum, type);
|
||||
return new_file_receiver(window, friendnumber, filenumber, type);
|
||||
}
|
||||
|
||||
if (direction == FILE_TRANSFER_SEND) {
|
||||
return new_file_sender(window, friendnum, filenum, type);
|
||||
return new_file_sender(window, friendnumber, filenumber, type);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -259,7 +264,7 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C
|
||||
}
|
||||
|
||||
if (CTRL >= 0) {
|
||||
tox_file_control(m, ft->friendnum, ft->filenum, (Tox_File_Control) CTRL, NULL);
|
||||
tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, NULL);
|
||||
}
|
||||
|
||||
if (message && self) {
|
||||
@ -269,28 +274,36 @@ void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int C
|
||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
}
|
||||
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
clear_file_transfer(ft);
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
||||
/* Kills active outgoing avatar file transfers for friendnumber */
|
||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
size_t i;
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnumber */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_all_file_transfers(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024^2 */
|
||||
@ -51,18 +51,16 @@ struct FileTransfer {
|
||||
ToxWindow *window;
|
||||
FILE *file;
|
||||
FILE_TRANSFER_STATE state;
|
||||
FILE_TRANSFER_DIRECTION direction;
|
||||
uint8_t file_type;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||
double bps;
|
||||
uint32_t filenum;
|
||||
uint32_t friendnum;
|
||||
uint32_t filenumber;
|
||||
uint32_t friendnumber;
|
||||
size_t index;
|
||||
uint64_t file_size;
|
||||
uint64_t position;
|
||||
time_t last_line_progress; /* The last time we updated the progress bar */
|
||||
time_t last_keep_alive; /* The last time we sent or received data */
|
||||
uint32_t line_id;
|
||||
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||
};
|
||||
@ -75,24 +73,24 @@ void init_progress_bar(char *progline);
|
||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum);
|
||||
void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber);
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||
* Returns NULL if filenumber is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber);
|
||||
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction);
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||
|
||||
/* Closes file transfer ft.
|
||||
@ -103,8 +101,11 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type);
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||
/* Kills active outgoing avatar file transfers for friendnumber */
|
||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||
|
||||
/* Kills all active file transfers for friendnumber */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||
|
||||
void kill_all_file_transfers(Tox *m);
|
||||
|
||||
|
189
src/friendlist.c
189
src/friendlist.c
@ -20,26 +20,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "avatars.h"
|
||||
#include "chat.h"
|
||||
#include "friendlist.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "notify.h"
|
||||
#include "help.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "avatars.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
@ -116,8 +116,8 @@ static void realloc_blocklist(int n)
|
||||
void kill_friendlist(ToxWindow *self)
|
||||
{
|
||||
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) {
|
||||
free(Friends.list[i].group_invite.key);
|
||||
if (Friends.list[i].active && Friends.list[i].conference_invite.key != NULL) {
|
||||
free(Friends.list[i].conference_invite.key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,6 +127,20 @@ void kill_friendlist(ToxWindow *self)
|
||||
del_window(self);
|
||||
}
|
||||
|
||||
static void clear_blocklist_index(size_t idx)
|
||||
{
|
||||
Blocked.list[idx] = (BlockedFriend) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
static void clear_friendlist_index(size_t idx)
|
||||
{
|
||||
Friends.list[idx] = (ToxicFriend) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
/* Saves the blocklist to path. If there are no items in the blocklist the
|
||||
* empty file will be removed.
|
||||
*
|
||||
@ -160,8 +174,7 @@ static int save_blocklist(char *path)
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockedFriend tmp;
|
||||
memset(&tmp, 0, sizeof(BlockedFriend));
|
||||
BlockedFriend tmp = {0};
|
||||
tmp.namelength = htons(Blocked.list[i].namelength);
|
||||
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1); // Include null byte
|
||||
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE);
|
||||
@ -187,13 +200,21 @@ static int save_blocklist(char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
char temp_path[strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1];
|
||||
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_BLOCKLIST_EXT);
|
||||
size_t temp_buf_size = strlen(path) + strlen(TEMP_BLOCKLIST_EXT) + 1;
|
||||
char *temp_path = malloc(temp_buf_size);
|
||||
|
||||
if (temp_path == NULL) {
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(temp_path, temp_buf_size, "%s%s", path, TEMP_BLOCKLIST_EXT);
|
||||
|
||||
FILE *fp = fopen(temp_path, "wb");
|
||||
|
||||
if (fp == NULL) {
|
||||
free(data);
|
||||
free(temp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -201,6 +222,7 @@ static int save_blocklist(char *path)
|
||||
fprintf(stderr, "Failed to write blocklist data.\n");
|
||||
fclose(fp);
|
||||
free(data);
|
||||
free(temp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -208,9 +230,12 @@ static int save_blocklist(char *path)
|
||||
free(data);
|
||||
|
||||
if (rename(temp_path, path) != 0) {
|
||||
free(temp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(temp_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -235,15 +260,22 @@ int load_blocklist(char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
char data[len];
|
||||
char *data = malloc(len);
|
||||
|
||||
if (data == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(data, len, 1, fp) != 1) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len % sizeof(BlockedFriend) != 0) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -251,12 +283,9 @@ int load_blocklist(char *path)
|
||||
Blocked.max_idx = num;
|
||||
realloc_blocklist(num);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
BlockedFriend tmp;
|
||||
memset(&tmp, 0, sizeof(BlockedFriend));
|
||||
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
|
||||
for (int i = 0; i < num; ++i) {
|
||||
BlockedFriend tmp = {0};
|
||||
clear_blocklist_index(i);
|
||||
|
||||
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
|
||||
Blocked.list[i].namelength = ntohs(tmp.namelength);
|
||||
@ -279,6 +308,8 @@ int load_blocklist(char *path)
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(data);
|
||||
|
||||
sort_blocklist_index();
|
||||
|
||||
return 0;
|
||||
@ -366,11 +397,8 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Mess
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(prompt, timefrmt, nick, NULL, IN_MSG, 0, 0, "%s", str);
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "* Warning: Too many windows are open.");
|
||||
line_info_add(prompt, true, nick, NULL, IN_MSG, 0, 0, "%s", str);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "* Warning: Too many windows are open.");
|
||||
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
@ -388,7 +416,7 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num,
|
||||
++Friends.num_online;
|
||||
|
||||
if (avatar_send(m, num) == -1) {
|
||||
fprintf(stderr, "avatar_send failed for friend %d\n", num);
|
||||
fprintf(stderr, "avatar_send failed for friend %u\n", num);
|
||||
}
|
||||
}
|
||||
|
||||
@ -422,7 +450,9 @@ static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (strcmp(oldname, newnamecpy) != 0) {
|
||||
rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin);
|
||||
if (rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin) != 0) {
|
||||
fprintf(stderr, "Failed to rename friend chat log from `%s` to `%s`\n", oldname, newnamecpy);
|
||||
}
|
||||
}
|
||||
|
||||
sort_friendlist_index();
|
||||
@ -457,7 +487,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
|
||||
UNUSED_VAR(self);
|
||||
|
||||
realloc_friends(Friends.max_idx + 1);
|
||||
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
|
||||
clear_friendlist_index(Friends.max_idx);
|
||||
|
||||
uint32_t i;
|
||||
|
||||
@ -491,10 +521,11 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
|
||||
|
||||
update_friend_last_online(i, t);
|
||||
|
||||
char tempname[TOX_MAX_NAME_LENGTH] = {0};
|
||||
get_nick_truncate(m, tempname, num);
|
||||
snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname);
|
||||
Friends.list[i].namelength = strlen(Friends.list[i].name);
|
||||
char tempname[TOX_MAX_NAME_LENGTH + 1];
|
||||
int name_len = get_nick_truncate(m, tempname, num);
|
||||
memcpy(Friends.list[i].name, tempname, name_len);
|
||||
Friends.list[i].name[name_len] = 0;
|
||||
Friends.list[i].namelength = name_len;
|
||||
|
||||
if (i == Friends.max_idx) {
|
||||
++Friends.max_idx;
|
||||
@ -516,7 +547,7 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
|
||||
static void friendlist_add_blocked(uint32_t fnum, uint32_t bnum)
|
||||
{
|
||||
realloc_friends(Friends.max_idx + 1);
|
||||
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
|
||||
clear_friendlist_index(Friends.max_idx);
|
||||
|
||||
int i;
|
||||
|
||||
@ -577,18 +608,19 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED,
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED,
|
||||
"* File transfer from %s failed: too many windows are open.", nick);
|
||||
|
||||
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key,
|
||||
uint16_t length)
|
||||
static void friendlist_onConferenceInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type,
|
||||
const char *conference_pub_key,
|
||||
uint16_t length)
|
||||
{
|
||||
UNUSED_VAR(self);
|
||||
UNUSED_VAR(type);
|
||||
UNUSED_VAR(group_pub_key);
|
||||
UNUSED_VAR(conference_pub_key);
|
||||
UNUSED_VAR(length);
|
||||
|
||||
if (num >= Friends.max_idx) {
|
||||
@ -607,8 +639,8 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, num);
|
||||
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED,
|
||||
"* Group chat invite from %s failed: too many windows are open.", nick);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED,
|
||||
"* Conference chat invite from %s failed: too many windows are open.", nick);
|
||||
|
||||
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
@ -654,11 +686,11 @@ static void delete_friend(Tox *m, uint32_t f_num)
|
||||
}
|
||||
}
|
||||
|
||||
if (Friends.list[f_num].group_invite.key != NULL) {
|
||||
free(Friends.list[f_num].group_invite.key);
|
||||
if (Friends.list[f_num].conference_invite.key != NULL) {
|
||||
free(Friends.list[f_num].conference_invite.key);
|
||||
}
|
||||
|
||||
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
|
||||
clear_friendlist_index(f_num);
|
||||
|
||||
int i;
|
||||
|
||||
@ -696,7 +728,7 @@ static void delete_blocked_friend(uint32_t bnum);
|
||||
/* deactivates delete friend popup and deletes friend if instructed */
|
||||
static void del_friend_deactivate(Tox *m, wint_t key)
|
||||
{
|
||||
if (key == 'y') {
|
||||
if (key == L'y') {
|
||||
if (blocklist_view == 0) {
|
||||
delete_friend(m, PendingDelete.num);
|
||||
sort_friendlist_index();
|
||||
@ -707,7 +739,11 @@ static void del_friend_deactivate(Tox *m, wint_t key)
|
||||
}
|
||||
|
||||
delwin(PendingDelete.popup);
|
||||
memset(&PendingDelete, 0, sizeof(PendingDelete));
|
||||
|
||||
PendingDelete = (struct PendingDel) {
|
||||
0
|
||||
};
|
||||
|
||||
clear();
|
||||
refresh();
|
||||
}
|
||||
@ -745,7 +781,7 @@ static void draw_del_popup(void)
|
||||
/* deletes contact from blocked list */
|
||||
static void delete_blocked_friend(uint32_t bnum)
|
||||
{
|
||||
memset(&Blocked.list[bnum], 0, sizeof(BlockedFriend));
|
||||
clear_blocklist_index(bnum);
|
||||
|
||||
int i;
|
||||
|
||||
@ -768,12 +804,12 @@ static void delete_blocked_friend(uint32_t bnum)
|
||||
/* deletes contact from friendlist and puts in blocklist */
|
||||
void block_friend(Tox *m, uint32_t fnum)
|
||||
{
|
||||
if (Friends.num_friends <= 0) {
|
||||
if (Friends.num_friends == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
realloc_blocklist(Blocked.max_idx + 1);
|
||||
memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend));
|
||||
clear_blocklist_index(Blocked.max_idx);
|
||||
|
||||
int i;
|
||||
|
||||
@ -815,7 +851,7 @@ static void unblock_friend(Tox *m, uint32_t bnum)
|
||||
uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err);
|
||||
|
||||
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -836,7 +872,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (key == 'h') {
|
||||
if (key == L'h') {
|
||||
help_init_menu(self);
|
||||
return true;
|
||||
}
|
||||
@ -859,7 +895,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
/* lock screen and force decision on deletion popup */
|
||||
if (PendingDelete.active) {
|
||||
if (key == 'y' || key == 'n') {
|
||||
if (key == L'y' || key == L'n') {
|
||||
del_friend_deactivate(m, key);
|
||||
}
|
||||
|
||||
@ -871,7 +907,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case '\r':
|
||||
case L'\r':
|
||||
if (blocklist_view) {
|
||||
break;
|
||||
}
|
||||
@ -884,7 +920,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
set_active_window_index(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);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, msg);
|
||||
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
@ -894,7 +930,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
del_friend_activate(f);
|
||||
break;
|
||||
|
||||
case 'b':
|
||||
case L'b':
|
||||
if (!blocklist_view) {
|
||||
block_friend(m, f);
|
||||
} else {
|
||||
@ -983,7 +1019,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
wmove(self->window, y2 - 1, 1);
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "Key: ");
|
||||
wprintw(self->window, "Public key: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
int i;
|
||||
@ -1018,6 +1054,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wprintw(self->window, "key for help\n\n");
|
||||
wattroff(self->window, COLOR_PAIR(CYAN));
|
||||
|
||||
draw_window_bar(self);
|
||||
|
||||
if (blocklist_view == 1) {
|
||||
blocklist_onDraw(self, m, y2, x2);
|
||||
return;
|
||||
@ -1030,7 +1068,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wprintw(self->window, " Online: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends);
|
||||
wprintw(self->window, "%zu/%zu \n\n", Friends.num_online, Friends.num_friends);
|
||||
|
||||
if ((y2 - FLIST_OFST) <= 0) {
|
||||
return;
|
||||
@ -1059,9 +1097,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
int num_selected = Friends.num_selected;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
bool f_selected = false;
|
||||
|
||||
if (is_active) {
|
||||
bool f_selected = false;
|
||||
|
||||
if (i == num_selected) {
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, " > ");
|
||||
@ -1208,7 +1246,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wmove(self->window, y2 - 1, 1);
|
||||
|
||||
wattron(self->window, A_BOLD);
|
||||
wprintw(self->window, "Key: ");
|
||||
wprintw(self->window, "Public key: ");
|
||||
wattroff(self->window, A_BOLD);
|
||||
|
||||
int i;
|
||||
@ -1226,6 +1264,21 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
}
|
||||
}
|
||||
|
||||
void friendlist_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
UNUSED_VAR(m);
|
||||
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
exit_toxic_err("failed in friendlist_onInit", FATALERR_CURSES);
|
||||
}
|
||||
|
||||
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - 2, 0);
|
||||
}
|
||||
|
||||
void disable_chatwin(uint32_t f_num)
|
||||
{
|
||||
Friends.list[f_num].chatwin = -1;
|
||||
@ -1251,10 +1304,10 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
|
||||
} else {
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_nick_truncate(m, nick, Friends.list[friend_number].num);
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
|
||||
|
||||
const char *errmsg = "* Warning: Too many windows are open.";
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, errmsg);
|
||||
|
||||
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
@ -1302,8 +1355,9 @@ ToxWindow *new_friendlist(void)
|
||||
exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
ret->is_friendlist = true;
|
||||
ret->type = WINDOW_TYPE_FRIEND_LIST;
|
||||
|
||||
ret->onInit = &friendlist_onInit;
|
||||
ret->onKey = &friendlist_onKey;
|
||||
ret->onDraw = &friendlist_onDraw;
|
||||
ret->onFriendAdded = &friendlist_onFriendAdded;
|
||||
@ -1313,7 +1367,7 @@ ToxWindow *new_friendlist(void)
|
||||
ret->onStatusChange = &friendlist_onStatusChange;
|
||||
ret->onStatusMessageChange = &friendlist_onStatusMessageChange;
|
||||
ret->onFileRecv = &friendlist_onFileRecv;
|
||||
ret->onGroupInvite = &friendlist_onGroupInvite;
|
||||
ret->onConferenceInvite = &friendlist_onConferenceInvite;
|
||||
|
||||
#ifdef AUDIO
|
||||
ret->onInvite = &friendlist_onAV;
|
||||
@ -1327,7 +1381,6 @@ ToxWindow *new_friendlist(void)
|
||||
ret->onEnd = &friendlist_onAV;
|
||||
|
||||
ret->is_call = false;
|
||||
ret->device_selection[0] = ret->device_selection[1] = -1;
|
||||
#endif /* AUDIO */
|
||||
|
||||
ret->num = -1;
|
||||
@ -1340,6 +1393,6 @@ ToxWindow *new_friendlist(void)
|
||||
}
|
||||
|
||||
ret->help = help;
|
||||
strcpy(ret->name, "contacts");
|
||||
strcpy(ret->name, "Contacts");
|
||||
return ret;
|
||||
}
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "file_transfers.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
struct LastOnline {
|
||||
uint64_t last_on;
|
||||
@ -35,7 +35,7 @@ struct LastOnline {
|
||||
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
|
||||
};
|
||||
|
||||
struct GroupChatInvite {
|
||||
struct ConferenceInvite {
|
||||
char *key;
|
||||
uint16_t length;
|
||||
uint8_t type;
|
||||
@ -57,7 +57,7 @@ typedef struct {
|
||||
Tox_User_Status status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct GroupChatInvite group_invite;
|
||||
struct ConferenceInvite conference_invite;
|
||||
|
||||
struct FileTransfer file_receiver[MAX_FILES];
|
||||
struct FileTransfer file_sender[MAX_FILES];
|
||||
@ -82,6 +82,7 @@ typedef struct {
|
||||
} FriendsList;
|
||||
|
||||
ToxWindow *new_friendlist(void);
|
||||
void friendlist_onInit(ToxWindow *self, Tox *m);
|
||||
void disable_chatwin(uint32_t f_num);
|
||||
int get_friendnum(uint8_t *name);
|
||||
int load_blocklist(char *data);
|
||||
|
@ -23,24 +23,25 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "friendlist.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "help.h"
|
||||
#include "term_mplex.h"
|
||||
#include "avatars.h"
|
||||
#include "conference.h"
|
||||
#include "friendlist.h"
|
||||
#include "help.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "misc_tools.h"
|
||||
#include "name_lookup.h"
|
||||
#include "prompt.h"
|
||||
#include "qr_code.h"
|
||||
#include "term_mplex.h"
|
||||
#include "toxic.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern ToxWindow *prompt;
|
||||
extern FriendsList Friends;
|
||||
extern FriendRequests FrndRequests;
|
||||
|
||||
/* command functions */
|
||||
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -48,19 +49,19 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int req = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FrndRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -68,14 +69,16 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
|
||||
|
||||
if (err != TOX_ERR_FRIEND_ADD_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to add friend (error %d\n)", err);
|
||||
return;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
|
||||
on_friend_added(m, friendnum, true);
|
||||
}
|
||||
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
FrndRequests.request[req] = (struct friend_request) {
|
||||
0
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
@ -87,7 +90,6 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
|
||||
FrndRequests.max_idx = i;
|
||||
--FrndRequests.num_requests;
|
||||
|
||||
}
|
||||
|
||||
void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg)
|
||||
@ -139,7 +141,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
|
||||
break;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
}
|
||||
|
||||
void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -147,7 +149,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -156,7 +158,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
|
||||
if (argc > 1) {
|
||||
if (argv[2][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Message must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -190,7 +192,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
xx[2] = '\0';
|
||||
|
||||
if (sscanf(xx, "%02x", &x) != 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid Tox ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -198,7 +200,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
}
|
||||
|
||||
if (friend_is_blocked(id_bin)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -214,7 +216,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
|
||||
if (argc != 1 || strlen(argv[1]) < 3) {
|
||||
avatar_unset(m);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -223,7 +225,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
int len = strlen(path);
|
||||
|
||||
if (len <= 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -232,13 +234,13 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
get_file_name(filename, sizeof(filename), path);
|
||||
|
||||
if (avatar_set(m, path, len) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
|
||||
MAX_AVATAR_FILE_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Avatar set to '%s'", filename);
|
||||
}
|
||||
|
||||
void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -256,7 +258,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc != 3) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -267,14 +269,14 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
long int port = strtol(port_str, NULL, 10);
|
||||
|
||||
if (port <= 0 || port > MAX_PORT_RANGE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid port.");
|
||||
return;
|
||||
}
|
||||
|
||||
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
|
||||
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -284,15 +286,15 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_BOOTSTRAP_BAD_HOST:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid IP.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_BAD_PORT:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed: Invalid port.");
|
||||
break;
|
||||
|
||||
case TOX_ERR_BOOTSTRAP_NULL:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -306,23 +308,25 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
UNUSED_VAR(m);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int req = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!FrndRequests.request[req].active) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
|
||||
FrndRequests.request[req] = (struct friend_request) {
|
||||
0
|
||||
};
|
||||
|
||||
int i;
|
||||
|
||||
@ -336,17 +340,17 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
|
||||
--FrndRequests.num_requests;
|
||||
}
|
||||
|
||||
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify group type: text | audio");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -357,31 +361,53 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
||||
} else if (!strcasecmp(argv[1], "text")) {
|
||||
type = TOX_CONFERENCE_TYPE_TEXT;
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||
uint32_t conferencenum = 0;
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||
Tox_Err_Conference_New err;
|
||||
|
||||
conferencenum = tox_conference_new(m, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
}
|
||||
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
#ifdef AUDIO
|
||||
conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL);
|
||||
|
||||
if (conferencenum == (uint32_t) -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_New err;
|
||||
#ifdef AUDIO
|
||||
|
||||
uint32_t groupnum = tox_conference_new(m, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
if (!init_conference_audio_input(m, conferencenum)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||
}
|
||||
}
|
||||
|
||||
if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat [%d] created.", groupnum);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum);
|
||||
}
|
||||
|
||||
void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -398,44 +424,30 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
|
||||
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *swch = argv[1];
|
||||
|
||||
if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
int log_ret = -1;
|
||||
|
||||
if (self->is_chat) {
|
||||
Friends.list[self->num].logging_on = true;
|
||||
log_ret = log_enable(self->name, myid, Friends.list[self->num].pub_key, log, LOG_CHAT);
|
||||
} else if (self->is_prompt) {
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_PROMPT);
|
||||
} else if (self->is_groupchat) {
|
||||
log_ret = log_enable(self->name, myid, NULL, log, LOG_GROUP);
|
||||
}
|
||||
|
||||
msg = log_ret == 0 ? "Logging enabled." : "Warning: Log failed to initialize.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log.";
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
|
||||
if (self->is_chat) {
|
||||
if (self->type == WINDOW_TYPE_CHAT) {
|
||||
Friends.list[self->num].logging_on = false;
|
||||
}
|
||||
|
||||
log_disable(log);
|
||||
|
||||
msg = "Logging disabled.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
}
|
||||
|
||||
void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -449,11 +461,11 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||
|
||||
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to print ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
|
||||
}
|
||||
|
||||
#ifdef QRCODE
|
||||
@ -466,7 +478,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
tox_self_get_address(m, (uint8_t *) bin_id);
|
||||
|
||||
if (bin_id_to_string(bin_id, sizeof(bin_id), id_string, sizeof(id_string)) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -476,45 +488,79 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
nick[nick_len] = '\0';
|
||||
|
||||
size_t data_file_len = strlen(DATA_FILE);
|
||||
char dir[data_file_len + 1];
|
||||
char *dir = malloc(data_file_len + 1);
|
||||
|
||||
if (dir == NULL) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory.");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
|
||||
|
||||
#ifdef QRPNG
|
||||
|
||||
if (argc == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
|
||||
free(dir);
|
||||
return;
|
||||
} else if (!strcmp(argv[1], "txt")) {
|
||||
|
||||
#endif /* QRPNG */
|
||||
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
|
||||
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||
size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT);
|
||||
char *qr_path = malloc(qr_path_buf_size);
|
||||
|
||||
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
if (qr_path == NULL) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
|
||||
free(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
|
||||
|
||||
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
free(dir);
|
||||
free(qr_path);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||
|
||||
free(qr_path);
|
||||
|
||||
#ifdef QRPNG
|
||||
} else if (!strcmp(argv[1], "png")) {
|
||||
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1];
|
||||
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
|
||||
size_t qr_path_buf_size = dir_len + nick_len + sizeof(QRCODE_FILENAME_EXT_PNG);
|
||||
char *qr_path = malloc(qr_path_buf_size);
|
||||
|
||||
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
if (qr_path == NULL) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
|
||||
free(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
|
||||
|
||||
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
|
||||
free(dir);
|
||||
free(qr_path);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
|
||||
|
||||
free(qr_path);
|
||||
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
|
||||
free(dir);
|
||||
return;
|
||||
}
|
||||
|
||||
#endif /* QRPNG */
|
||||
|
||||
free(dir);
|
||||
}
|
||||
#endif /* QRCODE */
|
||||
|
||||
@ -523,7 +569,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -532,7 +578,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
size_t len = strlen(nick);
|
||||
|
||||
if (!valid_nick(nick)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -550,7 +596,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -565,7 +611,7 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
nospam = strtol(argv[1], NULL, 16);
|
||||
|
||||
if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid nospam value.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -573,12 +619,12 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
uint32_t old_nospam = tox_self_get_nospam(m);
|
||||
tox_self_set_nospam(m, (uint32_t) nospam);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
|
||||
cmd_myid(window, self, m, 0, NULL);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Any services that relied on your old ID will need to be updated manually.");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
|
||||
old_nospam);
|
||||
}
|
||||
|
||||
@ -610,7 +656,7 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
UNUSED_VAR(argv);
|
||||
|
||||
if (FrndRequests.num_requests == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -630,11 +676,11 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
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", FrndRequests.request[i].msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg);
|
||||
|
||||
if (++count < FrndRequests.num_requests) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -649,7 +695,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
|
||||
if (argc < 1) {
|
||||
errmsg = "Require a status. Statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
@ -664,13 +710,13 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
status = TOX_USER_STATUS_BUSY;
|
||||
} else {
|
||||
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
tox_self_set_status(m, status);
|
||||
prompt_update_status(prompt, status);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
|
||||
|
||||
|
||||
finish:
|
||||
|
@ -23,8 +23,8 @@
|
||||
#ifndef GLOBAL_COMMANDS_H
|
||||
#define GLOBAL_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
@ -32,7 +32,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
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_conference(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]);
|
||||
#ifdef QRCODE
|
||||
|
@ -1,81 +0,0 @@
|
||||
/* group_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
Tox_Err_Conference_Title err;
|
||||
char title[MAX_STR_SIZE];
|
||||
|
||||
if (argc < 1) {
|
||||
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(title, sizeof(title), "%s", argv[1]);
|
||||
int len = strlen(title);
|
||||
|
||||
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
set_window_title(self, title, len);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
770
src/groupchat.c
770
src/groupchat.c
@ -1,770 +0,0 @@
|
||||
/* groupchat.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "execute.h"
|
||||
#include "misc_tools.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
#include "audio_device.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
|
||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
||||
static int max_groupchat_index = 0;
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
#ifdef PYTHON
|
||||
#define AC_NUM_GROUP_COMMANDS_PYTHON 1
|
||||
#else
|
||||
#define AC_NUM_GROUP_COMMANDS_PYTHON 0
|
||||
#endif /* PYTHON */
|
||||
#ifdef QRCODE
|
||||
#define AC_NUM_GROUP_COMMANDS_QRCODE 1
|
||||
#else
|
||||
#define AC_NUM_GROUP_COMMANDS_QRCODE 0
|
||||
#endif /* QRCODE */
|
||||
#define AC_NUM_GROUP_COMMANDS (19 + AC_NUM_GROUP_COMMANDS_PYTHON + AC_NUM_GROUP_COMMANDS_QRCODE)
|
||||
|
||||
/* Array of groupchat command names used for tab completion. */
|
||||
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/accept" },
|
||||
{ "/add" },
|
||||
{ "/avatar" },
|
||||
{ "/clear" },
|
||||
{ "/close" },
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/group" },
|
||||
{ "/help" },
|
||||
{ "/log" },
|
||||
{ "/myid" },
|
||||
#ifdef QRCODE
|
||||
{ "/myqr" },
|
||||
#endif /* QRCODE */
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/nospam" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
{ "/title" },
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
{ "/run" },
|
||||
|
||||
#endif /* PYTHON */
|
||||
};
|
||||
|
||||
static ToxWindow *new_group_chat(uint32_t groupnum);
|
||||
|
||||
static void kill_groupchat_window(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
log_disable(ctx->log);
|
||||
line_info_cleanup(ctx->hst);
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(ctx->sidebar);
|
||||
free(ctx->log);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
del_window(self);
|
||||
}
|
||||
|
||||
int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title,
|
||||
size_t title_length)
|
||||
{
|
||||
if (groupnum > MAX_GROUPCHAT_NUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ToxWindow *self = new_group_chat(groupnum);
|
||||
|
||||
for (int i = 0; i <= max_groupchat_index; ++i) {
|
||||
if (!groupchats[i].active) {
|
||||
groupchats[i].chatwin = add_window(m, self);
|
||||
groupchats[i].active = true;
|
||||
groupchats[i].num_peers = 0;
|
||||
groupchats[i].type = type;
|
||||
groupchats[i].start_time = get_unix_time();
|
||||
|
||||
set_active_window_index(groupchats[i].chatwin);
|
||||
set_window_title(self, title, title_length);
|
||||
|
||||
if (i == max_groupchat_index) {
|
||||
++max_groupchat_index;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
kill_groupchat_window(self);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void free_groupchat(ToxWindow *self, uint32_t groupnum)
|
||||
{
|
||||
free(groupchats[groupnum].name_list);
|
||||
free(groupchats[groupnum].peer_list);
|
||||
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = max_groupchat_index; i > 0; --i) {
|
||||
if (groupchats[i - 1].active) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
max_groupchat_index = i;
|
||||
kill_groupchat_window(self);
|
||||
}
|
||||
|
||||
static void delete_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||
{
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
free_groupchat(self, groupnum);
|
||||
}
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
endwin();
|
||||
refresh();
|
||||
clear();
|
||||
|
||||
int x2, y2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
y2 -= 2;
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->sidebar) {
|
||||
delwin(ctx->sidebar);
|
||||
ctx->sidebar = NULL;
|
||||
}
|
||||
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(self->window);
|
||||
|
||||
self->window = newwin(y2, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
|
||||
if (self->show_peerlist) {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
} else {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
}
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
|
||||
}
|
||||
|
||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
Tox_Message_Type type, const char *msg, size_t len)
|
||||
{
|
||||
UNUSED_VAR(len);
|
||||
|
||||
if (self->num != groupnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||
|
||||
/* Only play sound if mentioned by someone else */
|
||||
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
||||
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
|
||||
|
||||
if (self->active_box != -1) {
|
||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
|
||||
} else {
|
||||
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
|
||||
}
|
||||
|
||||
nick_clr = RED;
|
||||
} else {
|
||||
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg);
|
||||
write_to_log(msg, nick, ctx->log, false);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
const char *title,
|
||||
size_t length)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (self->num != groupnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_window_title(self, title, length);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
/* don't announce title when we join the room */
|
||||
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
static void group_update_name_list(uint32_t groupnum)
|
||||
{
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (!chat->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (chat->name_list) {
|
||||
free(chat->name_list);
|
||||
}
|
||||
|
||||
chat->name_list = malloc(sizeof(char *) * chat->num_peers * TOX_MAX_NAME_LENGTH);
|
||||
|
||||
if (chat->name_list == NULL) {
|
||||
exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
uint32_t i, count = 0;
|
||||
|
||||
for (i = 0; i < chat->max_idx; ++i) {
|
||||
if (chat->peer_list[i].active) {
|
||||
memcpy(&chat->name_list[count * TOX_MAX_NAME_LENGTH], chat->peer_list[i].name, chat->peer_list[i].name_length + 1);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(chat->name_list, count, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
||||
}
|
||||
|
||||
/* Reallocates groupnum's peer list. Increase is true if the list needs to grow.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
static int realloc_peer_list(GroupChat *chat, uint32_t num_peers)
|
||||
{
|
||||
if (!chat) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (num_peers == 0) {
|
||||
free(chat->peer_list);
|
||||
chat->peer_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct GroupPeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct GroupPeer));
|
||||
|
||||
if (!tmp_list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
chat->peer_list = tmp_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_peer_list(Tox *m, uint32_t groupnum, uint32_t num_peers)
|
||||
{
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (!chat->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
realloc_peer_list(chat, num_peers);
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < num_peers; ++i) {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
Tox_Err_Conference_Peer_Query err;
|
||||
size_t length = tox_conference_peer_get_name_size(m, groupnum, i, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK || length >= TOX_MAX_NAME_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tox_conference_peer_get_name(m, groupnum, i, (uint8_t *) peer->name, &err);
|
||||
peer->name[length] = 0;
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->active = true;
|
||||
peer->name_length = length;
|
||||
peer->peernumber = i;
|
||||
}
|
||||
|
||||
group_update_name_list(groupnum);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||
{
|
||||
if (self->num != groupnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (groupnum > max_groupchat_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (!chat->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Peer_Query err;
|
||||
|
||||
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
|
||||
uint32_t old_num = chat->num_peers;
|
||||
|
||||
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
chat->num_peers = num_peers;
|
||||
} else {
|
||||
num_peers = old_num;
|
||||
}
|
||||
|
||||
chat->max_idx = num_peers;
|
||||
update_peer_list(m, groupnum, num_peers);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupPeerNameChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
const char *name, size_t length)
|
||||
{
|
||||
UNUSED_VAR(length);
|
||||
|
||||
if (self->num != groupnum) {
|
||||
return;
|
||||
}
|
||||
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (!chat->active) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < chat->max_idx; ++i) {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
// Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting)
|
||||
if (peer->active && peer->peernumber == peernum && peer->name_length > 0) {
|
||||
ChatContext *ctx = self->chatwin;
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name);
|
||||
|
||||
write_to_log(tmp_event, peer->name, ctx->log, true);
|
||||
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
groupchat_onGroupNameListChange(self, m, groupnum);
|
||||
}
|
||||
|
||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||
{
|
||||
if (action == NULL) {
|
||||
wprintw(ctx->history, "Invalid syntax.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Send_Message err;
|
||||
|
||||
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if input is recognized by handler
|
||||
*/
|
||||
static bool groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int x, y, y2, x2;
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
UNUSED_VAR(y);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (self->help->active) {
|
||||
help_onKey(self, key);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ctx->pastemode && key == '\r') {
|
||||
key = '\n';
|
||||
}
|
||||
|
||||
if (ltr || key == '\n') { /* char is printable */
|
||||
input_new_char(self, key, x, x2);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (line_info_onKey(self, key)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input_handle(self, key, x, x2)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool input_ret = false;
|
||||
|
||||
if (key == '\t') { /* TAB key: auto-completes peer name or command */
|
||||
input_ret = true;
|
||||
|
||||
if (ctx->len > 0) {
|
||||
int diff;
|
||||
|
||||
/* TODO: make this not suck */
|
||||
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
|
||||
diff = complete_line(self, groupchats[self->num].name_list, groupchats[self->num].num_peers,
|
||||
TOX_MAX_NAME_LENGTH);
|
||||
} else if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/run");
|
||||
}
|
||||
|
||||
#endif
|
||||
else {
|
||||
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
}
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else if (key == T_KEY_C_DOWN) { /* Scroll peerlist up and down one position */
|
||||
input_ret = true;
|
||||
const int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||
|
||||
if (groupchats[self->num].side_pos < (int64_t) groupchats[self->num].num_peers - L) {
|
||||
++groupchats[self->num].side_pos;
|
||||
}
|
||||
} else if (key == T_KEY_C_UP) {
|
||||
input_ret = true;
|
||||
|
||||
if (groupchats[self->num].side_pos > 0) {
|
||||
--groupchats[self->num].side_pos;
|
||||
}
|
||||
} else if (key == '\r') {
|
||||
input_ret = true;
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
|
||||
if (!wstring_is_empty(ctx->line)) {
|
||||
add_line_to_hist(ctx);
|
||||
|
||||
wstrsubst(ctx->line, L'¶', L'\n');
|
||||
|
||||
char line[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
||||
memset(&line, 0, sizeof(line));
|
||||
}
|
||||
|
||||
if (line[0] == '/') {
|
||||
if (strcmp(line, "/close") == 0) {
|
||||
delete_groupchat(self, m, self->num);
|
||||
return true;
|
||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
||||
send_group_action(self, ctx, m, line + strlen("/me "));
|
||||
} else {
|
||||
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
||||
}
|
||||
} else {
|
||||
Tox_Err_Conference_Send_Message err;
|
||||
|
||||
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wclear(ctx->linewin);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
|
||||
return input_ret;
|
||||
}
|
||||
|
||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
UNUSED_VAR(m);
|
||||
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(self);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
|
||||
if (ctx->len > 0) {
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
}
|
||||
|
||||
wclear(ctx->sidebar);
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
|
||||
if (self->show_peerlist) {
|
||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
uint32_t num_peers = groupchats[self->num].num_peers;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wmove(ctx->sidebar, 0, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Peers: %"PRIu32"\n", num_peers);
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
|
||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
|
||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
uint32_t peer = i + groupchats[self->num].side_pos;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||
int maxlen = SIDEBAR_WIDTH - 2;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
memcpy(tmpnck, &groupchats[self->num].name_list[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
tmpnck[maxlen] = '\0';
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||
}
|
||||
}
|
||||
|
||||
int y, x;
|
||||
getyx(self->window, y, x);
|
||||
|
||||
UNUSED_VAR(x);
|
||||
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wnoutrefresh(self->window);
|
||||
|
||||
if (self->help->active) {
|
||||
help_onDraw(self);
|
||||
}
|
||||
}
|
||||
|
||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0) {
|
||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
|
||||
}
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
|
||||
ctx->hst = calloc(1, sizeof(struct history));
|
||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||
|
||||
if (ctx->log == NULL || ctx->hst == NULL) {
|
||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
}
|
||||
|
||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
}
|
||||
|
||||
static ToxWindow *new_group_chat(uint32_t groupnum)
|
||||
{
|
||||
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
|
||||
|
||||
if (ret == NULL) {
|
||||
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
ret->is_groupchat = true;
|
||||
|
||||
ret->onKey = &groupchat_onKey;
|
||||
ret->onDraw = &groupchat_onDraw;
|
||||
ret->onInit = &groupchat_onInit;
|
||||
ret->onGroupMessage = &groupchat_onGroupMessage;
|
||||
ret->onGroupNameListChange = &groupchat_onGroupNameListChange;
|
||||
ret->onGroupPeerNameChange = &groupchat_onGroupPeerNameChange;
|
||||
ret->onGroupTitleChange = &groupchat_onGroupTitleChange;
|
||||
|
||||
snprintf(ret->name, sizeof(ret->name), "Group %u", groupnum);
|
||||
|
||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||
Help *help = calloc(1, sizeof(Help));
|
||||
|
||||
if (chatwin == NULL || help == NULL) {
|
||||
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
ret->chatwin = chatwin;
|
||||
ret->help = help;
|
||||
|
||||
ret->num = groupnum;
|
||||
ret->show_peerlist = true;
|
||||
ret->active_box = -1;
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
/* groupchat.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROUPCHAT_H
|
||||
#define GROUPCHAT_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||
#define GROUP_EVENT_WAIT 3
|
||||
|
||||
typedef struct GroupPeer {
|
||||
bool active;
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
uint32_t peernumber;
|
||||
} GroupPeer;
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
time_t start_time;
|
||||
|
||||
GroupPeer *peer_list;
|
||||
uint32_t max_idx;
|
||||
|
||||
char *name_list;
|
||||
uint32_t num_peers;
|
||||
|
||||
} GroupChat;
|
||||
|
||||
/* Frees all Toxic associated data structures for a groupchat (does not call tox_conference_delete() ) */
|
||||
void free_groupchat(ToxWindow *self, uint32_t groupnum);
|
||||
|
||||
int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title, size_t title_length);
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self);
|
||||
|
||||
#endif /* GROUPCHAT_H */
|
75
src/help.c
75
src/help.c
@ -22,10 +22,10 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "help.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef PYTHON
|
||||
#include "api.h"
|
||||
@ -59,7 +59,10 @@ void help_init_menu(ToxWindow *self)
|
||||
static void help_exit(ToxWindow *self)
|
||||
{
|
||||
delwin(self->help->win);
|
||||
memset(self->help, 0, sizeof(Help));
|
||||
|
||||
*(self->help) = (struct Help) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
static void help_init_window(ToxWindow *self, int height, int width)
|
||||
@ -101,11 +104,11 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "hat commands\n");
|
||||
|
||||
wprintw(win, " g");
|
||||
wprintw(win, " c");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "r");
|
||||
wprintw(win, "o");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "oup commands\n");
|
||||
wprintw(win, "nference commands\n");
|
||||
|
||||
#ifdef PYTHON
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
@ -176,7 +179,7 @@ static void help_draw_global(ToxWindow *self)
|
||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
|
||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
||||
wprintw(win, " /conference <type> : Create a conference where type: text | audio\n");
|
||||
wprintw(win, " /myid : Print your Tox ID\n");
|
||||
#ifdef QRCODE
|
||||
#ifdef QRPNG
|
||||
@ -231,8 +234,8 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wprintw(win, "Chat Commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
||||
wprintw(win, " /join : Join a pending group chat\n");
|
||||
wprintw(win, " /invite <n> : Invite contact to a conference \n");
|
||||
wprintw(win, " /join : Join a pending conference\n");
|
||||
wprintw(win, " /sendfile <path> : Send a file\n");
|
||||
wprintw(win, " /savefile <id> : Receive a file\n");
|
||||
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
|
||||
@ -256,7 +259,9 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Video:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /video : Toggle video call\n");
|
||||
wprintw(win, " /res <width> <height> : Set video resolution\n");
|
||||
wprintw(win, " /vcall : Video call\n");
|
||||
wprintw(win, " /video : Toggle video in call\n");
|
||||
#endif /* VIDEO */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
@ -279,8 +284,8 @@ static void help_draw_keys(ToxWindow *self)
|
||||
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
||||
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
|
||||
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
|
||||
wprintw(win, " Ctrl+up and Ctrl+down : Scroll peer list in groupchats\n");
|
||||
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
|
||||
wprintw(win, " Ctrl+up and Ctrl+down : Scroll peer list in conference\n");
|
||||
wprintw(win, " Ctrl+B : Toggle the conference peerlist\n");
|
||||
wprintw(win, " Ctrl+J : Insert new line\n");
|
||||
wprintw(win, " Ctrl+T : Toggle paste mode\n\n");
|
||||
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
|
||||
@ -291,17 +296,27 @@ static void help_draw_keys(ToxWindow *self)
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_group(ToxWindow *self)
|
||||
static void help_draw_conference(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Group commands:\n");
|
||||
wprintw(win, "Conference commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
||||
wprintw(win, " /title <msg> : Show/set conference title\n");
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /audio <on> or <off> : Enable/disable audio in an audio conference\n");
|
||||
wprintw(win, " /mute : Toggle self audio mute status\n");
|
||||
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n");
|
||||
wprintw(win, " /ptt <on> or <off> : Toggle audio input Push-To-Talk (F2 to activate)\n");
|
||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
||||
#endif
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
@ -356,14 +371,14 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
int height;
|
||||
|
||||
switch (key) {
|
||||
case 'x':
|
||||
case L'x':
|
||||
case T_KEY_ESC:
|
||||
help_exit(self);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case L'c':
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 23, 80);
|
||||
help_init_window(self, 25, 80);
|
||||
#elif AUDIO
|
||||
help_init_window(self, 20, 80);
|
||||
#else
|
||||
@ -372,7 +387,7 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
self->help->type = HELP_CHAT;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case L'g':
|
||||
height = 22;
|
||||
#ifdef VIDEO
|
||||
height += 8;
|
||||
@ -386,30 +401,34 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
help_init_window(self, 6, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
case L'o':
|
||||
height = 6;
|
||||
#ifdef AUDIO
|
||||
height += 7;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_CONFERENCE;
|
||||
break;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
case 'p':
|
||||
case L'p':
|
||||
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
|
||||
self->help->type = HELP_PLUGIN;
|
||||
break;
|
||||
#endif /* PYTHON */
|
||||
|
||||
case 'f':
|
||||
case L'f':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_CONTACTS;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case L'k':
|
||||
help_init_window(self, 15, 80);
|
||||
self->help->type = HELP_KEYS;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
case L'm':
|
||||
help_init_menu(self);
|
||||
self->help->type = HELP_MENU;
|
||||
break;
|
||||
@ -439,8 +458,8 @@ void help_onDraw(ToxWindow *self)
|
||||
help_draw_contacts(self);
|
||||
break;
|
||||
|
||||
case HELP_GROUP:
|
||||
help_draw_group(self);
|
||||
case HELP_CONFERENCE:
|
||||
help_draw_conference(self);
|
||||
break;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
@ -30,7 +30,7 @@ typedef enum {
|
||||
HELP_MENU,
|
||||
HELP_GLOBAL,
|
||||
HELP_CHAT,
|
||||
HELP_GROUP,
|
||||
HELP_CONFERENCE,
|
||||
HELP_KEYS,
|
||||
HELP_CONTACTS,
|
||||
#ifdef PYTHON
|
||||
|
14
src/input.c
14
src/input.c
@ -26,14 +26,14 @@
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "conference.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "groupchat.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
@ -334,9 +334,9 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
|
||||
maybe convert entire function to if/else and make them all customizable keys? */
|
||||
if (!match) {
|
||||
if (key == user_settings->key_toggle_peerlist) {
|
||||
if (self->is_groupchat) {
|
||||
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
||||
self->show_peerlist ^= 1;
|
||||
redraw_groupchat_win(self);
|
||||
redraw_conference_win(self);
|
||||
}
|
||||
|
||||
match = true;
|
||||
|
530
src/line_info.c
530
src/line_info.c
@ -20,19 +20,19 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "conference.h"
|
||||
#include "line_info.h"
|
||||
#include "groupchat.h"
|
||||
#include "settings.h"
|
||||
#include "notify.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
@ -46,7 +46,7 @@ void line_info_init(struct history *hst)
|
||||
|
||||
hst->line_start = hst->line_root;
|
||||
hst->line_end = hst->line_start;
|
||||
hst->queue_sz = 0;
|
||||
hst->queue_size = 0;
|
||||
}
|
||||
|
||||
/* resets line_start (moves to end of chat history) */
|
||||
@ -54,27 +54,28 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
struct line_info *line = hst->line_end;
|
||||
|
||||
if (line->prev == NULL) {
|
||||
if (line == NULL || line->prev == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
int y2, x2;
|
||||
int y2;
|
||||
int x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
UNUSED_VAR(x2);
|
||||
|
||||
int side_offst = self->show_peerlist ? SIDEBAR_WIDTH : 0;
|
||||
int top_offst = self->is_chat || self->is_prompt ? 2 : 0;
|
||||
int max_y = (y2 - CHATBOX_HEIGHT - top_offst);
|
||||
int top_offst = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||
int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT - top_offst;
|
||||
|
||||
int curlines = 0;
|
||||
int nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
uint16_t curlines = 0;
|
||||
|
||||
do {
|
||||
curlines += 1 + nxtlines;
|
||||
curlines += line->format_lines;
|
||||
line = line->prev;
|
||||
nxtlines = line->newlines + (line->len / (x2 - side_offst));
|
||||
} while (line->prev && curlines + nxtlines < max_y);
|
||||
} while (line->prev && curlines + line->format_lines <= max_y);
|
||||
|
||||
hst->line_start = line;
|
||||
|
||||
self->scroll_pause = false;
|
||||
}
|
||||
|
||||
void line_info_cleanup(struct history *hst)
|
||||
@ -87,9 +88,7 @@ void line_info_cleanup(struct history *hst)
|
||||
tmp1 = tmp2;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hst->queue_sz; ++i) {
|
||||
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||
if (hst->queue[i]) {
|
||||
free(hst->queue[i]);
|
||||
}
|
||||
@ -117,29 +116,230 @@ static void line_info_root_fwd(struct history *hst)
|
||||
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
|
||||
static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
{
|
||||
if (hst->queue_sz <= 0) {
|
||||
if (hst->queue_size == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct line_info *line = hst->queue[0];
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hst->queue_sz; ++i) {
|
||||
for (size_t i = 0; i < hst->queue_size; ++i) {
|
||||
hst->queue[i] = hst->queue[i + 1];
|
||||
}
|
||||
|
||||
--hst->queue_sz;
|
||||
--hst->queue_size;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
/* Prints a maximum of `n` chars from `s` to `win`.
|
||||
*
|
||||
* Return 1 if the string contains a newline byte.
|
||||
* Return 0 if string does not contain a newline byte.
|
||||
* Return -1 if printing was aborted.
|
||||
*/
|
||||
static int print_n_chars(WINDOW *win, const char *s, size_t n, int max_y)
|
||||
{
|
||||
bool newline = false;
|
||||
char ch;
|
||||
|
||||
for (size_t i = 0; i < n && (ch = s[i]); ++i) {
|
||||
if (ch == '\n') {
|
||||
newline = true;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
UNUSED_VAR(x);
|
||||
getyx(win, y, x);
|
||||
|
||||
// make sure cursor will wrap correctly after newline to prevent display bugs
|
||||
if (y + 1 >= max_y) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (win) {
|
||||
wprintw(win, "%c", ch);
|
||||
}
|
||||
}
|
||||
|
||||
return newline;
|
||||
}
|
||||
|
||||
/* Returns the index of the last space character in `s` found before `limit`.
|
||||
* Returns -1 if no space is found.
|
||||
*/
|
||||
static int rspace_index(const char *s, int limit)
|
||||
{
|
||||
for (int i = limit; i >= 0; --i) {
|
||||
if (s[i] == ' ') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns the first index in `s` containing a newline byte found before `limit`.
|
||||
* Returns -1 if no newline is found.
|
||||
*/
|
||||
static int newline_index(const char *s, int limit)
|
||||
{
|
||||
char ch;
|
||||
|
||||
for (int i = 0; i < limit && (ch = s[i]); ++i) {
|
||||
if (ch == '\n') {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Returns the number of newline bytes in `s` */
|
||||
static unsigned int newline_count(const char *s)
|
||||
{
|
||||
char ch;
|
||||
unsigned int count = 0;
|
||||
|
||||
for (size_t i = 0; (ch = s[i]); ++i) {
|
||||
if (ch == '\n') {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/* Prints `line` message to window, wrapping at the last word that fits on the current line.
|
||||
* This function updates the `format_lines` field of `line` according to current window dimensions.
|
||||
*
|
||||
* If `win` is null nothing will be printed to the window. This is useful to set the
|
||||
* `format_lines` field on initialization.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 if not all characters in line's message were printed to screen.
|
||||
*/
|
||||
static int print_wrap(WINDOW *win, struct line_info *line, int max_x, int max_y)
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
UNUSED_VAR(y);
|
||||
|
||||
const char *msg = line->msg;
|
||||
uint16_t length = line->msg_len;
|
||||
uint16_t lines = 0;
|
||||
const int x_start = line->len - line->msg_len - 1; // manually keep track of x position because ncurses sucks
|
||||
int x_limit = max_x - x_start;
|
||||
|
||||
if (x_limit <= 1) {
|
||||
fprintf(stderr, "Warning: x_limit <= 0 in print_wrap(): %d\n", x_limit);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (msg) {
|
||||
getyx(win, y, x);
|
||||
|
||||
// next line would print past window limit so we abort; we don't want to update format_lines
|
||||
if (x > x_start) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length < x_limit) {
|
||||
int p_ret = print_n_chars(win, msg, length, max_y);
|
||||
|
||||
if (p_ret == 1) {
|
||||
lines += newline_count(msg);
|
||||
} else if (p_ret == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++lines;
|
||||
break;
|
||||
}
|
||||
|
||||
int newline_idx = newline_index(msg, x_limit - 1);
|
||||
|
||||
if (newline_idx >= 0) {
|
||||
if (print_n_chars(win, msg, newline_idx + 1, max_y) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg += (newline_idx + 1);
|
||||
length -= (newline_idx + 1);
|
||||
x_limit = max_x; // if we find a newline we stop adding column padding for rest of message
|
||||
++lines;
|
||||
continue;
|
||||
}
|
||||
|
||||
int space_idx = rspace_index(msg, x_limit - 1);
|
||||
|
||||
if (space_idx >= 1) {
|
||||
if (print_n_chars(win, msg, space_idx, max_y) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg += space_idx + 1;
|
||||
length -= (space_idx + 1);
|
||||
|
||||
if (win) {
|
||||
waddch(win, '\n');
|
||||
}
|
||||
} else {
|
||||
if (print_n_chars(win, msg, x_limit, max_y) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
msg += x_limit;
|
||||
length -= x_limit;
|
||||
}
|
||||
|
||||
// Add padding to the start of the next line
|
||||
if (win && x_limit < max_x) {
|
||||
for (size_t i = 0; i < x_start; ++i) {
|
||||
waddch(win, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
++lines;
|
||||
}
|
||||
|
||||
if (win && line->noread_flag) {
|
||||
getyx(win, y, x);
|
||||
|
||||
if (x >= max_x - 1 || x == x_start) {
|
||||
++lines;
|
||||
}
|
||||
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x");
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
}
|
||||
|
||||
line->format_lines = lines;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void line_info_init_line(ToxWindow *self, struct line_info *line)
|
||||
{
|
||||
int y2;
|
||||
int x2;
|
||||
UNUSED_VAR(y2);
|
||||
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||
|
||||
print_wrap(NULL, line, max_x, max_y);
|
||||
}
|
||||
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
*
|
||||
* Returns the id of the new line.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||
{
|
||||
if (!self) {
|
||||
@ -148,7 +348,7 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
||||
|
||||
struct history *hst = self->chatwin->hst;
|
||||
|
||||
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) {
|
||||
if (hst->queue_size >= MAX_LINE_INFO_QUEUE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -158,7 +358,8 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
||||
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
|
||||
char frmt_msg[MAX_LINE_INFO_MSG_SIZE];
|
||||
frmt_msg[0] = 0;
|
||||
|
||||
va_list args;
|
||||
va_start(args, msg);
|
||||
@ -173,33 +374,33 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
||||
|
||||
/* fallthrough */
|
||||
case OUT_ACTION:
|
||||
len += strlen(user_settings->line_normal) + 2;
|
||||
len += strlen(user_settings->line_normal) + 2; // two spaces
|
||||
break;
|
||||
|
||||
case IN_MSG:
|
||||
|
||||
/* fallthrough */
|
||||
case OUT_MSG:
|
||||
len += strlen(user_settings->line_normal) + 3;
|
||||
len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
len += strlen(user_settings->line_join) + 2;
|
||||
len += strlen(user_settings->line_join) + 2; // two spaces
|
||||
break;
|
||||
|
||||
case DISCONNECTION:
|
||||
len += strlen(user_settings->line_quit) + 2;
|
||||
len += strlen(user_settings->line_quit) + 2; // two spaces
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
break;
|
||||
|
||||
case NAME_CHANGE:
|
||||
len += strlen(user_settings->line_alert) + 1;
|
||||
len += strlen(user_settings->line_alert) + 2; // two spaces
|
||||
break;
|
||||
|
||||
case PROMPT:
|
||||
++len;
|
||||
len += 2; // '$' char and a space
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -207,21 +408,16 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t msg_len = 0;
|
||||
|
||||
if (frmt_msg[0]) {
|
||||
snprintf(new_line->msg, sizeof(new_line->msg), "%s", frmt_msg);
|
||||
len += strlen(new_line->msg);
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; frmt_msg[i]; ++i) {
|
||||
if (frmt_msg[i] == '\n') {
|
||||
++new_line->newlines;
|
||||
}
|
||||
}
|
||||
msg_len = strlen(new_line->msg);
|
||||
len += msg_len;
|
||||
}
|
||||
|
||||
if (timestr) {
|
||||
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr);
|
||||
if (show_timestamp) {
|
||||
get_time_str(new_line->timestr, sizeof(new_line->timestr));
|
||||
len += strlen(new_line->timestr) + 1;
|
||||
}
|
||||
|
||||
@ -235,15 +431,18 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
|
||||
len += strlen(new_line->name2);
|
||||
}
|
||||
|
||||
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
|
||||
new_line->id = (hst->line_end->id + 1 + hst->queue_size) % INT_MAX;
|
||||
new_line->len = len;
|
||||
new_line->msg_len = msg_len;
|
||||
new_line->type = type;
|
||||
new_line->bold = bold;
|
||||
new_line->colour = colour;
|
||||
new_line->noread_flag = false;
|
||||
new_line->timestamp = get_unix_time();
|
||||
|
||||
hst->queue[hst->queue_sz++] = new_line;
|
||||
line_info_init_line(self, new_line);
|
||||
|
||||
hst->queue[hst->queue_size++] = new_line;
|
||||
|
||||
return new_line->id;
|
||||
}
|
||||
@ -267,27 +466,8 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
hst->line_end = line;
|
||||
hst->line_end->id = line->id;
|
||||
|
||||
int y, y2, x, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
getyx(self->chatwin->history, y, x);
|
||||
|
||||
UNUSED_VAR(x);
|
||||
|
||||
if (x2 <= SIDEBAR_WIDTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
|
||||
int lines = 1 + line->newlines + (line->len / (x2 - offst));
|
||||
int max_y = y2 - CHATBOX_HEIGHT;
|
||||
|
||||
/* move line_start forward proportionate to the number of new lines */
|
||||
if (y + lines - 1 >= max_y) {
|
||||
while (lines > 0 && hst->line_start->next) {
|
||||
lines -= 1 + hst->line_start->next->newlines + (hst->line_start->next->len / (x2 - offst));
|
||||
hst->line_start = hst->line_start->next;
|
||||
++hst->start_id;
|
||||
}
|
||||
if (!self->scroll_pause) {
|
||||
line_info_reset_start(self, hst);
|
||||
}
|
||||
}
|
||||
|
||||
@ -310,25 +490,44 @@ void line_info_print(ToxWindow *self)
|
||||
|
||||
wclear(win);
|
||||
|
||||
int y2, x2;
|
||||
int y2;
|
||||
|
||||
int x2;
|
||||
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= SIDEBAR_WIDTH) {
|
||||
if (x2 - 1 <= SIDEBAR_WIDTH) { // leave room on x axis for sidebar padding
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->is_groupchat) {
|
||||
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
||||
wmove(win, 0, 0);
|
||||
} else {
|
||||
wmove(win, 2, 0);
|
||||
wmove(win, TOP_BAR_HEIGHT, 0);
|
||||
}
|
||||
|
||||
struct line_info *line = hst->line_start->next;
|
||||
|
||||
int numlines = 0;
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int max_y = y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT;
|
||||
const int max_x = self->show_peerlist ? x2 - 1 - SIDEBAR_WIDTH : x2;
|
||||
uint16_t numlines = line->format_lines;
|
||||
int print_ret = 0;
|
||||
|
||||
while (line && numlines++ <= max_y && print_ret == 0) {
|
||||
int y;
|
||||
int x;
|
||||
UNUSED_VAR(y);
|
||||
|
||||
getyx(win, y, x);
|
||||
|
||||
if (x > 0) { // Prevents us from printing off the screen
|
||||
break;
|
||||
}
|
||||
|
||||
while (line && numlines++ <= y2) {
|
||||
uint8_t type = line->type;
|
||||
|
||||
switch (type) {
|
||||
@ -355,43 +554,32 @@ void line_info_print(ToxWindow *self)
|
||||
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
|
||||
wattroff(win, COLOR_PAIR(nameclr));
|
||||
|
||||
char *msg = line->msg;
|
||||
|
||||
while (msg) {
|
||||
char *line = strsep(&msg, "\n");
|
||||
|
||||
if (line[0] == '>') {
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
} else if (line[0] == '<') {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
}
|
||||
|
||||
wprintw(win, "%s%c", line, msg ? '\n' : '\0');
|
||||
|
||||
if (line[0] == '>') {
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
} else if (line[0] == '<') {
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
}
|
||||
|
||||
// change the \0 set by strsep back to \n
|
||||
if (msg) {
|
||||
msg[-1] = '\n';
|
||||
}
|
||||
if (line->msg[0] == 0) {
|
||||
waddch(win, '\n');
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
if (line->msg[0] == '>') {
|
||||
wattron(win, COLOR_PAIR(GREEN));
|
||||
} else if (line->msg[0] == '<') {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x");
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
}
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
|
||||
if (line->msg[0] == '>') {
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
} else if (line->msg[0] == '<') {
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
}
|
||||
|
||||
if (type == OUT_MSG && !line->read_flag) {
|
||||
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
waddch(win, '\n');
|
||||
break;
|
||||
|
||||
case OUT_ACTION_READ:
|
||||
@ -406,21 +594,17 @@ void line_info_print(ToxWindow *self)
|
||||
wattroff(win, COLOR_PAIR(BLUE));
|
||||
|
||||
wattron(win, COLOR_PAIR(YELLOW));
|
||||
wprintw(win, "%s %s %s", user_settings->line_normal, line->name1, line->msg);
|
||||
wprintw(win, "%s %s ", user_settings->line_normal, line->name1);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
wattroff(win, COLOR_PAIR(YELLOW));
|
||||
|
||||
if (type == OUT_ACTION && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
wattron(win, COLOR_PAIR(RED));
|
||||
wprintw(win, " x", line->msg);
|
||||
wattroff(win, COLOR_PAIR(RED));
|
||||
|
||||
if (line->noread_flag == false) {
|
||||
if (type == OUT_ACTION && !line->read_flag) {
|
||||
if (timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
|
||||
line->noread_flag = true;
|
||||
line->len += 2;
|
||||
}
|
||||
}
|
||||
|
||||
wprintw(win, "\n", line->msg);
|
||||
waddch(win, '\n');
|
||||
break;
|
||||
|
||||
case SYS_MSG:
|
||||
@ -438,7 +622,8 @@ void line_info_print(ToxWindow *self)
|
||||
wattron(win, COLOR_PAIR(line->colour));
|
||||
}
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
waddch(win, '\n');
|
||||
|
||||
if (line->bold) {
|
||||
wattroff(win, A_BOLD);
|
||||
@ -456,10 +641,10 @@ void line_info_print(ToxWindow *self)
|
||||
wattroff(win, COLOR_PAIR(GREEN));
|
||||
|
||||
if (line->msg[0]) {
|
||||
wprintw(win, "%s", line->msg);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
}
|
||||
|
||||
wprintw(win, "\n");
|
||||
waddch(win, '\n');
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
@ -474,7 +659,9 @@ void line_info_print(ToxWindow *self)
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
waddch(win, '\n');
|
||||
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
break;
|
||||
@ -491,7 +678,9 @@ void line_info_print(ToxWindow *self)
|
||||
wprintw(win, "%s ", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s\n", line->msg);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
waddch(win, '\n');
|
||||
|
||||
wattroff(win, COLOR_PAIR(line->colour));
|
||||
|
||||
break;
|
||||
@ -507,7 +696,7 @@ void line_info_print(ToxWindow *self)
|
||||
wprintw(win, "%s", line->name1);
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, "%s", line->msg);
|
||||
print_ret = print_wrap(win, line, max_x, max_y);
|
||||
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "%s\n", line->name2);
|
||||
@ -521,11 +710,43 @@ void line_info_print(ToxWindow *self)
|
||||
}
|
||||
|
||||
/* keep calling until queue is empty */
|
||||
if (hst->queue_sz > 0) {
|
||||
if (hst->queue_size > 0) {
|
||||
line_info_print(self);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if all lines starting from `line` can fit on the screen.
|
||||
*/
|
||||
static bool line_info_screen_fit(ToxWindow *self, struct line_info *line)
|
||||
{
|
||||
if (!line) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(self->chatwin->history, y2, x2);
|
||||
|
||||
UNUSED_VAR(x2);
|
||||
|
||||
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||
const int max_y = y2 - top_offset;
|
||||
|
||||
uint16_t lines = line->format_lines;
|
||||
|
||||
while (line) {
|
||||
if (lines > max_y) {
|
||||
return false;
|
||||
}
|
||||
|
||||
lines += line->format_lines;
|
||||
line = line->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* puts msg in specified line_info msg buffer */
|
||||
void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||
{
|
||||
@ -533,6 +754,9 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||
|
||||
while (line) {
|
||||
if (line->id == id) {
|
||||
size_t new_len = strlen(msg);
|
||||
line->len = line->len - line->msg_len + new_len;
|
||||
line->msg_len = new_len;
|
||||
snprintf(line->msg, sizeof(line->msg), "%s", msg);
|
||||
return;
|
||||
}
|
||||
@ -541,56 +765,74 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
|
||||
}
|
||||
}
|
||||
|
||||
/* static void line_info_goto_root(struct history *hst)
|
||||
{
|
||||
hst->line_start = hst->line_root;
|
||||
} */
|
||||
|
||||
static void line_info_scroll_up(struct history *hst)
|
||||
static void line_info_scroll_up(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
if (hst->line_start->prev) {
|
||||
hst->line_start = hst->line_start->prev;
|
||||
} else {
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
self->scroll_pause = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void line_info_scroll_down(struct history *hst)
|
||||
static void line_info_scroll_down(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
if (hst->line_start->next) {
|
||||
hst->line_start = hst->line_start->next;
|
||||
struct line_info *next = hst->line_start->next;
|
||||
|
||||
if (next && self->scroll_pause) {
|
||||
if (line_info_screen_fit(self, next->next)) {
|
||||
line_info_reset_start(self, hst);
|
||||
} else {
|
||||
hst->line_start = next;
|
||||
}
|
||||
} else {
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
line_info_reset_start(self, hst);
|
||||
}
|
||||
}
|
||||
|
||||
static void line_info_page_up(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
int x2, y2;
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
UNUSED_VAR(x2);
|
||||
|
||||
int jump_dist = y2 / 2;
|
||||
int i;
|
||||
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||
const int max_y = y2 - top_offset;
|
||||
size_t jump_dist = max_y / 2;
|
||||
|
||||
for (i = 0; i < jump_dist && hst->line_start->prev; ++i) {
|
||||
for (size_t i = 0; i < jump_dist && hst->line_start->prev; ++i) {
|
||||
hst->line_start = hst->line_start->prev;
|
||||
}
|
||||
|
||||
self->scroll_pause = true;
|
||||
}
|
||||
|
||||
static void line_info_page_down(ToxWindow *self, struct history *hst)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
if (!self->scroll_pause) {
|
||||
return;
|
||||
}
|
||||
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(self->chatwin->history, y2, x2);
|
||||
|
||||
UNUSED_VAR(x2);
|
||||
|
||||
int jump_dist = y2 / 2;
|
||||
int i;
|
||||
const int top_offset = (self->type == WINDOW_TYPE_CHAT) || (self->type == WINDOW_TYPE_PROMPT) ? TOP_BAR_HEIGHT : 0;
|
||||
const int max_y = y2 - top_offset;
|
||||
size_t jump_dist = max_y / 2;
|
||||
|
||||
for (i = 0; i < jump_dist && hst->line_start->next; ++i) {
|
||||
hst->line_start = hst->line_start->next;
|
||||
struct line_info *next = hst->line_start->next;
|
||||
|
||||
for (size_t i = 0; i < jump_dist && next; ++i) {
|
||||
if (line_info_screen_fit(self, next->next)) {
|
||||
line_info_reset_start(self, hst);
|
||||
break;
|
||||
}
|
||||
|
||||
hst->line_start = next;
|
||||
next = hst->line_start->next;
|
||||
}
|
||||
}
|
||||
|
||||
@ -604,9 +846,9 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
|
||||
} else if (key == user_settings->key_half_page_down) {
|
||||
line_info_page_down(self, hst);
|
||||
} else if (key == user_settings->key_scroll_line_up) {
|
||||
line_info_scroll_up(hst);
|
||||
line_info_scroll_up(self, hst);
|
||||
} else if (key == user_settings->key_scroll_line_down) {
|
||||
line_info_scroll_down(hst);
|
||||
line_info_scroll_down(self, hst);
|
||||
} else if (key == user_settings->key_page_bottom) {
|
||||
line_info_reset_start(self, hst);
|
||||
} else {
|
||||
|
@ -23,15 +23,15 @@
|
||||
#ifndef LINE_INFO_H
|
||||
#define LINE_INFO_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_HISTORY 100000
|
||||
#define MIN_HISTORY 40
|
||||
#define MAX_LINE_INFO_QUEUE 1024
|
||||
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
|
||||
#define MAX_LINE_INFO_MSG_SIZE (MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32) /* needs extra room for log loading */
|
||||
|
||||
typedef enum {
|
||||
typedef enum LINE_TYPE {
|
||||
SYS_MSG,
|
||||
IN_MSG,
|
||||
OUT_MSG,
|
||||
@ -54,10 +54,12 @@ struct line_info {
|
||||
uint8_t type;
|
||||
uint8_t bold;
|
||||
uint8_t colour;
|
||||
uint8_t noread_flag; /* true if a line should be flagged as unread */
|
||||
bool noread_flag; /* true if a line should be flagged as unread */
|
||||
bool read_flag; /* true if a message has been flagged as read */
|
||||
uint32_t id;
|
||||
uint16_t len; /* combined len of entire line */
|
||||
uint8_t newlines;
|
||||
uint16_t len; /* combined length of entire line */
|
||||
uint16_t msg_len; /* length of the message */
|
||||
uint16_t format_lines; /* number of lines the combined string takes up (dynamically set) */
|
||||
|
||||
struct line_info *prev;
|
||||
struct line_info *next;
|
||||
@ -71,7 +73,7 @@ struct history {
|
||||
uint32_t start_id; /* keeps track of where line_start should be when at bottom of history */
|
||||
|
||||
struct line_info *queue[MAX_LINE_INFO_QUEUE];
|
||||
int queue_sz;
|
||||
size_t queue_size;
|
||||
};
|
||||
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
@ -79,7 +81,7 @@ struct history {
|
||||
* Returns the id of the new line.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
int line_info_add(ToxWindow *self, bool show_timestamp, const char *name1, const char *name2, LINE_TYPE type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||
|
||||
/* Prints a section of history starting at line_start */
|
||||
|
264
src/log.c
264
src/log.c
@ -22,42 +22,46 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* There are three types of logs: chat logs, groupchat logs, and prompt logs (see LOG_TYPE in log.h)
|
||||
A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||
A chat log is in the format: LOGDIR/selfkey-friendname-otherkey.log
|
||||
A groupchat log is in the format: LOGDIR/selfkey-groupname-date[time].log
|
||||
|
||||
Only the first (KEY_IDENT_DIGITS * 2) numbers of the key are used.
|
||||
|
||||
Returns 0 on success, -1 if the path is too long */
|
||||
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
|
||||
/* Creates a log path and puts it in `dest.
|
||||
*
|
||||
* There are two types of logs: chat logs and prompt logs (see LOG_TYPE in log.h)
|
||||
* A prompt log is in the format: LOGDIR/selfkey-home.log
|
||||
* A chat log is in the format: LOGDIR/selfkey-name-otherkey.log
|
||||
*
|
||||
* For friend chats `otherkey` is the first 6 bytes of the friend's Tox ID.
|
||||
* For Conferences/groups `otherkey` is the first 6 bytes of the group's unique ID.
|
||||
*
|
||||
* Return path length on success.
|
||||
* Return -1 if the path is too long.
|
||||
*/
|
||||
static int get_log_path(char *dest, int destsize, const char *name, const char *selfkey, const char *otherkey)
|
||||
{
|
||||
if (!valid_nick(name)) {
|
||||
name = UNKNOWN_NAME;
|
||||
}
|
||||
|
||||
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
|
||||
const char *namedash = otherkey ? "-" : "";
|
||||
const char *set_path = user_settings->chatlogs_path;
|
||||
|
||||
char *user_config_dir = get_user_config_dir();
|
||||
int path_len = strlen(name) + strlen(".log") + strlen("-") + strlen(namedash);
|
||||
path_len += strlen(set_path) ? *set_path : strlen(user_config_dir) + strlen(LOGDIR);
|
||||
|
||||
/* first 6 digits of selfkey */
|
||||
char self_id[32];
|
||||
/* first 6 bytes of selfkey */
|
||||
char self_id[32] = {0};
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&self_id[0], "%02X", selfkey[0] & 0xff);
|
||||
sprintf(&self_id[2], "%02X", selfkey[1] & 0xff);
|
||||
@ -66,19 +70,13 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
|
||||
|
||||
char other_id[32] = {0};
|
||||
|
||||
switch (logtype) {
|
||||
case LOG_CHAT:
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
break;
|
||||
|
||||
case LOG_GROUP:
|
||||
strftime(other_id, sizeof(other_id), "%Y-%m-%d[%H:%M:%S]", get_time());
|
||||
path_len += strlen(other_id);
|
||||
break;
|
||||
if (otherkey) {
|
||||
/* first 6 bytes of otherkey */
|
||||
path_len += KEY_IDENT_DIGITS * 2;
|
||||
sprintf(&other_id[0], "%02X", otherkey[0] & 0xff);
|
||||
sprintf(&other_id[2], "%02X", otherkey[1] & 0xff);
|
||||
sprintf(&other_id[4], "%02X", otherkey[2] & 0xff);
|
||||
other_id[KEY_IDENT_DIGITS * 2] = '\0';
|
||||
}
|
||||
|
||||
if (path_len >= destsize) {
|
||||
@ -94,28 +92,35 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
|
||||
|
||||
free(user_config_dir);
|
||||
|
||||
return 0;
|
||||
return path_len;
|
||||
}
|
||||
|
||||
/* Opens log file or creates a new one */
|
||||
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
/* Initializes log path for `log`.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
static int init_logging_session(const char *name, const char *selfkey, const char *otherkey, struct chatlog *log,
|
||||
LOG_TYPE type)
|
||||
{
|
||||
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) {
|
||||
if (log == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (selfkey == NULL || (type == LOG_TYPE_CHAT && otherkey == NULL)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char log_path[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) {
|
||||
int path_len = get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey);
|
||||
|
||||
if (path_len == -1 || path_len >= sizeof(log->path)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log->file = fopen(log_path, "a+");
|
||||
snprintf(log->path, sizeof(log->path), "%s", log_path);
|
||||
|
||||
if (log->file == NULL) {
|
||||
return -1;
|
||||
}
|
||||
memcpy(log->path, log_path, path_len);
|
||||
log->path[path_len] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -124,6 +129,10 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth
|
||||
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
|
||||
{
|
||||
if (log == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!log->log_on) {
|
||||
return;
|
||||
}
|
||||
@ -154,92 +163,162 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
|
||||
|
||||
void log_disable(struct chatlog *log)
|
||||
{
|
||||
if (log->file != NULL) {
|
||||
fclose(log->file);
|
||||
if (log == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(log, 0, sizeof(struct chatlog));
|
||||
if (log->file != NULL) {
|
||||
fclose(log->file);
|
||||
log->file = NULL;
|
||||
}
|
||||
|
||||
log->lastwrite = 0;
|
||||
log->log_on = false;
|
||||
}
|
||||
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
|
||||
int log_enable(struct chatlog *log)
|
||||
{
|
||||
log->log_on = true;
|
||||
if (log == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (log->file != NULL) {
|
||||
if (log->log_on) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
|
||||
log_disable(log);
|
||||
if (*log->path == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (log->file != NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log->file = fopen(log->path, "a+");
|
||||
|
||||
if (log->file == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log->log_on = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||
/* Initializes a log. This function must be called before any other logging operations.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type)
|
||||
{
|
||||
if (log->file == NULL) {
|
||||
return;
|
||||
if (log == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (log->file != NULL || log->log_on) {
|
||||
fprintf(stderr, "Warning: Called log_init() on an already initialized log\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (init_logging_session(name, selfkey, otherkey, log, type) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_disable(log);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Loads chat log history and prints it to `self` window.
|
||||
*
|
||||
* Return 0 on success or if log file doesn't exist.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int load_chat_history(ToxWindow *self, struct chatlog *log)
|
||||
{
|
||||
if (log == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (*log->path == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
off_t sz = file_size(log->path);
|
||||
|
||||
if (sz <= 0) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *hstbuf = malloc(sz + 1);
|
||||
FILE *fp = fopen(log->path, "r");
|
||||
|
||||
if (hstbuf == NULL) {
|
||||
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(log->file, 0L, SEEK_SET) == -1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
char *buf = malloc(sz + 1);
|
||||
|
||||
if (buf == NULL) {
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(hstbuf, sz, 1, log->file) != 1) {
|
||||
free(hstbuf);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to read log file");
|
||||
return;
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hstbuf[sz] = '\0';
|
||||
if (fread(buf, sz, 1, fp) != 1) {
|
||||
free(buf);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
buf[sz] = 0;
|
||||
|
||||
/* Number of history lines to load: must not be larger than MAX_LINE_INFO_QUEUE - 2 */
|
||||
int L = MIN(MAX_LINE_INFO_QUEUE - 2, user_settings->history_size);
|
||||
int start, count = 0;
|
||||
|
||||
int start = 0;
|
||||
int count = 0;
|
||||
|
||||
/* start at end and backtrace L lines or to the beginning of buffer */
|
||||
for (start = sz - 1; start >= 0 && count < L; --start) {
|
||||
if (hstbuf[start] == '\n') {
|
||||
if (buf[start] == '\n') {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
const char *line = strtok(&hstbuf[start + 1], "\n");
|
||||
char *tmp = NULL;
|
||||
const char *line = strtok_r(&buf[start + 1], "\n", &tmp);
|
||||
|
||||
if (line == NULL) {
|
||||
free(hstbuf);
|
||||
return;
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (line != NULL && count--) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||
line = strtok(NULL, "\n");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
|
||||
line = strtok_r(NULL, "\n", &tmp);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
free(hstbuf);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---");
|
||||
|
||||
free(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||
/* Renames chatlog file `src` to `dest`.
|
||||
*
|
||||
* Return 0 on success or if no log exists.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum)
|
||||
{
|
||||
ToxWindow *toxwin = get_window_ptr(winnum);
|
||||
struct chatlog *log = NULL;
|
||||
@ -248,6 +327,11 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
||||
/* disable log if necessary and save its state */
|
||||
if (toxwin != NULL) {
|
||||
log = toxwin->chatwin->log;
|
||||
|
||||
if (log == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
log_on = log->log_on;
|
||||
}
|
||||
|
||||
@ -258,24 +342,36 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
||||
char newpath[MAX_STR_SIZE];
|
||||
char oldpath[MAX_STR_SIZE];
|
||||
|
||||
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) {
|
||||
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey) == -1) {
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (!file_exists(oldpath)) {
|
||||
init_logging_session(dest, selfkey, otherkey, log, LOG_TYPE_CHAT); // still need to rename path
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) {
|
||||
int new_path_len = get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey);
|
||||
|
||||
if (new_path_len == -1 || new_path_len >= MAX_STR_SIZE) {
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (rename(oldpath, newpath) != 0) {
|
||||
if (file_exists(newpath)) {
|
||||
if (remove(oldpath) != 0) {
|
||||
fprintf(stderr, "Warning: remove() failed to remove log path `%s`\n", oldpath);
|
||||
}
|
||||
} else if (rename(oldpath, newpath) != 0) {
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (log_on) {
|
||||
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
|
||||
if (log != NULL) {
|
||||
memcpy(log->path, newpath, new_path_len);
|
||||
log->path[new_path_len] = 0;
|
||||
|
||||
if (log_on) {
|
||||
log_enable(log);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -283,7 +379,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
|
||||
on_error:
|
||||
|
||||
if (log_on) {
|
||||
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
|
||||
log_enable(log);
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
35
src/log.h
35
src/log.h
@ -30,30 +30,43 @@ struct chatlog {
|
||||
bool log_on; /* specific to current chat window */
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LOG_GROUP,
|
||||
LOG_PROMPT,
|
||||
LOG_CHAT,
|
||||
typedef enum LOG_TYPE {
|
||||
LOG_TYPE_PROMPT,
|
||||
LOG_TYPE_CHAT,
|
||||
} LOG_TYPE;
|
||||
|
||||
/* Initializes a log. This function must be called before any other logging operations.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int log_init(struct chatlog *log, const char *name, const char *selfkey, const char *otherkey, LOG_TYPE type);
|
||||
|
||||
/* formats/writes line to log file */
|
||||
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event);
|
||||
|
||||
/* enables logging for specified log and creates/fetches file if necessary.
|
||||
/* enables logging for specified log.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int log_enable(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype);
|
||||
int log_enable(struct chatlog *log);
|
||||
|
||||
/* disables logging for specified log and closes file */
|
||||
void log_disable(struct chatlog *log);
|
||||
|
||||
/* Loads previous history from chat log */
|
||||
void load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||
/* Loads chat log history and prints it to `self` window.
|
||||
*
|
||||
* Return 0 on success or if log file doesn't exist.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int load_chat_history(ToxWindow *self, struct chatlog *log);
|
||||
|
||||
/* renames chatlog file replacing src with dest.
|
||||
Returns 0 on success or if no log exists, -1 on failure. */
|
||||
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||
/* Renames chatlog file `src` to `dest`.
|
||||
*
|
||||
* Return 0 on success or if no log exists.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int rename_logfile(const char *src, const char *dest, const char *selfkey, const char *otherkey, int winnum);
|
||||
|
||||
#endif /* LOG_H */
|
||||
|
@ -22,12 +22,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cqueue_cleanup(struct chat_queue *q)
|
||||
{
|
||||
@ -59,7 +59,7 @@ void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type,
|
||||
new_m->type = type;
|
||||
new_m->line_id = line_id;
|
||||
new_m->last_send_try = 0;
|
||||
new_m->receipt = 0;
|
||||
new_m->receipt = -1;
|
||||
new_m->next = NULL;
|
||||
|
||||
if (q->root == NULL) {
|
||||
@ -86,9 +86,9 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
||||
|
||||
line->type = msg->type == OUT_ACTION ? OUT_ACTION_READ : OUT_MSG_READ;
|
||||
|
||||
if (line->noread_flag == true) {
|
||||
line->len -= 2;
|
||||
if (line->noread_flag) {
|
||||
line->noread_flag = false;
|
||||
line->read_flag = true;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -98,6 +98,7 @@ static void cqueue_mark_read(ToxWindow *self, struct cqueue_msg *msg)
|
||||
/* 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 chatlog *log = self->chatwin->log;
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
|
||||
@ -107,13 +108,16 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
|
||||
continue;
|
||||
}
|
||||
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfname);
|
||||
if (log->log_on) {
|
||||
char selfname[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfname);
|
||||
|
||||
size_t len = tox_self_get_name_size(m);
|
||||
selfname[len] = '\0';
|
||||
size_t len = tox_self_get_name_size(m);
|
||||
selfname[len] = 0;
|
||||
|
||||
write_to_log(msg->message, selfname, log, msg->type == OUT_ACTION);
|
||||
}
|
||||
|
||||
write_to_log(msg->message, selfname, self->chatwin->log, msg->type == OUT_ACTION);
|
||||
cqueue_mark_read(self, msg);
|
||||
|
||||
struct cqueue_msg *next = msg->next;
|
||||
@ -123,35 +127,65 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
|
||||
next->prev = NULL;
|
||||
}
|
||||
|
||||
free(msg);
|
||||
q->root = next;
|
||||
} else {
|
||||
struct cqueue_msg *prev = msg->prev;
|
||||
free(msg);
|
||||
prev->next = next;
|
||||
next->prev = prev;
|
||||
}
|
||||
|
||||
free(msg);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#define CQUEUE_TRY_SEND_INTERVAL 10
|
||||
// We use knowledge of toxcore internals (bad!) to determine that if we haven't received a read receipt for a
|
||||
// sent packet after this amount of time, the connection has been severed and the packet needs to be re-sent.
|
||||
#define TRY_SEND_TIMEOUT 32
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
/*
|
||||
* Marks all timed out messages in queue as unsent.
|
||||
*/
|
||||
static void cqueue_check_timeouts(struct cqueue_msg *msg)
|
||||
{
|
||||
while (msg) {
|
||||
if (timed_out(msg->last_send_try, TRY_SEND_TIMEOUT)) {
|
||||
msg->receipt = -1;
|
||||
}
|
||||
|
||||
msg = msg->next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to send all messages in the send queue in sequential order.
|
||||
* If a message fails to send the function will immediately return.
|
||||
*/
|
||||
void cqueue_try_send(ToxWindow *self, Tox *m)
|
||||
{
|
||||
struct chat_queue *q = self->chatwin->cqueue;
|
||||
struct cqueue_msg *msg = q->root;
|
||||
|
||||
if (!msg) {
|
||||
return;
|
||||
}
|
||||
while (msg) {
|
||||
if (msg->receipt != -1) {
|
||||
// we can no longer try to send unsent messages until we get receipts for our previous sent
|
||||
// messages, but we continue to iterate the list, checking timestamps for any further
|
||||
// successfully sent messages that have not yet gotten a receipt.
|
||||
cqueue_check_timeouts(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) {
|
||||
return;
|
||||
}
|
||||
TOX_ERR_FRIEND_SEND_MESSAGE err;
|
||||
Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||
uint32_t receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, &err);
|
||||
|
||||
Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
|
||||
msg->receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
|
||||
msg->last_send_try = get_unix_time();
|
||||
if (err != TOX_ERR_FRIEND_SEND_MESSAGE_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg->receipt = receipt;
|
||||
msg->last_send_try = get_unix_time();
|
||||
msg = msg->next;
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ struct cqueue_msg {
|
||||
char message[MAX_STR_SIZE];
|
||||
size_t len;
|
||||
int line_id;
|
||||
uint8_t type;
|
||||
uint32_t receipt;
|
||||
time_t last_send_try;
|
||||
uint8_t type;
|
||||
int64_t receipt;
|
||||
struct cqueue_msg *next;
|
||||
struct cqueue_msg *prev;
|
||||
};
|
||||
@ -42,7 +42,10 @@ struct chat_queue {
|
||||
void cqueue_cleanup(struct chat_queue *q);
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
/*
|
||||
* Tries to send all messages in the send queue in sequential order.
|
||||
* If a message fails to send the function will immediately return.
|
||||
*/
|
||||
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. */
|
||||
|
245
src/misc_tools.c
245
src/misc_tools.c
@ -20,46 +20,44 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_transfers.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "file_transfers.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
void clear_screen(void)
|
||||
{
|
||||
if (system("clear") != 0) {
|
||||
fprintf(stderr, "Warning: system() failed to clear screen\n");
|
||||
}
|
||||
printf("\033[2J\033[1;1H");
|
||||
}
|
||||
|
||||
void hst_to_net(uint8_t *num, uint16_t numbytes)
|
||||
{
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
uint32_t i;
|
||||
uint8_t buff[numbytes];
|
||||
uint8_t *buff = malloc(numbytes);
|
||||
|
||||
for (i = 0; i < numbytes; ++i) {
|
||||
if (buff == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < numbytes; ++i) {
|
||||
buff[i] = num[numbytes - i - 1];
|
||||
}
|
||||
|
||||
memcpy(num, buff, numbytes);
|
||||
free(buff);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
time_t get_unix_time(void)
|
||||
@ -73,6 +71,22 @@ int timed_out(time_t timestamp, time_t timeout)
|
||||
return timestamp + timeout <= get_unix_time();
|
||||
}
|
||||
|
||||
/* Attempts to sleep the caller's thread for `usec` microseconds */
|
||||
void sleep_thread(long int usec)
|
||||
{
|
||||
struct timespec req;
|
||||
struct timespec rem;
|
||||
|
||||
req.tv_sec = 0;
|
||||
req.tv_nsec = usec * 1000L;
|
||||
|
||||
if (nanosleep(&req, &rem) == -1) {
|
||||
if (nanosleep(&rem, NULL) == -1) {
|
||||
fprintf(stderr, "nanosleep() returned -1\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the current local time */
|
||||
struct tm *get_time(void)
|
||||
{
|
||||
@ -82,11 +96,16 @@ struct tm *get_time(void)
|
||||
return timeinfo;
|
||||
}
|
||||
|
||||
/*Puts the current time in buf in the format of [HH:mm:ss] */
|
||||
void get_time_str(char *buf, int bufsize)
|
||||
/* Puts the current time in buf in the format of specified by the config */
|
||||
void get_time_str(char *buf, size_t bufsize)
|
||||
{
|
||||
if (buf == NULL || bufsize == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
*buf = 0;
|
||||
|
||||
if (user_settings->timestamps == TIMESTAMPS_OFF) {
|
||||
buf[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
@ -144,11 +163,10 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int i, res;
|
||||
const char *pos = keystr;
|
||||
|
||||
for (i = 0; i < size; ++i) {
|
||||
res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
int res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
|
||||
pos += 2;
|
||||
|
||||
if (res == EOF || res < 1) {
|
||||
@ -170,15 +188,31 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||
for (size_t i = 0; i < TOX_ADDRESS_SIZE; ++i) {
|
||||
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Converts a binary representation of a Tox public key into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size)
|
||||
{
|
||||
if (bin_pubkey_size != TOX_PUBLIC_KEY_SIZE || output_size < (TOX_PUBLIC_KEY_SIZE * 2 + 1)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) {
|
||||
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_pubkey[i] & 0xff);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Returns 1 if the string is empty, 0 otherwise */
|
||||
int string_is_empty(const char *string)
|
||||
{
|
||||
@ -237,43 +271,65 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
|
||||
return strcasecmp((const char *) str1, (const char *) str2);
|
||||
}
|
||||
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick)
|
||||
/* case-insensitive string compare function for use with qsort */
|
||||
int qsort_ptr_char_array_helper(const void *str1, const void *str2)
|
||||
{
|
||||
if (!nick[0] || nick[0] == ' ') {
|
||||
return 0;
|
||||
}
|
||||
return strcasecmp(*(char **)str1, *(char **)str2);
|
||||
}
|
||||
|
||||
int i;
|
||||
static const char invalid_chars[] = {'/', '\n', '\t', '\v', '\r', '\0'};
|
||||
|
||||
for (i = 0; nick[i]; ++i) {
|
||||
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|
||||
|| nick[i] == '/'
|
||||
|| nick[i] == '\n'
|
||||
|| nick[i] == '\t'
|
||||
|| nick[i] == '\v'
|
||||
|| nick[i] == '\r')
|
||||
/*
|
||||
* Helper function for `valid_nick()`.
|
||||
*
|
||||
* Returns true if `ch` is not in the `invalid_chars` array.
|
||||
*/
|
||||
static bool is_valid_char(char ch)
|
||||
{
|
||||
char tmp;
|
||||
|
||||
{
|
||||
return 0;
|
||||
for (size_t i = 0; (tmp = invalid_chars[i]); ++i) {
|
||||
if (tmp == ch) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Returns true if nick is valid.
|
||||
*
|
||||
* A valid toxic nick:
|
||||
* - cannot be empty
|
||||
* - cannot start with a space
|
||||
* - must not contain a forward slash (for logfile naming purposes)
|
||||
* - must not contain contiguous spaces
|
||||
* - must not contain a newline or tab seqeunce
|
||||
*/
|
||||
bool valid_nick(const char *nick)
|
||||
{
|
||||
if (!nick[0] || nick[0] == ' ') {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; nick[i]; ++i) {
|
||||
char ch = nick[i];
|
||||
|
||||
if ((ch == ' ' && nick[i + 1] == ' ') || !is_valid_char(ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||
void filter_str(char *str, size_t len)
|
||||
{
|
||||
size_t i;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
char ch = str[i];
|
||||
|
||||
for (i = 0; i < len; ++i) {
|
||||
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') {
|
||||
if (!is_valid_char(ch) || str[i] == '\0') {
|
||||
str[i] = ' ';
|
||||
}
|
||||
}
|
||||
@ -377,16 +433,16 @@ on_error:
|
||||
return len;
|
||||
}
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
|
||||
/* same as get_nick_truncate but for conferences */
|
||||
int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum)
|
||||
{
|
||||
Tox_Err_Conference_Peer_Query err;
|
||||
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
|
||||
size_t len = tox_conference_peer_get_name_size(m, conferencenum, peernum, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
goto on_error;
|
||||
} else {
|
||||
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
|
||||
if (!tox_conference_peer_get_name(m, conferencenum, peernum, (uint8_t *) buf, NULL)) {
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
@ -527,27 +583,6 @@ off_t file_size(const char *path)
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
/* compares the first size bytes of fp to signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
|
||||
{
|
||||
char buf[size];
|
||||
|
||||
if (fread(buf, size, 1, fp) != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = memcmp(signature, buf, size);
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len)
|
||||
{
|
||||
@ -557,8 +592,8 @@ void set_window_title(ToxWindow *self, const char *title, int len)
|
||||
|
||||
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
|
||||
if (self->is_groupchat) { /* keep groupnumber in title */
|
||||
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
|
||||
if (self->type == WINDOW_TYPE_CONFERENCE) { /* keep conferencenumber in title for invites */
|
||||
snprintf(cpy, sizeof(cpy), "%u %s", self->num, title);
|
||||
} else {
|
||||
snprintf(cpy, sizeof(cpy), "%s", title);
|
||||
}
|
||||
@ -571,35 +606,49 @@ void set_window_title(ToxWindow *self, const char *title, int len)
|
||||
snprintf(self->name, sizeof(self->name), "%s", cpy);
|
||||
}
|
||||
|
||||
/* Return true if address appears to be a valid ipv4 address. */
|
||||
bool is_ip4_address(const char *address)
|
||||
/*
|
||||
* Frees all members of a pointer array plus `arr`.
|
||||
*/
|
||||
void free_ptr_array(void **arr)
|
||||
{
|
||||
struct sockaddr_in s_addr;
|
||||
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
|
||||
if (arr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
void **tmp = arr;
|
||||
|
||||
while (*arr) {
|
||||
free(*arr);
|
||||
++arr;
|
||||
}
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||
/*
|
||||
* Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes.
|
||||
* Returns NULL on failure.
|
||||
*
|
||||
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||
* for now the only guarantee is that it won't return true if the
|
||||
* address is a domain or ipv4 address, and should only be used if you're
|
||||
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||
* The caller is responsible for freeing the array with `free_ptr_array`.
|
||||
*/
|
||||
bool is_ip6_address(const char *address)
|
||||
void **malloc_ptr_array(size_t length, size_t bytes)
|
||||
{
|
||||
size_t i;
|
||||
size_t num_colons = 0;
|
||||
char ch = 0;
|
||||
void **arr = malloc((length + 1) * sizeof(void *));
|
||||
|
||||
for (i = 0; (ch = address[i]); ++i) {
|
||||
if (ch == '.') {
|
||||
return false;
|
||||
}
|
||||
if (arr == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ch == ':') {
|
||||
++num_colons;
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
arr[i] = malloc(bytes);
|
||||
|
||||
if (arr[i] == NULL) {
|
||||
free_ptr_array(arr);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return num_colons > 1 && num_colons < 8;
|
||||
arr[length] = NULL;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
@ -24,8 +24,8 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
@ -71,11 +71,18 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr);
|
||||
*/
|
||||
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size);
|
||||
|
||||
/* Converts a binary representation of a Tox public key into a string.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char *output, size_t output_size);
|
||||
|
||||
/* get the current unix time (not thread safe) */
|
||||
time_t get_unix_time(void);
|
||||
|
||||
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */
|
||||
void get_time_str(char *buf, int bufsize);
|
||||
/* Puts the current time in buf in the format of specified by the config */
|
||||
void get_time_str(char *buf, size_t bufsize);
|
||||
|
||||
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
|
||||
void get_elapsed_time_str(char *buf, int bufsize, time_t secs);
|
||||
@ -101,19 +108,28 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
|
||||
/* Returns 1 if connection has timed out, 0 otherwise */
|
||||
int timed_out(time_t timestamp, time_t timeout);
|
||||
|
||||
/* Attempts to sleep the caller's thread for `usec` microseconds */
|
||||
void sleep_thread(long int usec);
|
||||
|
||||
/* Colours the window tab according to type. Beeps if is_beep is true */
|
||||
void alert_window(ToxWindow *self, int type, bool is_beep);
|
||||
|
||||
/* case-insensitive string compare function for use with qsort */
|
||||
int qsort_strcasecmp_hlpr(const void *str1, const void *str2);
|
||||
|
||||
/* Returns 1 if nick is valid, 0 if not. A valid toxic nick:
|
||||
- cannot be empty
|
||||
- cannot start with a space
|
||||
- must not contain a forward slash (for logfile naming purposes)
|
||||
- must not contain contiguous spaces
|
||||
- must not contain a newline or tab seqeunce */
|
||||
int valid_nick(const char *nick);
|
||||
/* case-insensitive string compare function for use with qsort */
|
||||
int qsort_ptr_char_array_helper(const void *str1, const void *str2);
|
||||
|
||||
/* Returns true if nick is valid.
|
||||
*
|
||||
* A valid toxic nick:
|
||||
* - cannot be empty
|
||||
* - cannot start with a space
|
||||
* - must not contain a forward slash (for logfile naming purposes)
|
||||
* - must not contain contiguous spaces
|
||||
* - must not contain a newline or tab seqeunce
|
||||
*/
|
||||
bool valid_nick(const char *nick);
|
||||
|
||||
/* Converts all newline/tab chars to spaces (use for strings that should be contained to a single line) */
|
||||
void filter_str(char *str, size_t len);
|
||||
@ -136,8 +152,8 @@ void str_to_lower(char *str);
|
||||
Returns nick len on success, -1 on failure */
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
|
||||
/* same as get_nick_truncate but for conferences */
|
||||
int get_conference_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t conferencenum);
|
||||
|
||||
/* copies data to msg buffer.
|
||||
returns length of msg, which will be no larger than size-1 */
|
||||
@ -169,25 +185,20 @@ File_Type file_type(const char *path);
|
||||
/* returns file size. If file doesn't exist returns 0. */
|
||||
off_t file_size(const char *path);
|
||||
|
||||
/* compares the first size bytes of fp and signature.
|
||||
Returns 0 if they are the same, 1 if they differ, and -1 on error.
|
||||
|
||||
On success this function will seek back to the beginning of fp */
|
||||
int check_file_signature(const unsigned char *signature, size_t size, FILE *fp);
|
||||
|
||||
/* sets window title in tab bar. */
|
||||
void set_window_title(ToxWindow *self, const char *title, int len);
|
||||
|
||||
/* Return true if address appears to be a valid ipv4 address. */
|
||||
bool is_ip4_address(const char *address);
|
||||
|
||||
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||
*
|
||||
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||
* for now the only guarantee is that it won't return true if the
|
||||
* address is a domain or ipv4 address, and should only be used if you're
|
||||
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||
/*
|
||||
* Frees all members of a pointer array plus `arr`.
|
||||
*/
|
||||
bool is_ip6_address(const char *address);
|
||||
void free_ptr_array(void **arr);
|
||||
|
||||
/*
|
||||
* Returns a null terminated array of `length` pointers. Each pointer is allocated `bytes` bytes.
|
||||
* Returns NULL on failure.
|
||||
*
|
||||
* The caller is responsible for freeing the array with `free_ptr_array`.
|
||||
*/
|
||||
void **malloc_ptr_array(size_t length, size_t bytes);
|
||||
|
||||
#endif /* MISC_TOOLS_H */
|
||||
|
@ -20,18 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "global_commands.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
#include "curl_util.h"
|
||||
#include "global_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct arg_opts arg_opts;
|
||||
extern struct Winthread Winthread;
|
||||
@ -63,6 +63,13 @@ static struct lookup_thread {
|
||||
pthread_attr_t attr;
|
||||
} lookup_thread;
|
||||
|
||||
static void clear_thread_data(void)
|
||||
{
|
||||
t_data = (struct thread_data) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||
{
|
||||
char frmt_msg[MAX_STR_SIZE];
|
||||
@ -73,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||
va_end(args);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookup failed: %s", frmt_msg);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
return -1;
|
||||
@ -81,7 +88,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...)
|
||||
|
||||
static void kill_lookup_thread(void)
|
||||
{
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
clear_thread_data();
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
@ -265,11 +272,14 @@ void *lookup_thread_func(void *data)
|
||||
kill_lookup_thread();
|
||||
}
|
||||
|
||||
struct Recv_Curl_Data recv_data;
|
||||
struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data));
|
||||
|
||||
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
|
||||
if (recv_data == NULL) {
|
||||
lookup_error(self, "memory allocation error");
|
||||
kill_lookup_thread();
|
||||
}
|
||||
|
||||
char post_data[MAX_STR_SIZE];
|
||||
char post_data[MAX_STR_SIZE + 30];
|
||||
|
||||
snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
|
||||
|
||||
@ -285,7 +295,7 @@ void *lookup_thread_func(void *data)
|
||||
|
||||
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
|
||||
|
||||
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data);
|
||||
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, recv_data);
|
||||
|
||||
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
|
||||
|
||||
@ -334,7 +344,7 @@ void *lookup_thread_func(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
if (process_response(&recv_data) == -1) {
|
||||
if (process_response(recv_data) == -1) {
|
||||
lookup_error(self, "Bad response.");
|
||||
goto on_exit;
|
||||
}
|
||||
@ -344,6 +354,7 @@ void *lookup_thread_func(void *data)
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
on_exit:
|
||||
free(recv_data);
|
||||
curl_slist_free_all(headers);
|
||||
curl_easy_cleanup(c_handle);
|
||||
kill_lookup_thread();
|
||||
@ -354,12 +365,12 @@ on_exit:
|
||||
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
|
||||
{
|
||||
if (t_data.disabled) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "name lookups are disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (t_data.busy) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please wait for previous name lookup to finish.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -371,22 +382,22 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
|
||||
t_data.busy = true;
|
||||
|
||||
if (pthread_attr_init(&lookup_thread.attr) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
|
||||
clear_thread_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to set");
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
clear_thread_data();
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread failed to init");
|
||||
pthread_attr_destroy(&lookup_thread.attr);
|
||||
memset(&t_data, 0, sizeof(struct thread_data));
|
||||
clear_thread_data();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
143
src/notify.c
143
src/notify.c
@ -20,22 +20,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "notify.h"
|
||||
#include "audio_device.h"
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "xtra.h"
|
||||
#include "notify.h"
|
||||
#include "settings.h"
|
||||
#include "x11focus.h"
|
||||
|
||||
#if defined(AUDIO) || defined(SOUND_NOTIFY)
|
||||
#ifdef __APPLE__
|
||||
@ -60,7 +60,7 @@
|
||||
|
||||
#define MAX_BOX_MSG_LEN 127
|
||||
#define SOUNDS_SIZE 10
|
||||
#define ACTIVE_NOTIFS_MAX 50
|
||||
#define ACTIVE_NOTIFS_MAX 10
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
@ -101,6 +101,17 @@ static struct _ActiveNotifications {
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
|
||||
static void clear_actives_index(size_t idx)
|
||||
{
|
||||
if (actives[idx].id_indicator) {
|
||||
*actives[idx].id_indicator = -1;
|
||||
}
|
||||
|
||||
actives[idx] = (struct _ActiveNotifications) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
/* coloured tab notifications: primary notification type */
|
||||
static void tab_notify(ToxWindow *self, uint64_t flags)
|
||||
{
|
||||
@ -115,6 +126,8 @@ static void tab_notify(ToxWindow *self, uint64_t flags)
|
||||
} else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
|
||||
self->alert = WINDOW_ALERT_2;
|
||||
}
|
||||
|
||||
++self->pending_messages;
|
||||
}
|
||||
|
||||
static bool notifications_are_disabled(uint64_t flags)
|
||||
@ -169,7 +182,7 @@ void m_open_device(void)
|
||||
}
|
||||
|
||||
/* Blah error check */
|
||||
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
|
||||
open_output_device(&Control.device_idx, 48000, 20, 1);
|
||||
|
||||
device_opened = true;
|
||||
}
|
||||
@ -193,7 +206,7 @@ void graceful_clear(void)
|
||||
while (1) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
|
||||
if (actives[i].active) {
|
||||
#ifdef BOX_NOTIFY
|
||||
|
||||
@ -213,7 +226,7 @@ void graceful_clear(void)
|
||||
stop_sound(i);
|
||||
} else {
|
||||
if (!is_playing(actives[i].source)) {
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(i);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -227,7 +240,7 @@ void graceful_clear(void)
|
||||
return;
|
||||
}
|
||||
|
||||
usleep(1000);
|
||||
sleep_thread(1000L);
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
@ -249,7 +262,7 @@ void *do_playing(void *_p)
|
||||
bool test_active_notify = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
|
||||
|
||||
if (actives[i].looping) {
|
||||
has_looping = true;
|
||||
@ -270,7 +283,7 @@ void *do_playing(void *_p)
|
||||
alSourceStop(actives[i].source);
|
||||
alDeleteSources(1, &actives[i].source);
|
||||
alDeleteBuffers(1, &actives[i].buffer);
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -289,7 +302,7 @@ void *do_playing(void *_p)
|
||||
alSourceStop(actives[i].source);
|
||||
alDeleteSources(1, &actives[i].source);
|
||||
alDeleteBuffers(1, &actives[i].buffer);
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(i);
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,7 +318,7 @@ void *do_playing(void *_p)
|
||||
has_looping = false;
|
||||
|
||||
control_unlock();
|
||||
usleep(10000);
|
||||
sleep_thread(10000L);
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
@ -315,7 +328,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
|
||||
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; ++i);
|
||||
|
||||
if (i == ACTIVE_NOTIFS_MAX) {
|
||||
return -1; /* Full */
|
||||
@ -344,24 +357,16 @@ void *do_playing(void *_p)
|
||||
break;
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
|
||||
if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
|
||||
GError *ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
|
||||
if (actives[i].id_indicator) {
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
}
|
||||
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(i);
|
||||
}
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
usleep(10000);
|
||||
sleep_thread(10000L);
|
||||
}
|
||||
|
||||
pthread_exit(NULL);
|
||||
@ -369,27 +374,48 @@ void *do_playing(void *_p)
|
||||
|
||||
void graceful_clear(void)
|
||||
{
|
||||
int i;
|
||||
control_lock();
|
||||
|
||||
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
|
||||
for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
|
||||
if (actives[i].box) {
|
||||
GError *ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
actives[i].box = NULL;
|
||||
}
|
||||
|
||||
if (actives[i].id_indicator) {
|
||||
*actives[i].id_indicator = -1; /* reset indicator value */
|
||||
}
|
||||
|
||||
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(i);
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
}
|
||||
|
||||
#endif /* SOUND_NOTIFY */
|
||||
|
||||
/* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */
|
||||
void kill_notifs(int id)
|
||||
{
|
||||
control_lock();
|
||||
|
||||
for (size_t i = 0; i < ACTIVE_NOTIFS_MAX; ++i) {
|
||||
if (!actives[i].id_indicator) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*actives[i].id_indicator == id) {
|
||||
#ifdef BOX_NOTIFY
|
||||
|
||||
if (actives[i].box) {
|
||||
GError *ignore;
|
||||
notify_notification_close(actives[i].box, &ignore);
|
||||
}
|
||||
|
||||
#endif // BOX_NOTIFY
|
||||
clear_actives_index(i);
|
||||
}
|
||||
}
|
||||
|
||||
control_unlock();
|
||||
}
|
||||
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
/**********************************************************************************/
|
||||
@ -450,7 +476,7 @@ void terminate_notify(void)
|
||||
#ifdef SOUND_NOTIFY
|
||||
int i = 0;
|
||||
|
||||
for (; i < SOUNDS_SIZE; i ++) {
|
||||
for (; i < SOUNDS_SIZE; ++i) {
|
||||
free(Control.sounds[i]);
|
||||
}
|
||||
|
||||
@ -523,7 +549,6 @@ int play_notify_sound(Notification notif, uint64_t flags)
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void stop_sound(int id)
|
||||
{
|
||||
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) {
|
||||
@ -536,15 +561,11 @@ void stop_sound(int id)
|
||||
|
||||
#endif /* BOX_NOTIFY */
|
||||
|
||||
if (actives[id].id_indicator) {
|
||||
*actives[id].id_indicator = -1;
|
||||
}
|
||||
|
||||
// alSourcei(actives[id].source, AL_LOOPING, false);
|
||||
alSourceStop(actives[id].source);
|
||||
alDeleteSources(1, &actives[id].source);
|
||||
alDeleteBuffers(1, &actives[id].buffer);
|
||||
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
|
||||
clear_actives_index(id);
|
||||
}
|
||||
}
|
||||
#endif /* SOUND_NOTIFY */
|
||||
@ -754,20 +775,18 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
|
||||
actives[id].size++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2)));
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
for (size_t i = 0; i < actives[id].size; ++i) {
|
||||
strcat(formatted, actives[id].messages[i]);
|
||||
strcat(formatted, "\n");
|
||||
}
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_update(actives[id].box, actives[id].title, formatted, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
free(formatted);
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
@ -863,20 +882,18 @@ int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *form
|
||||
actives[id].size ++;
|
||||
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
|
||||
|
||||
char formated[128 * 129] = {'\0'};
|
||||
char *formatted = calloc(1, sizeof(char) * ((MAX_BOX_MSG_LEN + 1) * (MAX_BOX_MSG_LEN + 2)));
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < actives[id].size; i ++) {
|
||||
strcat(formated, actives[id].messages[i]);
|
||||
strcat(formated, "\n");
|
||||
for (size_t i = 0; i < actives[id].size; ++i) {
|
||||
strcat(formatted, actives[id].messages[i]);
|
||||
strcat(formatted, "\n");
|
||||
}
|
||||
|
||||
formated[strlen(formated) - 1] = '\0';
|
||||
|
||||
notify_notification_update(actives[id].box, actives[id].title, formated, NULL);
|
||||
notify_notification_update(actives[id].box, actives[id].title, formatted, NULL);
|
||||
notify_notification_show(actives[id].box, NULL);
|
||||
|
||||
free(formatted);
|
||||
|
||||
control_unlock();
|
||||
|
||||
return id;
|
||||
|
@ -62,6 +62,9 @@ typedef enum _Flags {
|
||||
int init_notify(int login_cooldown, int notification_timeout);
|
||||
void terminate_notify(void);
|
||||
|
||||
/* Kills all notifications for `id`. This must be called before freeing a ToxWindow. */
|
||||
void kill_notifs(int id);
|
||||
|
||||
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
|
||||
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id);
|
||||
|
||||
|
280
src/prompt.c
280
src/prompt.c
@ -28,20 +28,20 @@
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "prompt.h"
|
||||
#include "friendlist.h"
|
||||
#include "execute.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
#include "execute.h"
|
||||
#include "friendlist.h"
|
||||
#include "help.h"
|
||||
#include "input.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
@ -49,68 +49,47 @@ extern struct Winthread Winthread;
|
||||
|
||||
extern FriendsList Friends;
|
||||
FriendRequests FrndRequests;
|
||||
#ifdef AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS_AUDIO 2
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS_AUDIO 0
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
#define AC_NUM_GLOB_COMMANDS_VIDEO 2
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS_VIDEO 0
|
||||
#endif /* VIDEO */
|
||||
#ifdef PYTHON
|
||||
#define AC_NUM_GLOB_COMMANDS_PYTHON 1
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS_PYTHON 0
|
||||
#endif /* PYTHON */
|
||||
#ifdef QRCODE
|
||||
#define AC_NUM_GLOB_COMMANDS_QRCODE 1
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS_QRCODE 0
|
||||
#endif /* QRCODE */
|
||||
#define AC_NUM_GLOB_COMMANDS (17 + AC_NUM_GLOB_COMMANDS_AUDIO + AC_NUM_GLOB_COMMANDS_VIDEO + AC_NUM_GLOB_COMMANDS_PYTHON + AC_NUM_GLOB_COMMANDS_QRCODE)
|
||||
|
||||
/* Array of global command names used for tab completion. */
|
||||
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/accept" },
|
||||
{ "/add" },
|
||||
{ "/avatar" },
|
||||
{ "/clear" },
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/group" },
|
||||
{ "/help" },
|
||||
{ "/log" },
|
||||
{ "/myid" },
|
||||
static const char *glob_cmd_list[] = {
|
||||
"/accept",
|
||||
"/add",
|
||||
"/avatar",
|
||||
"/clear",
|
||||
"/connect",
|
||||
"/decline",
|
||||
"/exit",
|
||||
"/conference",
|
||||
"/help",
|
||||
"/log",
|
||||
"/myid",
|
||||
#ifdef QRCODE
|
||||
{ "/myqr" },
|
||||
"/myqr",
|
||||
#endif /* QRCODE */
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/nospam" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
"/nick",
|
||||
"/note",
|
||||
"/nospam",
|
||||
"/quit",
|
||||
"/requests",
|
||||
"/status",
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
"/lsdev",
|
||||
"/sdev",
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
{ "/lsvdev" },
|
||||
{ "/svdev" },
|
||||
"/lsvdev",
|
||||
"/svdev",
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
{ "/run" },
|
||||
"/run",
|
||||
|
||||
#endif /* PYTHON */
|
||||
|
||||
@ -165,7 +144,7 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
|
||||
tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err);
|
||||
|
||||
if (err != TOX_ERR_SET_INFO_OK) {
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -272,14 +251,14 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
#endif
|
||||
|
||||
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
|
||||
const char status_cmd_list[3][8] = {
|
||||
{"online"},
|
||||
{"away"},
|
||||
{"busy"},
|
||||
const char *status_cmd_list[] = {
|
||||
"online",
|
||||
"away",
|
||||
"busy",
|
||||
};
|
||||
diff = complete_line(self, status_cmd_list, 3, 8);
|
||||
diff = complete_line(self, status_cmd_list, sizeof(status_cmd_list) / sizeof(char *));
|
||||
} else {
|
||||
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
diff = complete_line(self, glob_cmd_list, sizeof(glob_cmd_list) / sizeof(char *));
|
||||
}
|
||||
|
||||
if (diff != -1) {
|
||||
@ -302,18 +281,18 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
add_line_to_hist(ctx);
|
||||
wstrsubst(ctx->line, L'¶', L'\n');
|
||||
|
||||
char line[MAX_STR_SIZE] = {0};
|
||||
char line[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
|
||||
memset(&line, 0, sizeof(line));
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
|
||||
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
|
||||
}
|
||||
|
||||
wclear(ctx->linewin);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
wmove(self->window, y2, 0);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
|
||||
@ -322,7 +301,8 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
@ -337,64 +317,88 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
|
||||
if (ctx->len > 0) {
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
mvwprintw(ctx->linewin, 0, 0, "%ls", &ctx->line[ctx->start]);
|
||||
}
|
||||
|
||||
mvwhline(ctx->linewin, 0, ctx->len, ' ', x2 - ctx->len);
|
||||
|
||||
curs_set(1);
|
||||
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
mvwhline(statusbar->topline, 1, 0, ACS_HLINE, x2);
|
||||
wmove(statusbar->topline, 0, 0);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
Tox_Connection connection = statusbar->connection;
|
||||
Tox_User_Status status = statusbar->status;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
if (connection != TOX_CONNECTION_NONE) {
|
||||
int colour = MAGENTA;
|
||||
const char *status_text = "ERROR";
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
Tox_User_Status status = statusbar->status;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
switch (status) {
|
||||
case TOX_USER_STATUS_NONE:
|
||||
status_text = "Online";
|
||||
colour = GREEN;
|
||||
colour = STATUS_ONLINE;
|
||||
break;
|
||||
|
||||
case TOX_USER_STATUS_AWAY:
|
||||
status_text = "Away";
|
||||
colour = YELLOW;
|
||||
colour = STATUS_AWAY;
|
||||
break;
|
||||
|
||||
case TOX_USER_STATUS_BUSY:
|
||||
status_text = "Busy";
|
||||
colour = RED;
|
||||
colour = STATUS_BUSY;
|
||||
break;
|
||||
}
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
wprintw(statusbar->topline, " [%s]", status_text);
|
||||
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(statusbar->topline, " [");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
|
||||
wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
|
||||
wprintw(statusbar->topline, "%s", status_text);
|
||||
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(statusbar->topline, "]");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
} else {
|
||||
wprintw(statusbar->topline, " [Offline]");
|
||||
wattron(statusbar->topline, A_BOLD);
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(statusbar->topline, " [");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
wprintw(statusbar->topline, "Offline");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(statusbar->topline, "]");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
wprintw(statusbar->topline, " %s", statusbar->nick);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
wattroff(statusbar->topline, A_BOLD);
|
||||
}
|
||||
|
||||
int s_y;
|
||||
int s_x;
|
||||
getyx(statusbar->topline, s_y, s_x);
|
||||
|
||||
mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x);
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
|
||||
/* Reset statusbar->statusmsg on window resize */
|
||||
if (x2 != self->x) {
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
@ -422,20 +426,27 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
}
|
||||
|
||||
if (statusbar->statusmsg[0]) {
|
||||
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(statusbar->topline, " | ");
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
|
||||
|
||||
wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
wprintw(statusbar->topline, "%s", statusbar->statusmsg);
|
||||
wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
|
||||
int y, x;
|
||||
int y;
|
||||
int x;
|
||||
getyx(self->window, y, x);
|
||||
|
||||
UNUSED_VAR(x);
|
||||
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
wmove(self->window, y, new_x);
|
||||
|
||||
draw_window_bar(self);
|
||||
|
||||
wnoutrefresh(self->window);
|
||||
|
||||
@ -455,8 +466,6 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
|
||||
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
|
||||
}
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
const char *msg;
|
||||
|
||||
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
|
||||
@ -465,7 +474,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
|
||||
|
||||
if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has come online";
|
||||
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||
line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1) {
|
||||
@ -477,7 +486,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
|
||||
}
|
||||
} else if (connection_status == TOX_CONNECTION_NONE) {
|
||||
msg = "has gone offline";
|
||||
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||
line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg);
|
||||
write_to_log(msg, nick, ctx->log, true);
|
||||
|
||||
if (self->active_box != -1) {
|
||||
@ -497,21 +506,18 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
|
||||
line_info_add(self, true, 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, 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);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/accept %d\" or \"/decline %d\"", n, n);
|
||||
line_info_add(self, false, 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);
|
||||
}
|
||||
|
||||
@ -556,29 +562,50 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
|
||||
prompt_update_nick(prompt, nick);
|
||||
|
||||
/* Init statusbar subwindow */
|
||||
statusbar->topline = subwin(self->window, 2, x2, 0, 0);
|
||||
statusbar->topline = subwin(self->window, TOP_BAR_HEIGHT, x2, 0, 0);
|
||||
}
|
||||
|
||||
static void print_welcome_msg(ToxWindow *self)
|
||||
{
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ ");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ ");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
|
||||
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg);
|
||||
msg = "Type \"/help\" for assistance. Further help may be found via the man page.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 1, CYAN, msg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
|
||||
static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
if (log_enable(ctx->log) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
curs_set(1);
|
||||
int y2, x2;
|
||||
|
||||
int y2;
|
||||
int x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
@ -586,8 +613,10 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
}
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||
self->window_bar = subwin(self->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
|
||||
|
||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||
ctx->hst = calloc(1, sizeof(struct history));
|
||||
@ -598,14 +627,7 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
}
|
||||
prompt_init_log(self, m, self->name);
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
@ -624,7 +646,7 @@ ToxWindow *new_prompt(void)
|
||||
}
|
||||
|
||||
ret->num = -1;
|
||||
ret->is_prompt = true;
|
||||
ret->type = WINDOW_TYPE_PROMPT;
|
||||
|
||||
ret->onKey = &prompt_onKey;
|
||||
ret->onDraw = &prompt_onDraw;
|
||||
@ -632,7 +654,7 @@ ToxWindow *new_prompt(void)
|
||||
ret->onConnectionChange = &prompt_onConnectionChange;
|
||||
ret->onFriendRequest = &prompt_onFriendRequest;
|
||||
|
||||
strcpy(ret->name, "home");
|
||||
strcpy(ret->name, "Home");
|
||||
|
||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||
StatusBar *stb = calloc(1, sizeof(StatusBar));
|
||||
|
@ -26,22 +26,20 @@
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define MAX_FRIEND_REQUESTS 32
|
||||
#define MAX_FRIEND_REQUESTS 20
|
||||
|
||||
struct friend_request {
|
||||
bool active;
|
||||
char msg[MAX_STR_SIZE];
|
||||
char msg[TOX_MAX_FRIEND_REQUEST_LENGTH + 1];
|
||||
uint8_t key[TOX_PUBLIC_KEY_SIZE];
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
typedef struct FriendRequests {
|
||||
int max_idx;
|
||||
int num_requests;
|
||||
struct friend_request request[MAX_FRIEND_REQUESTS];
|
||||
} FriendRequests;
|
||||
|
||||
extern FriendRequests FrndRequests;
|
||||
|
||||
ToxWindow *new_prompt(void);
|
||||
|
||||
void prep_prompt_win(void);
|
||||
|
@ -114,25 +114,23 @@ static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
|
||||
|
||||
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
|
||||
{
|
||||
size_t i, ii;
|
||||
FriendsList friends;
|
||||
PyObject *cur, *ret;
|
||||
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
|
||||
if (!PyArg_ParseTuple(args, "")) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
friends = api_get_friendslist();
|
||||
ret = PyList_New(0);
|
||||
PyObject *ret = PyList_New(0);
|
||||
|
||||
for (i = 0; i < friends.num_friends; i++) {
|
||||
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
|
||||
for (size_t i = 0; i < friends.num_friends; i++) {
|
||||
for (size_t ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
|
||||
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
|
||||
}
|
||||
|
||||
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
|
||||
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
|
||||
PyObject *cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
|
||||
PyList_Append(ret, cur);
|
||||
}
|
||||
|
||||
@ -252,26 +250,26 @@ PyMODINIT_FUNC PyInit_toxic_api(void)
|
||||
PyObject *m = PyModule_Create(&toxic_api_module);
|
||||
PyObject *global_command_const = Py_BuildValue("i", GLOBAL_COMMAND_MODE);
|
||||
PyObject *chat_command_const = Py_BuildValue("i", CHAT_COMMAND_MODE);
|
||||
PyObject *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE);
|
||||
PyObject *conference_command_const = Py_BuildValue("i", CONFERENCE_COMMAND_MODE);
|
||||
PyObject_SetAttrString(m, "GLOBAL_COMMAND", global_command_const);
|
||||
PyObject_SetAttrString(m, "CHAT_COMMAND", chat_command_const);
|
||||
PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const);
|
||||
PyObject_SetAttrString(m, "CONFERENCE_COMMAND", conference_command_const);
|
||||
Py_DECREF(global_command_const);
|
||||
Py_DECREF(chat_command_const);
|
||||
Py_DECREF(groupchat_command_const);
|
||||
Py_DECREF(conference_command_const);
|
||||
return m;
|
||||
}
|
||||
|
||||
void terminate_python(void)
|
||||
{
|
||||
struct python_registered_func *cur, *old;
|
||||
|
||||
if (python_commands.name != NULL) {
|
||||
free(python_commands.name);
|
||||
}
|
||||
|
||||
struct python_registered_func *cur = NULL;
|
||||
|
||||
for (cur = python_commands.next; cur != NULL;) {
|
||||
old = cur;
|
||||
struct python_registered_func *old = cur;
|
||||
cur = cur->next;
|
||||
free(old->name);
|
||||
free(old);
|
||||
|
@ -22,14 +22,14 @@
|
||||
|
||||
#ifdef QRCODE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <qrencode.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "qr_code.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "qr_code.h"
|
||||
|
||||
#ifdef QRPNG
|
||||
#include <png.h>
|
||||
@ -138,12 +138,19 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile)
|
||||
|
||||
real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE;
|
||||
size_t row_size = real_width * 4;
|
||||
unsigned char row[row_size];
|
||||
unsigned char *row = malloc(row_size);
|
||||
|
||||
if (row == NULL) {
|
||||
fclose(fp);
|
||||
QRcode_free(qr_obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
|
||||
if (png_ptr == NULL) {
|
||||
fclose(fp);
|
||||
free(row);
|
||||
QRcode_free(qr_obj);
|
||||
return -1;
|
||||
}
|
||||
@ -152,12 +159,14 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile)
|
||||
|
||||
if (info_ptr == NULL) {
|
||||
fclose(fp);
|
||||
free(row);
|
||||
QRcode_free(qr_obj);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
fclose(fp);
|
||||
free(row);
|
||||
QRcode_free(qr_obj);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
return -1;
|
||||
@ -206,10 +215,12 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile)
|
||||
png_write_row(png_ptr, row);
|
||||
}
|
||||
|
||||
free(row);
|
||||
fclose(fp);
|
||||
|
||||
png_write_end(png_ptr, info_ptr);
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
|
||||
fclose(fp);
|
||||
QRcode_free(qr_obj);
|
||||
|
||||
return 0;
|
||||
|
@ -20,23 +20,23 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <libconfig.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <libconfig.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "configdir.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "configdir.h"
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_device.h"
|
||||
#endif /* AUDIO */
|
||||
|
||||
#include "settings.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
|
||||
#ifndef PACKAGE_DATADIR
|
||||
#define PACKAGE_DATADIR "."
|
||||
@ -73,6 +73,11 @@ static struct ui_strings {
|
||||
|
||||
const char *mplex_away;
|
||||
const char *mplex_away_note;
|
||||
|
||||
const char *color_bar_bg;
|
||||
const char *color_bar_fg;
|
||||
const char *color_bar_accent;
|
||||
const char *color_bar_notify;
|
||||
} ui_strings = {
|
||||
"ui",
|
||||
"timestamps",
|
||||
@ -100,6 +105,10 @@ static struct ui_strings {
|
||||
"line_normal",
|
||||
"mplex_away",
|
||||
"mplex_away_note",
|
||||
"color_bar_bg",
|
||||
"color_bar_fg",
|
||||
"color_bar_accent",
|
||||
"color_bar_notify",
|
||||
};
|
||||
|
||||
static void ui_defaults(struct user_settings *settings)
|
||||
@ -116,7 +125,7 @@ static void ui_defaults(struct user_settings *settings)
|
||||
settings->bell_on_invite = 0;
|
||||
settings->colour_theme = DFLT_COLS;
|
||||
settings->history_size = 700;
|
||||
settings->notification_timeout = 3000;
|
||||
settings->notification_timeout = 6000;
|
||||
settings->show_typing_self = SHOW_TYPING_ON;
|
||||
settings->show_typing_other = SHOW_TYPING_ON;
|
||||
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
|
||||
@ -130,10 +139,7 @@ static void ui_defaults(struct user_settings *settings)
|
||||
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
|
||||
|
||||
settings->mplex_away = MPLEX_ON;
|
||||
snprintf(settings->mplex_away_note,
|
||||
sizeof(settings->mplex_away_note),
|
||||
"%s",
|
||||
MPLEX_AWAY_NOTE);
|
||||
snprintf(settings->mplex_away_note, sizeof(settings->mplex_away_note), "%s", MPLEX_AWAY_NOTE);
|
||||
}
|
||||
|
||||
static const struct keys_strings {
|
||||
@ -204,19 +210,28 @@ static const struct audio_strings {
|
||||
const char *self;
|
||||
const char *input_device;
|
||||
const char *output_device;
|
||||
const char *VAD_treshold;
|
||||
const char *VAD_threshold;
|
||||
const char *conference_audio_channels;
|
||||
const char *chat_audio_channels;
|
||||
const char *push_to_talk;
|
||||
} audio_strings = {
|
||||
"audio",
|
||||
"input_device",
|
||||
"output_device",
|
||||
"VAD_treshold",
|
||||
"VAD_threshold",
|
||||
"conference_audio_channels",
|
||||
"chat_audio_channels",
|
||||
"push_to_talk",
|
||||
};
|
||||
|
||||
static void audio_defaults(struct user_settings *settings)
|
||||
{
|
||||
settings->audio_in_dev = 0;
|
||||
settings->audio_out_dev = 0;
|
||||
settings->VAD_treshold = 40.0;
|
||||
settings->VAD_threshold = 5.0;
|
||||
settings->conference_audio_channels = 1;
|
||||
settings->chat_audio_channels = 2;
|
||||
settings->push_to_talk = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -330,7 +345,7 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
|
||||
if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) {
|
||||
if (time == 12) {
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M %p");
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
|
||||
}
|
||||
}
|
||||
@ -339,6 +354,22 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str);
|
||||
}
|
||||
|
||||
if (config_setting_lookup_string(setting, ui_strings.color_bar_bg, &str)) {
|
||||
snprintf(s->color_bar_bg, sizeof(s->color_bar_bg), "%s", str);
|
||||
}
|
||||
|
||||
if (config_setting_lookup_string(setting, ui_strings.color_bar_fg, &str)) {
|
||||
snprintf(s->color_bar_fg, sizeof(s->color_bar_fg), "%s", str);
|
||||
}
|
||||
|
||||
if (config_setting_lookup_string(setting, ui_strings.color_bar_accent, &str)) {
|
||||
snprintf(s->color_bar_accent, sizeof(s->color_bar_accent), "%s", str);
|
||||
}
|
||||
|
||||
if (config_setting_lookup_string(setting, ui_strings.color_bar_notify, &str)) {
|
||||
snprintf(s->color_bar_notify, sizeof(s->color_bar_notify), "%s", str);
|
||||
}
|
||||
|
||||
if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) {
|
||||
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
|
||||
}
|
||||
@ -505,7 +536,16 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
config_setting_lookup_int(setting, audio_strings.output_device, &s->audio_out_dev);
|
||||
s->audio_out_dev = s->audio_out_dev < 0 || s->audio_out_dev > MAX_DEVICES ? 0 : s->audio_out_dev;
|
||||
|
||||
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
|
||||
config_setting_lookup_float(setting, audio_strings.VAD_threshold, &s->VAD_threshold);
|
||||
|
||||
config_setting_lookup_int(setting, audio_strings.conference_audio_channels, &s->conference_audio_channels);
|
||||
s->conference_audio_channels = s->conference_audio_channels <= 0
|
||||
|| s->conference_audio_channels > 2 ? 1 : s->conference_audio_channels;
|
||||
|
||||
config_setting_lookup_int(setting, audio_strings.chat_audio_channels, &s->chat_audio_channels);
|
||||
s->chat_audio_channels = s->chat_audio_channels <= 0 || s->chat_audio_channels > 2 ? 2 : s->chat_audio_channels;
|
||||
|
||||
config_setting_lookup_bool(setting, audio_strings.push_to_talk, &s->push_to_talk);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -68,6 +68,11 @@ struct user_settings {
|
||||
char autorun_path[PATH_MAX];
|
||||
char password_eval[PASSWORD_EVAL_MAX];
|
||||
|
||||
char color_bar_bg[COLOR_STR_SIZE];
|
||||
char color_bar_fg[COLOR_STR_SIZE];
|
||||
char color_bar_accent[COLOR_STR_SIZE];
|
||||
char color_bar_notify[COLOR_STR_SIZE];
|
||||
|
||||
int key_next_tab;
|
||||
int key_prev_tab;
|
||||
int key_scroll_line_up;
|
||||
@ -84,7 +89,10 @@ struct user_settings {
|
||||
#ifdef AUDIO
|
||||
int audio_in_dev;
|
||||
int audio_out_dev;
|
||||
double VAD_treshold;
|
||||
double VAD_threshold;
|
||||
int conference_audio_channels;
|
||||
int chat_audio_channels;
|
||||
int push_to_talk; /* boolean */
|
||||
#endif
|
||||
};
|
||||
|
||||
@ -119,10 +127,10 @@ enum settings_values {
|
||||
#define LINE_JOIN "-->"
|
||||
#define LINE_QUIT "<--"
|
||||
#define LINE_ALERT "-!-"
|
||||
#define LINE_NORMAL "---"
|
||||
#define TIMESTAMP_DEFAULT "%H:%M:%S"
|
||||
#define LINE_NORMAL "-"
|
||||
#define TIMESTAMP_DEFAULT "%H:%M"
|
||||
#define LOG_TIMESTAMP_DEFAULT "%Y/%m/%d [%H:%M:%S]"
|
||||
#define MPLEX_AWAY_NOTE "Detached from screen"
|
||||
#define MPLEX_AWAY_NOTE "Away from keyboard, be back soon!"
|
||||
|
||||
int settings_load(struct user_settings *s, const char *patharg);
|
||||
|
||||
|
@ -26,18 +26,18 @@
|
||||
#include <string.h> /* strlen, strcpy, strstr, strchr, strrchr, strcat, strncmp */
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "execute.h"
|
||||
#include "windows.h"
|
||||
#include "settings.h"
|
||||
#include "term_mplex.h"
|
||||
#include "toxic.h"
|
||||
#include "settings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct ToxWindow *prompt;
|
||||
extern struct user_settings *user_settings;
|
||||
@ -100,9 +100,19 @@ static char *read_into_dyn_buffer(FILE *stream)
|
||||
int length = dyn_buffer_size + strlen(input_ptr);
|
||||
|
||||
if (dyn_buffer) {
|
||||
dyn_buffer = (char *) realloc(dyn_buffer, length);
|
||||
char *tmp = realloc(dyn_buffer, length);
|
||||
|
||||
if (tmp == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dyn_buffer = tmp;
|
||||
} else {
|
||||
dyn_buffer = (char *) malloc(length);
|
||||
dyn_buffer = malloc(length);
|
||||
|
||||
if (dyn_buffer == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(dyn_buffer + dyn_buffer_size - 1, input_ptr);
|
||||
@ -143,7 +153,12 @@ static char *extract_socket_path(const char *info)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = (char *) malloc(end - pos + 1);
|
||||
path = malloc(end - pos + 1);
|
||||
|
||||
if (path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*end = '\0';
|
||||
return strcpy(path, pos);
|
||||
}
|
||||
@ -300,7 +315,12 @@ static int tmux_is_detached(void)
|
||||
session_info_stream = NULL;
|
||||
|
||||
/* prepare search string, for finding the current session's entry */
|
||||
search_str = (char *) malloc(numstr_len + 2);
|
||||
search_str = malloc(numstr_len + 2);
|
||||
|
||||
if (search_str == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
search_str[0] = '\n';
|
||||
strcpy(search_str + 1, mplex_data);
|
||||
|
||||
|
464
src/toxic.c
464
src/toxic.c
@ -20,53 +20,53 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <curses.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <getopt.h>
|
||||
#include <limits.h>
|
||||
#include <locale.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <getopt.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <termios.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "audio_device.h"
|
||||
#include "bootstrap.h"
|
||||
#include "conference.h"
|
||||
#include "configdir.h"
|
||||
#include "execute.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "name_lookup.h"
|
||||
#include "notify.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
#include "term_mplex.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "prompt.h"
|
||||
#include "misc_tools.h"
|
||||
#include "groupchat.h"
|
||||
#include "file_transfers.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "notify.h"
|
||||
#include "audio_device.h"
|
||||
#include "message_queue.h"
|
||||
#include "execute.h"
|
||||
#include "term_mplex.h"
|
||||
#include "name_lookup.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
#ifdef X11
|
||||
#include "xtra.h"
|
||||
#include "x11focus.h"
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO
|
||||
@ -103,7 +103,6 @@ struct av_thread av_thread;
|
||||
struct arg_opts arg_opts;
|
||||
struct user_settings *user_settings = NULL;
|
||||
|
||||
|
||||
static struct user_password {
|
||||
bool data_is_encrypted;
|
||||
char pass[MAX_PASSWORD_LEN + 1];
|
||||
@ -129,12 +128,12 @@ static void catch_SIGSEGV(int sig)
|
||||
{
|
||||
UNUSED_VAR(sig);
|
||||
|
||||
if (!freopen("/dev/tty", "w", stderr)) { // make sure stderr is enabled since we may have disabled it
|
||||
fprintf(stderr, "Warning: Failed to enable stderr\n");
|
||||
if (freopen("/dev/tty", "w", stderr)) { // make sure stderr is enabled since we may have disabled it
|
||||
fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n");
|
||||
}
|
||||
|
||||
endwin();
|
||||
fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -173,7 +172,10 @@ void free_global_data(void)
|
||||
void exit_toxic_success(Tox *m)
|
||||
{
|
||||
store_data(m, DATA_FILE);
|
||||
memset(&user_password, 0, sizeof(struct user_password));
|
||||
|
||||
user_password = (struct user_password) {
|
||||
0
|
||||
};
|
||||
|
||||
terminate_notify();
|
||||
|
||||
@ -203,10 +205,7 @@ void exit_toxic_success(Tox *m)
|
||||
curl_global_cleanup();
|
||||
|
||||
#ifdef X11
|
||||
/* We have to terminate xtra last coz reasons
|
||||
* Please don't call this anywhere else coz trust me
|
||||
*/
|
||||
terminate_xtra();
|
||||
terminate_x11focus();
|
||||
#endif /* X11 */
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
@ -216,12 +215,11 @@ void exit_toxic_err(const char *errmsg, int errcode)
|
||||
{
|
||||
free_global_data();
|
||||
|
||||
if (!freopen("/dev/tty", "w", stderr)) {
|
||||
fprintf(stderr, "Warning: Failed to open stderr\n");
|
||||
if (freopen("/dev/tty", "w", stderr)) {
|
||||
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
|
||||
}
|
||||
|
||||
endwin();
|
||||
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
@ -258,10 +256,14 @@ static void init_term(void)
|
||||
keypad(stdscr, 1);
|
||||
noecho();
|
||||
nonl();
|
||||
timeout(100);
|
||||
timeout(50);
|
||||
|
||||
if (has_colors()) {
|
||||
short bg_color = COLOR_BLACK;
|
||||
short bar_bg_color = COLOR_BLUE;
|
||||
short bar_fg_color = COLOR_WHITE;
|
||||
short bar_accent_color = COLOR_CYAN;
|
||||
short bar_notify_color = COLOR_YELLOW;
|
||||
start_color();
|
||||
|
||||
if (user_settings->colour_theme == NATIVE_COLS) {
|
||||
@ -270,15 +272,120 @@ static void init_term(void)
|
||||
}
|
||||
}
|
||||
|
||||
init_pair(0, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(1, COLOR_GREEN, bg_color);
|
||||
init_pair(2, COLOR_CYAN, bg_color);
|
||||
init_pair(3, COLOR_RED, bg_color);
|
||||
init_pair(4, COLOR_BLUE, bg_color);
|
||||
init_pair(5, COLOR_YELLOW, bg_color);
|
||||
init_pair(6, COLOR_MAGENTA, bg_color);
|
||||
init_pair(7, COLOR_BLACK, COLOR_BLACK);
|
||||
init_pair(8, COLOR_BLACK, COLOR_WHITE);
|
||||
if (!string_is_empty(user_settings->color_bar_bg)) {
|
||||
if (strcmp(user_settings->color_bar_bg, "black") == 0) {
|
||||
bar_bg_color = COLOR_BLACK;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "red") == 0) {
|
||||
bar_bg_color = COLOR_RED;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "blue") == 0) {
|
||||
bar_bg_color = COLOR_BLUE;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "cyan") == 0) {
|
||||
bar_bg_color = COLOR_CYAN;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "green") == 0) {
|
||||
bar_bg_color = COLOR_GREEN;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "yellow") == 0) {
|
||||
bar_bg_color = COLOR_YELLOW;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "magenta") == 0) {
|
||||
bar_bg_color = COLOR_MAGENTA;
|
||||
} else if (strcmp(user_settings->color_bar_bg, "white") == 0) {
|
||||
bar_bg_color = COLOR_WHITE;
|
||||
} else {
|
||||
bar_bg_color = COLOR_BLUE;
|
||||
}
|
||||
} else {
|
||||
bar_bg_color = COLOR_BLUE;
|
||||
}
|
||||
|
||||
if (!string_is_empty(user_settings->color_bar_fg)) {
|
||||
if (strcmp(user_settings->color_bar_fg, "black") == 0) {
|
||||
bar_fg_color = COLOR_BLACK;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "red") == 0) {
|
||||
bar_fg_color = COLOR_RED;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "blue") == 0) {
|
||||
bar_fg_color = COLOR_BLUE;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "cyan") == 0) {
|
||||
bar_fg_color = COLOR_CYAN;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "green") == 0) {
|
||||
bar_fg_color = COLOR_GREEN;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "yellow") == 0) {
|
||||
bar_fg_color = COLOR_YELLOW;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "magenta") == 0) {
|
||||
bar_fg_color = COLOR_MAGENTA;
|
||||
} else if (strcmp(user_settings->color_bar_fg, "white") == 0) {
|
||||
bar_fg_color = COLOR_WHITE;
|
||||
} else {
|
||||
bar_fg_color = COLOR_WHITE;
|
||||
}
|
||||
} else {
|
||||
bar_fg_color = COLOR_WHITE;
|
||||
}
|
||||
|
||||
if (!string_is_empty(user_settings->color_bar_accent)) {
|
||||
if (strcmp(user_settings->color_bar_accent, "black") == 0) {
|
||||
bar_accent_color = COLOR_BLACK;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "red") == 0) {
|
||||
bar_accent_color = COLOR_RED;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "blue") == 0) {
|
||||
bar_accent_color = COLOR_BLUE;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "cyan") == 0) {
|
||||
bar_accent_color = COLOR_CYAN;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "green") == 0) {
|
||||
bar_accent_color = COLOR_GREEN;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "yellow") == 0) {
|
||||
bar_accent_color = COLOR_YELLOW;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "magenta") == 0) {
|
||||
bar_accent_color = COLOR_MAGENTA;
|
||||
} else if (strcmp(user_settings->color_bar_accent, "white") == 0) {
|
||||
bar_accent_color = COLOR_WHITE;
|
||||
} else {
|
||||
bar_accent_color = COLOR_CYAN;
|
||||
}
|
||||
} else {
|
||||
bar_accent_color = COLOR_CYAN;
|
||||
}
|
||||
|
||||
if (!string_is_empty(user_settings->color_bar_notify)) {
|
||||
if (strcmp(user_settings->color_bar_notify, "black") == 0) {
|
||||
bar_notify_color = COLOR_BLACK;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "red") == 0) {
|
||||
bar_notify_color = COLOR_RED;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "blue") == 0) {
|
||||
bar_notify_color = COLOR_BLUE;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "cyan") == 0) {
|
||||
bar_notify_color = COLOR_CYAN;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "green") == 0) {
|
||||
bar_notify_color = COLOR_GREEN;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "yellow") == 0) {
|
||||
bar_notify_color = COLOR_YELLOW;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "magenta") == 0) {
|
||||
bar_notify_color = COLOR_MAGENTA;
|
||||
} else if (strcmp(user_settings->color_bar_notify, "white") == 0) {
|
||||
bar_notify_color = COLOR_WHITE;
|
||||
} else {
|
||||
bar_notify_color = COLOR_YELLOW;
|
||||
}
|
||||
} else {
|
||||
bar_notify_color = COLOR_YELLOW;
|
||||
}
|
||||
|
||||
init_pair(WHITE, COLOR_WHITE, COLOR_BLACK);
|
||||
init_pair(GREEN, COLOR_GREEN, bg_color);
|
||||
init_pair(CYAN, COLOR_CYAN, bg_color);
|
||||
init_pair(RED, COLOR_RED, bg_color);
|
||||
init_pair(BLUE, COLOR_BLUE, bg_color);
|
||||
init_pair(YELLOW, COLOR_YELLOW, bg_color);
|
||||
init_pair(MAGENTA, COLOR_MAGENTA, bg_color);
|
||||
init_pair(BLACK, COLOR_BLACK, COLOR_BLACK);
|
||||
init_pair(BLUE_BLACK, COLOR_BLUE, COLOR_BLACK);
|
||||
init_pair(BLACK_WHITE, COLOR_BLACK, COLOR_WHITE);
|
||||
init_pair(BLACK_BG, COLOR_BLACK, bar_bg_color);
|
||||
init_pair(PURPLE_BG, COLOR_MAGENTA, bar_bg_color);
|
||||
init_pair(BAR_TEXT, bar_fg_color, bar_bg_color);
|
||||
init_pair(BAR_ACCENT, bar_accent_color, bar_bg_color);
|
||||
init_pair(BAR_NOTIFY, bar_notify_color, bar_bg_color);
|
||||
init_pair(STATUS_ONLINE, COLOR_GREEN, bar_bg_color);
|
||||
init_pair(STATUS_AWAY, COLOR_YELLOW, bar_bg_color);
|
||||
init_pair(STATUS_BUSY, COLOR_RED, bar_bg_color);
|
||||
}
|
||||
|
||||
refresh();
|
||||
@ -363,33 +470,39 @@ static void load_conferences(Tox *m)
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t chatlist[num_chats];
|
||||
uint32_t *chatlist = malloc(num_chats * sizeof(uint32_t));
|
||||
|
||||
if (chatlist == NULL) {
|
||||
fprintf(stderr, "malloc() failed in load_conferences()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
tox_conference_get_chatlist(m, chatlist);
|
||||
|
||||
for (size_t i = 0; i < num_chats; ++i) {
|
||||
uint32_t groupnum = chatlist[i];
|
||||
uint32_t conferencenum = chatlist[i];
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Get_Type err;
|
||||
Tox_Conference_Type type = tox_conference_get_type(m, groupnum, &err);
|
||||
Tox_Conference_Type type = tox_conference_get_type(m, conferencenum, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_GET_TYPE_OK) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
Tox_Err_Conference_Title t_err;
|
||||
size_t length = tox_conference_get_title_size(m, groupnum, &t_err);
|
||||
size_t length = tox_conference_get_title_size(m, conferencenum, &t_err);
|
||||
uint8_t title[MAX_STR_SIZE];
|
||||
|
||||
if (t_err != TOX_ERR_CONFERENCE_TITLE_OK || length >= sizeof(title)) {
|
||||
length = 0;
|
||||
} else {
|
||||
tox_conference_get_title(m, groupnum, title, &t_err);
|
||||
tox_conference_get_title(m, conferencenum, title, &t_err);
|
||||
|
||||
if (t_err != TOX_ERR_CONFERENCE_TITLE_OK) {
|
||||
length = 0;
|
||||
@ -398,11 +511,25 @@ static void load_conferences(Tox *m)
|
||||
|
||||
title[length] = 0;
|
||||
|
||||
if (init_groupchat_win(m, groupnum, type, (const char *) title, length) == -1) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
int win_idx = init_conference_win(m, conferencenum, type, (const char *) title, length);
|
||||
|
||||
if (win_idx == -1) {
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
line_info_add(get_window_ptr(win_idx), NULL, NULL, NULL, SYS_MSG, 0, 0,
|
||||
#ifdef AUDIO
|
||||
"Use \"/audio on\" to enable audio in this conference."
|
||||
#else
|
||||
"Audio support disabled by compile-time option."
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
free(chatlist);
|
||||
}
|
||||
|
||||
/* return length of password on success, 0 on failure */
|
||||
@ -422,12 +549,17 @@ static int password_prompt(char *buf, int size)
|
||||
}
|
||||
|
||||
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) {
|
||||
if (p == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t len = strlen(buf);
|
||||
|
||||
if (len <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -568,12 +700,19 @@ int store_data(Tox *m, const char *path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
char temp_path[strlen(path) + strlen(TEMP_PROFILE_EXT) + 1];
|
||||
snprintf(temp_path, sizeof(temp_path), "%s%s", path, TEMP_PROFILE_EXT);
|
||||
size_t temp_buf_size = strlen(path) + strlen(TEMP_PROFILE_EXT) + 1;
|
||||
char *temp_path = malloc(temp_buf_size);
|
||||
|
||||
if (temp_path == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(temp_path, temp_buf_size, "%s%s", path, TEMP_PROFILE_EXT);
|
||||
|
||||
FILE *fp = fopen(temp_path, "wb");
|
||||
|
||||
if (fp == NULL) {
|
||||
free(temp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -581,6 +720,7 @@ int store_data(Tox *m, const char *path)
|
||||
char *data = malloc(data_len * sizeof(char));
|
||||
|
||||
if (data == NULL) {
|
||||
free(temp_path);
|
||||
fclose(fp);
|
||||
return -1;
|
||||
}
|
||||
@ -593,6 +733,7 @@ int store_data(Tox *m, const char *path)
|
||||
|
||||
if (enc_data == NULL) {
|
||||
fclose(fp);
|
||||
free(temp_path);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
@ -604,6 +745,7 @@ int store_data(Tox *m, const char *path)
|
||||
if (err != TOX_ERR_ENCRYPTION_OK) {
|
||||
fprintf(stderr, "tox_pass_encrypt() failed with error %d\n", err);
|
||||
fclose(fp);
|
||||
free(temp_path);
|
||||
free(data);
|
||||
free(enc_data);
|
||||
return -1;
|
||||
@ -612,6 +754,7 @@ int store_data(Tox *m, const char *path)
|
||||
if (fwrite(enc_data, enc_len, 1, fp) != 1) {
|
||||
fprintf(stderr, "Failed to write profile data.\n");
|
||||
fclose(fp);
|
||||
free(temp_path);
|
||||
free(data);
|
||||
free(enc_data);
|
||||
return -1;
|
||||
@ -622,6 +765,7 @@ int store_data(Tox *m, const char *path)
|
||||
if (fwrite(data, data_len, 1, fp) != 1) {
|
||||
fprintf(stderr, "Failed to write profile data.\n");
|
||||
fclose(fp);
|
||||
free(temp_path);
|
||||
free(data);
|
||||
return -1;
|
||||
}
|
||||
@ -631,9 +775,12 @@ int store_data(Tox *m, const char *path)
|
||||
free(data);
|
||||
|
||||
if (rename(temp_path, path) != 0) {
|
||||
free(temp_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(temp_path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -722,10 +869,16 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
|
||||
}
|
||||
|
||||
char data[len];
|
||||
char *data = malloc(len);
|
||||
|
||||
if (fread(data, sizeof(data), 1, fp) != 1) {
|
||||
if (data == NULL) {
|
||||
fclose(fp);
|
||||
exit_toxic_err("failed in load_tox", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
if (fread(data, len, 1, fp) != 1) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
|
||||
}
|
||||
|
||||
@ -734,6 +887,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
/* attempt to encrypt an already encrypted data file */
|
||||
if (arg_opts.encrypt_data && is_encrypted) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT);
|
||||
}
|
||||
|
||||
@ -757,7 +911,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
}
|
||||
|
||||
size_t plain_len = len - TOX_PASS_ENCRYPTION_EXTRA_LENGTH;
|
||||
char plain[plain_len];
|
||||
char *plain = malloc(plain_len); // must be freed after tox_new()
|
||||
|
||||
if (plain == NULL) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
exit_toxic_err("failed in load_tox", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
fflush(stdout); // Flush before prompts so the user sees the question/message
|
||||
@ -772,6 +932,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
|
||||
if (strcasecmp(user_password.pass, "q") == 0) {
|
||||
fclose(fp);
|
||||
free(plain);
|
||||
free(data);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
@ -795,6 +957,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
|
||||
if (m == NULL) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
free(plain);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -806,9 +970,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
pweval = 0;
|
||||
} else {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
free(plain);
|
||||
exit_toxic_err("tox_pass_decrypt() failed", pwerr);
|
||||
}
|
||||
}
|
||||
|
||||
free(plain);
|
||||
} else { /* data is not encrypted */
|
||||
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
|
||||
tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len);
|
||||
@ -817,11 +985,13 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New
|
||||
|
||||
if (m == NULL) {
|
||||
fclose(fp);
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(data);
|
||||
} else { /* Data file does not/should not exist */
|
||||
if (file_exists(data_path)) {
|
||||
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
|
||||
@ -894,6 +1064,7 @@ static void do_toxic(Tox *m)
|
||||
|
||||
tox_iterate(m, NULL);
|
||||
do_tox_connection(m);
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
|
||||
@ -932,20 +1103,18 @@ void *thread_cqueue(void *data)
|
||||
while (true) {
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
for (size_t i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
ToxWindow *toxwin = get_window_ptr(i);
|
||||
|
||||
if (toxwin != NULL && toxwin->is_chat
|
||||
&& get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE) {
|
||||
if ((toxwin != NULL) && (toxwin->type == WINDOW_TYPE_CHAT)
|
||||
&& (get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE)) {
|
||||
cqueue_try_send(toxwin, m);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
usleep(4000);
|
||||
sleep_thread(750000L); // 0.75 seconds
|
||||
}
|
||||
}
|
||||
|
||||
@ -959,7 +1128,8 @@ void *thread_av(void *data)
|
||||
toxav_iterate(av);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
usleep(toxav_iteration_interval(av) * 1000);
|
||||
long int sleep_duration = toxav_iteration_interval(av) * 1000;
|
||||
sleep_thread(sleep_duration);
|
||||
}
|
||||
}
|
||||
#endif /* AUDIO */
|
||||
@ -995,7 +1165,9 @@ static void print_version(void)
|
||||
|
||||
static void set_default_opts(void)
|
||||
{
|
||||
memset(&arg_opts, 0, sizeof(struct arg_opts));
|
||||
arg_opts = (struct arg_opts) {
|
||||
0
|
||||
};
|
||||
|
||||
/* set any non-zero defaults here*/
|
||||
arg_opts.proxy_type = TOX_PROXY_TYPE_NONE;
|
||||
@ -1043,6 +1215,11 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
|
||||
|
||||
if (!file_exists(arg_opts.config_path)) {
|
||||
@ -1061,23 +1238,31 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
arg_opts.use_custom_data = 1;
|
||||
|
||||
if (DATA_FILE) {
|
||||
free(DATA_FILE);
|
||||
DATA_FILE = NULL;
|
||||
}
|
||||
|
||||
if (BLOCK_FILE) {
|
||||
free(BLOCK_FILE);
|
||||
BLOCK_FILE = NULL;
|
||||
}
|
||||
|
||||
DATA_FILE = malloc(strlen(optarg) + 1);
|
||||
strcpy(DATA_FILE, optarg);
|
||||
|
||||
if (DATA_FILE == NULL) {
|
||||
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
strcpy(DATA_FILE, optarg);
|
||||
|
||||
BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1);
|
||||
|
||||
if (BLOCK_FILE == NULL) {
|
||||
@ -1118,6 +1303,11 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
|
||||
break;
|
||||
|
||||
@ -1127,6 +1317,11 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
|
||||
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
|
||||
|
||||
@ -1144,6 +1339,11 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
|
||||
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
|
||||
|
||||
@ -1161,6 +1361,11 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg);
|
||||
|
||||
if (!file_exists(arg_opts.nameserver_path)) {
|
||||
@ -1174,10 +1379,15 @@ static void parse_args(int argc, char *argv[])
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
if (optarg == NULL) {
|
||||
queue_init_message("Invalid argument for option: %d", opt);
|
||||
break;
|
||||
}
|
||||
|
||||
port = strtol(optarg, NULL, 10);
|
||||
|
||||
if (port <= 0 || port > MAX_PORT_RANGE) {
|
||||
port = 14191;
|
||||
port = MAX_PORT_RANGE;
|
||||
}
|
||||
|
||||
arg_opts.tcp_port = port;
|
||||
@ -1192,6 +1402,8 @@ static void parse_args(int argc, char *argv[])
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case 'h':
|
||||
|
||||
// Intentional fallthrough
|
||||
default:
|
||||
print_usage();
|
||||
exit(EXIT_SUCCESS);
|
||||
@ -1199,50 +1411,6 @@ static void parse_args(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
|
||||
/* Looks for an old default profile data file and blocklist, and renames them to the new default names.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
#define OLD_DATA_NAME "data"
|
||||
#define OLD_DATA_BLOCKLIST_NAME "data-blocklist"
|
||||
static int rename_old_profile(const char *user_config_dir)
|
||||
{
|
||||
char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1];
|
||||
snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME);
|
||||
|
||||
if (!file_exists(old_data_file)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file_exists(DATA_FILE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rename(old_data_file, DATA_FILE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
queue_init_message("Data file has been moved to %s", DATA_FILE);
|
||||
|
||||
char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1];
|
||||
snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME);
|
||||
|
||||
if (!file_exists(old_data_blocklist)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (file_exists(BLOCK_FILE)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (rename(old_data_blocklist, BLOCK_FILE) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initializes the default config directory and data files used by toxic.
|
||||
*
|
||||
* Exits the process with an error on failure.
|
||||
@ -1285,29 +1453,9 @@ static void init_default_data_files(void)
|
||||
strcat(BLOCK_FILE, BLOCKNAME);
|
||||
}
|
||||
|
||||
/* For backwards compatibility with old toxic profile names. TODO: remove this some day */
|
||||
if (rename_old_profile(user_config_dir) == -1) {
|
||||
queue_init_message("Warning: Profile backwards compatibility failed.");
|
||||
}
|
||||
|
||||
free(user_config_dir);
|
||||
}
|
||||
|
||||
// this doesn't do anything (yet)
|
||||
#ifdef X11
|
||||
void DnD_callback(const char *asdv, DropType dt)
|
||||
{
|
||||
UNUSED_VAR(asdv);
|
||||
UNUSED_VAR(dt);
|
||||
// if (dt != DT_plain)
|
||||
// return;
|
||||
|
||||
// pthread_mutex_lock(&Winthread.lock);
|
||||
// line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, asdv);
|
||||
// pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
#endif /* X11 */
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Make sure all written files are read/writeable only by the current user. */
|
||||
@ -1365,7 +1513,7 @@ int main(int argc, char **argv)
|
||||
|
||||
#ifdef X11
|
||||
|
||||
if (init_xtra(DnD_callback) == -1) {
|
||||
if (init_x11focus() == -1) {
|
||||
queue_init_message("X failed to initialize");
|
||||
}
|
||||
|
||||
@ -1382,21 +1530,12 @@ int main(int argc, char **argv)
|
||||
prompt = init_windows(m);
|
||||
prompt_init_statusbar(prompt, m, !datafile_exists);
|
||||
load_conferences(m);
|
||||
set_active_window_index(0);
|
||||
|
||||
/* thread for ncurses stuff */
|
||||
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
|
||||
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
|
||||
}
|
||||
|
||||
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) {
|
||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@ -1411,8 +1550,8 @@ int main(int argc, char **argv)
|
||||
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
|
||||
}
|
||||
|
||||
set_primary_device(input, user_settings->audio_in_dev);
|
||||
set_primary_device(output, user_settings->audio_out_dev);
|
||||
set_al_device(input, user_settings->audio_in_dev);
|
||||
set_al_device(output, user_settings->audio_out_dev);
|
||||
|
||||
#elif SOUND_NOTIFY
|
||||
|
||||
@ -1422,6 +1561,16 @@ int main(int argc, char **argv)
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
/* thread for ncurses stuff */
|
||||
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 PYTHON
|
||||
|
||||
init_python(m);
|
||||
@ -1464,7 +1613,7 @@ int main(int argc, char **argv)
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
if (store_data(m, DATA_FILE) != 0) {
|
||||
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
|
||||
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
@ -1472,7 +1621,8 @@ int main(int argc, char **argv)
|
||||
last_save = cur_time;
|
||||
}
|
||||
|
||||
usleep(tox_iteration_interval(m) * 1000);
|
||||
long int sleep_duration = tox_iteration_interval(m) * 1000;
|
||||
sleep_thread(sleep_duration);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
13
src/toxic.h
13
src/toxic.h
@ -48,6 +48,7 @@
|
||||
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */
|
||||
#define KEY_IDENT_DIGITS 3 /* number of hex digits to display for the pub-key based identifier */
|
||||
#define TIME_STR_SIZE 32
|
||||
#define COLOR_STR_SIZE 10 /* should fit every color option */
|
||||
|
||||
#ifndef MAX_PORT_RANGE
|
||||
#define MAX_PORT_RANGE 65535
|
||||
@ -75,7 +76,7 @@
|
||||
#define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */
|
||||
#define T_KEY_TAB 0x09 /* TAB key */
|
||||
|
||||
#define ONLINE_CHAR "*"
|
||||
#define ONLINE_CHAR "o"
|
||||
#define OFFLINE_CHAR "o"
|
||||
|
||||
typedef enum _FATAL_ERRS {
|
||||
@ -115,14 +116,14 @@ void on_friend_name(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t
|
||||
void on_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata);
|
||||
void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
|
||||
void on_friend_added(Tox *m, uint32_t friendnumber, bool sort);
|
||||
void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
|
||||
void on_conference_message(Tox *m, uint32_t conferencenumber, uint32_t peernumber, Tox_Message_Type type,
|
||||
const uint8_t *message, size_t length, void *userdata);
|
||||
void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key,
|
||||
void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *conference_pub_key,
|
||||
size_t length, void *userdata);
|
||||
void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata);
|
||||
void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
|
||||
void on_conference_peer_list_changed(Tox *m, uint32_t conferencenumber, void *userdata);
|
||||
void on_conference_peer_name(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *name,
|
||||
size_t length, void *userdata);
|
||||
void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void on_conference_title(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void *userdata);
|
||||
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
|
||||
void *userdata);
|
||||
|
@ -24,11 +24,11 @@
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
|
||||
int add_char_to_buf(ChatContext *ctx, wint_t ch)
|
||||
|
317
src/video_call.c
317
src/video_call.c
@ -20,27 +20,27 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "video_call.h"
|
||||
#include "video_device.h"
|
||||
#include "chat_commands.h"
|
||||
#include "global_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "notify.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "video_call.h"
|
||||
#include "video_device.h"
|
||||
#include "windows.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <curses.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <curses.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
#define default_video_bit_rate 5000
|
||||
#define DEFAULT_VIDEO_BIT_RATE 5000
|
||||
|
||||
void on_video_receive_frame(ToxAV *av, uint32_t friend_number,
|
||||
uint16_t width, uint16_t height,
|
||||
@ -52,7 +52,7 @@ void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rat
|
||||
|
||||
static void print_err(ToxWindow *self, const char *error_str)
|
||||
{
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||
}
|
||||
|
||||
ToxAV *init_video(ToxWindow *self, Tox *tox)
|
||||
@ -62,17 +62,17 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
|
||||
CallControl.video_errors = ve_None;
|
||||
|
||||
CallControl.video_enabled = true;
|
||||
CallControl.video_bit_rate = 0;
|
||||
CallControl.default_video_bit_rate = 0;
|
||||
CallControl.video_frame_duration = 10;
|
||||
|
||||
if (!CallControl.av) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Video failed to init with ToxAV instance");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (init_video_devices(CallControl.av) == vde_InternalError) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices");
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -92,8 +92,9 @@ void terminate_video(void)
|
||||
|
||||
stop_video_transmission(this_call, i);
|
||||
|
||||
if (this_call->vout_idx != -1) {
|
||||
if (this_call->status == cs_Active && this_call->vout_idx != -1) {
|
||||
close_video_device(vdt_output, this_call->vout_idx);
|
||||
this_call->vout_idx = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,18 +109,18 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y,
|
||||
Toxav_Err_Send_Frame error;
|
||||
|
||||
/* Drop frame if video sending is disabled */
|
||||
if (CallControl.video_bit_rate == 0 || this_call->vin_idx == -1) {
|
||||
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped.");
|
||||
if (this_call->video_bit_rate == 0 || this_call->status != cs_Active || this_call->vin_idx == -1) {
|
||||
line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error) == false) {
|
||||
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame");
|
||||
line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to send video frame");
|
||||
|
||||
if (error == TOXAV_ERR_SEND_FRAME_NULL) {
|
||||
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame");
|
||||
line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to capture video frame");
|
||||
} else if (error == TOXAV_ERR_SEND_FRAME_INVALID) {
|
||||
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame");
|
||||
line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -137,24 +138,22 @@ void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_
|
||||
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
|
||||
{
|
||||
if (!self || !av) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission");
|
||||
return -1;
|
||||
}
|
||||
|
||||
CallControl.video_bit_rate = default_video_bit_rate;
|
||||
|
||||
if (toxav_video_set_bit_rate(CallControl.av, self->num, CallControl.video_bit_rate, NULL) == false) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (open_primary_video_device(vdt_input, &call->vin_idx) != vde_None) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!");
|
||||
if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -163,8 +162,12 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
|
||||
|
||||
int stop_video_transmission(Call *call, int friend_number)
|
||||
{
|
||||
CallControl.video_bit_rate = 0;
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
|
||||
if (call->status != cs_Active) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
call->video_bit_rate = 0;
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, call->video_bit_rate, NULL);
|
||||
|
||||
if (call->vin_idx != -1) {
|
||||
close_video_device(vdt_input, call->vin_idx);
|
||||
@ -200,24 +203,33 @@ void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rat
|
||||
UNUSED_VAR(av);
|
||||
UNUSED_VAR(user_data);
|
||||
|
||||
CallControl.video_bit_rate = video_bit_rate;
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
|
||||
Call *call = &CallControl.calls[friend_number];
|
||||
call->video_bit_rate = video_bit_rate;
|
||||
|
||||
/* TODO: with current toxav using one-pass VP8, the value of
|
||||
* video_bit_rate has no effect, except to disable video if it is 0.
|
||||
* Automatically change resolution instead? */
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, call->video_bit_rate, NULL);
|
||||
}
|
||||
|
||||
void callback_recv_video_starting(uint32_t friend_number)
|
||||
{
|
||||
Call *this_call = &CallControl.calls[friend_number];
|
||||
|
||||
if (this_call->vout_idx != -1) {
|
||||
if (this_call->status != cs_Active || this_call->vout_idx != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
open_primary_video_device(vdt_output, &this_call->vout_idx);
|
||||
open_primary_video_device(vdt_output, &this_call->vout_idx, NULL, NULL);
|
||||
}
|
||||
void callback_recv_video_end(uint32_t friend_number)
|
||||
{
|
||||
Call *this_call = &CallControl.calls[friend_number];
|
||||
|
||||
if (this_call->status != cs_Active || this_call->vout_idx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
close_video_device(vdt_output, this_call->vout_idx);
|
||||
this_call->vout_idx = -1;
|
||||
}
|
||||
@ -235,12 +247,9 @@ void callback_video_starting(uint32_t friend_number)
|
||||
ToxWindow *window = get_window_ptr(i);
|
||||
|
||||
if (window != NULL && window->is_call && window->num == friend_number) {
|
||||
if (0 != start_video_transmission(window, CallControl.av, this_call)) {
|
||||
line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
|
||||
return;
|
||||
if (start_video_transmission(window, CallControl.av, this_call) == 0) {
|
||||
line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting.");
|
||||
}
|
||||
|
||||
line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,44 +267,122 @@ void callback_video_end(uint32_t friend_number)
|
||||
/*
|
||||
* Commands from chat_commands.h
|
||||
*/
|
||||
void cmd_vcall(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
UNUSED_VAR(argv);
|
||||
|
||||
if (argc != 0) {
|
||||
print_err(self, "Unknown arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CallControl.av) {
|
||||
print_err(self, "ToxAV not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->stb->connection) {
|
||||
print_err(self, "Friend is offline.");
|
||||
return;
|
||||
}
|
||||
|
||||
Call *call = &CallControl.calls[self->num];
|
||||
|
||||
if (call->status != cs_None) {
|
||||
print_err(self, "Already calling.");
|
||||
return;
|
||||
}
|
||||
|
||||
init_call(call);
|
||||
|
||||
call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE;
|
||||
|
||||
place_call(self);
|
||||
}
|
||||
|
||||
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
UNUSED_VAR(argv);
|
||||
|
||||
const char *error_str;
|
||||
Call *this_call = &CallControl.calls[self->num];
|
||||
|
||||
if (argc != 0) {
|
||||
error_str = "Unknown arguments.";
|
||||
goto on_error;
|
||||
print_err(self, "Unknown arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CallControl.av) {
|
||||
error_str = "ToxAV not supported!";
|
||||
goto on_error;
|
||||
print_err(self, "ToxAV not supported!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->stb->connection) {
|
||||
error_str = "Friend is offline.";
|
||||
goto on_error;
|
||||
print_err(self, "Friend is offline.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!self->is_call) {
|
||||
error_str = "Not in call!";
|
||||
goto on_error;
|
||||
if (this_call->status != cs_Active) {
|
||||
print_err(self, "Not in call!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this_call->vin_idx == -1) {
|
||||
this_call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE;
|
||||
callback_video_starting(self->num);
|
||||
} else {
|
||||
callback_video_end(self->num);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err(self, error_str);
|
||||
void cmd_res(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
Call *call = &CallControl.calls[self->num];
|
||||
|
||||
if (argc == 0) {
|
||||
if (call->status == cs_Active && call->vin_idx != -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Resolution of current call: %u x %u",
|
||||
call->video_width, call->video_height);
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"Initial resolution for video calls: %u x %u",
|
||||
CallControl.default_video_width, CallControl.default_video_height);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc != 2) {
|
||||
print_err(self, "Require 0 or 2 arguments.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *endw, *endh;
|
||||
const long int width = strtol(argv[1], &endw, 10);
|
||||
const long int height = strtol(argv[2], &endh, 10);
|
||||
|
||||
if (*endw || *endh || width < 0 || height < 0) {
|
||||
print_err(self, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
if (call->status == cs_Active && call->vin_idx != -1) {
|
||||
stop_video_transmission(call, self->num);
|
||||
call->video_width = width;
|
||||
call->video_height = height;
|
||||
call->video_bit_rate = DEFAULT_VIDEO_BIT_RATE;
|
||||
start_video_transmission(self, CallControl.av, call);
|
||||
} else {
|
||||
CallControl.default_video_width = width;
|
||||
CallControl.default_video_height = height;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
@ -303,16 +390,14 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 1) {
|
||||
if (argc < 1) {
|
||||
error_str = "Type must be specified!";
|
||||
print_err(self, "Type must be specified!");
|
||||
} else {
|
||||
error_str = "Only one argument allowed!";
|
||||
print_err(self, "Only one argument allowed!");
|
||||
}
|
||||
|
||||
goto on_error;
|
||||
return;
|
||||
}
|
||||
|
||||
VideoDeviceType type;
|
||||
@ -326,15 +411,11 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c
|
||||
}
|
||||
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
print_video_devices(self, type);
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err(self, error_str);
|
||||
}
|
||||
|
||||
/* This changes primary video device only */
|
||||
@ -343,18 +424,16 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 2) {
|
||||
if (argc < 1) {
|
||||
error_str = "Type must be specified!";
|
||||
print_err(self, "Type must be specified!");
|
||||
} else if (argc < 2) {
|
||||
error_str = "Must have id!";
|
||||
print_err(self, "Must have id!");
|
||||
} else {
|
||||
error_str = "Only two arguments allowed!";
|
||||
print_err(self, "Only two arguments allowed!");
|
||||
}
|
||||
|
||||
goto on_error;
|
||||
return;
|
||||
}
|
||||
|
||||
VideoDeviceType type;
|
||||
@ -368,7 +447,7 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
||||
}
|
||||
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -377,98 +456,14 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
|
||||
long int selection = strtol(argv[2], &end, 10);
|
||||
|
||||
if (*end) {
|
||||
error_str = "Invalid input";
|
||||
goto on_error;
|
||||
print_err(self, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
if (set_primary_video_device(type, selection) == vde_InvalidSelection) {
|
||||
error_str = "Invalid selection!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err(self, error_str);
|
||||
}
|
||||
|
||||
void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
const char *error_str;
|
||||
|
||||
if (argc != 2) {
|
||||
if (argc < 1) {
|
||||
error_str = "Type must be specified!";
|
||||
} else if (argc < 2) {
|
||||
error_str = "Must have id!";
|
||||
} else {
|
||||
error_str = "Only two arguments allowed!";
|
||||
}
|
||||
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
VideoDeviceType type;
|
||||
|
||||
if (strcmp(argv[1], "in") == 0) { /* Input devices */
|
||||
type = vdt_input;
|
||||
}
|
||||
|
||||
else if (strcmp(argv[1], "out") == 0) { /* Output devices */
|
||||
type = vdt_output;
|
||||
}
|
||||
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
|
||||
print_err(self, "Invalid selection!");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
char *end;
|
||||
long int selection = strtol(argv[2], &end, 10);
|
||||
|
||||
if (*end) {
|
||||
error_str = "Invalid input";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (video_selection_valid(type, selection) == vde_InvalidSelection) {
|
||||
error_str = "Invalid selection!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* If call is active, change device */
|
||||
if (self->is_call) {
|
||||
Call *this_call = &CallControl.calls[self->num];
|
||||
|
||||
if (this_call->ttas) {
|
||||
|
||||
if (type == vdt_output) {
|
||||
} else {
|
||||
/* TODO: check for failure */
|
||||
close_video_device(vdt_input, this_call->vin_idx);
|
||||
open_video_device(vdt_input, selection, &this_call->vin_idx);
|
||||
register_video_device_callback(self->num, this_call->vin_idx, read_video_device_callback, &self->num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self->video_device_selection[type] = selection;
|
||||
|
||||
return;
|
||||
on_error:
|
||||
print_err(self, error_str);
|
||||
}
|
||||
|
||||
void stop_video_stream(ToxWindow *self)
|
||||
{
|
||||
Call *this_call = &CallControl.calls[self->num];
|
||||
|
||||
if (this_call && this_call->vin_idx != -1) {
|
||||
stop_video_transmission(this_call, self->num);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
@ -34,7 +34,6 @@ ToxAV *init_video(ToxWindow *self, Tox *tox);
|
||||
void terminate_video(void);
|
||||
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call);
|
||||
int stop_video_transmission(Call *call, int friend_number);
|
||||
void stop_video_stream(ToxWindow *self);
|
||||
|
||||
void callback_recv_video_starting(uint32_t friend_number);
|
||||
void callback_recv_video_end(uint32_t friend_number);
|
||||
|
@ -20,8 +20,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "video_device.h"
|
||||
#include "video_call.h"
|
||||
#include "video_device.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
@ -30,13 +30,13 @@
|
||||
#if defined(__OSX__) || defined(__APPLE__)
|
||||
#import "osx_video.h"
|
||||
#else
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xos.h>
|
||||
#include <X11/Xutil.h>
|
||||
#if defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#include <sys/videoio.h>
|
||||
#else
|
||||
@ -45,16 +45,15 @@
|
||||
#endif /* __OSX__ || __APPLE__ */
|
||||
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
@ -181,7 +180,7 @@ static int xioctl(int fh, unsigned long request, void *arg)
|
||||
#ifdef VIDEO
|
||||
VideoDeviceError init_video_devices(ToxAV *av_)
|
||||
#else
|
||||
VideoDeviceError init_video_devices()
|
||||
VideoDeviceError init_video_devices(void)
|
||||
#endif /* VIDEO */
|
||||
{
|
||||
size[vdt_input] = 0;
|
||||
@ -258,7 +257,7 @@ VideoDeviceError terminate_video_devices(void)
|
||||
video_thread_running = false;
|
||||
unlock;
|
||||
|
||||
usleep(20000);
|
||||
sleep_thread(20000L);
|
||||
|
||||
int i;
|
||||
|
||||
@ -315,9 +314,10 @@ VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selectio
|
||||
return vde_None;
|
||||
}
|
||||
|
||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx)
|
||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx,
|
||||
uint32_t *width, uint32_t *height)
|
||||
{
|
||||
return open_video_device(type, primary_video_device[type], device_idx);
|
||||
return open_video_device(type, primary_video_device[type], device_idx, width, height);
|
||||
}
|
||||
|
||||
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
|
||||
@ -325,7 +325,8 @@ void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
|
||||
memcpy(buf, dvideo_device_names[type], size);
|
||||
}
|
||||
|
||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx)
|
||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx,
|
||||
uint32_t *width, uint32_t *height)
|
||||
{
|
||||
if (size[type] <= selection || selection < 0) {
|
||||
return vde_InvalidSelection;
|
||||
@ -372,6 +373,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
|
||||
#if defined(__OSX__) || defined(__APPLE__)
|
||||
|
||||
/* TODO: use requested resolution */
|
||||
if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) {
|
||||
free(device);
|
||||
unlock;
|
||||
@ -401,11 +403,12 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
}
|
||||
|
||||
/* Setup video format */
|
||||
struct v4l2_format fmt;
|
||||
memset(&(fmt), 0, sizeof(fmt));
|
||||
struct v4l2_format fmt = {0};
|
||||
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
|
||||
fmt.fmt.pix.width = width == NULL ? 0 : *width;
|
||||
fmt.fmt.pix.height = height == NULL ? 0 : *height;
|
||||
|
||||
if (-1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt)) {
|
||||
close(device->fd);
|
||||
@ -418,8 +421,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
device->video_height = fmt.fmt.pix.height;
|
||||
|
||||
/* Request buffers */
|
||||
struct v4l2_requestbuffers req;
|
||||
memset(&(req), 0, sizeof(req));
|
||||
struct v4l2_requestbuffers req = {0};
|
||||
|
||||
req.count = 4;
|
||||
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
req.memory = V4L2_MEMORY_MMAP;
|
||||
@ -441,8 +444,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
device->buffers = calloc(req.count, sizeof(struct VideoBuffer));
|
||||
|
||||
for (i = 0; i < req.count; ++i) {
|
||||
struct v4l2_buffer buf;
|
||||
memset(&(buf), 0, sizeof(buf));
|
||||
struct v4l2_buffer buf = {0};
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
@ -479,8 +481,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
enum v4l2_buf_type type;
|
||||
|
||||
for (i = 0; i < device->n_buffers; ++i) {
|
||||
struct v4l2_buffer buf;
|
||||
memset(&(buf), 0, sizeof(buf));
|
||||
struct v4l2_buffer buf = {0};
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
@ -546,6 +547,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
|
||||
vpx_img_alloc(&device->input, VPX_IMG_FMT_I420, device->video_width, device->video_height, 1);
|
||||
|
||||
if (width != NULL) {
|
||||
*width = device->video_width;
|
||||
}
|
||||
|
||||
if (height != NULL) {
|
||||
*height = device->video_height;
|
||||
}
|
||||
|
||||
video_thread_paused = false;
|
||||
} else { /* vdt_output */
|
||||
|
||||
@ -676,7 +685,7 @@ void *video_thread_poll(void *arg) // TODO: maybe use thread for every input so
|
||||
unlock;
|
||||
|
||||
if (video_thread_paused) {
|
||||
usleep(10000); /* Wait for unpause. */
|
||||
sleep_thread(10000L); /* Wait for unpause. */
|
||||
} else {
|
||||
for (i = 0; i < size[vdt_input]; ++i) {
|
||||
lock;
|
||||
@ -698,8 +707,7 @@ void *video_thread_poll(void *arg) // TODO: maybe use thread for every input so
|
||||
}
|
||||
|
||||
#else /* not __OSX__ || __APPLE__ */
|
||||
struct v4l2_buffer buf;
|
||||
memset(&(buf), 0, sizeof(buf));
|
||||
struct v4l2_buffer buf = {0};
|
||||
|
||||
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
buf.memory = V4L2_MEMORY_MMAP;
|
||||
@ -765,7 +773,8 @@ void *video_thread_poll(void *arg) // TODO: maybe use thread for every input so
|
||||
unlock;
|
||||
}
|
||||
|
||||
usleep(1000 * 1000 / 24);
|
||||
long int sleep_duration = 1000 * 1000 / 24;
|
||||
sleep_thread(sleep_duration);
|
||||
}
|
||||
}
|
||||
|
||||
@ -843,7 +852,7 @@ void print_video_devices(ToxWindow *self, VideoDeviceType type)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size[type]; ++i) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -62,9 +62,11 @@ VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t devic
|
||||
void *get_video_device_callback_data(uint32_t device_idx);
|
||||
|
||||
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection);
|
||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx);
|
||||
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx,
|
||||
uint32_t *width, uint32_t *height);
|
||||
/* Start device */
|
||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx);
|
||||
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx,
|
||||
uint32_t *width, uint32_t *height);
|
||||
/* Stop device */
|
||||
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
|
||||
|
||||
|
246
src/windows.c
246
src/windows.c
@ -20,22 +20,22 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "friendlist.h"
|
||||
#include "prompt.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "groupchat.h"
|
||||
#include "avatars.h"
|
||||
#include "chat.h"
|
||||
#include "conference.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "avatars.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
#include "file_transfers.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
extern struct Winthread Winthread;
|
||||
@ -66,6 +66,8 @@ void on_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection c
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
|
||||
on_avatar_friend_connection_status(m, friendnumber, connection_status);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onConnectionChange != NULL) {
|
||||
windows[i]->onConnectionChange(windows[i], m, friendnumber, connection_status);
|
||||
@ -158,7 +160,7 @@ void on_friend_added(Tox *m, uint32_t friendnumber, bool sort)
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
|
||||
void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
|
||||
void on_conference_message(Tox *m, uint32_t conferencenumber, uint32_t peernumber, Tox_Message_Type type,
|
||||
const uint8_t *message, size_t length, void *userdata)
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
@ -167,36 +169,36 @@ void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, To
|
||||
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onGroupMessage != NULL) {
|
||||
windows[i]->onGroupMessage(windows[i], m, groupnumber, peernumber, type, msg, length);
|
||||
if (windows[i] != NULL && windows[i]->onConferenceMessage != NULL) {
|
||||
windows[i]->onConferenceMessage(windows[i], m, conferencenumber, peernumber, type, msg, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key,
|
||||
void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *conference_pub_key,
|
||||
size_t length, void *userdata)
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onGroupInvite != NULL) {
|
||||
windows[i]->onGroupInvite(windows[i], m, friendnumber, type, (char *) group_pub_key, length);
|
||||
if (windows[i] != NULL && windows[i]->onConferenceInvite != NULL) {
|
||||
windows[i]->onConferenceInvite(windows[i], m, friendnumber, type, (char *) conference_pub_key, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata)
|
||||
void on_conference_peer_list_changed(Tox *m, uint32_t conferencenumber, void *userdata)
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onGroupNameListChange != NULL) {
|
||||
windows[i]->onGroupNameListChange(windows[i], m, groupnumber);
|
||||
if (windows[i] != NULL && windows[i]->onConferenceNameListChange != NULL) {
|
||||
windows[i]->onConferenceNameListChange(windows[i], m, conferencenumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
|
||||
void on_conference_peer_name(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *name,
|
||||
size_t length, void *userdata)
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
@ -206,13 +208,13 @@ void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber,
|
||||
filter_str(nick, length);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onGroupPeerNameChange != NULL) {
|
||||
windows[i]->onGroupPeerNameChange(windows[i], m, groupnumber, peernumber, nick, length);
|
||||
if (windows[i] != NULL && windows[i]->onConferencePeerNameChange != NULL) {
|
||||
windows[i]->onConferencePeerNameChange(windows[i], m, conferencenumber, peernumber, nick, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void on_conference_title(Tox *m, uint32_t conferencenumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
UNUSED_VAR(userdata);
|
||||
@ -221,8 +223,8 @@ void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, cons
|
||||
length = copy_tox_str(data, sizeof(data), (const char *) title, length);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] != NULL && windows[i]->onGroupTitleChange != NULL) {
|
||||
windows[i]->onGroupTitleChange(windows[i], m, groupnumber, peernumber, data, length);
|
||||
if (windows[i] != NULL && windows[i]->onConferenceTitleChange != NULL) {
|
||||
windows[i]->onConferenceTitleChange(windows[i], m, conferencenumber, peernumber, data, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -334,7 +336,7 @@ int add_window(Tox *m, ToxWindow *w)
|
||||
}
|
||||
|
||||
w->index = i;
|
||||
w->window = newwin(LINES - 2, COLS, 0, 0);
|
||||
w->window = newwin(LINES, COLS, 0, 0);
|
||||
|
||||
if (w->window == NULL) {
|
||||
return -1;
|
||||
@ -395,8 +397,9 @@ void del_window(ToxWindow *w)
|
||||
set_active_window_index(0);
|
||||
|
||||
uint8_t idx = w->index;
|
||||
delwin(w->window_bar);
|
||||
delwin(w->window);
|
||||
free(windows[idx]);
|
||||
free(w);
|
||||
windows[idx] = NULL;
|
||||
|
||||
clear();
|
||||
@ -406,7 +409,12 @@ void del_window(ToxWindow *w)
|
||||
|
||||
ToxWindow *init_windows(Tox *m)
|
||||
{
|
||||
if (COLS <= CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT) {
|
||||
exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT);
|
||||
}
|
||||
|
||||
prompt = new_prompt();
|
||||
|
||||
int n_prompt = add_window(m, prompt);
|
||||
|
||||
if (n_prompt < 0) {
|
||||
@ -428,25 +436,18 @@ void on_window_resize(void)
|
||||
refresh();
|
||||
clear();
|
||||
|
||||
/* equivalent to LINES and COLS */
|
||||
int x2, y2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
y2 -= 2;
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] == NULL) {
|
||||
ToxWindow *w = windows[i];
|
||||
|
||||
if (w == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ToxWindow *w = windows[i];
|
||||
|
||||
if (windows[i]->is_friendlist) {
|
||||
if (w->type == WINDOW_TYPE_FRIEND_LIST) {
|
||||
delwin(w->window_bar);
|
||||
delwin(w->window);
|
||||
w->window = newwin(y2, x2, 0, 0);
|
||||
w->window = newwin(LINES, COLS, 0, 0);
|
||||
w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -454,7 +455,7 @@ void on_window_resize(void)
|
||||
wclear(w->help->win);
|
||||
}
|
||||
|
||||
if (w->is_groupchat) {
|
||||
if (w->type == WINDOW_TYPE_CONFERENCE) {
|
||||
delwin(w->chatwin->sidebar);
|
||||
w->chatwin->sidebar = NULL;
|
||||
} else {
|
||||
@ -463,22 +464,35 @@ void on_window_resize(void)
|
||||
|
||||
delwin(w->chatwin->linewin);
|
||||
delwin(w->chatwin->history);
|
||||
delwin(w->window_bar);
|
||||
delwin(w->window);
|
||||
|
||||
w->window = newwin(y2, x2, 0, 0);
|
||||
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
w->window = newwin(LINES, COLS, 0, 0);
|
||||
|
||||
int x2;
|
||||
int y2;
|
||||
getmaxyx(w->window, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
fprintf(stderr, "Failed to resize window: max_x: %d, max_y: %d\n", x2, y2);
|
||||
delwin(w->window);
|
||||
return;
|
||||
}
|
||||
|
||||
if (w->show_peerlist) {
|
||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
w->chatwin->sidebar = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
} else {
|
||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
|
||||
|
||||
if (!w->is_groupchat) {
|
||||
w->stb->topline = subwin(w->window, 2, x2, 0, 0);
|
||||
if (w->type != WINDOW_TYPE_CONFERENCE) {
|
||||
w->stb->topline = subwin(w->window, TOP_BAR_HEIGHT, x2, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, x2, y2 - (CHATBOX_HEIGHT + WINDOW_BAR_HEIGHT), 0);
|
||||
w->chatwin->linewin = subwin(w->window, CHATBOX_HEIGHT, x2, y2 - WINDOW_BAR_HEIGHT, 0);
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (w->chatwin->infobox.active) {
|
||||
@ -489,82 +503,103 @@ void on_window_resize(void)
|
||||
#endif /* AUDIO */
|
||||
|
||||
scrollok(w->chatwin->history, 0);
|
||||
wmove(w->window, y2 - CURS_Y_OFFSET, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_window_tab(ToxWindow *toxwin)
|
||||
static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window)
|
||||
{
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
if (toxwin->alert != WINDOW_ALERT_NONE) {
|
||||
attron(COLOR_PAIR(toxwin->alert));
|
||||
}
|
||||
bool has_alert = toxwin->alert != WINDOW_ALERT_NONE;
|
||||
unsigned int pending_messages = toxwin->pending_messages;
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
clrtoeol();
|
||||
printw(" [%s]", toxwin->name);
|
||||
WINDOW_TYPE type = toxwin->type;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
|
||||
if (toxwin->alert != WINDOW_ALERT_NONE) {
|
||||
attroff(COLOR_PAIR(toxwin->alert));
|
||||
if (active_window) {
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, " [");
|
||||
wattroff(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wattron(win, COLOR_PAIR(BAR_TEXT));
|
||||
} else {
|
||||
if (has_alert) {
|
||||
wattron(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, " [");
|
||||
wattroff(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert));
|
||||
} else {
|
||||
wattron(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, " [");
|
||||
wattroff(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wattron(win, COLOR_PAIR(BAR_TEXT));
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
if (active_window || (type == WINDOW_TYPE_PROMPT || type == WINDOW_TYPE_FRIEND_LIST)) {
|
||||
wprintw(win, "%s", toxwin->name);
|
||||
} else {
|
||||
if (pending_messages > 0) {
|
||||
wprintw(win, "%u", pending_messages);
|
||||
} else {
|
||||
wprintw(win, "-");
|
||||
}
|
||||
}
|
||||
|
||||
if (active_window) {
|
||||
wattroff(win, COLOR_PAIR(BAR_TEXT));
|
||||
wattron(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, "]");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BAR_ACCENT));
|
||||
} else {
|
||||
if (has_alert) {
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert));
|
||||
wattron(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, "]");
|
||||
wattroff(win, COLOR_PAIR(BAR_ACCENT));
|
||||
} else {
|
||||
wattroff(win, COLOR_PAIR(BAR_TEXT));
|
||||
wattron(win, COLOR_PAIR(BAR_ACCENT));
|
||||
wprintw(win, "]");
|
||||
wattroff(win, COLOR_PAIR(BAR_ACCENT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void draw_bar(void)
|
||||
void draw_window_bar(ToxWindow *self)
|
||||
{
|
||||
int y, x;
|
||||
WINDOW *win = self->window_bar;
|
||||
wclear(win);
|
||||
|
||||
ToxWindow *w = windows[active_window_index];
|
||||
|
||||
if (w == NULL) {
|
||||
return;
|
||||
if (self->scroll_pause) {
|
||||
wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY));
|
||||
wprintw(win, "^");
|
||||
wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY));
|
||||
} else {
|
||||
wattron(win, COLOR_PAIR(BAR_TEXT));
|
||||
wprintw(win, " ");
|
||||
wattroff(win, COLOR_PAIR(BAR_TEXT));
|
||||
}
|
||||
|
||||
// save current cursor position
|
||||
getyx(w->window, y, x);
|
||||
|
||||
attron(COLOR_PAIR(BLUE));
|
||||
mvhline(LINES - 2, 0, '_', COLS);
|
||||
attroff(COLOR_PAIR(BLUE));
|
||||
|
||||
move(LINES - 1, 0);
|
||||
|
||||
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == active_window_index) {
|
||||
|
||||
#ifdef URXVT_FIX
|
||||
attron(A_BOLD | COLOR_PAIR(GREEN));
|
||||
} else {
|
||||
#endif
|
||||
|
||||
attron(A_BOLD);
|
||||
}
|
||||
|
||||
draw_window_tab(windows[i]);
|
||||
|
||||
if (i == active_window_index) {
|
||||
|
||||
#ifdef URXVT_FIX
|
||||
attroff(A_BOLD | COLOR_PAIR(GREEN));
|
||||
} else {
|
||||
#endif
|
||||
|
||||
attroff(A_BOLD);
|
||||
}
|
||||
bool active_window = i == active_window_index;
|
||||
draw_window_tab(win, windows[i], active_window);
|
||||
}
|
||||
|
||||
// restore cursor position after drawing
|
||||
move(y, x);
|
||||
int cur_x;
|
||||
int cur_y;
|
||||
UNUSED_VAR(cur_y);
|
||||
|
||||
refresh();
|
||||
getyx(win, cur_y, cur_x);
|
||||
|
||||
wattron(win, COLOR_PAIR(BAR_TEXT));
|
||||
mvwhline(win, 0, cur_x, ' ', COLS - cur_x);
|
||||
wattroff(win, COLOR_PAIR(BAR_TEXT));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -664,10 +699,9 @@ void draw_active_window(Tox *m)
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
a->alert = WINDOW_ALERT_NONE;
|
||||
a->pending_messages = 0;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
draw_bar();
|
||||
|
||||
touchwin(a->window);
|
||||
a->onDraw(a, m);
|
||||
wrefresh(a->window);
|
||||
@ -682,7 +716,7 @@ void draw_active_window(Tox *m)
|
||||
if (printable == 0 && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) {
|
||||
set_next_window((int) ch);
|
||||
return;
|
||||
} else if (printable == 0 && !a->is_friendlist) {
|
||||
} else if ((printable == 0) && (a->type != WINDOW_TYPE_FRIEND_LIST)) {
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
bool input_ret = a->onKey(a, m, ch, (bool) printable);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
@ -716,7 +750,7 @@ void refresh_inactive_windows(void)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i != active_window_index && !toxwin->is_friendlist) {
|
||||
if ((i != active_window_index) && (toxwin->type != WINDOW_TYPE_FRIEND_LIST)) {
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(toxwin);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
@ -754,7 +788,7 @@ int get_num_active_windows(void)
|
||||
return num_active_windows;
|
||||
}
|
||||
|
||||
/* destroys all chat and groupchat windows (should only be called on shutdown) */
|
||||
/* destroys all chat and conference windows (should only be called on shutdown) */
|
||||
void kill_all_windows(Tox *m)
|
||||
{
|
||||
for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) {
|
||||
@ -762,10 +796,10 @@ void kill_all_windows(Tox *m)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (windows[i]->is_chat) {
|
||||
if (windows[i]->type == WINDOW_TYPE_CHAT) {
|
||||
kill_chat_window(windows[i], m);
|
||||
} else if (windows[i]->is_groupchat) {
|
||||
free_groupchat(windows[i], windows[i]->num);
|
||||
} else if (windows[i]->type == WINDOW_TYPE_CONFERENCE) {
|
||||
free_conference(windows[i], windows[i]->num);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,9 +24,9 @@
|
||||
#define WINDOWS_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <wctype.h>
|
||||
#include <wchar.h>
|
||||
#include <signal.h>
|
||||
#include <wchar.h>
|
||||
#include <wctype.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
@ -36,12 +36,14 @@
|
||||
|
||||
#include "toxic.h"
|
||||
|
||||
#define MAX_WINDOWS_NUM 16
|
||||
#define MAX_WINDOWS_NUM 20
|
||||
#define MAX_WINDOW_NAME_LENGTH 22
|
||||
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
|
||||
#define CHATBOX_HEIGHT 2
|
||||
#define CHATBOX_HEIGHT 1
|
||||
#define TOP_BAR_HEIGHT 1
|
||||
#define WINDOW_BAR_HEIGHT 1
|
||||
|
||||
/* Curses foreground colours (background is black) */
|
||||
/* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */
|
||||
typedef enum {
|
||||
WHITE,
|
||||
GREEN,
|
||||
@ -51,16 +53,33 @@ typedef enum {
|
||||
YELLOW,
|
||||
MAGENTA,
|
||||
BLACK,
|
||||
BLUE_BLACK,
|
||||
BLACK_WHITE,
|
||||
BAR_TEXT,
|
||||
STATUS_ONLINE,
|
||||
BAR_ACCENT,
|
||||
PURPLE_BG,
|
||||
BLACK_BG,
|
||||
STATUS_BUSY,
|
||||
STATUS_AWAY,
|
||||
BAR_NOTIFY,
|
||||
} C_COLOURS;
|
||||
|
||||
/* 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,
|
||||
WINDOW_ALERT_1 = RED,
|
||||
WINDOW_ALERT_2 = MAGENTA,
|
||||
WINDOW_ALERT_0 = STATUS_ONLINE,
|
||||
WINDOW_ALERT_1 = BAR_ACCENT,
|
||||
WINDOW_ALERT_2 = PURPLE_BG,
|
||||
} WINDOW_ALERTS;
|
||||
|
||||
typedef enum {
|
||||
WINDOW_TYPE_PROMPT,
|
||||
WINDOW_TYPE_CHAT,
|
||||
WINDOW_TYPE_CONFERENCE,
|
||||
WINDOW_TYPE_FRIEND_LIST,
|
||||
} WINDOW_TYPE;
|
||||
|
||||
/* Fixes text color problem on some terminals.
|
||||
Uncomment if necessary */
|
||||
/* #define URXVT_FIX */
|
||||
@ -125,11 +144,11 @@ struct ToxWindow {
|
||||
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
|
||||
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, Tox_User_Status);
|
||||
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
|
||||
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Message_Type, const char *, size_t);
|
||||
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||
void(*onGroupNameListChange)(ToxWindow *, Tox *, uint32_t);
|
||||
void(*onGroupPeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onConferenceMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Message_Type, const char *, size_t);
|
||||
void(*onConferenceInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||
void(*onConferenceNameListChange)(ToxWindow *, Tox *, uint32_t);
|
||||
void(*onConferencePeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onConferenceTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
|
||||
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
||||
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_File_Control);
|
||||
@ -150,16 +169,9 @@ struct ToxWindow {
|
||||
void(*onEnd)(ToxWindow *, ToxAV *, uint32_t, int);
|
||||
void(*onWriteDevice)(ToxWindow *, Tox *, uint32_t, int, const int16_t *, unsigned int, uint8_t, unsigned int);
|
||||
|
||||
int device_selection[2]; /* -1 if not set, if set uses these selections instead of primary device */
|
||||
bool is_call;
|
||||
int ringing_sound;
|
||||
|
||||
#ifdef VIDEO
|
||||
|
||||
int video_device_selection[2]; /* -1 if not set, if set uses these selections instead of primary video device */
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
int active_box; /* For box notify */
|
||||
@ -167,13 +179,13 @@ struct ToxWindow {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
uint32_t num; /* corresponds to friendnumber in chat windows */
|
||||
uint8_t index; /* This window's index in the windows array */
|
||||
bool scroll_pause; /* true if this window is not scrolled to the bottom */
|
||||
unsigned int pending_messages; /* # of new messages in this window since the last time it was focused */
|
||||
int x;
|
||||
|
||||
bool is_chat;
|
||||
bool is_prompt;
|
||||
bool is_friendlist;
|
||||
bool is_groupchat;
|
||||
int show_peerlist; /* used to toggle groupchat peerlist */
|
||||
WINDOW_TYPE type;
|
||||
|
||||
int show_peerlist; /* used to toggle conference peerlist */
|
||||
|
||||
WINDOW_ALERTS alert;
|
||||
|
||||
@ -182,6 +194,7 @@ struct ToxWindow {
|
||||
Help *help;
|
||||
|
||||
WINDOW *window;
|
||||
WINDOW *window_bar;
|
||||
};
|
||||
|
||||
/* statusbar info holder */
|
||||
@ -218,7 +231,7 @@ struct infobox {
|
||||
|
||||
#define MAX_LINE_HIST 128
|
||||
|
||||
/* chat and groupchat window/buffer holder */
|
||||
/* chat and conference window/buffer holder */
|
||||
struct ChatContext {
|
||||
wchar_t line[MAX_STR_SIZE];
|
||||
int pos;
|
||||
@ -265,6 +278,7 @@ void on_window_resize(void);
|
||||
void force_refresh(WINDOW *w);
|
||||
ToxWindow *get_window_ptr(size_t i);
|
||||
ToxWindow *get_active_window(void);
|
||||
void draw_window_bar(ToxWindow *self);
|
||||
|
||||
/* refresh inactive windows to prevent scrolling bugs.
|
||||
call at least once per second */
|
||||
|
87
src/x11focus.c
Normal file
87
src/x11focus.c
Normal file
@ -0,0 +1,87 @@
|
||||
/* x11focus.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 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 "x11focus.h"
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
|
||||
static struct Focus {
|
||||
Display *display;
|
||||
Window terminal_window;
|
||||
} Focus;
|
||||
|
||||
static long unsigned int focused_window_id(void)
|
||||
{
|
||||
if (!Focus.display) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Window focus;
|
||||
int revert;
|
||||
|
||||
XLockDisplay(Focus.display);
|
||||
XGetInputFocus(Focus.display, &focus, &revert);
|
||||
XUnlockDisplay(Focus.display);
|
||||
|
||||
return focus;
|
||||
}
|
||||
|
||||
bool is_focused(void)
|
||||
{
|
||||
if (!Focus.display) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Focus.terminal_window == focused_window_id();
|
||||
}
|
||||
|
||||
int init_x11focus(void)
|
||||
{
|
||||
if (XInitThreads() == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Focus.display = XOpenDisplay(NULL);
|
||||
|
||||
if (!Focus.display) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Focus.terminal_window = focused_window_id();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void terminate_x11focus(void)
|
||||
{
|
||||
if (!Focus.display || !Focus.terminal_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
XLockDisplay(Focus.display);
|
||||
XCloseDisplay(Focus.display);
|
||||
XUnlockDisplay(Focus.display);
|
||||
}
|
||||
|
||||
#endif /* !__APPLE__ */
|
@ -1,7 +1,7 @@
|
||||
/* group_commands.h
|
||||
/* xtra.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
@ -20,12 +20,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROUP_COMMANDS_H
|
||||
#define GROUP_COMMANDS_H
|
||||
#ifndef X11FOCUS_H
|
||||
#define X11FOCUS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
/* NOTE: If no xlib present don't compile */
|
||||
|
||||
#endif /* GROUP_COMMANDS_H */
|
||||
int init_x11focus(void);
|
||||
void terminate_x11focus(void);
|
||||
bool is_focused(void);
|
||||
|
||||
#endif /* X11FOCUS */
|
425
src/xtra.c
425
src/xtra.c
@ -1,425 +0,0 @@
|
||||
/* xtra.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 "xtra.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
const Atom XtraTerminate = 1;
|
||||
const Atom XtraNil = 0;
|
||||
|
||||
static Atom XdndAware;
|
||||
static Atom XdndEnter;
|
||||
static Atom XdndLeave;
|
||||
static Atom XdndPosition;
|
||||
static Atom XdndStatus;
|
||||
static Atom XdndDrop;
|
||||
static Atom XdndSelection;
|
||||
static Atom XdndDATA;
|
||||
static Atom XdndTypeList;
|
||||
static Atom XdndActionCopy;
|
||||
static Atom XdndFinished;
|
||||
|
||||
struct Xtra {
|
||||
drop_callback on_drop;
|
||||
Display *display;
|
||||
Window terminal_window;
|
||||
Window proxy_window;
|
||||
Window source_window; /* When we have a drop */
|
||||
Atom handling_version;
|
||||
Atom expecting_type;
|
||||
} Xtra;
|
||||
|
||||
typedef struct Property {
|
||||
unsigned char *data;
|
||||
int read_format;
|
||||
unsigned long read_num;
|
||||
Atom read_type;
|
||||
} Property;
|
||||
|
||||
static Property read_property(Window s, Atom p)
|
||||
{
|
||||
Atom read_type;
|
||||
int read_format;
|
||||
unsigned long read_num;
|
||||
unsigned long left_bytes;
|
||||
unsigned char *data = NULL;
|
||||
|
||||
int read_bytes = 1024;
|
||||
|
||||
/* Keep trying to read the property until there are no bytes unread */
|
||||
do {
|
||||
if (data) {
|
||||
XFree(data);
|
||||
}
|
||||
|
||||
XGetWindowProperty(Xtra.display, s,
|
||||
p, 0,
|
||||
read_bytes,
|
||||
False, AnyPropertyType,
|
||||
&read_type, &read_format,
|
||||
&read_num, &left_bytes,
|
||||
&data);
|
||||
|
||||
read_bytes *= 2;
|
||||
} while (left_bytes != 0);
|
||||
|
||||
Property property = {data, read_format, read_num, read_type};
|
||||
return property;
|
||||
}
|
||||
|
||||
static Atom get_dnd_type(long *a, int l)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (; i < l; i ++) {
|
||||
if (a[i] != XtraNil) {
|
||||
return a[i]; /* Get first valid */
|
||||
}
|
||||
}
|
||||
|
||||
return XtraNil;
|
||||
}
|
||||
|
||||
/* TODO maybe support only certain types in the future */
|
||||
static void handle_xdnd_enter(XClientMessageEvent *e)
|
||||
{
|
||||
Xtra.handling_version = (e->data.l[1] >> 24);
|
||||
|
||||
if ((e->data.l[1] & 1)) {
|
||||
// Fetch the list of possible conversions
|
||||
Property p = read_property(e->data.l[0], XdndTypeList);
|
||||
Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num);
|
||||
XFree(p.data);
|
||||
} else {
|
||||
// Use the available list
|
||||
Xtra.expecting_type = get_dnd_type(e->data.l + 2, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_xdnd_position(XClientMessageEvent *e)
|
||||
{
|
||||
XEvent ev = {
|
||||
.xclient = {
|
||||
.type = ClientMessage,
|
||||
.display = e->display,
|
||||
.window = e->data.l[0],
|
||||
.message_type = XdndStatus,
|
||||
.format = 32,
|
||||
.data = {
|
||||
.l = {
|
||||
Xtra.proxy_window,
|
||||
(Xtra.expecting_type != XtraNil),
|
||||
0, 0,
|
||||
XdndActionCopy
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
|
||||
XFlush(Xtra.display);
|
||||
}
|
||||
|
||||
static void handle_xdnd_drop(XClientMessageEvent *e)
|
||||
{
|
||||
/* Not expecting any type */
|
||||
if (Xtra.expecting_type == XtraNil) {
|
||||
XEvent ev = {
|
||||
.xclient = {
|
||||
.type = ClientMessage,
|
||||
.display = e->display,
|
||||
.window = e->data.l[0],
|
||||
.message_type = XdndFinished,
|
||||
.format = 32,
|
||||
.data = {
|
||||
.l = {Xtra.proxy_window, 0, 0}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XSendEvent(Xtra.display, e->data.l[0], False, NoEventMask, &ev);
|
||||
} else {
|
||||
Xtra.source_window = e->data.l[0];
|
||||
XConvertSelection(Xtra.display,
|
||||
XdndSelection,
|
||||
Xtra.expecting_type,
|
||||
XdndSelection,
|
||||
Xtra.proxy_window,
|
||||
Xtra.handling_version >= 1 ? e->data.l[2] : CurrentTime);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_xdnd_selection(XSelectionEvent *e)
|
||||
{
|
||||
UNUSED_VAR(e);
|
||||
|
||||
/* DnD succesfully finished, send finished and call callback */
|
||||
XEvent ev = {
|
||||
.xclient = {
|
||||
.type = ClientMessage,
|
||||
.display = Xtra.display,
|
||||
.window = Xtra.source_window,
|
||||
.message_type = XdndFinished,
|
||||
.format = 32,
|
||||
.data = {
|
||||
.l = {Xtra.proxy_window, 1, XdndActionCopy}
|
||||
}
|
||||
}
|
||||
};
|
||||
XSendEvent(Xtra.display, Xtra.source_window, False, NoEventMask, &ev);
|
||||
|
||||
Property p = read_property(Xtra.proxy_window, XdndSelection);
|
||||
DropType dt;
|
||||
|
||||
if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) {
|
||||
dt = DT_file_list;
|
||||
} else { /* text/uri-list */
|
||||
dt = DT_plain;
|
||||
}
|
||||
|
||||
|
||||
/* Call callback for every entry */
|
||||
if (Xtra.on_drop && p.read_num) {
|
||||
char *sptr;
|
||||
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
|
||||
|
||||
if (str) {
|
||||
Xtra.on_drop(str, dt);
|
||||
}
|
||||
|
||||
while ((str = strtok_r(NULL, "\n\r", &sptr))) {
|
||||
Xtra.on_drop(str, dt);
|
||||
}
|
||||
}
|
||||
|
||||
if (p.data) {
|
||||
XFree(p.data);
|
||||
}
|
||||
}
|
||||
|
||||
void *event_loop(void *p)
|
||||
{
|
||||
/* Handle events like a real nigga */
|
||||
|
||||
UNUSED_VAR(p); /* DINDUNOTHIN */
|
||||
|
||||
XEvent event;
|
||||
int pending;
|
||||
|
||||
while (Xtra.display) {
|
||||
/* NEEDMOEVENTSFODEMPROGRAMS */
|
||||
|
||||
XLockDisplay(Xtra.display);
|
||||
|
||||
if ((pending = XPending(Xtra.display))) {
|
||||
XNextEvent(Xtra.display, &event);
|
||||
}
|
||||
|
||||
if (!pending) {
|
||||
XUnlockDisplay(Xtra.display);
|
||||
usleep(10000);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event.type == ClientMessage) {
|
||||
Atom type = event.xclient.message_type;
|
||||
|
||||
if (type == XdndEnter) {
|
||||
handle_xdnd_enter(&event.xclient);
|
||||
} else if (type == XdndPosition) {
|
||||
handle_xdnd_position(&event.xclient);
|
||||
} else if (type == XdndDrop) {
|
||||
handle_xdnd_drop(&event.xclient);
|
||||
} else if (type == XtraTerminate) {
|
||||
break;
|
||||
}
|
||||
} else if (event.type == SelectionNotify) {
|
||||
handle_xdnd_selection(&event.xselection);
|
||||
}
|
||||
/* AINNOBODYCANHANDLEDEMEVENTS*/
|
||||
else {
|
||||
XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
|
||||
}
|
||||
|
||||
XUnlockDisplay(Xtra.display);
|
||||
}
|
||||
|
||||
/* Actual XTRA termination
|
||||
* Please call xtra_terminate() at exit
|
||||
* otherwise HEWUSAGUDBOI happens
|
||||
*/
|
||||
if (Xtra.display) {
|
||||
XCloseDisplay(Xtra.display);
|
||||
}
|
||||
|
||||
return (Xtra.display = NULL);
|
||||
}
|
||||
|
||||
static long unsigned int focused_window_id(void)
|
||||
{
|
||||
if (!Xtra.display) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Window focus;
|
||||
int revert;
|
||||
XLockDisplay(Xtra.display);
|
||||
XGetInputFocus(Xtra.display, &focus, &revert);
|
||||
XUnlockDisplay(Xtra.display);
|
||||
return focus;
|
||||
}
|
||||
|
||||
int is_focused(void)
|
||||
{
|
||||
return Xtra.display && (Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id());
|
||||
}
|
||||
|
||||
int init_xtra(drop_callback d)
|
||||
{
|
||||
if (!d) {
|
||||
return -1;
|
||||
} else {
|
||||
Xtra.on_drop = d;
|
||||
}
|
||||
|
||||
XInitThreads();
|
||||
|
||||
if (!(Xtra.display = XOpenDisplay(NULL))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Xtra.terminal_window = focused_window_id();
|
||||
|
||||
/* OSX: if focused window is 0, it means toxic is ran from
|
||||
* native terminal and not X11 terminal window, silently exit */
|
||||
if (!Xtra.terminal_window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
{
|
||||
/* Create an invisible window which will act as proxy for the DnD operation. */
|
||||
XSetWindowAttributes attr = {0};
|
||||
attr.event_mask = EnterWindowMask |
|
||||
LeaveWindowMask |
|
||||
ButtonMotionMask |
|
||||
ButtonPressMask |
|
||||
ButtonReleaseMask |
|
||||
ResizeRedirectMask;
|
||||
|
||||
attr.do_not_propagate_mask = NoEventMask;
|
||||
|
||||
Window root;
|
||||
int x, y;
|
||||
unsigned int wht, hht, b, d;
|
||||
|
||||
/* Since we cannot capture resize events for parent window we will have to create
|
||||
* this window to have maximum size as defined in root window
|
||||
*/
|
||||
XGetGeometry(Xtra.display,
|
||||
XDefaultRootWindow(Xtra.display),
|
||||
&root, &x, &y, &wht, &hht, &b, &d);
|
||||
|
||||
if (!(Xtra.proxy_window = XCreateWindow
|
||||
(Xtra.display, Xtra.terminal_window, /* Parent */
|
||||
0, 0, /* Position */
|
||||
wht, hht, /* Width + height */
|
||||
0, /* Border width */
|
||||
CopyFromParent, /* Depth */
|
||||
InputOnly, /* Class */
|
||||
CopyFromParent, /* Visual */
|
||||
CWEventMask | CWCursor, /* Value mask */
|
||||
&attr))) { /* Attributes for value mask */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */
|
||||
XLowerWindow(Xtra.display, Xtra.proxy_window); /* Don't interfere with parent lmao */
|
||||
|
||||
XdndAware = XInternAtom(Xtra.display, "XdndAware", False);
|
||||
XdndEnter = XInternAtom(Xtra.display, "XdndEnter", False);
|
||||
XdndLeave = XInternAtom(Xtra.display, "XdndLeave", False);
|
||||
XdndPosition = XInternAtom(Xtra.display, "XdndPosition", False);
|
||||
XdndStatus = XInternAtom(Xtra.display, "XdndStatus", False);
|
||||
XdndDrop = XInternAtom(Xtra.display, "XdndDrop", False);
|
||||
XdndSelection = XInternAtom(Xtra.display, "XdndSelection", False);
|
||||
XdndDATA = XInternAtom(Xtra.display, "XdndDATA", False);
|
||||
XdndTypeList = XInternAtom(Xtra.display, "XdndTypeList", False);
|
||||
XdndActionCopy = XInternAtom(Xtra.display, "XdndActionCopy", False);
|
||||
XdndFinished = XInternAtom(Xtra.display, "XdndFinished", False);
|
||||
|
||||
/* Inform my nigga windows that we are aware of dnd */
|
||||
Atom XdndVersion = 3;
|
||||
XChangeProperty(Xtra.display,
|
||||
Xtra.proxy_window,
|
||||
XdndAware,
|
||||
XA_ATOM,
|
||||
32,
|
||||
PropModeReplace,
|
||||
(unsigned char *)&XdndVersion, 1);
|
||||
|
||||
pthread_t id;
|
||||
|
||||
if (pthread_create(&id, NULL, event_loop, NULL) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_detach(id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void terminate_xtra(void)
|
||||
{
|
||||
if (!Xtra.display || !Xtra.terminal_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
XEvent terminate = {
|
||||
.xclient = {
|
||||
.type = ClientMessage,
|
||||
.display = Xtra.display,
|
||||
.message_type = XtraTerminate,
|
||||
}
|
||||
};
|
||||
|
||||
XLockDisplay(Xtra.display);
|
||||
XDeleteProperty(Xtra.display, Xtra.proxy_window, XdndAware);
|
||||
XSendEvent(Xtra.display, Xtra.proxy_window, 0, NoEventMask, &terminate);
|
||||
XUnlockDisplay(Xtra.display);
|
||||
|
||||
while (Xtra.display); /* Wait for termination */
|
||||
}
|
||||
|
||||
#endif /* !__APPLE__ */
|
Reference in New Issue
Block a user