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

Compare commits

...

22 Commits

Author SHA1 Message Date
3dd5c58a7b Bump to version 0.11.0 2021-04-22 13:08:00 -04:00
30513e2161 Show previous window instead of Home after closing a window 2021-04-22 12:57:15 -04:00
960ad7c4cb Add ability to compile without game support 2021-04-22 12:57:15 -04:00
787d081e74 Add networking to game engine / add multiplayer chess 2021-04-22 12:57:15 -04:00
97cb43a218 Begin implementing chess 2021-04-22 12:57:14 -04:00
b9ea733470 Add game module (WIP) 2021-04-22 12:57:11 -04:00
a2f0f298aa Fix bug showing friends with busy status as online 2021-04-20 13:29:41 -04:00
0fc1d9e994 Add two missing mutex unlocks 2021-03-27 11:50:57 -04:00
80fd3d3b97 Fix buggy behaviour when printing non-ascii characters in Home input field
This line doesn't appear to be doing anything useful - most likely a leftover
from the recent UI overhaul
2021-01-25 22:36:05 -05:00
07a41f9e0b Show previous window instead of Home after closing a window 2021-01-25 17:09:23 -05:00
f643975941 Fix bug preventing the /clear command from working in Home window 2021-01-23 21:17:46 -05:00
a5246bcbc6 Bump to version 0.10.1 2021-01-20 16:18:43 -05:00
b37e28c2d0 Add a script for building minimal static toxic
The intention is that JFreegman would run it locally on his machine for
every Toxic release and publish the build artifacts on GitHub for
download, similarly to how the qTox team makes release builds. This
should mitigate the loss of the Jenkins build server to some extent,
which used to provide Toxic builds before.
2021-01-19 21:48:51 -05:00
973e60ef11 Fix Makefile assignment bug
When a user provides variable=value as an argument to make, all
assignments to that variable are ignored within the Makefile because the
user has explicitly overrode variable to be "value". This made the
ENABLE_ASAN assignment to be ignored, resulting in Toxic always enabling
ASAN unless you run `make ENABLE_ASAN=disabled`, which is not
documented and not how it's intended to work. This can be fixed by
prefixing the assignment with "override", but to be in line with other
argument assignments we just change the variable name.

See more at:
https://www.gnu.org/software/make/manual/html_node/Overriding.html
2021-01-19 20:18:24 -05:00
ae94bc593b Rename some build options for naming consistency 2021-01-19 15:21:31 -05:00
81eb58532e Fix bug sometimes causing join message spam on conference join 2021-01-17 17:10:10 -05:00
8464ea9a7a Fix minor display bug with the audio call info box 2021-01-12 19:18:22 -05:00
b77bff35a1 Add config man entries for colour scheme options 2020-12-06 11:52:46 -05:00
eb964b64c2 Fix off by one error and comment magic numbers 2020-12-05 20:53:09 -05:00
2ff9d29491 Custom colors proof of concept
Fix formatting

Fix repeated variable in example config
2020-12-05 17:46:13 -06:00
2640919318 Don't announce when ghost peers leave the group
This appears to be a toxcore bug, but nonetheless it looks ugly. Also changed
group -> conference for join/part messages
2020-12-03 11:49:48 -05:00
2fcbc4fa1c Remove lots of duplicate code related to printing timestamps 2020-11-30 17:55:57 -05:00
54 changed files with 7952 additions and 415 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ build/toxic
build/*.o build/*.o
build/*.d build/*.d
apidoc/python/build apidoc/python/build
*.vim

View File

@ -58,6 +58,7 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
* `DISABLE_QRCODE` → Disable QR exporting support * `DISABLE_QRCODE` → Disable QR exporting support
* `DISABLE_QRPNG` → Disable support for exporting QR as PNG * `DISABLE_QRPNG` → Disable support for exporting QR as PNG
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
* `DISABLE_GAMES=1` → Disable support for games
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support * `ENABLE_PYTHON=1` → Build toxic with Python scripting support
* `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations * `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
* `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled * `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled

View File

@ -13,12 +13,12 @@ LDFLAGS ?=
LDFLAGS += ${USER_LDFLAGS} LDFLAGS += ${USER_LDFLAGS}
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o 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 conference_commands.o conference.o help.o input.o OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o conference.o help.o input.o line_info.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 += 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 OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check if debug build is enabled # Check if debug build is enabled
RELEASE := $(shell if [ -z "$(RELEASE_ENABLED)" ] || [ "$(RELEASE_ENABLED)" = "0" ] ; then echo disabled ; else echo enabled ; fi) RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(RELEASE), enabled) ifneq ($(RELEASE), enabled)
CFLAGS += -O0 -g -DDEBUG CFLAGS += -O0 -g -DDEBUG
LDFLAGS += -O0 LDFLAGS += -O0
@ -28,8 +28,8 @@ else
endif endif
# Check if LLVM Address Sanitizer is enabled # Check if LLVM Address Sanitizer is enabled
ASAN_ENABLED := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi) ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(ASAN_ENABLED), disabled) ifneq ($(ASAN), disabled)
CFLAGS += -fsanitize=address -fno-omit-frame-pointer CFLAGS += -fsanitize=address -fno-omit-frame-pointer
endif endif

View File

@ -55,9 +55,9 @@ author = 'Jakob Kreuze'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.10.0' version = '0.11.0'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.10.0' release = '0.11.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@ -19,6 +19,13 @@ ifneq ($(AUDIO), disabled)
ifneq ($(VIDEO), disabled) ifneq ($(VIDEO), disabled)
-include $(CHECKS_DIR)/video.mk -include $(CHECKS_DIR)/video.mk
endif endif
#check if we want to build with game support
GAMES := $(shell if [ -z "$(DISABLE_GAMES)" ] || [ "$(DISABLE_GAMES)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(GAMES), disabled)
-include $(CHECKS_DIR)/games.mk
endif
endif endif
endif endif

5
cfg/checks/games.mk Normal file
View File

@ -0,0 +1,5 @@
# Variables for game support
GAMES_CFLAGS = -DGAMES
GAMES_OBJ = game_base.o game_centipede.o game_chess.o game_util.o game_snake.o
CFLAGS += $(GAMES_CFLAGS)
OBJ += $(GAMES_OBJ)

View File

@ -1,5 +1,5 @@
# Version # Version
TOXIC_VERSION = 0.10.0 TOXIC_VERSION = 0.11.0
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error") REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV))) ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION) VERSION = $(TOXIC_VERSION)

View File

@ -16,9 +16,10 @@ help:
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" @echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support" @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 " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
@echo " DISABLE_GAMES: Set to \"1\" to force building without game support"
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support" @echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
@echo " RELEASE_ENABLED: Set to \"1\" to build without debug symbols and with full compiler optimizations" @echo " ENABLE_RELEASE: Set to \"1\" to build without debug symbols and with full compiler optimizations"
@echo " ASAN_ENABLED: Set to \"1\" to build with LLVM address sanitizer enabled. @echo " ENABLE_ASAN: Set to \"1\" to build with LLVM address sanitizer enabled.
@echo " USER_CFLAGS: Add custom flags to default CFLAGS" @echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS" @echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" @echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"

View File

@ -2,12 +2,12 @@
.\" Title: toxic.conf .\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section] .\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/> .\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 2020-05-07 .\" Date: 2020-11-18
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC\&.CONF" "5" "2020\-05\-07" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC\&.CONF" "5" "2020\-11\-18" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * 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 Select between native terminal colors and toxic color theme\&. true or false
.RE .RE
.PP .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 \fBautolog\fR
.RS 4 .RS 4
Enable or disable autologging\&. true or false Enable or disable autologging\&. true or false
@ -222,11 +242,6 @@ Audio output device\&. Integer value\&. Number corresponds to
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0 Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
.RE .RE
.PP .PP
\fBpush_to_talk\fR
.RS 4
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
.RE
.PP
\fBconference_audio_channels\fR \fBconference_audio_channels\fR
.RS 4 .RS 4
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
@ -236,6 +251,11 @@ Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) o
.RS 4 .RS 4
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo) Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
.RE .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 .RE
.PP .PP
\fBtox\fR \fBtox\fR

View File

@ -60,6 +60,18 @@ OPTIONS
*native_colors*;; *native_colors*;;
Select between native terminal colors and toxic color theme. true or false 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*;; *autolog*;;
Enable or disable autologging. true or false Enable or disable autologging. true or false

View File

@ -9,20 +9,32 @@ ui = {
alerts=true; alerts=true;
// Output a bell when receiving a message (see manpage) // 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) // 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) // 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) // 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 // true to use native terminal colours, false to use toxic default colour theme
native_colors=false; 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 // true to enable autologging, false to disable
autolog=false; autolog=false;
@ -56,10 +68,10 @@ ui = {
// time in milliseconds to display a notification // time in milliseconds to display a notification
notification_timeout=6000; 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="-->"; line_join="-->";
// Indicator for display when someone disconnects or leaves a group. // Indicator for display when someone disconnects or leaves a group
line_quit="<--"; line_quit="<--";
// Indicator for alert messages. // Indicator for alert messages.
@ -131,6 +143,6 @@ keys = {
half_page_up="Ctrl+F"; half_page_up="Ctrl+F";
half_page_down="Ctrl+V"; half_page_down="Ctrl+V";
page_bottom="Ctrl+H"; page_bottom="Ctrl+H";
toggle_peerlist="Ctrl+b"; toggle_peerlist="Ctrl+B";
toggle_paste_mode="Ctrl+T"; toggle_paste_mode="Ctrl+T";
}; };

View 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"

View File

@ -99,18 +99,16 @@ void api_send(const char *msg)
} }
char *name = api_get_nick(); char *name = api_get_nick();
char timefrmt[TIME_STR_SIZE];
if (name == NULL) { if (name == NULL) {
return; return;
} }
self_window = get_active_window(); self_window = get_active_window();
get_time_str(timefrmt, sizeof(timefrmt));
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line)); strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
add_line_to_hist(self_window->chatwin); 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); cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
free(name); 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."; 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; return;
} }
@ -165,7 +163,7 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (fp == NULL) { if (fp == NULL) {
error_str = "Path does not exist."; 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; return;
} }

View File

@ -90,7 +90,7 @@ void audio_bit_rate_callback(ToxAV *av, uint32_t friend_number, uint32_t audio_b
static void print_err(ToxWindow *self, const char *error_str) static void print_err(ToxWindow *self, const char *error_str)
{ {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
} }
ToxAV *init_audio(ToxWindow *self, Tox *tox) ToxAV *init_audio(ToxWindow *self, Tox *tox)
@ -113,13 +113,13 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
if (!CallControl.av) { if (!CallControl.av) {
CallControl.audio_errors |= ae_StartingCoreAudio; CallControl.audio_errors |= ae_StartingCoreAudio;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV");
return NULL; return NULL;
} }
if (init_devices() == de_InternalError) { if (init_devices() == de_InternalError) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to init devices");
toxav_kill(CallControl.av); toxav_kill(CallControl.av);
return CallControl.av = NULL; return CallControl.av = NULL;
@ -200,7 +200,7 @@ static bool cancel_call(Call *call)
static int start_transmission(ToxWindow *self, Call *call) static int start_transmission(ToxWindow *self, Call *call)
{ {
if (!self || !CallControl.av) { if (!self || !CallControl.av) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare audio transmission"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare audio transmission");
return -1; return -1;
} }
@ -209,17 +209,17 @@ static int start_transmission(ToxWindow *self, Call *call)
if (error != de_None) { if (error != de_None) {
if (error == de_FailedStart) { if (error == de_FailedStart) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to start audio input device"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to start audio input device");
} }
if (error == de_InternalError) { if (error == de_InternalError) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening audio input device"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Internal error with opening audio input device");
} }
} }
if (open_output_device(&call->out_idx, if (open_output_device(&call->out_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) { CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open audio output device!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open audio output device!");
} }
return 0; return 0;
@ -342,7 +342,7 @@ void on_call_state(ToxAV *av, uint32_t friend_number, uint32_t state, void *user
case TOXAV_FRIEND_CALL_STATE_ERROR: case TOXAV_FRIEND_CALL_STATE_ERROR:
case TOXAV_FRIEND_CALL_STATE_FINISHED: case TOXAV_FRIEND_CALL_STATE_FINISHED:
if (state == TOXAV_FRIEND_CALL_STATE_ERROR) { if (state == TOXAV_FRIEND_CALL_STATE_ERROR) {
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!"); line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "ToxAV callstate error!");
} }
if (call->status == cs_Pending) { if (call->status == cs_Pending) {
@ -669,7 +669,7 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
} }
else { 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; return;
} }
@ -708,7 +708,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
} }
else { 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; return;
} }
@ -748,7 +748,7 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
} }
else { 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; return;
} }
@ -816,7 +816,7 @@ void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
} }
if (argc == 0) { if (argc == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Current audio encoding bitrate: %u", call->audio_bit_rate); "Current audio encoding bitrate: %u", call->audio_bit_rate);
return; return;
} }

View File

@ -575,6 +575,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
Device *device = &audio_state->devices[type][device_idx]; Device *device = &audio_state->devices[type][device_idx];
if (!device->active) { if (!device->active) {
unlock(type);
return de_DeviceNotActive; return de_DeviceNotActive;
} }
@ -775,7 +776,7 @@ float get_input_volume(void)
void print_al_devices(ToxWindow *self, DeviceType type) void print_al_devices(ToxWindow *self, DeviceType type)
{ {
for (int i = 0; i < audio_state->num_al_devices[type]; ++i) { for (int i = 0; i < audio_state->num_al_devices[type]; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, line_info_add(self, false, NULL, NULL, SYS_MSG,
audio_state->current_al_device_name[type] audio_state->current_al_device_name[type]
&& strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0, && strcmp(audio_state->current_al_device_name[type], audio_state->al_device_names[type][i]) == 0 ? 1 : 0,
0, "%d: %s", i, audio_state->al_device_names[type][i]); 0, "%d: %s", i, audio_state->al_device_names[type][i]);

View File

@ -45,10 +45,10 @@ static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matc
} }
for (size_t i = 0; i < n_matches; ++i) { for (size_t i = 0; i < n_matches; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", list[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, ""); 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. /* puts match in match buffer. if more than one match, add first n chars that are identical.

View File

@ -47,6 +47,10 @@
#include "toxic_strings.h" #include "toxic_strings.h"
#include "windows.h" #include "windows.h"
#ifdef GAMES
#include "game_base.h"
#endif
#ifdef AUDIO #ifdef AUDIO
#include "audio_call.h" #include "audio_call.h"
#ifdef VIDEO #ifdef VIDEO
@ -76,6 +80,9 @@ static const char *chat_cmd_list[] = {
"/connect", "/connect",
"/exit", "/exit",
"/conference", "/conference",
#ifdef GAMES
"/game",
#endif
"/help", "/help",
"/invite", "/invite",
"/join", "/join",
@ -87,6 +94,7 @@ static const char *chat_cmd_list[] = {
"/nick", "/nick",
"/note", "/note",
"/nospam", "/nospam",
"/play",
"/quit", "/quit",
"/savefile", "/savefile",
"/sendfile", "/sendfile",
@ -167,11 +175,11 @@ void kill_chat_window(ToxWindow *self, Tox *m)
del_window(self); del_window(self);
} }
static void recv_message_helper(ToxWindow *self, const char *msg, const char *nick, const char *timefrmt) static void recv_message_helper(ToxWindow *self, const char *msg, const char *nick)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, 0, "%s", msg); line_info_add(self, true, nick, NULL, IN_MSG, 0, 0, "%s", msg);
write_to_log(msg, nick, ctx->log, false); write_to_log(msg, nick, ctx->log, false);
if (self->active_box != -1) { if (self->active_box != -1) {
@ -183,11 +191,11 @@ static void recv_message_helper(ToxWindow *self, const char *msg, const char *ni
} }
} }
static void recv_action_helper(ToxWindow *self, const char *action, const char *nick, const char *timefrmt) static void recv_action_helper(ToxWindow *self, const char *action, const char *nick)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action); line_info_add(self, true, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true); write_to_log(action, nick, ctx->log, true);
if (self->active_box != -1) { if (self->active_box != -1) {
@ -210,16 +218,13 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Message_Ty
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); get_nick_truncate(m, nick, num);
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
if (type == TOX_MESSAGE_TYPE_NORMAL) { if (type == TOX_MESSAGE_TYPE_NORMAL) {
recv_message_helper(self, msg, nick, timefrmt); recv_message_helper(self, msg, nick);
return; return;
} }
if (type == TOX_MESSAGE_TYPE_ACTION) { if (type == TOX_MESSAGE_TYPE_ACTION) {
recv_action_helper(self, msg, nick, timefrmt); recv_action_helper(self, msg, nick);
return; return;
} }
} }
@ -237,9 +242,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
const char *msg; const char *msg;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); get_nick_truncate(m, nick, num);
@ -254,7 +256,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C
chat_resume_file_senders(self, m, num); chat_resume_file_senders(self, m, num);
msg = "has come online"; msg = "has come online";
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, nick, ctx->log, true); write_to_log(msg, nick, ctx->log, true);
} else if (connection_status == TOX_CONNECTION_NONE) { } else if (connection_status == TOX_CONNECTION_NONE) {
Friends.list[num].is_typing = false; Friends.list[num].is_typing = false;
@ -266,7 +268,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_C
chat_pause_file_transfers(num); chat_pause_file_transfers(num);
msg = "has gone offline"; 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); write_to_log(msg, nick, ctx->log, true);
} }
} }
@ -509,11 +511,11 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
/* transfer is accepted */ /* transfer is accepted */
if (ft->state == FILE_TRANSFER_PENDING) { if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED; ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%zu] for '%s' accepted.", line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%zu] for '%s' accepted.",
ft->index, ft->file_name); ft->index, ft->file_name);
char progline[MAX_STR_SIZE]; char progline[MAX_STR_SIZE];
init_progress_bar(progline); 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);
sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL); sound_notify(self, silent, NT_NOFOCUS | user_settings->bell_on_filetrans_accept | NT_WNDALERT_2, NULL);
ft->line_id = self->chatwin->hst->line_end->id + 2; ft->line_id = self->chatwin->hst->line_end->id + 2;
} else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */ } else if (ft->state == FILE_TRANSFER_PAUSED) { /* transfer is resumed */
@ -637,14 +639,14 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
if (!ft) { if (!ft) {
tox_file_control(m, friendnum, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); tox_file_control(m, friendnum, filenumber, TOX_FILE_CONTROL_CANCEL, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"File transfer request failed: Too many concurrent file transfers."); "File transfer request failed: Too many concurrent file transfers.");
return; return;
} }
char sizestr[32]; char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), file_size); bytes_convert_str(sizestr, sizeof(sizestr), file_size);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
if (!valid_file_name(filename, name_length)) { if (!valid_file_name(filename, name_length)) {
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, "File transfer failed: Invalid file name.", notif_error); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, "File transfer failed: Invalid file name.", notif_error);
@ -703,7 +705,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
} }
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %zu' to accept the file transfer.", ft->index); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type '/savefile %zu' to accept the file transfer.", ft->index);
ft->file_size = file_size; ft->file_size = file_size;
snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path); snprintf(ft->file_path, sizeof(ft->file_path), "%s", file_path);
@ -758,10 +760,82 @@ static void chat_onConferenceInvite(ToxWindow *self, Tox *m, int32_t friendnumbe
"invites you to join %s", description); "invites you to join %s", description);
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to %s.", name, description);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
} }
#ifdef GAMES
void chat_onGameInvite(ToxWindow *self, Tox *m, uint32_t friend_number, const uint8_t *data, size_t length)
{
if (!self || self->num != friend_number) {
return;
}
if (length < GAME_PACKET_HEADER_SIZE) {
return;
}
uint8_t version = data[0];
if (version != GAME_NETWORKING_VERSION) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Game invite failed. Friend has network protocol version %d, you have version %d.", version, GAME_NETWORKING_VERSION);
return;
}
GameType type = data[1];
if (!game_type_is_multiplayer(type)) {
return;
}
uint32_t id;
game_util_unpack_u32(data + 2, &id);
const char *game_string = game_get_name_string(type);
if (game_string == NULL) {
return;
}
Friends.list[friend_number].game_invite.type = type;
Friends.list[friend_number].game_invite.id = id;
Friends.list[friend_number].game_invite.pending = true;
uint32_t data_length = length - GAME_PACKET_HEADER_SIZE;
Friends.list[friend_number].game_invite.data_length = data_length;
if (data_length > 0) {
free(Friends.list[friend_number].game_invite.data);
uint8_t *buf = calloc(1, data_length);
if (buf == NULL) {
return;
}
memcpy(buf, data + GAME_PACKET_HEADER_SIZE, data_length);
Friends.list[friend_number].game_invite.data = buf;
}
char name[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, name, friend_number);
if (self->active_box != -1) {
box_notify2(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, self->active_box,
"invites you to play %s", game_string);
} else {
box_notify(self, generic_message, NT_WNDALERT_2 | user_settings->bell_on_invite, &self->active_box, name,
"invites you to play %s", game_string);
}
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a game of %s.", name, game_string);
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type \"/play\" to join the game.");
}
#endif // GAMES
/* AV Stuff */ /* AV Stuff */
#ifdef AUDIO #ifdef AUDIO
@ -777,7 +851,7 @@ void chat_onInvite(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
/* call is flagged active here */ /* call is flagged active here */
self->is_call = true; self->is_call = true;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\""); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
if (self->ringing_sound == -1) { if (self->ringing_sound == -1) {
sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound); sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound);
@ -799,7 +873,7 @@ void chat_onRinging(ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
@ -821,7 +895,7 @@ void chat_onStarting(ToxWindow *self, ToxAV *av, uint32_t friend_number, int sta
init_infobox(self); init_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
/* call is flagged active here */ /* call is flagged active here */
self->is_call = true; self->is_call = true;
@ -841,7 +915,7 @@ void chat_onEnding(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
} }
kill_infobox(self); kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
self->is_call = false; self->is_call = false;
@ -860,7 +934,7 @@ void chat_onError(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
} }
self->is_call = false; self->is_call = false;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error!");
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound); stop_sound(self->ringing_sound);
@ -881,7 +955,7 @@ void chat_onStart(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
init_infobox(self); init_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call started! Type: \"/hangup\" to end it.");
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound); stop_sound(self->ringing_sound);
@ -899,7 +973,7 @@ void chat_onCancel(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
self->is_call = false; self->is_call = false;
kill_infobox(self); kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call canceled!");
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
stop_sound(self->ringing_sound); stop_sound(self->ringing_sound);
@ -915,7 +989,7 @@ void chat_onReject(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
self->is_call = false; self->is_call = false;
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
@ -933,7 +1007,7 @@ void chat_onEnd(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
} }
kill_infobox(self); kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
self->is_call = false; self->is_call = false;
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
@ -1033,7 +1107,7 @@ static void draw_infobox(ToxWindow *self)
wattroff(infobox->win, A_BOLD); wattroff(infobox->win, A_BOLD);
wprintw(infobox->win, "%.2f\n", (double) infobox->vad_lvl); wprintw(infobox->win, "%.2f\n", (double) infobox->vad_lvl);
wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' '); wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_ULCORNER, ' ', ACS_LLCORNER, ' ');
wnoutrefresh(infobox->win); wnoutrefresh(infobox->win);
} }
@ -1051,10 +1125,7 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
size_t len = tox_self_get_name_size(m); size_t len = tox_self_get_name_size(m);
selfname[len] = '\0'; selfname[len] = '\0';
char timefrmt[TIME_STR_SIZE]; int id = line_info_add(self, true, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
get_time_str(timefrmt, sizeof(timefrmt));
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id); cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id);
} }
@ -1170,13 +1241,10 @@ bool chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
size_t len = tox_self_get_name_size(m); size_t len = tox_self_get_name_size(m);
selfname[len] = '\0'; selfname[len] = '\0';
char timefrmt[TIME_STR_SIZE]; int id = line_info_add(self, true, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
get_time_str(timefrmt, sizeof(timefrmt));
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id); cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
} }
} }
@ -1227,63 +1295,63 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
switch (status) { switch (status) {
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
colour = GREEN_BLUE; colour = STATUS_ONLINE;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
colour = YELLOW_BLUE; colour = STATUS_AWAY;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
colour = RED_BLUE; colour = STATUS_BUSY;
break; break;
} }
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " ["); wprintw(statusbar->topline, " [");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wattron(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wprintw(statusbar->topline, "%s", ONLINE_CHAR); wprintw(statusbar->topline, "%s", ONLINE_CHAR);
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD); wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "] "); wprintw(statusbar->topline, "] ");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
const bool is_typing = Friends.list[self->num].is_typing; const bool is_typing = Friends.list[self->num].is_typing;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
if (is_typing) { if (is_typing) {
wattron(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE)); wattron(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_NOTIFY));
} else { } else {
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
} }
wprintw(statusbar->topline, "%s", statusbar->nick); wprintw(statusbar->topline, "%s", statusbar->nick);
if (is_typing) { if (is_typing) {
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(YELLOW_BLUE)); wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_NOTIFY));
} else { } else {
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(BAR_TEXT));
} }
} else { } else {
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " ["); wprintw(statusbar->topline, " [");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wprintw(statusbar->topline, "%s", OFFLINE_CHAR); wprintw(statusbar->topline, "%s", OFFLINE_CHAR);
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "] "); wprintw(statusbar->topline, "] ");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wprintw(statusbar->topline, "%s", statusbar->nick); wprintw(statusbar->topline, "%s", statusbar->nick);
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
} }
/* Reset statusbar->statusmsg on window resize */ /* Reset statusbar->statusmsg on window resize */
@ -1312,14 +1380,14 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
} }
if (statusbar->statusmsg[0]) { if (statusbar->statusmsg[0]) {
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " | "); wprintw(statusbar->topline, " | ");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wprintw(statusbar->topline, "%s ", statusbar->statusmsg); wprintw(statusbar->topline, "%s ", statusbar->statusmsg);
} else { } else {
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
} }
int s_y; int s_y;
@ -1327,25 +1395,25 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
getyx(statusbar->topline, s_y, s_x); getyx(statusbar->topline, s_y, s_x);
mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x - (KEY_IDENT_DIGITS * 2) - 3); mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x - (KEY_IDENT_DIGITS * 2) - 3);
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3); wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "{"); wprintw(statusbar->topline, "{");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
for (size_t i = 0; i < KEY_IDENT_DIGITS; ++i) { for (size_t i = 0; i < KEY_IDENT_DIGITS; ++i) {
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff); wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
} }
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "} "); wprintw(statusbar->topline, "} ");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
int y; int y;
int x; int x;
@ -1385,17 +1453,17 @@ static void chat_init_log(ToxWindow *self, Tox *m, const char *self_nick)
tox_self_get_address(m, (uint8_t *) myid); tox_self_get_address(m, (uint8_t *) myid);
if (log_init(ctx->log, self_nick, myid, Friends.list[self->num].pub_key, LOG_TYPE_CHAT) != 0) { if (log_init(ctx->log, self_nick, myid, Friends.list[self->num].pub_key, LOG_TYPE_CHAT) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to initialize chat log."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to initialize chat log.");
return; return;
} }
if (load_chat_history(self, ctx->log) != 0) { if (load_chat_history(self, ctx->log) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history.");
} }
if (Friends.list[self->num].logging_on) { if (Friends.list[self->num].logging_on) {
if (log_enable(ctx->log) != 0) { if (log_enable(ctx->log) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log.");
} }
} }
} }
@ -1502,6 +1570,10 @@ ToxWindow *new_chat(Tox *m, uint32_t friendnum)
ret->ringing_sound = -1; ret->ringing_sound = -1;
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef GAMES
ret->onGameInvite = &chat_onGameInvite;
#endif /* GAMES */
ret->active_box = -1; ret->active_box = -1;
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];

View File

@ -41,7 +41,7 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 2) { 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; 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); long int idx = strtol(argv[2], NULL, 10);
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) { if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
return; return;
} }
@ -62,17 +62,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
} else if (strcasecmp(inoutstr, "out") == 0) { } else if (strcasecmp(inoutstr, "out") == 0) {
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND); ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
} else { } 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; return;
} }
if (!ft) { 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; return;
} }
if (ft->state == FILE_TRANSFER_INACTIVE) { 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; return;
} }
@ -85,25 +85,25 @@ void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference number required."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required.");
return; return;
} }
long int conferencenum = strtol(argv[1], NULL, 10); long int conferencenum = strtol(argv[1], NULL, 10);
if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) { if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number.");
return; return;
} }
Tox_Err_Conference_Invite err; Tox_Err_Conference_Invite err;
if (!tox_conference_invite(m, self->num, conferencenum, &err)) { if (!tox_conference_invite(m, self->num, conferencenum, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err);
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum);
} }
void cmd_conference_join(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])
@ -113,7 +113,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
UNUSED_VAR(argv); UNUSED_VAR(argv);
if (get_num_active_windows() >= MAX_WINDOWS_NUM) { 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; return;
} }
@ -122,7 +122,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
uint8_t type = Friends.list[self->num].conference_invite.type; uint8_t type = Friends.list[self->num].conference_invite.type;
if (!Friends.list[self->num].conference_invite.pending) { if (!Friends.list[self->num].conference_invite.pending) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite.");
return; return;
} }
@ -133,7 +133,7 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err); conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
if (err != TOX_ERR_CONFERENCE_JOIN_OK) { if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
return; return;
} }
} else if (type == TOX_CONFERENCE_TYPE_AV) { } else if (type == TOX_CONFERENCE_TYPE_AV) {
@ -142,21 +142,21 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
audio_conference_callback, NULL); audio_conference_callback, NULL);
if (conferencenum == (uint32_t) -1) { if (conferencenum == (uint32_t) -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
return; return;
} }
#else #else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
return; return;
#endif #endif
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
return; return;
} }
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
tox_conference_delete(m, conferencenum, NULL); tox_conference_delete(m, conferencenum, NULL);
return; return;
} }
@ -165,38 +165,95 @@ void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
if (type == TOX_CONFERENCE_TYPE_AV) { if (type == TOX_CONFERENCE_TYPE_AV) {
if (!init_conference_audio_input(m, conferencenum)) { if (!init_conference_audio_input(m, conferencenum)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
} }
} }
#endif #endif
} }
#ifdef GAMES
void cmd_game_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
UNUSED_VAR(m);
bool force_small = false;
if (argc >= 2) {
force_small = strcasecmp(argv[2], "small") == 0;
}
if (!Friends.list[self->num].game_invite.pending) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending game invite.");
return;
}
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
GameType type = Friends.list[self->num].game_invite.type;
uint32_t id = Friends.list[self->num].game_invite.id;
uint8_t *data = Friends.list[self->num].game_invite.data;
size_t length = Friends.list[self->num].game_invite.data_length;
int ret = game_initialize(self, m, type, id, data, length, force_small);
switch (ret) {
case 0: {
free(data);
Friends.list[self->num].game_invite.data = NULL;
Friends.list[self->num].game_invite.pending = false;
break;
}
case -1: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Window is too small. Try enlarging your window.");
return;
}
case -2: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize (network error)");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize (error %d)", ret);
return;
}
}
}
#endif // GAMES
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { 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; return;
} }
long int idx = strtol(argv[1], NULL, 10); long int idx = strtol(argv[1], NULL, 10);
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) { if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
return; return;
} }
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV); struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
if (!ft) { 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; return;
} }
if (ft->state != FILE_TRANSFER_PENDING) { 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; return;
} }
@ -213,12 +270,12 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
goto on_recv_error; goto on_recv_error;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] 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 */ /* prep progress bar line */
char progline[MAX_STR_SIZE]; char progline[MAX_STR_SIZE];
init_progress_bar(progline); 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->line_id = self->chatwin->hst->line_end->id + 2;
ft->state = FILE_TRANSFER_STARTED; ft->state = FILE_TRANSFER_STARTED;
@ -229,23 +286,23 @@ on_recv_error:
switch (err) { switch (err) {
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND: 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; return;
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED: 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; return;
case TOX_ERR_FILE_CONTROL_NOT_FOUND: 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; return;
case TOX_ERR_FILE_CONTROL_SENDQ: 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; return;
default: 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; return;
} }
} }
@ -257,7 +314,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
const char *errmsg = NULL; const char *errmsg = NULL;
if (argc < 1) { 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; return;
} }
@ -266,21 +323,21 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
int path_len = strlen(path); int path_len = strlen(path);
if (path_len >= MAX_STR_SIZE) { 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; return;
} }
FILE *file_to_send = fopen(path, "r"); FILE *file_to_send = fopen(path, "r");
if (file_to_send == NULL) { 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; return;
} }
off_t filesize = file_size(path); off_t filesize = file_size(path);
if (filesize == 0) { 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); fclose(file_to_send);
return; return;
} }
@ -310,7 +367,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
char sizestr[32]; char sizestr[32];
bytes_convert_str(sizestr, sizeof(sizestr), filesize); bytes_convert_str(sizestr, sizeof(sizestr), filesize);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", 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; return;
@ -338,7 +395,7 @@ on_send_error:
break; 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); tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
fclose(file_to_send); fclose(file_to_send);
} }

View File

@ -29,6 +29,7 @@
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_conference_invite(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_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_game_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_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);

View File

@ -88,6 +88,9 @@ static const char *conference_cmd_list[] = {
"/decline", "/decline",
"/exit", "/exit",
"/conference", "/conference",
#ifdef GAME
"/game",
#endif
"/help", "/help",
"/log", "/log",
#ifdef AUDIO #ifdef AUDIO
@ -164,17 +167,17 @@ static void init_conference_logging(ToxWindow *self, Tox *m, uint32_t conference
tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id); tox_conference_get_id(m, conferencenum, (uint8_t *) conference_id);
if (log_init(ctx->log, conferences[self->num].title, my_id, conference_id, LOG_TYPE_CHAT) != 0) { if (log_init(ctx->log, conferences[self->num].title, my_id, conference_id, LOG_TYPE_CHAT) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
return; return;
} }
if (load_chat_history(self, ctx->log) != 0) { if (load_chat_history(self, ctx->log) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to load chat history.");
} }
if (user_settings->autolog == AUTOLOG_ON) { if (user_settings->autolog == AUTOLOG_ON) {
if (log_enable(ctx->log) != 0) { if (log_enable(ctx->log) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to enable chat log.");
} }
} }
@ -380,10 +383,7 @@ static void conference_onConferenceMessage(ToxWindow *self, Tox *m, uint32_t con
sound_notify(self, silent, NT_WNDALERT_1, NULL); sound_notify(self, silent, NT_WNDALERT_1, NULL);
} }
char timefrmt[TIME_STR_SIZE]; line_info_add(self, true, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg);
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); write_to_log(msg, nick, ctx->log, false);
} }
@ -407,9 +407,6 @@ static void conference_onConferenceTitleChange(ToxWindow *self, Tox *m, uint32_t
conference_set_title(self, conferencenum, title, length); conference_set_title(self, conferencenum, title, length);
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
/* don't announce title when we join the room */ /* don't announce title when we join the room */
if (!timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) { if (!timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) {
return; return;
@ -417,7 +414,7 @@ static void conference_onConferenceTitleChange(ToxWindow *self, Tox *m, uint32_t
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_conference_nick_truncate(m, nick, peernum, conferencenum); get_conference_nick_truncate(m, nick, peernum, conferencenum);
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); line_info_add(self, true, nick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title);
char tmp_event[MAX_STR_SIZE]; char tmp_event[MAX_STR_SIZE];
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
@ -673,8 +670,6 @@ static void update_peer_list(ToxWindow *self, Tox *m, uint32_t conferencenum, ui
return; return;
} }
char timefrmt[TIME_STR_SIZE];
for (uint32_t i = 0; i < num_peers; ++i) { for (uint32_t i = 0; i < num_peers; ++i) {
ConferencePeer *peer = &chat->peer_list[i]; ConferencePeer *peer = &chat->peer_list[i];
@ -718,9 +713,8 @@ static void update_peer_list(ToxWindow *self, Tox *m, uint32_t conferencenum, ui
peer->peernum = i; peer->peernum = i;
if (new_peer && peer->name_length > 0 && timed_out(chat->start_time, CONFERENCE_EVENT_WAIT)) { if (new_peer && peer->name_length > 0 && timed_out(chat->start_time, CONFERENCE_EVENT_WAIT)) {
const char *msg = "has joined the group"; const char *msg = "has joined the conference";
get_time_str(timefrmt, sizeof(timefrmt)); line_info_add(self, true, peer->name, NULL, CONNECTION, 0, GREEN, msg);
line_info_add(self, timefrmt, peer->name, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, peer->name, ctx->log, true); write_to_log(msg, peer->name, ctx->log, true);
} }
@ -735,11 +729,9 @@ static void update_peer_list(ToxWindow *self, Tox *m, uint32_t conferencenum, ui
ConferencePeer *old_peer = &old_peer_list[i]; ConferencePeer *old_peer = &old_peer_list[i];
if (old_peer->active) { if (old_peer->active) {
if (!find_peer_by_pubkey(chat->peer_list, chat->num_peers, old_peer->pubkey, NULL)) { if (old_peer->name_length > 0 && !find_peer_by_pubkey(chat->peer_list, chat->num_peers, old_peer->pubkey, NULL)) {
const char *msg = "has left the group"; const char *msg = "has left the conference";
get_time_str(timefrmt, sizeof(timefrmt)); line_info_add(self, true, old_peer->name, NULL, DISCONNECTION, 0, RED, msg);
line_info_add(self, timefrmt, old_peer->name, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, old_peer->name, ctx->log, true); write_to_log(msg, old_peer->name, ctx->log, true);
} }
@ -796,19 +788,17 @@ static void conference_onConferencePeerNameChange(ToxWindow *self, Tox *m, uint3
if (peer != NULL) { if (peer != NULL) {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
if (peer->name_length > 0) { if (peer->name_length > 0) {
char log_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; char log_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as "); line_info_add(self, true, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
snprintf(log_event, sizeof(log_event), "is now known as %s", (const char *) name); snprintf(log_event, sizeof(log_event), "is now known as %s", (const char *) name);
write_to_log(log_event, peer->name, ctx->log, true); write_to_log(log_event, peer->name, ctx->log, true);
} else { // this is kind of a hack; peers always join a group with no name set and then set it after // this is kind of a hack; peers always join a group with no name set and then set it after
const char *msg = "has joined the group"; } else if (timed_out(conferences[conferencenum].start_time, CONFERENCE_EVENT_WAIT)) {
line_info_add(self, timefrmt, name, NULL, CONNECTION, 0, GREEN, msg); const char *msg = "has joined the conference";
line_info_add(self, true, name, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, name, ctx->log, true); write_to_log(msg, name, ctx->log, true);
} }
} }
@ -826,7 +816,7 @@ static void send_conference_action(ToxWindow *self, ChatContext *ctx, Tox *m, ch
Tox_Err_Conference_Send_Message err; Tox_Err_Conference_Send_Message err;
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &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); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
} }
} }
@ -992,10 +982,10 @@ static bool conference_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
Tox_Err_Conference_Send_Message err; Tox_Err_Conference_Send_Message err;
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &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); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
} }
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
} }
} }

View File

@ -32,7 +32,7 @@
static void print_err(ToxWindow *self, const char *error_str) static void print_err(ToxWindow *self, const char *error_str)
{ {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str); line_info_add(self, 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]) void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -56,7 +56,7 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
} }
title[tlen] = '\0'; title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
return; return;
} }
@ -64,14 +64,14 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
size_t len = strlen(argv[1]); size_t len = strlen(argv[1]);
if (len >= sizeof(title)) { if (len >= sizeof(title)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
return; return;
} }
snprintf(title, sizeof(title), "%s", argv[1]); snprintf(title, sizeof(title), "%s", argv[1]);
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) { 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); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
return; return;
} }
@ -79,16 +79,13 @@ void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc,
conference_set_title(self, self->num, title, len); conference_set_title(self, self->num, title, len);
char timefrmt[TIME_STR_SIZE];
char selfnick[TOX_MAX_NAME_LENGTH]; char selfnick[TOX_MAX_NAME_LENGTH];
get_time_str(timefrmt, sizeof(timefrmt));
tox_self_get_name(m, (uint8_t *) selfnick); tox_self_get_name(m, (uint8_t *) selfnick);
size_t sn_len = tox_self_get_name_size(m); size_t sn_len = tox_self_get_name_size(m);
selfnick[sn_len] = '\0'; selfnick[sn_len] = '\0';
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title); 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]; char tmp_event[MAX_STR_SIZE + 20];
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title); snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
@ -142,14 +139,14 @@ void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char
print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):"); print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):");
for (uint32_t i = 0; i < n; ++i) { for (uint32_t i = 0; i < n; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
} }
return; return;
} }
if (conference_mute_peer(m, self->num, entries[0]->peernum)) { if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
} else { } else {
print_err(self, "Peer is not on the call"); print_err(self, "Peer is not on the call");
} }
@ -162,7 +159,7 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha
UNUSED_VAR(m); UNUSED_VAR(m);
if (argc == 0) { if (argc == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f", line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f",
(double) conference_get_VAD_threshold(self->num)); (double) conference_get_VAD_threshold(self->num));
return; return;
} }
@ -181,7 +178,7 @@ void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, cha
} }
if (conference_set_VAD_threshold(self->num, value)) { if (conference_set_VAD_threshold(self->num, value)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value);
} else { } else {
print_err(self, "Failed to set conference audio input sensitivity."); print_err(self, "Failed to set conference audio input sensitivity.");
} }

View File

@ -49,6 +49,9 @@ static struct cmd_func global_commands[] = {
{ "/decline", cmd_decline }, { "/decline", cmd_decline },
{ "/exit", cmd_quit }, { "/exit", cmd_quit },
{ "/conference", cmd_conference }, { "/conference", cmd_conference },
#ifdef GAMES
{ "/game", cmd_game },
#endif
{ "/help", cmd_prompt_help }, { "/help", cmd_prompt_help },
{ "/log", cmd_log }, { "/log", cmd_log },
{ "/myid", cmd_myid }, { "/myid", cmd_myid },
@ -80,6 +83,9 @@ static struct cmd_func chat_commands[] = {
{ "/cancel", cmd_cancelfile }, { "/cancel", cmd_cancelfile },
{ "/invite", cmd_conference_invite }, { "/invite", cmd_conference_invite },
{ "/join", cmd_conference_join }, { "/join", cmd_conference_join },
#ifdef GAMES
{ "/play", cmd_game_join },
#endif
{ "/savefile", cmd_savefile }, { "/savefile", cmd_savefile },
{ "/sendfile", cmd_sendfile }, { "/sendfile", cmd_sendfile },
#ifdef AUDIO #ifdef AUDIO
@ -271,5 +277,5 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
#endif #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.");
} }

View File

@ -274,7 +274,7 @@ 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); 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);
} }
clear_file_transfer(ft); clear_file_transfer(ft);

View File

@ -116,8 +116,11 @@ static void realloc_blocklist(int n)
void kill_friendlist(ToxWindow *self) void kill_friendlist(ToxWindow *self)
{ {
for (size_t i = 0; i < Friends.max_idx; ++i) { for (size_t i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].conference_invite.key != NULL) { if (Friends.list[i].active) {
free(Friends.list[i].conference_invite.key); free(Friends.list[i].conference_invite.key);
#ifdef GAMES
free(Friends.list[i].game_invite.data);
#endif
} }
} }
@ -397,11 +400,8 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Mess
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); get_nick_truncate(m, nick, num);
char timefrmt[TIME_STR_SIZE]; line_info_add(prompt, true, nick, NULL, IN_MSG, 0, 0, "%s", str);
get_time_str(timefrmt, sizeof(timefrmt)); line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "* Warning: Too many windows are open.");
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.");
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
@ -585,6 +585,38 @@ static void friendlist_add_blocked(uint32_t fnum, uint32_t bnum)
} }
} }
#ifdef GAMES
static void friendlist_onGameInvite(ToxWindow *self, Tox *m, uint32_t friend_number, const uint8_t *data, size_t length)
{
UNUSED_VAR(self);
UNUSED_VAR(data);
UNUSED_VAR(length);
if (friend_number >= Friends.max_idx) {
return;
}
if (Friends.list[friend_number].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
return;
}
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, friend_number);
line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED,
"* Game invite from %s failed: Too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
#endif // GAMES
static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum, static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_t filenum,
uint64_t file_size, const char *filename, size_t name_length) uint64_t file_size, const char *filename, size_t name_length)
{ {
@ -611,7 +643,7 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); 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); "* File transfer from %s failed: too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
@ -642,7 +674,7 @@ static void friendlist_onConferenceInvite(ToxWindow *self, Tox *m, int32_t num,
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num); 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,
"* Conference chat invite from %s failed: too many windows are open.", nick); "* Conference chat invite from %s failed: too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
@ -854,7 +886,7 @@ static void unblock_friend(Tox *m, uint32_t bnum)
uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err); uint32_t friendnum = tox_friend_add_norequest(m, (uint8_t *) Blocked.list[bnum].pub_key, &err);
if (err != TOX_ERR_FRIEND_ADD_OK) { if (err != TOX_ERR_FRIEND_ADD_OK) {
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err); line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to unblock friend (error %d)", err);
return; return;
} }
@ -923,7 +955,7 @@ static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
set_active_window_index(Friends.list[f].chatwin); set_active_window_index(Friends.list[f].chatwin);
} else { } else {
const char *msg = "* Warning: Too many windows are open."; 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); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
@ -1307,10 +1339,10 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
} else { } else {
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, Friends.list[friend_number].num); get_nick_truncate(m, nick, Friends.list[friend_number].num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick); line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Audio action from: %s!", nick);
const char *errmsg = "* Warning: Too many windows are open."; const char *errmsg = "* Warning: Too many windows are open.";
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); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
@ -1386,6 +1418,10 @@ ToxWindow *new_friendlist(void)
ret->is_call = false; ret->is_call = false;
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef GAMES
ret->onGameInvite = &friendlist_onGameInvite;
#endif
ret->num = -1; ret->num = -1;
ret->active_box = -1; ret->active_box = -1;

View File

@ -29,6 +29,10 @@
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#ifdef GAMES
#include "game_base.h"
#endif
struct LastOnline { struct LastOnline {
uint64_t last_on; uint64_t last_on;
struct tm tm; struct tm tm;
@ -42,6 +46,18 @@ struct ConferenceInvite {
bool pending; bool pending;
}; };
#ifdef GAMES
struct GameInvite {
uint8_t *data;
size_t data_length;
GameType type;
uint32_t id;
bool pending;
};
#endif // GAMES
typedef struct { typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
uint16_t namelength; uint16_t namelength;
@ -59,6 +75,10 @@ typedef struct {
struct LastOnline last_online; struct LastOnline last_online;
struct ConferenceInvite conference_invite; struct ConferenceInvite conference_invite;
#ifdef GAMES
struct GameInvite game_invite;
#endif
struct FileTransfer file_receiver[MAX_FILES]; struct FileTransfer file_receiver[MAX_FILES];
struct FileTransfer file_sender[MAX_FILES]; struct FileTransfer file_sender[MAX_FILES];
} ToxicFriend; } ToxicFriend;

1074
src/game_base.c Normal file

File diff suppressed because it is too large Load Diff

383
src/game_base.h Normal file
View File

@ -0,0 +1,383 @@
/* game_base.h
*
*
* 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/>.
*
*/
#ifndef GAME_BASE
#define GAME_BASE
#include <ncurses.h>
#include <time.h>
#include <tox/tox.h>
#include "game_util.h"
#include "windows.h"
#define GAME_BORDER_COLOUR BAR_TEXT
/* Max size of a default size square game window */
#define GAME_MAX_SQUARE_Y 26
#define GAME_MAX_SQUARE_X (GAME_MAX_SQUARE_Y * 2)
/* Max size of a small square game window */
#define GAME_MAX_SQUARE_Y_SMALL 18
#define GAME_MAX_SQUARE_X_SMALL (GAME_MAX_SQUARE_Y_SMALL * 2)
/* Max size of a default size rectangle game window */
#define GAME_MAX_RECT_Y 24
#define GAME_MAX_RECT_X (GAME_MAX_RECT_Y * 4)
/* Max size of a small rectangle game window */
#define GAME_MAX_RECT_Y_SMALL 14
#define GAME_MAX_RECT_X_SMALL (GAME_MAX_RECT_Y_SMALL * 4)
/* Maximum length of a game message set with game_set_message() */
#define GAME_MAX_MESSAGE_SIZE 64
/* Default number of seconds a game message is displayed for */
#define GAME_MESSAGE_DEFAULT_TIMEOUT 3
/***** NETWORKING DEFINES *****/
/* Header starts after custom packet type byte. Comprised of: NetworkVersion (1b) + GameType (1b) + id (4b) */
#define GAME_PACKET_HEADER_SIZE (1 + 1 + sizeof(uint32_t))
/* Max size of a game packet including the header */
#define GAME_MAX_PACKET_SIZE 1024
/* Max size of a game packet payload */
#define GAME_MAX_DATA_SIZE (GAME_MAX_PACKET_SIZE - GAME_PACKET_HEADER_SIZE - 1)
/* Current version of networking protocol */
#define GAME_NETWORKING_VERSION 0x01
typedef void cb_game_update_state(GameData *game, void *cb_data);
typedef void cb_game_render_window(GameData *game, WINDOW *window, void *cb_data);
typedef void cb_game_kill(GameData *game, void *cb_data);
typedef void cb_game_pause(GameData *game, bool is_paused, void *cb_data);
typedef void cb_game_key_press(GameData *game, int key, void *cb_data);
typedef void cb_game_on_packet(GameData *game, const uint8_t *data, size_t length, void *cb_data);
typedef enum GamePacketType {
GP_Invite = 0u,
GP_Data,
} GamePacketType;
typedef enum GameWindowShape {
GW_ShapeSquare = 0u,
GW_ShapeRectangle,
GW_ShapeInvalid,
} GameWindowShape;
typedef enum GameStatus {
GS_None = 0u,
GS_Paused,
GS_Running,
GS_Finished,
GS_Invalid,
} GameStatus;
typedef enum GameType {
GT_Centipede = 0u,
GT_Chess,
GT_Snake,
GT_Invalid,
} GameType;
typedef struct GameMessage {
char message[GAME_MAX_MESSAGE_SIZE + 1];
size_t length;
const Coords *coords; // pointer to coords so we can track movement
Coords original_coords; // static coords at time of being set
time_t timeout;
time_t set_time;
int attributes;
int colour;
Direction direction;
bool sticky;
bool priority;
} GameMessage;
struct GameData {
TIME_MS last_frame_time;
TIME_MS update_interval; // determines the refresh rate (lower means faster)
long int score;
size_t high_score;
int lives;
size_t level;
GameStatus status;
GameType type;
bool is_multiplayer;
bool show_lives;
bool show_score;
bool show_high_score;
bool show_level;
GameMessage *messages;
size_t messages_size;
int game_max_x; // max dimensions of game window
int game_max_y;
int parent_max_x; // max dimensions of parent window
int parent_max_y;
int window_id;
WINDOW *window;
Tox *tox; // must be locked with Winthread mutex
GameWindowShape window_shape;
uint32_t id; // indentifies multiplayer instance
uint32_t friend_number; // friendnumber associated with parent window
cb_game_update_state *cb_game_update_state;
void *cb_game_update_state_data;
cb_game_render_window *cb_game_render_window;
void *cb_game_render_window_data;
cb_game_kill *cb_game_kill;
void *cb_game_kill_data;
cb_game_pause *cb_game_pause;
void *cb_game_pause_data;
cb_game_key_press *cb_game_key_press;
void *cb_game_key_press_data;
cb_game_on_packet *cb_game_on_packet;
void *cb_game_on_packet_data;
};
/*
* Sets the callback for game state updates.
*/
void game_set_cb_update_state(GameData *game, cb_game_update_state *func, void *cb_data);
/*
* Sets the callback for frame rendering.
*/
void game_set_cb_render_window(GameData *game, cb_game_render_window *func, void *cb_data);
/*
* Sets the callback for game termination.
*/
void game_set_cb_kill(GameData *game, cb_game_kill *func, void *cb_data);
/*
* Sets the callback for the game pause event.
*/
void game_set_cb_on_pause(GameData *game, cb_game_pause *func, void *cb_data);
/*
* Sets the callback for the key press event.
*/
void game_set_cb_on_keypress(GameData *game, cb_game_key_press *func, void *cb_data);
/*
* Sets the callback for the game packet event.
*/
void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_data);
/*
* Initializes game instance.
*
* `type` must be a valid GameType.
*
* `id` should be a unique integer to indentify the game instance. If we're being invited to a game
* this identifier should be sent via the invite packet.
*
* `force_small_window` will make the game window small.
*
* if `multiplayer_data` is non-null this indicates that we accepted a game invite from a contact.
* The data contains any information we need to initialize the game state.
*
* Return 0 on success.
* Return -1 if screen is too small.
* Return -2 on network related error.
* Return -3 if multiplayer game is being initialized outside of a contact's window.
* Return -4 on other failure.
*/
int game_initialize(const ToxWindow *self, Tox *m, GameType type, uint32_t id, const uint8_t *multiplayer_data,
size_t length, bool force_small_window);
/*
* Sets game window to `shape` and attempts to adjust size for best fit.
*
* This should be called in the game's initialize function.
*
* Return 0 on success.
* Return -1 if window is too small or shape is invalid.
* Return -2 if function is called while the game state is valid.
*/
int game_set_window_shape(GameData *game, GameWindowShape shape);
/*
* Returns the GameType associated with `game_string`.
*/
GameType game_get_type(const char *game_string);
/*
* Returns the name represented as a string associated with `type`.
*/
const char *game_get_name_string(GameType type);
/*
* Prints all available games to window associated with `self`.
*/
void game_list_print(ToxWindow *self);
/*
* Return true if game `type` has a multiplayer mode.
*/
bool game_type_is_multiplayer(GameType type);
/*
* Returns true if coordinates designated by `x` and `y` are within the game window boundaries.
*/
bool game_coordinates_in_bounds(const GameData *game, int x, int y);
/*
* Put random coordinates that fit within the game window in `coords`.
*/
void game_random_coords(const GameData *game, Coords *coords);
/*
* Gets the current max dimensions of the game window.
*/
void game_max_x_y(const GameData *game, int *x, int *y);
/*
* Returns the respective coordinate boundary of the game window.
*/
int game_y_bottom_bound(const GameData *game);
int game_y_top_bound(const GameData *game);
int game_x_right_bound(const GameData *game);
int game_x_left_bound(const GameData *game);
/*
* Toggle whether the respective game info is shown around the game window.
*/
void game_show_score(GameData *game, bool show_score);
void game_show_high_score(GameData *game, bool show_high_score);
void game_show_lives(GameData *game, bool show_lives);
void game_show_level(GameData *game, bool show_level);
/*
* Updates game score.
*/
void game_update_score(GameData *game, long int points);
/*
* Returns the game's current score.
*/
long int game_get_score(const GameData *game);
/*
* Increments level.
*
* This function should be called on initialization if game wishes to display level.
*/
void game_increment_level(GameData *game);
/*
* Updates lives with `amount`.
*
* If lives becomes negative the lives counter will not be drawn.
*/
void game_update_lives(GameData *game, int amount);
/*
* Returns the remaining number of lives for the game.
*/
int game_get_lives(const GameData *game);
/*
* Returns the current level.
*/
size_t game_get_current_level(const GameData *game);
/*
* Sets the game status to `status`.
*/
void game_set_status(GameData *game, GameStatus status);
/*
* Sets the game base update interval.
*
* Lower values of `update_interval` make the game faster, where 1 is the fastest and 50 is slowest.
* If this function is never called the game chooses a reasonable default.
*/
void game_set_update_interval(GameData *game, TIME_MS update_interval);
/*
* Creates a message `message` of size `length` to be displayed at `coords` for `timeout` seconds.
*
* Message must be no greater than GAME_MAX_MESSAGE_SIZE bytes in length.
*
* If `sticky` is true the message will follow coords if they move.
*
* If `dir` is a valid direction, the message will be positioned a few squares away from `coords`
* so as to not overlap with its associated object.
*
* If `timeout` is zero, the default timeout value will be used.
*
* If `priority` true, messages will be printed on top of game objects.
*
* Return 0 on success.
* Return -1 on failure.
*/
int game_set_message(GameData *game, const char *message, size_t length, Direction dir, int attributes, int colour,
time_t timeout, const Coords *coords, bool sticky, bool priority);
/*
* Returns true if game should update an object's state according to its last moved time and current speed.
*
* This is used to independently control the speed of various game objects.
*/
bool game_do_object_state_update(const GameData *game, TIME_MS current_time, TIME_MS last_moved_time, TIME_MS speed);
/*
* Returns the current wall time in milliseconds.
*/
TIME_MS get_time_millis(void);
/*
* Ends game associated with `self` and cleans up.
*/
void game_kill(ToxWindow *self);
/*
* Sends a packet containing payload `data` of size `length` to the friendnumber associated with the game's
* parent window.
*
* `length` must not exceed GAME_MAX_DATA_SIZE bytes.
*
* `packet_type` should be GP_Invite for an invite packet or GP_Data for all other game data.
*/
int game_send_packet(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type);
#endif // GAME_BASE

1745
src/game_centipede.c Normal file

File diff suppressed because it is too large Load Diff

30
src/game_centipede.h Normal file
View File

@ -0,0 +1,30 @@
/* game_centipede.h
*
*
* 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/>.
*
*/
#ifndef GAME_CENTIPEDE
#define GAME_CENTIPEDE
#include "game_base.h"
int centipede_initialize(GameData *game);
#endif // GAME_CENTIPEDE

2133
src/game_chess.c Normal file

File diff suppressed because it is too large Load Diff

44
src/game_chess.h Normal file
View File

@ -0,0 +1,44 @@
/* game_chess.h
*
*
* 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/>.
*
*/
#ifndef GAME_CHESS
#define GAME_CHESS
#include "game_base.h"
/*
* Initializes chess game state.
*
* If `init_data` is non-null, this indicates that we were invited to the game.
*
* If we're the inviter, we send an invite packet after initialization. If we're the
* invitee, we send a handshake response packet to the inviter.
*
* Return 0 on success.
* Return -1 if window is too small.
* Return -2 on network related error.
* Return -3 on other error.
*/
int chess_initialize(GameData *game, const uint8_t *init_data, size_t length);
#endif // GAME_CHESS

904
src/game_snake.c Normal file
View File

@ -0,0 +1,904 @@
/* game_snake.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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "game_base.h"
#include "game_util.h"
#include "game_snake.h"
#include "misc_tools.h"
#define SNAKE_MAX_SNAKE_LENGTH (GAME_MAX_SQUARE_X * GAME_MAX_SQUARE_Y)
#define SNAKE_AGENT_MAX_LIST_SIZE (GAME_MAX_SQUARE_X * GAME_MAX_SQUARE_Y)
#define SNAKE_DEFAULT_SNAKE_SPEED 6
#define SNAKE_DEFAULT_AGENT_SPEED 1
#define SNAKE_MAX_SNAKE_SPEED 12
#define SNAKE_MAX_AGENT_SPEED (SNAKE_MAX_SNAKE_SPEED / 2)
#define SNAKE_DEFAULT_UPDATE_INTERVAL 20
/* How often we update frames independent of the speed of the game or objects */
#define SNAKE_FRAME_DRAW_SPEED 5
/* How long a regular message stays on the screen */
#define SNAKE_DEFAULT_MESSAGE_TIMER 5
/* Increment snake speed by 1 every time level increases by this amount */
#define SNAKE_LEVEL_SPEED_INTERVAL 5
/* Increment level by 1 every time snake eats this many foods */
#define SNAKE_LEVEL_UP_FOOD_LIMIT 4
/* Increment agent speed by 1 every time level increases by this amount */
#define SNAKE_AGENT_LEVEL_SPEED_INTERVAL 2
/* Points multiplier for getting a powerup */
#define SNAKE_POWERUP_BONUS_MULTIPLIER 5
/* Extra bonus for running over a glowing agent */
#define SNAKE_AGENT_GLOWING_MULTIPLIER 2
/* Agents begin glowing if their speed is greater than this */
#define SNAKE_AGENT_GLOWING_SPEED (SNAKE_DEFAULT_AGENT_SPEED + 2)
/* A new powerup is placed on the board after this many seconds since last one wore off */
#define SNAKE_POWERUP_INTERVAL 45
/* How long a powerup lasts */
#define SNAKE_POWERUP_TIMER 12
/* Number of key presses to queue; one key press is retrieved per state update */
#define SNAKE_KEY_PRESS_QUEUE_SIZE 3
/* These decide how many points to decay and how often to penalize camping for powerups */
#define SNAKE_DECAY_POINTS_INTERVAL 1
#define SNAKE_DECAY_POINTS_FRACTION 10
#define SNAKE_HEAD_COLOUR GREEN
#define SNAKE_DEAD_BODY_CHAR 'o'
#define SNAKE_DEAD_BODY_COLOUR RED
#define SNAKE_BODY_COLOUR CYAN
#define SNAKE_BODY_CHAR 'o'
#define SNAKE_FOOD_COLOUR YELLOW
#define SNAKE_FOOD_CHAR '*'
#define SNAKE_AGENT_NORMAL_COLOUR RED
#define SNAKE_AGENT_GLOWING_COLOUR GREEN
#define SNAKE_AGENT_NORMAL_CHAR 'x'
#define SNAKE_AGENT_GLOWING_CHAR 'X'
#define SNAKE_POWERUP_CHAR 'P'
typedef struct NasaAgent {
Coords coords;
bool is_alive;
bool is_glowing;
TIME_MS last_time_moved;
size_t speed;
char display_char;
int colour;
int attributes;
} NasaAgent;
typedef struct Snake {
Coords coords;
char display_char;
int colour;
int attributes;
} Snake;
typedef struct SnakeState {
Snake *snake;
size_t snake_length;
size_t snake_speed;
TIME_MS snake_time_last_moved;
bool has_powerup;
Direction direction;
Coords powerup;
TIME_S powerup_timer;
TIME_S last_powerup_time;
Coords food;
NasaAgent *agents;
size_t agent_list_size;
TIME_S last_time_points_decayed;
TIME_S pause_time;
int key_press_queue[SNAKE_KEY_PRESS_QUEUE_SIZE];
size_t keys_skip_counter;
TIME_MS last_draw_update;
bool game_over;
} SnakeState;
static void snake_create_points_message(GameData *game, Direction dir, long int points, const Coords *coords)
{
char buf[GAME_MAX_MESSAGE_SIZE + 1];
snprintf(buf, sizeof(buf), "%ld", points);
if (game_set_message(game, buf, strlen(buf), dir, A_BOLD, WHITE, 0, coords, false, false) == -1) {
fprintf(stderr, "failed to set points message\n");
}
}
static void snake_create_message(GameData *game, Direction dir, const char *message, int attributes,
int colour, TIME_S timeout, const Coords *coords, bool priority)
{
if (game_set_message(game, message, strlen(message), dir, attributes, colour, timeout, coords, false, priority) == -1) {
fprintf(stderr, "failed to set message\n");
}
}
static Coords *snake_get_head_coords(const SnakeState *state)
{
return &state->snake[0].coords;
}
static void snake_set_head_char(SnakeState *state)
{
Snake *snake_head = &state->snake[0];
switch (state->direction) {
case NORTH:
snake_head->display_char = '^';
break;
case SOUTH:
snake_head->display_char = 'v';
break;
case EAST:
snake_head->display_char = '>';
return;
case WEST:
snake_head->display_char = '<';
break;
default:
snake_head->display_char = '?';
break;
}
}
static bool snake_validate_direction(const SnakeState *state, Direction dir)
{
if (!GAME_UTIL_DIRECTION_VALID(dir)) {
return false;
}
const int diff = abs((int)state->direction - (int)dir);
return diff != 1;
}
static void snake_update_direction(SnakeState *state)
{
for (size_t i = 0; i < SNAKE_KEY_PRESS_QUEUE_SIZE; ++i) {
int key = state->key_press_queue[i];
if (key == 0) {
continue;
}
Direction dir = game_util_get_direction(key);
if (!GAME_UTIL_DIRECTION_VALID(dir)) {
state->key_press_queue[i] = 0;
continue;
}
if (snake_validate_direction(state, dir)) {
state->direction = dir;
snake_set_head_char(state);
state->key_press_queue[i] = 0;
state->keys_skip_counter = 0;
break;
}
if (state->keys_skip_counter++ >= SNAKE_KEY_PRESS_QUEUE_SIZE) {
state->keys_skip_counter = 0;
memset(state->key_press_queue, 0, sizeof(state->key_press_queue));
}
}
}
static void snake_set_key_press(SnakeState *state, int key)
{
for (size_t i = 0; i < SNAKE_KEY_PRESS_QUEUE_SIZE; ++i) {
if (state->key_press_queue[i] == 0) {
state->key_press_queue[i] = key;
return;
}
}
memset(state->key_press_queue, 0, sizeof(state->key_press_queue));
state->key_press_queue[0] = key;
}
static void snake_update_score(GameData *game, const SnakeState *state, long int points)
{
const Coords *head = snake_get_head_coords(state);
snake_create_points_message(game, state->direction, points, head);
game_update_score(game, points);
}
static long int snake_get_move_points(const SnakeState *state)
{
return state->snake_length + (2 * state->snake_speed);
}
/* Return true if snake body is occupying given coordinates */
static bool snake_coords_contain_body(const SnakeState *state, const Coords *coords)
{
for (size_t i = 1; i < state->snake_length; ++i) {
Coords snake_coords = state->snake[i].coords;
if (COORDINATES_OVERLAP(coords->x, coords->y, snake_coords.x, snake_coords.y)) {
return true;
}
}
return false;
}
static bool snake_self_consume(const SnakeState *state)
{
const Coords *head = snake_get_head_coords(state);
return snake_coords_contain_body(state, head);
}
/*
* Returns a pointer to the agent at x and y coordinates.
*
* Returns NULL if no living agent is at coords.
*/
static NasaAgent *snake_get_agent_at_coords(const SnakeState *state, const Coords *coords)
{
for (size_t i = 0; i < state->agent_list_size; ++i) {
NasaAgent *agent = &state->agents[i];
if (!agent->is_alive) {
continue;
}
if (COORDINATES_OVERLAP(coords->x, coords->y, agent->coords.x, agent->coords.y)) {
return agent;
}
}
return NULL;
}
/*
* Return true if snake got caught by an agent and doesn't have a powerup.
*
* If snake runs over agent and does have a powerup the agent is killed and score updated.
*/
static bool snake_agent_caught(GameData *game, SnakeState *state, const Coords *coords)
{
NasaAgent *agent = snake_get_agent_at_coords(state, coords);
if (agent == NULL) {
return false;
}
if (!state->has_powerup) {
return true;
}
agent->is_alive = false;
long int points = snake_get_move_points(state) * (agent->speed + 1);
if (agent->is_glowing) {
points *= SNAKE_AGENT_GLOWING_MULTIPLIER;
}
snake_update_score(game, state, points);
return false;
}
static bool snake_state_valid(GameData *game, SnakeState *state)
{
const Coords *head = snake_get_head_coords(state);
if (!game_coordinates_in_bounds(game, head->x, head->y)) {
snake_create_message(game, state->direction, "Ouch!", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head, true);
return false;
}
if (snake_self_consume(state)) {
snake_create_message(game, state->direction, "Tastes like chicken", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head,
true);
return false;
}
if (snake_agent_caught(game, state, head)) {
snake_create_message(game, state->direction, "ARGH they got me!", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head,
true);
return false;
}
return true;
}
/*
* Sets colour and attributes for entire snake except head.
*
* If colour is set to -1 a random colour is chosen for each body part.
*/
static void snake_set_body_attributes(Snake *snake, size_t length, int colour, int attributes)
{
for (size_t i = 1; i < length; ++i) {
Snake *body = &snake[i];
if (colour == -1) {
body->colour = game_util_random_colour();
} else {
body->colour = colour;
}
body->attributes = attributes;
}
}
static void snake_move_body(SnakeState *state)
{
for (size_t i = state->snake_length - 1; i > 0; --i) {
Coords *curr = &state->snake[i].coords;
Coords prev = state->snake[i - 1].coords;
curr->x = prev.x;
curr->y = prev.y;
}
}
static void snake_move_head(SnakeState *state)
{
Coords *head = snake_get_head_coords(state);
game_util_move_coords(state->direction, head);
}
static void snake_grow(SnakeState *state)
{
size_t index = state->snake_length;
if (index >= SNAKE_MAX_SNAKE_LENGTH) {
return;
}
state->snake[index].coords.x = -1;
state->snake[index].coords.y = -1;
state->snake[index].display_char = SNAKE_BODY_CHAR;
state->snake[index].colour = SNAKE_BODY_COLOUR;
state->snake[index].attributes = A_BOLD;
state->snake_length = index + 1;
}
static long int snake_check_food(const GameData *game, SnakeState *state)
{
Coords *food = &state->food;
const Coords *head = snake_get_head_coords(state);
if (!COORDINATES_OVERLAP(head->x, head->y, food->x, food->y)) {
return 0;
}
snake_grow(state);
game_random_coords(game, food);
return snake_get_move_points(state);
}
static long int snake_check_powerup(GameData *game, SnakeState *state)
{
Coords *powerup = &state->powerup;
const Coords *head = snake_get_head_coords(state);
if (!COORDINATES_OVERLAP(head->x, head->y, powerup->x, powerup->y)) {
return 0;
}
snake_create_message(game, state->direction, "AAAAA", A_BOLD, RED, 2, head, false);
TIME_S t = get_unix_time();
state->has_powerup = true;
state->powerup_timer = t;
powerup->x = -1;
powerup->y = -1;
return snake_get_move_points(state) * SNAKE_POWERUP_BONUS_MULTIPLIER;
}
/*
* Returns the first unoccupied index in agent array.
*/
static size_t snake_get_empty_agent_index(const NasaAgent *agents)
{
for (size_t i = 0; i < SNAKE_AGENT_MAX_LIST_SIZE; ++i) {
if (!agents[i].is_alive) {
return i;
}
}
fprintf(stderr, "Warning: Agent array is full. This should be impossible\n");
return 0;
}
static void snake_initialize_agent(SnakeState *state, const Coords *coords)
{
size_t idx = snake_get_empty_agent_index(state->agents);
if ((idx >= state->agent_list_size) && (idx + 1 <= SNAKE_AGENT_MAX_LIST_SIZE)) {
state->agent_list_size = idx + 1;
}
NasaAgent *agent = &state->agents[idx];
agent->coords = (Coords) {
coords->x,
coords->y
};
agent->is_alive = true;
agent->is_glowing = false;
agent->display_char = SNAKE_AGENT_NORMAL_CHAR;
agent->colour = SNAKE_AGENT_NORMAL_COLOUR;
agent->attributes = A_BOLD;
agent->last_time_moved = 0;
agent->speed = SNAKE_DEFAULT_AGENT_SPEED;
}
static void snake_dispatch_new_agent(const GameData *game, SnakeState *state)
{
Coords new_coords;
const Coords *head = snake_get_head_coords(state);
size_t tries = 0;
do {
if (tries++ >= 10) {
return;
}
game_random_coords(game, &new_coords);
} while (COORDINATES_OVERLAP(new_coords.x, new_coords.y, head->x, head->y)
|| snake_get_agent_at_coords(state, &new_coords) != NULL);
snake_initialize_agent(state, &new_coords);
}
static void snake_place_powerup(const GameData *game, SnakeState *state)
{
Coords *powerup = &state->powerup;
if (powerup->x != -1) {
return;
}
if (!timed_out(state->last_powerup_time, SNAKE_POWERUP_INTERVAL)) {
return;
}
game_random_coords(game, powerup);
}
static void snake_do_powerup(const GameData *game, SnakeState *state)
{
if (!state->has_powerup) {
snake_place_powerup(game, state);
return;
}
if (timed_out(state->powerup_timer, SNAKE_POWERUP_TIMER)) {
state->last_powerup_time = get_unix_time();
state->has_powerup = false;
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_BODY_COLOUR, A_BOLD);
}
}
static void snake_decay_points(GameData *game, SnakeState *state)
{
long int score = game_get_score(game);
long int decay = snake_get_move_points(state) / SNAKE_DECAY_POINTS_FRACTION;
if (score > decay && timed_out(state->last_time_points_decayed, SNAKE_DECAY_POINTS_INTERVAL)) {
game_update_score(game, -decay);
state->last_time_points_decayed = get_unix_time();
}
}
static void snake_do_points_update(GameData *game, SnakeState *state, long int points)
{
snake_update_score(game, state, points);
if (state->snake_length % SNAKE_LEVEL_UP_FOOD_LIMIT != 0) {
return;
}
game_increment_level(game);
size_t level = game_get_current_level(game);
if (level % SNAKE_LEVEL_SPEED_INTERVAL == 0 && state->snake_speed < SNAKE_MAX_SNAKE_SPEED) {
++state->snake_speed;
}
if (level % SNAKE_AGENT_LEVEL_SPEED_INTERVAL == 0) {
for (size_t i = 0; i < state->agent_list_size; ++i) {
NasaAgent *agent = &state->agents[i];
if (!agent->is_alive) {
continue;
}
if (agent->speed < SNAKE_MAX_AGENT_SPEED) {
++agent->speed;
}
if (agent->speed > SNAKE_AGENT_GLOWING_SPEED && !agent->is_glowing) {
agent->is_glowing = true;
agent->display_char = SNAKE_AGENT_GLOWING_CHAR;
agent->colour = SNAKE_AGENT_GLOWING_COLOUR;
snake_create_message(game, state->direction, "*glows*", A_BOLD, GREEN, 2, &agent->coords, false);
}
}
}
snake_dispatch_new_agent(game, state);
}
static void snake_game_over(SnakeState *state)
{
state->game_over = true;
state->has_powerup = false;
state->snake[0].colour = SNAKE_DEAD_BODY_COLOUR;
state->snake[0].attributes = A_BOLD | A_BLINK;
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_DEAD_BODY_COLOUR, A_BOLD | A_BLINK);
}
static void snake_move(GameData *game, SnakeState *state, TIME_MS cur_time)
{
const TIME_MS real_speed = GAME_UTIL_REAL_SPEED(state->direction, state->snake_speed);
if (!game_do_object_state_update(game, cur_time, state->snake_time_last_moved, real_speed)) {
return;
}
state->snake_time_last_moved = cur_time;
snake_update_direction(state);
snake_move_body(state);
snake_move_head(state);
if (!snake_state_valid(game, state)) {
snake_game_over(state);
game_set_status(game, GS_Finished);
return;
}
long int points = snake_check_food(game, state) + snake_check_powerup(game, state);
if (points > 0) {
snake_do_points_update(game, state, points);
}
}
/*
* Attempts to move every agent in list.
*
* If an agent is normal it will move in a random direction. If it's glowing it will
* move towards the snake.
*/
static void snake_agent_move(GameData *game, SnakeState *state, TIME_MS cur_time)
{
const Coords *head = snake_get_head_coords(state);
for (size_t i = 0; i < state->agent_list_size; ++i) {
NasaAgent *agent = &state->agents[i];
if (!agent->is_alive) {
continue;
}
Coords *coords = &agent->coords;
Coords new_coords = (Coords) {
coords->x,
coords->y
};
Direction dir = !agent->is_glowing ? game_util_random_direction()
: game_util_move_towards(coords, head, state->has_powerup);
const TIME_MS real_speed = agent->is_glowing ? GAME_UTIL_REAL_SPEED(dir, agent->speed) : agent->speed;
if (!game_do_object_state_update(game, cur_time, agent->last_time_moved, real_speed)) {
continue;
}
agent->last_time_moved = cur_time;
game_util_move_coords(dir, &new_coords);
if (!game_coordinates_in_bounds(game, new_coords.x, new_coords.y)) {
continue;
}
if (snake_coords_contain_body(state, &new_coords)) {
continue;
}
if (snake_get_agent_at_coords(state, &new_coords) != NULL) {
continue;
}
coords->x = new_coords.x;
coords->y = new_coords.y;
if (!state->has_powerup && COORDINATES_OVERLAP(head->x, head->y, new_coords.x, new_coords.y)) {
snake_game_over(state);
game_set_status(game, GS_Finished);
return;
}
}
}
static void snake_update_frames(const GameData *game, SnakeState *state, TIME_MS cur_time)
{
if (!game_do_object_state_update(game, cur_time, state->last_draw_update, SNAKE_FRAME_DRAW_SPEED)) {
return;
}
state->last_draw_update = cur_time;
if (state->has_powerup) {
const int time_left = SNAKE_POWERUP_TIMER - (get_unix_time() - state->powerup_timer);
if (time_left <= 5 && time_left % 2 == 0) {
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_BODY_COLOUR, A_BOLD);
} else {
snake_set_body_attributes(state->snake, state->snake_length, -1, A_BOLD);
}
}
}
static void snake_draw_self(WINDOW *win, const SnakeState *state)
{
for (size_t i = 0; i < state->snake_length; ++i) {
const Snake *body = &state->snake[i];
if (body->coords.x <= 0 || body->coords.y <= 0) {
continue;
}
wattron(win, body->attributes | COLOR_PAIR(body->colour));
mvwaddch(win, body->coords.y, body->coords.x, body->display_char);
wattroff(win, body->attributes | COLOR_PAIR(body->colour));
}
}
static void snake_draw_food(WINDOW *win, const SnakeState *state)
{
wattron(win, A_BOLD | COLOR_PAIR(SNAKE_FOOD_COLOUR));
mvwaddch(win, state->food.y, state->food.x, SNAKE_FOOD_CHAR);
wattroff(win, A_BOLD | COLOR_PAIR(SNAKE_FOOD_COLOUR));
}
static void snake_draw_agent(WINDOW *win, const SnakeState *state)
{
for (size_t i = 0; i < state->agent_list_size; ++i) {
NasaAgent agent = state->agents[i];
if (agent.is_alive) {
Coords coords = agent.coords;
wattron(win, agent.attributes | COLOR_PAIR(agent.colour));
mvwaddch(win, coords.y, coords.x, agent.display_char);
wattroff(win, agent.attributes | COLOR_PAIR(agent.colour));
}
}
}
static void snake_draw_powerup(WINDOW *win, const SnakeState *state)
{
Coords powerup = state->powerup;
if (powerup.x != -1) {
int colour = game_util_random_colour();
wattron(win, A_BOLD | COLOR_PAIR(colour));
mvwaddch(win, powerup.y, powerup.x, SNAKE_POWERUP_CHAR);
wattroff(win, A_BOLD | COLOR_PAIR(colour));
}
}
void snake_cb_update_game_state(GameData *game, void *cb_data)
{
if (!cb_data) {
return;
}
SnakeState *state = (SnakeState *)cb_data;
TIME_MS cur_time = get_time_millis();
snake_do_powerup(game, state);
snake_agent_move(game, state, cur_time);
snake_move(game, state, cur_time);
snake_decay_points(game, state);
if (!state->game_over) {
snake_update_frames(game, state, cur_time);
}
}
void snake_cb_render_window(GameData *game, WINDOW *win, void *cb_data)
{
if (!cb_data) {
return;
}
SnakeState *state = (SnakeState *)cb_data;
snake_draw_food(win, state);
snake_draw_powerup(win, state);
snake_draw_agent(win, state);
snake_draw_self(win, state);
}
void snake_cb_kill(GameData *game, void *cb_data)
{
if (!cb_data) {
return;
}
SnakeState *state = (SnakeState *)cb_data;
free(state->snake);
free(state->agents);
free(state);
game_set_cb_update_state(game, NULL, NULL);
game_set_cb_render_window(game, NULL, NULL);
game_set_cb_kill(game, NULL, NULL);
game_set_cb_on_pause(game, NULL, NULL);
}
void snake_cb_on_keypress(GameData *game, int key, void *cb_data)
{
if (!cb_data) {
return;
}
SnakeState *state = (SnakeState *)cb_data;
snake_set_key_press(state, key);
}
void snake_cb_pause(GameData *game, bool is_paused, void *cb_data)
{
UNUSED_VAR(game);
if (!cb_data) {
return;
}
SnakeState *state = (SnakeState *)cb_data;
TIME_S t = get_unix_time();
if (is_paused) {
state->pause_time = t;
} else {
state->powerup_timer += (t - state->pause_time);
state->last_powerup_time += (t - state->pause_time);
}
}
static void snake_initialize_snake_head(const GameData *game, Snake *snake)
{
int max_x;
int max_y;
game_max_x_y(game, &max_x, &max_y);
snake[0].coords.x = max_x / 2;
snake[0].coords.y = max_y / 2;
snake[0].colour = SNAKE_HEAD_COLOUR;
snake[0].attributes = A_BOLD;
}
int snake_initialize(GameData *game)
{
if (game_set_window_shape(game, GW_ShapeSquare) == -1) {
return -1;
}
SnakeState *state = calloc(1, sizeof(SnakeState));
if (state == NULL) {
return -1;
}
state->snake = calloc(1, SNAKE_MAX_SNAKE_LENGTH * sizeof(Snake));
if (state->snake == NULL) {
free(state);
return -1;
}
state->agents = calloc(1, SNAKE_AGENT_MAX_LIST_SIZE * sizeof(NasaAgent));
if (state->agents == NULL) {
free(state->snake);
free(state);
return -1;
}
snake_initialize_snake_head(game, state->snake);
state->snake_speed = SNAKE_DEFAULT_SNAKE_SPEED;
state->snake_length = 1;
state->direction = NORTH;
snake_set_head_char(state);
state->powerup.x = -1;
state->powerup.y = -1;
state->last_powerup_time = get_unix_time();
game_show_level(game, true);
game_show_score(game, true);
game_show_high_score(game, true);
game_increment_level(game);
game_set_update_interval(game, SNAKE_DEFAULT_UPDATE_INTERVAL);
game_random_coords(game, &state->food);
game_set_cb_update_state(game, snake_cb_update_game_state, state);
game_set_cb_render_window(game, snake_cb_render_window, state);
game_set_cb_on_keypress(game, snake_cb_on_keypress, state);
game_set_cb_kill(game, snake_cb_kill, state);
game_set_cb_on_pause(game, snake_cb_pause, state);
return 0;
}

30
src/game_snake.h Normal file
View File

@ -0,0 +1,30 @@
/* game_snake.h
*
*
* 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/>.
*
*/
#ifndef GAME_SNAKE
#define GAME_SNAKE
#include "game_base.h"
int snake_initialize(GameData *game);
#endif // GAME_SNAKE

194
src/game_util.c Normal file
View File

@ -0,0 +1,194 @@
/* game_util.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 <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include "game_util.h"
#include "toxic.h"
#include "windows.h"
Direction game_util_get_direction(int key)
{
switch (key) {
case KEY_UP: {
return NORTH;
}
case KEY_DOWN: {
return SOUTH;
}
case KEY_RIGHT: {
return EAST;
}
case KEY_LEFT: {
return WEST;
}
default: {
return INVALID_DIRECTION;
}
}
}
Direction game_util_move_towards(const Coords *coords_a, const Coords *coords_b, bool inverse)
{
const int x1 = coords_a->x;
const int y1 = coords_a->y;
const int x2 = coords_b->x;
const int y2 = coords_b->y;
const int x_diff = abs(x1 - x2);
const int y_diff = abs(y1 - y2);
if (inverse) {
if (x_diff > y_diff) {
return x2 >= x1 ? WEST : EAST;
} else {
return y2 >= y1 ? NORTH : SOUTH;
}
} else {
if (x_diff > y_diff) {
return x2 < x1 ? WEST : EAST;
} else {
return y2 < y1 ? NORTH : SOUTH;
}
}
}
Direction game_util_random_direction(void)
{
int r = rand() % 4;
switch (r) {
case 0:
return NORTH;
case 1:
return SOUTH;
case 2:
return EAST;
case 3:
return WEST;
default: // impossible
return NORTH;
}
}
void game_util_move_coords(Direction direction, Coords *coords)
{
switch (direction) {
case NORTH: {
--(coords->y);
break;
}
case SOUTH: {
++(coords->y);
break;
}
case EAST: {
++(coords->x);
break;
}
case WEST: {
--(coords->x);
break;
}
default: {
fprintf(stderr, "Warning: tried to move in an invalid direction\n");
return;
}
}
}
int game_util_random_colour(void)
{
int r = rand() % 6;
switch (r) {
case 0:
return GREEN;
case 1:
return CYAN;
case 2:
return RED;
case 3:
return BLUE;
case 4:
return YELLOW;
case 5:
return MAGENTA;
default: // impossible
return RED;
}
}
static size_t net_pack_u16(uint8_t *bytes, uint16_t v)
{
bytes[0] = (v >> 8) & 0xff;
bytes[1] = v & 0xff;
return sizeof(v);
}
static size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v)
{
uint8_t hi = bytes[0];
uint8_t lo = bytes[1];
*v = ((uint16_t)hi << 8) | lo;
return sizeof(*v);
}
size_t game_util_pack_u32(uint8_t *bytes, uint32_t v)
{
uint8_t *p = bytes;
p += net_pack_u16(p, (v >> 16) & 0xffff);
p += net_pack_u16(p, v & 0xffff);
return p - bytes;
}
size_t game_util_unpack_u32(const uint8_t *bytes, uint32_t *v)
{
const uint8_t *p = bytes;
uint16_t hi;
uint16_t lo;
p += net_unpack_u16(p, &hi);
p += net_unpack_u16(p, &lo);
*v = ((uint32_t)hi << 16) | lo;
return p - bytes;
}

103
src/game_util.h Normal file
View File

@ -0,0 +1,103 @@
/* game_util.h
*
*
* 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/>.
*
*/
#ifndef GAME_UTIL
#define GAME_UTIL
#include <stdbool.h>
typedef struct Coords {
int x;
int y;
} Coords;
// don't change these
typedef enum Direction {
NORTH = 0u,
SOUTH = 1,
EAST = 3,
WEST = 4,
INVALID_DIRECTION = 5
} Direction;
/*
* Use these for ms and second timestamps respectively so we don't accidentally interchange them
*/
typedef int64_t TIME_MS;
typedef time_t TIME_S;
/*
* Return true if coordinates x1, y1 overlap with x2, y2.
*/
#define COORDINATES_OVERLAP(x1, y1, x2, y2)(((x1) == (x2)) && ((y1) == (y2)))
/*
* Halves speed if moving north or south. This accounts for the fact that Y steps are twice as large as X steps.
*/
#define GAME_UTIL_REAL_SPEED(dir, speed)((((dir) == (NORTH)) || ((dir) == (SOUTH))) ? (MAX(1, ((speed) / 2))) : (speed))
/*
* Return true if dir is a valid Direction.
*/
#define GAME_UTIL_DIRECTION_VALID(dir)((dir) < (INVALID_DIRECTION))
/*
* Returns cardinal direction mapped to `key`.
*/
Direction game_util_get_direction(int key);
/*
* Returns the direction that will move `coords_a` closest to `coords_b`.
*
* If `inverse` is true, returns the opposite result.
*/
Direction game_util_move_towards(const Coords *coords_a, const Coords *coords_b, bool inverse);
/*
* Returns a random direction.
*/
Direction game_util_random_direction(void);
/*
* Moves `coords` one square towards `direction`.
*/
void game_util_move_coords(Direction direction, Coords *coords);
/*
* Returns a random colour.
*/
int game_util_random_colour(void);
/*
* Packs an unsigned 32 bit integer `v` into `bytes`.
*/
size_t game_util_pack_u32(uint8_t *bytes, uint32_t v);
/*
* Unpacks an unsigned 32 bit integer in `bytes` to `v`.
*/
size_t game_util_unpack_u32(const uint8_t *bytes, uint32_t *v);
#endif // GAME_UTIL

View File

@ -38,6 +38,10 @@
#include "toxic_strings.h" #include "toxic_strings.h"
#include "windows.h" #include "windows.h"
#ifdef GAMES
#include "game_base.h"
#endif
extern char *DATA_FILE; extern char *DATA_FILE;
extern ToxWindow *prompt; extern ToxWindow *prompt;
extern FriendsList Friends; extern FriendsList Friends;
@ -49,19 +53,19 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { 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; return;
} }
long int req = strtol(argv[1], NULL, 10); long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return; return;
} }
if (!FrndRequests.request[req].active) { 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; return;
} }
@ -69,10 +73,10 @@ 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); uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
if (err != TOX_ERR_FRIEND_ADD_OK) { 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; return;
} else { } 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); on_friend_added(m, friendnum, true);
} }
@ -141,7 +145,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
break; 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]) void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -149,7 +153,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { 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; return;
} }
@ -158,7 +162,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (argc > 1) { if (argc > 1) {
if (argv[2][0] != '\"') { 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; return;
} }
@ -192,7 +196,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
xx[2] = '\0'; xx[2] = '\0';
if (sscanf(xx, "%02x", &x) != 1) { 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; return;
} }
@ -200,7 +204,7 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
} }
if (friend_is_blocked(id_bin)) { 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; return;
} }
@ -216,7 +220,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
if (argc != 1 || strlen(argv[1]) < 3) { if (argc != 1 || strlen(argv[1]) < 3) {
avatar_unset(m); 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; return;
} }
@ -225,7 +229,7 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int len = strlen(path); int len = strlen(path);
if (len <= 0) { 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; return;
} }
@ -234,13 +238,13 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
get_file_name(filename, sizeof(filename), path); get_file_name(filename, sizeof(filename), path);
if (avatar_set(m, path, len) == -1) { 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.", "Failed to set avatar. Avatars must be in PNG format and may not exceed %d bytes.",
MAX_AVATAR_FILE_SIZE); MAX_AVATAR_FILE_SIZE);
return; 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]) void cmd_clear(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -258,7 +262,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc != 3) { 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; return;
} }
@ -269,14 +273,14 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
long int port = strtol(port_str, NULL, 10); long int port = strtol(port_str, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) { 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; return;
} }
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1]; 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) { 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; return;
} }
@ -286,15 +290,15 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
switch (err) { switch (err) {
case TOX_ERR_BOOTSTRAP_BAD_HOST: 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; break;
case TOX_ERR_BOOTSTRAP_BAD_PORT: 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; break;
case TOX_ERR_BOOTSTRAP_NULL: 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; break;
default: default:
@ -308,19 +312,19 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
UNUSED_VAR(m); UNUSED_VAR(m);
if (argc < 1) { 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; return;
} }
long int req = strtol(argv[1], NULL, 10); long int req = strtol(argv[1], NULL, 10);
if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) { if ((req == 0 && strcmp(argv[1], "0")) || req < 0 || req >= MAX_FRIEND_REQUESTS) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending friend request with that ID.");
return; return;
} }
if (!FrndRequests.request[req].active) { 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; return;
} }
@ -340,17 +344,86 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
--FrndRequests.num_requests; --FrndRequests.num_requests;
} }
#ifdef GAMES
void cmd_game(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
UNUSED_VAR(window);
if (argc < 1) {
game_list_print(self);
return;
}
GameType type = game_get_type(argv[1]);
if (type >= GT_Invalid) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown game.");
return;
}
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return;
}
bool force_small = false;
if (argc >= 2) {
force_small = strcasecmp(argv[2], "small") == 0;
if (!force_small) {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown argument.");
return;
}
}
uint32_t id = rand();
int ret = game_initialize(self, m, type, id, NULL, 0, force_small);
switch (ret) {
case 0: {
break;
}
case -1: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Window is too small. Try enlarging your window or re-running the command with the 'small' argument.");
return;
}
case -2: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize: Network error.");
return;
}
case -3: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Game is multiplayer only. Try the command again in the chat window of the contact you wish to play with.");
return;
}
default: {
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize (error %d)", ret);
return;
}
}
}
#endif // GAMES
void cmd_conference(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); UNUSED_VAR(window);
if (get_num_active_windows() >= MAX_WINDOWS_NUM) { 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; return;
} }
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Please specify conference type: text | audio");
return; return;
} }
@ -361,7 +434,7 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
} else if (!strcasecmp(argv[1], "text")) { } else if (!strcasecmp(argv[1], "text")) {
type = TOX_CONFERENCE_TYPE_TEXT; type = TOX_CONFERENCE_TYPE_TEXT;
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Valid conference types are: text | audio");
return; return;
} }
@ -373,7 +446,7 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
conferencenum = tox_conference_new(m, &err); conferencenum = tox_conference_new(m, &err);
if (err != TOX_ERR_CONFERENCE_NEW_OK) { if (err != TOX_ERR_CONFERENCE_NEW_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
return; return;
} }
} else if (type == TOX_CONFERENCE_TYPE_AV) { } else if (type == TOX_CONFERENCE_TYPE_AV) {
@ -381,18 +454,18 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL); conferencenum = toxav_add_av_groupchat(m, audio_conference_callback, NULL);
if (conferencenum == (uint32_t) -1) { if (conferencenum == (uint32_t) -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
return; return;
} }
#else #else
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
return; return;
#endif #endif
} }
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) { if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
tox_conference_delete(m, conferencenum, NULL); tox_conference_delete(m, conferencenum, NULL);
return; return;
} }
@ -401,13 +474,13 @@ void cmd_conference(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
if (type == TOX_CONFERENCE_TYPE_AV) { if (type == TOX_CONFERENCE_TYPE_AV) {
if (!init_conference_audio_input(m, conferencenum)) { if (!init_conference_audio_input(m, conferencenum)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
} }
} }
#endif #endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Conference [%d] created.", conferencenum); 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]) void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -424,7 +497,7 @@ 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."; 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; return;
} }
@ -432,7 +505,7 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (!strcmp(swch, "1") || !strcmp(swch, "on")) { if (!strcmp(swch, "1") || !strcmp(swch, "on")) {
msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log."; msg = log_enable(log) == 0 ? "Logging enabled." : "Warning: Failed to enable log.";
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; return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) { } else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->type == WINDOW_TYPE_CHAT) { if (self->type == WINDOW_TYPE_CHAT) {
@ -442,12 +515,12 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
log_disable(log); log_disable(log);
msg = "Logging disabled."; msg = "Logging disabled.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, msg);
return; return;
} }
msg = "Invalid option. Use \"/log on\" and \"/log off\" to toggle logging."; 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]) void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -461,11 +534,11 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
tox_self_get_address(m, (uint8_t *) bin_id); 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) { 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; 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 #ifdef QRCODE
@ -478,7 +551,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
tox_self_get_address(m, (uint8_t *) bin_id); 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) { 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; return;
} }
@ -491,7 +564,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
char *dir = malloc(data_file_len + 1); char *dir = malloc(data_file_len + 1);
if (dir == NULL) { if (dir == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory.");
return; return;
} }
@ -500,7 +573,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
#ifdef QRPNG #ifdef QRPNG
if (argc == 0) { 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); free(dir);
return; return;
} else if (!strcmp(argv[1], "txt")) { } else if (!strcmp(argv[1], "txt")) {
@ -510,7 +583,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
char *qr_path = malloc(qr_path_buf_size); char *qr_path = malloc(qr_path_buf_size);
if (qr_path == NULL) { if (qr_path == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
free(dir); free(dir);
return; return;
} }
@ -518,13 +591,13 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT); 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) { 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."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
free(dir); free(dir);
free(qr_path); free(qr_path);
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); 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); free(qr_path);
@ -534,7 +607,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
char *qr_path = malloc(qr_path_buf_size); char *qr_path = malloc(qr_path_buf_size);
if (qr_path == NULL) { if (qr_path == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code: Out of memory");
free(dir); free(dir);
return; return;
} }
@ -542,18 +615,18 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
snprintf(qr_path, qr_path_buf_size, "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG); 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) { 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."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
free(dir); free(dir);
free(qr_path); free(qr_path);
return; return;
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path); 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); free(qr_path);
} else { } 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); free(dir);
return; return;
} }
@ -569,7 +642,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { 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; return;
} }
@ -578,7 +651,7 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
size_t len = strlen(nick); size_t len = strlen(nick);
if (!valid_nick(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; return;
} }
@ -596,7 +669,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
UNUSED_VAR(window); UNUSED_VAR(window);
if (argc < 1) { 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; return;
} }
@ -611,7 +684,7 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
nospam = strtol(argv[1], NULL, 16); nospam = strtol(argv[1], NULL, 16);
if ((nospam == 0 && strcmp(argv[1], "0")) || nospam < 0) { 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; return;
} }
} }
@ -619,12 +692,12 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
uint32_t old_nospam = tox_self_get_nospam(m); uint32_t old_nospam = tox_self_get_nospam(m);
tox_self_set_nospam(m, (uint32_t) nospam); 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); cmd_myid(window, self, m, 0, NULL);
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, NULL, 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."); "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); old_nospam);
} }
@ -656,7 +729,7 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
UNUSED_VAR(argv); UNUSED_VAR(argv);
if (FrndRequests.num_requests == 0) { 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; return;
} }
@ -676,11 +749,11 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
strcat(id, d); strcat(id, d);
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); line_info_add(self, false, 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, "%s", FrndRequests.request[i].msg);
if (++count < FrndRequests.num_requests) { 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, "");
} }
} }
} }
@ -695,7 +768,7 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
if (argc < 1) { if (argc < 1) {
errmsg = "Require a status. Statuses are: online, busy and away."; 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; goto finish;
} }
@ -710,13 +783,13 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
status = TOX_USER_STATUS_BUSY; status = TOX_USER_STATUS_BUSY;
} else { } else {
errmsg = "Invalid status. Valid statuses are: online, busy and away."; 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; goto finish;
} }
tox_self_set_status(m, status); tox_self_set_status(m, status);
prompt_update_status(prompt, 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: finish:

View File

@ -62,4 +62,8 @@ void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* PYTHON */ #endif /* PYTHON */
#ifdef GAMES
void cmd_game(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GAMES */
#endif /* GLOBAL_COMMANDS_H */ #endif /* GLOBAL_COMMANDS_H */

View File

@ -181,6 +181,10 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /log <on> or <off> : Enable/disable logging\n"); wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /conference <type> : Create a conference 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"); wprintw(win, " /myid : Print your Tox ID\n");
#ifdef GAMES
wprintw(win, " /game : Play a game\n");
#endif /* GAMES */
#ifdef QRCODE #ifdef QRCODE
#ifdef QRPNG #ifdef QRPNG
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n"); wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
@ -264,6 +268,10 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /video : Toggle video in call\n"); wprintw(win, " /video : Toggle video in call\n");
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef GAMES
wprintw(win, " /game : Play a game with contact\n");
#endif /* GAMES */
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
@ -378,11 +386,11 @@ void help_onKey(ToxWindow *self, wint_t key)
case L'c': case L'c':
#ifdef VIDEO #ifdef VIDEO
help_init_window(self, 25, 80); help_init_window(self, 26, 80);
#elif AUDIO #elif AUDIO
help_init_window(self, 20, 80); help_init_window(self, 21, 80);
#else #else
help_init_window(self, 10, 80); help_init_window(self, 11, 80);
#endif #endif
self->help->type = HELP_CHAT; self->help->type = HELP_CHAT;
break; break;
@ -396,6 +404,9 @@ void help_onKey(ToxWindow *self, wint_t key)
#endif #endif
#ifdef PYTHON #ifdef PYTHON
height += 2; height += 2;
#endif
#ifdef GAMES
height += 1;
#endif #endif
help_init_window(self, height, 80); help_init_window(self, height, 80);
self->help->type = HELP_GLOBAL; self->help->type = HELP_GLOBAL;

View File

@ -339,7 +339,7 @@ static void line_info_init_line(ToxWindow *self, struct line_info *line)
* Returns the id of the new line. * Returns the id of the new line.
* Returns -1 on failure. * 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, ...) uint8_t bold, uint8_t colour, const char *msg, ...)
{ {
if (!self) { if (!self) {
@ -374,33 +374,33 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
/* fallthrough */ /* fallthrough */
case OUT_ACTION: case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2; len += strlen(user_settings->line_normal) + 2; // two spaces
break; break;
case IN_MSG: case IN_MSG:
/* fallthrough */ /* fallthrough */
case OUT_MSG: case OUT_MSG:
len += strlen(user_settings->line_normal) + 3; len += strlen(user_settings->line_normal) + 3; // two spaces and a ':' char
break; break;
case CONNECTION: case CONNECTION:
len += strlen(user_settings->line_join) + 2; len += strlen(user_settings->line_join) + 2; // two spaces
break; break;
case DISCONNECTION: case DISCONNECTION:
len += strlen(user_settings->line_quit) + 2; len += strlen(user_settings->line_quit) + 2; // two spaces
break; break;
case SYS_MSG: case SYS_MSG:
break; break;
case NAME_CHANGE: case NAME_CHANGE:
len += strlen(user_settings->line_alert) + 1; len += strlen(user_settings->line_alert) + 2; // two spaces
break; break;
case PROMPT: case PROMPT:
len += 2; len += 2; // '$' char and a space
break; break;
default: default:
@ -416,8 +416,8 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
len += msg_len; len += msg_len;
} }
if (timestr) { if (show_timestamp) {
snprintf(new_line->timestr, sizeof(new_line->timestr), "%s", timestr); get_time_str(new_line->timestr, sizeof(new_line->timestr));
len += strlen(new_line->timestr) + 1; len += strlen(new_line->timestr) + 1;
} }

View File

@ -31,7 +31,7 @@
#define MAX_LINE_INFO_QUEUE 1024 #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, SYS_MSG,
IN_MSG, IN_MSG,
OUT_MSG, OUT_MSG,
@ -81,7 +81,7 @@ struct history {
* Returns the id of the new line. * Returns the id of the new line.
* Returns -1 on failure. * 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, ...); uint8_t bold, uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */ /* Prints a section of history starting at line_start */

View File

@ -302,11 +302,11 @@ int load_chat_history(ToxWindow *self, struct chatlog *log)
} }
while (line != NULL && count--) { while (line != NULL && count--) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", line); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", line);
line = strtok_r(NULL, "\n", &tmp); line = strtok_r(NULL, "\n", &tmp);
} }
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, YELLOW, "---"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, YELLOW, "---");
free(buf); free(buf);

View File

@ -96,11 +96,16 @@ struct tm *get_time(void)
return timeinfo; return timeinfo;
} }
/*Puts the current time in buf in the format of [HH:mm:ss] */ /* Puts the current time in buf in the format of specified by the config */
void get_time_str(char *buf, int bufsize) void get_time_str(char *buf, size_t bufsize)
{ {
if (buf == NULL || bufsize == 0) {
return;
}
*buf = 0;
if (user_settings->timestamps == TIMESTAMPS_OFF) { if (user_settings->timestamps == TIMESTAMPS_OFF) {
buf[0] = '\0';
return; return;
} }

View File

@ -81,8 +81,8 @@ int bin_pubkey_to_string(const uint8_t *bin_pubkey, size_t bin_pubkey_size, char
/* get the current unix time (not thread safe) */ /* get the current unix time (not thread safe) */
time_t get_unix_time(void); time_t get_unix_time(void);
/* Puts the current time in buf in the format of [HH:mm:ss] (not thread safe) */ /* Puts the current time in buf in the format of specified by the config */
void get_time_str(char *buf, int bufsize); void get_time_str(char *buf, size_t bufsize);
/* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */ /* Converts seconds to string in format HH:mm:ss; truncates hours and minutes when necessary */
void get_elapsed_time_str(char *buf, int bufsize, time_t secs); void get_elapsed_time_str(char *buf, int bufsize, time_t secs);

View File

@ -80,7 +80,7 @@ static int lookup_error(ToxWindow *self, const char *errmsg, ...)
va_end(args); va_end(args);
pthread_mutex_lock(&Winthread.lock); 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); pthread_mutex_unlock(&Winthread.lock);
return -1; return -1;
@ -365,12 +365,12 @@ on_exit:
void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message) void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr, const char *message)
{ {
if (t_data.disabled) { 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; return;
} }
if (t_data.busy) { 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; return;
} }
@ -382,20 +382,20 @@ void name_lookup(ToxWindow *self, Tox *m, const char *id_bin, const char *addr,
t_data.busy = true; t_data.busy = true;
if (pthread_attr_init(&lookup_thread.attr) != 0) { 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"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, "Error: lookup thread attr failed to init");
clear_thread_data(); clear_thread_data();
return; return;
} }
if (pthread_attr_setdetachstate(&lookup_thread.attr, PTHREAD_CREATE_DETACHED) != 0) { 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); pthread_attr_destroy(&lookup_thread.attr);
clear_thread_data(); clear_thread_data();
return; return;
} }
if (pthread_create(&lookup_thread.tid, &lookup_thread.attr, lookup_thread_func, NULL) != 0) { 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); pthread_attr_destroy(&lookup_thread.attr);
clear_thread_data(); clear_thread_data();
return; return;

View File

@ -707,6 +707,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi
#else #else
if (id == -1) { if (id == -1) {
control_unlock();
return -1; return -1;
} }

View File

@ -60,6 +60,9 @@ static const char *glob_cmd_list[] = {
"/decline", "/decline",
"/exit", "/exit",
"/conference", "/conference",
#ifdef GAME
"/game",
#endif
"/help", "/help",
"/log", "/log",
"/myid", "/myid",
@ -144,7 +147,7 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err); tox_self_set_status_message(m, (const uint8_t *) statusmsg, len, &err);
if (err != TOX_ERR_SET_INFO_OK) { 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);
} }
} }
@ -284,9 +287,12 @@ static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
char line[MAX_STR_SIZE]; char line[MAX_STR_SIZE];
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) { if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Failed to parse message.");
} else { } else {
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); if (strcmp(line, "/clear") != 0) {
line_info_add(self, false, NULL, NULL, PROMPT, 0, 0, "%s", line);
}
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE); execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
} }
} }
@ -321,8 +327,6 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
mvwprintw(ctx->linewin, 0, 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); curs_set(1);
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
@ -341,51 +345,51 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
switch (status) { switch (status) {
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
status_text = "Online"; status_text = "Online";
colour = GREEN_BLUE; colour = STATUS_ONLINE;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
status_text = "Away"; status_text = "Away";
colour = YELLOW_BLUE; colour = STATUS_AWAY;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
status_text = "Busy"; status_text = "Busy";
colour = RED_BLUE; colour = STATUS_BUSY;
break; break;
} }
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " ["); wprintw(statusbar->topline, " [");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); wattron(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
wprintw(statusbar->topline, "%s", status_text); wprintw(statusbar->topline, "%s", status_text);
wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour)); wattroff(statusbar->topline, A_BOLD | COLOR_PAIR(colour));
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "]"); wprintw(statusbar->topline, "]");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick); wprintw(statusbar->topline, " %s", statusbar->nick);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} else { } else {
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " ["); wprintw(statusbar->topline, " [");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wprintw(statusbar->topline, "Offline"); wprintw(statusbar->topline, "Offline");
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, "]"); wprintw(statusbar->topline, "]");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
wprintw(statusbar->topline, " %s", statusbar->nick); wprintw(statusbar->topline, " %s", statusbar->nick);
@ -397,7 +401,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
getyx(statusbar->topline, s_y, s_x); getyx(statusbar->topline, s_y, s_x);
mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x); mvwhline(statusbar->topline, s_y, s_x, ' ', x2 - s_x);
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
/* Reset statusbar->statusmsg on window resize */ /* Reset statusbar->statusmsg on window resize */
if (x2 != self->x) { if (x2 != self->x) {
@ -426,13 +430,13 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
} }
if (statusbar->statusmsg[0]) { if (statusbar->statusmsg[0]) {
wattron(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wprintw(statusbar->topline, " | "); wprintw(statusbar->topline, " | ");
wattroff(statusbar->topline, COLOR_PAIR(CYAN_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_ACCENT));
wattron(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattron(statusbar->topline, COLOR_PAIR(BAR_TEXT));
wprintw(statusbar->topline, "%s", statusbar->statusmsg); wprintw(statusbar->topline, "%s", statusbar->statusmsg);
wattroff(statusbar->topline, COLOR_PAIR(WHITE_BLUE)); wattroff(statusbar->topline, COLOR_PAIR(BAR_TEXT));
} }
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -466,8 +470,6 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
} }
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
const char *msg; const char *msg;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) { if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
@ -476,7 +478,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) { if (connection_status != TOX_CONNECTION_NONE && Friends.list[friendnum].connection_status == TOX_CONNECTION_NONE) {
msg = "has come online"; msg = "has come online";
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); line_info_add(self, true, nick, NULL, CONNECTION, 0, GREEN, msg);
write_to_log(msg, nick, ctx->log, true); write_to_log(msg, nick, ctx->log, true);
if (self->active_box != -1) { if (self->active_box != -1) {
@ -488,7 +490,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
} }
} else if (connection_status == TOX_CONNECTION_NONE) { } else if (connection_status == TOX_CONNECTION_NONE) {
msg = "has gone offline"; msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); line_info_add(self, true, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true); write_to_log(msg, nick, ctx->log, true);
if (self->active_box != -1) { if (self->active_box != -1) {
@ -508,21 +510,18 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE]; line_info_add(self, true, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, NULL, NULL, SYS_MSG, 0, 0, "Friend request with the message '%s'", data);
write_to_log("Friend request with the message '%s'", "", ctx->log, true); write_to_log("Friend request with the message '%s'", "", ctx->log, true);
int n = add_friend_request(key, data); int n = add_friend_request(key, data);
if (n == -1) { if (n == -1) {
const char *errmsg = "Friend request queue is full. Discarding request."; 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; 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); sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
} }
@ -572,18 +571,18 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
static void print_welcome_msg(ToxWindow *self) static void print_welcome_msg(ToxWindow *self)
{ {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ "); line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " _____ _____ _____ ____ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|"); line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " |_ _/ _ \\ \\/ /_ _/ ___|");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | "); line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " | || | | \\ / | | | ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ "); line_info_add(self, false, NULL, NULL, SYS_MSG, 1, BLUE, " | || |_| / \\ | | |___ ");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER); line_info_add(self, false, 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, 0, 0, "");
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messaging client."; 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."; 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, false, 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, 0, 0, "");
} }
static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name) static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name)
@ -594,13 +593,13 @@ static void prompt_init_log(ToxWindow *self, Tox *m, const char *self_name)
tox_self_get_address(m, (uint8_t *) myid); tox_self_get_address(m, (uint8_t *) myid);
if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) { if (log_init(ctx->log, self->name, myid, NULL, LOG_TYPE_PROMPT) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
return; return;
} }
if (user_settings->autolog == AUTOLOG_ON) { if (user_settings->autolog == AUTOLOG_ON) {
if (log_enable(ctx->log) == -1) { if (log_enable(ctx->log) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log."); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Warning: Failed to enable log.");
} }
} }
} }

View File

@ -73,6 +73,11 @@ static struct ui_strings {
const char *mplex_away; const char *mplex_away;
const char *mplex_away_note; 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_strings = {
"ui", "ui",
"timestamps", "timestamps",
@ -100,6 +105,10 @@ static struct ui_strings {
"line_normal", "line_normal",
"mplex_away", "mplex_away",
"mplex_away_note", "mplex_away_note",
"color_bar_bg",
"color_bar_fg",
"color_bar_accent",
"color_bar_notify",
}; };
static void ui_defaults(struct user_settings *settings) static void ui_defaults(struct user_settings *settings)
@ -345,6 +354,22 @@ int settings_load(struct user_settings *s, const char *patharg)
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", str); 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)) { if (config_setting_lookup_string(setting, ui_strings.log_timestamp_format, &str)) {
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str); snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", str);
} }

View File

@ -68,6 +68,11 @@ struct user_settings {
char autorun_path[PATH_MAX]; char autorun_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_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_next_tab;
int key_prev_tab; int key_prev_tab;
int key_scroll_line_up; int key_scroll_line_up;

View File

@ -256,10 +256,14 @@ static void init_term(void)
keypad(stdscr, 1); keypad(stdscr, 1);
noecho(); noecho();
nonl(); nonl();
timeout(50); timeout(30);
if (has_colors()) { if (has_colors()) {
short bg_color = COLOR_BLACK; 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(); start_color();
if (user_settings->colour_theme == NATIVE_COLS) { if (user_settings->colour_theme == NATIVE_COLS) {
@ -268,6 +272,102 @@ static void init_term(void)
} }
} }
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(WHITE, COLOR_WHITE, COLOR_BLACK);
init_pair(GREEN, COLOR_GREEN, bg_color); init_pair(GREEN, COLOR_GREEN, bg_color);
init_pair(CYAN, COLOR_CYAN, bg_color); init_pair(CYAN, COLOR_CYAN, bg_color);
@ -276,15 +376,18 @@ static void init_term(void)
init_pair(YELLOW, COLOR_YELLOW, bg_color); init_pair(YELLOW, COLOR_YELLOW, bg_color);
init_pair(MAGENTA, COLOR_MAGENTA, bg_color); init_pair(MAGENTA, COLOR_MAGENTA, bg_color);
init_pair(BLACK, COLOR_BLACK, COLOR_BLACK); 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(WHITE_BLUE, COLOR_WHITE, COLOR_BLUE); init_pair(WHITE_BLUE, COLOR_WHITE, COLOR_BLUE);
init_pair(CYAN_BLUE, COLOR_CYAN, COLOR_BLUE); init_pair(BLACK_WHITE, COLOR_BLACK, COLOR_WHITE);
init_pair(GREEN_BLUE, COLOR_GREEN, COLOR_BLUE); init_pair(WHITE_BLACK, COLOR_WHITE, COLOR_BLACK);
init_pair(PURPLE_BLUE, COLOR_MAGENTA, COLOR_BLUE); init_pair(WHITE_GREEN, COLOR_WHITE, COLOR_GREEN);
init_pair(BLACK_BLUE, COLOR_BLACK, COLOR_BLUE); init_pair(BLACK_BG, COLOR_BLACK, bar_bg_color);
init_pair(YELLOW_BLUE, COLOR_YELLOW, COLOR_BLUE); init_pair(PURPLE_BG, COLOR_MAGENTA, bar_bg_color);
init_pair(RED_BLUE, COLOR_RED, COLOR_BLUE); 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(); refresh();
@ -703,6 +806,7 @@ static void init_tox_callbacks(Tox *m)
tox_callback_file_chunk_request(m, on_file_chunk_request); tox_callback_file_chunk_request(m, on_file_chunk_request);
tox_callback_file_recv_control(m, on_file_recv_control); tox_callback_file_recv_control(m, on_file_recv_control);
tox_callback_file_recv_chunk(m, on_file_recv_chunk); tox_callback_file_recv_chunk(m, on_file_recv_chunk);
tox_callback_friend_lossless_packet(m, on_lossless_custom_packet);
} }
static void init_tox_options(struct Tox_Options *tox_opts) static void init_tox_options(struct Tox_Options *tox_opts)
@ -1360,6 +1464,8 @@ int main(int argc, char **argv)
/* Make sure all written files are read/writeable only by the current user. */ /* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
srand(time(NULL)); // We use rand() for trivial/non-security related things
parse_args(argc, argv); parse_args(argc, argv);
/* Use the -b flag to enable stderr */ /* Use the -b flag to enable stderr */
@ -1512,7 +1618,7 @@ int main(int argc, char **argv)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (store_data(m, DATA_FILE) != 0) { if (store_data(m, DATA_FILE) != 0) {
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); line_info_add(prompt, false, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
} }
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);

View File

@ -48,6 +48,7 @@
#define TOXIC_MAX_NAME_LENGTH 32 /* Must be <= TOX_MAX_NAME_LENGTH */ #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 KEY_IDENT_DIGITS 3 /* number of hex digits to display for the pub-key based identifier */
#define TIME_STR_SIZE 32 #define TIME_STR_SIZE 32
#define COLOR_STR_SIZE 10 /* should fit every color option */
#ifndef MAX_PORT_RANGE #ifndef MAX_PORT_RANGE
#define MAX_PORT_RANGE 65535 #define MAX_PORT_RANGE 65535
@ -134,5 +135,7 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
const uint8_t *filename, size_t filename_length, void *userdata); const uint8_t *filename, size_t filename_length, void *userdata);
void on_friend_typing(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata); void on_friend_typing(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_friend_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata); void on_friend_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
void on_lossless_custom_packet(Tox *m, uint32_t friendnumber, const uint8_t *data, size_t length, void *userdata);
#endif /* TOXIC_H */ #endif /* TOXIC_H */

View File

@ -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) 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) ToxAV *init_video(ToxWindow *self, Tox *tox)
@ -66,13 +66,13 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
CallControl.video_frame_duration = 10; CallControl.video_frame_duration = 10;
if (!CallControl.av) { 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; return NULL;
} }
if (init_video_devices(CallControl.av) == vde_InternalError) { 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; return NULL;
} }
@ -110,17 +110,17 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y,
/* Drop frame if video sending is disabled */ /* Drop frame if video sending is disabled */
if (this_call->video_bit_rate == 0 || this_call->status != cs_Active || this_call->vin_idx == -1) { if (this_call->video_bit_rate == 0 || this_call->status != cs_Active || this_call->vin_idx == -1) {
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped."); line_info_add(CallControl.prompt, false, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped.");
return; return;
} }
if (toxav_video_send_frame(CallControl.av, friend_number, width, height, y, u, v, &error) == false) { 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) { 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) { } 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");
} }
} }
} }
@ -138,22 +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) int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
{ {
if (!self || !av) { if (!self || !av) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video transmission");
return -1; return -1;
} }
if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) { if (open_primary_video_device(vdt_input, &call->vin_idx, &call->video_width, &call->video_height) != vde_None) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!");
return -1; return -1;
} }
if (register_video_device_callback(self->num, call->vin_idx, read_video_device_callback, &self->num) != vde_None) { 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; return -1;
} }
if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) { if (!toxav_video_set_bit_rate(CallControl.av, self->num, call->video_bit_rate, NULL)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
return -1; return -1;
} }
@ -347,11 +347,11 @@ void cmd_res(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
if (argc == 0) { if (argc == 0) {
if (call->status == cs_Active && call->vin_idx != -1) { if (call->status == cs_Active && call->vin_idx != -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Resolution of current call: %u x %u", "Resolution of current call: %u x %u",
call->video_width, call->video_height); call->video_width, call->video_height);
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
"Initial resolution for video calls: %u x %u", "Initial resolution for video calls: %u x %u",
CallControl.default_video_width, CallControl.default_video_height); CallControl.default_video_width, CallControl.default_video_height);
} }
@ -411,7 +411,7 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c
} }
else { 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; return;
} }
@ -447,7 +447,7 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
} }
else { 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; return;
} }

View File

@ -852,7 +852,7 @@ void print_video_devices(ToxWindow *self, VideoDeviceType type)
int i; int i;
for (i = 0; i < size[type]; ++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; return;

View File

@ -37,6 +37,10 @@
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#ifdef GAMES
#include "game_base.h"
#endif
extern char *DATA_FILE; extern char *DATA_FILE;
extern struct Winthread Winthread; extern struct Winthread Winthread;
@ -322,6 +326,53 @@ void on_friend_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, voi
} }
} }
} }
void on_lossless_custom_packet(Tox *m, uint32_t friendnumber, const uint8_t *data, size_t length, void *userdata)
{
UNUSED_VAR(userdata);
if (length == 0 || data == NULL) {
return;
}
uint8_t type = data[0];
switch (type) {
#ifdef GAMES
case CUSTOM_PACKET_GAME_INVITE: {
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *window = windows[i];
if (window != NULL && window->onGameInvite != NULL) {
window->onGameInvite(window, m, friendnumber, data + 1, length - 1);
}
}
break;
}
case CUSTOM_PACKET_GAME_DATA: {
for (size_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *window = windows[i];
if (window != NULL && window->onGameData != NULL) {
window->onGameData(window, m, friendnumber, data + 1, length - 1);
}
}
break;
}
#endif // GAMES
default: {
fprintf(stderr, "Got unknown custom packet of type: %u\n", type);
return;
}
}
}
/* CALLBACKS END */ /* CALLBACKS END */
int add_window(Tox *m, ToxWindow *w) int add_window(Tox *m, ToxWindow *w)
@ -367,7 +418,9 @@ void set_active_window_index(uint8_t index)
} }
} }
/* Shows next window when tab or back-tab is pressed */ /* Displays the next window if `ch` is equal to the next window key binding.
* Otherwise displays the previous window.
*/
void set_next_window(int ch) void set_next_window(int ch)
{ {
if (ch == user_settings->key_next_tab) { if (ch == user_settings->key_next_tab) {
@ -394,8 +447,6 @@ void set_next_window(int ch)
/* Deletes window w and cleans up */ /* Deletes window w and cleans up */
void del_window(ToxWindow *w) void del_window(ToxWindow *w)
{ {
set_active_window_index(0);
uint8_t idx = w->index; uint8_t idx = w->index;
delwin(w->window_bar); delwin(w->window_bar);
delwin(w->window); delwin(w->window);
@ -404,7 +455,11 @@ void del_window(ToxWindow *w)
clear(); clear();
refresh(); refresh();
--num_active_windows;
if (num_active_windows > 0) {
set_next_window(-1);
--num_active_windows;
}
} }
ToxWindow *init_windows(Tox *m) ToxWindow *init_windows(Tox *m)
@ -436,6 +491,9 @@ void on_window_resize(void)
refresh(); refresh();
clear(); clear();
int x2;
int y2;
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *w = windows[i]; ToxWindow *w = windows[i];
@ -451,6 +509,23 @@ void on_window_resize(void)
continue; continue;
} }
#ifdef GAMES
if (w->type == WINDOW_TYPE_GAME) {
delwin(w->window_bar);
delwin(w->window);
delwin(w->game->window);
w->window = newwin(LINES, COLS, 0, 0);
getmaxyx(w->window, y2, x2);
w->window_bar = subwin(w->window, WINDOW_BAR_HEIGHT, COLS, LINES - 2, 0);
w->game->window = subwin(w->window, y2 - CHATBOX_HEIGHT - WINDOW_BAR_HEIGHT, x2, 0, 0);
continue;
}
#endif // GAMES
if (w->help->active) { if (w->help->active) {
wclear(w->help->win); wclear(w->help->win);
} }
@ -469,8 +544,6 @@ void on_window_resize(void)
w->window = newwin(LINES, COLS, 0, 0); w->window = newwin(LINES, COLS, 0, 0);
int x2;
int y2;
getmaxyx(w->window, y2, x2); getmaxyx(w->window, y2, x2);
if (y2 <= 0 || x2 <= 0) { if (y2 <= 0 || x2 <= 0) {
@ -519,21 +592,21 @@ static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window)
WINDOW_TYPE type = toxwin->type; WINDOW_TYPE type = toxwin->type;
if (active_window) { if (active_window) {
wattron(win, A_BOLD | COLOR_PAIR(CYAN_BLUE)); wattron(win, A_BOLD | COLOR_PAIR(BAR_ACCENT));
wprintw(win, " ["); wprintw(win, " [");
wattroff(win, COLOR_PAIR(CYAN_BLUE)); wattroff(win, COLOR_PAIR(BAR_ACCENT));
wattron(win, COLOR_PAIR(WHITE_BLUE)); wattron(win, COLOR_PAIR(BAR_TEXT));
} else { } else {
if (has_alert) { if (has_alert) {
wattron(win, COLOR_PAIR(CYAN_BLUE)); wattron(win, COLOR_PAIR(BAR_ACCENT));
wprintw(win, " ["); wprintw(win, " [");
wattroff(win, COLOR_PAIR(CYAN_BLUE)); wattroff(win, COLOR_PAIR(BAR_ACCENT));
wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert)); wattron(win, A_BOLD | COLOR_PAIR(toxwin->alert));
} else { } else {
wattron(win, COLOR_PAIR(CYAN_BLUE)); wattron(win, COLOR_PAIR(BAR_ACCENT));
wprintw(win, " ["); wprintw(win, " [");
wattroff(win, COLOR_PAIR(CYAN_BLUE)); wattroff(win, COLOR_PAIR(BAR_ACCENT));
wattron(win, COLOR_PAIR(WHITE_BLUE)); wattron(win, COLOR_PAIR(BAR_TEXT));
} }
} }
@ -548,21 +621,21 @@ static void draw_window_tab(WINDOW *win, ToxWindow *toxwin, bool active_window)
} }
if (active_window) { if (active_window) {
wattroff(win, COLOR_PAIR(WHITE_BLUE)); wattroff(win, COLOR_PAIR(BAR_TEXT));
wattron(win, COLOR_PAIR(CYAN_BLUE)); wattron(win, COLOR_PAIR(BAR_ACCENT));
wprintw(win, "]"); wprintw(win, "]");
wattroff(win, A_BOLD | COLOR_PAIR(CYAN_BLUE)); wattroff(win, A_BOLD | COLOR_PAIR(BAR_ACCENT));
} else { } else {
if (has_alert) { if (has_alert) {
wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert)); wattroff(win, A_BOLD | COLOR_PAIR(toxwin->alert));
wattron(win, COLOR_PAIR(CYAN_BLUE)); wattron(win, COLOR_PAIR(BAR_ACCENT));
wprintw(win, "]"); wprintw(win, "]");
wattroff(win, COLOR_PAIR(CYAN_BLUE)); wattroff(win, COLOR_PAIR(BAR_ACCENT));
} else { } else {
wattroff(win, COLOR_PAIR(WHITE_BLUE)); wattroff(win, COLOR_PAIR(BAR_TEXT));
wattron(win, COLOR_PAIR(CYAN_BLUE)); wattron(win, COLOR_PAIR(BAR_ACCENT));
wprintw(win, "]"); wprintw(win, "]");
wattroff(win, COLOR_PAIR(CYAN_BLUE)); wattroff(win, COLOR_PAIR(BAR_ACCENT));
} }
} }
} }
@ -573,13 +646,13 @@ void draw_window_bar(ToxWindow *self)
wclear(win); wclear(win);
if (self->scroll_pause) { if (self->scroll_pause) {
wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE)); wattron(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY));
wprintw(win, "^"); wprintw(win, "^");
wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(YELLOW_BLUE)); wattroff(win, A_BLINK | A_BOLD | COLOR_PAIR(BAR_NOTIFY));
} else { } else {
wattron(win, COLOR_PAIR(WHITE_BLUE)); wattron(win, COLOR_PAIR(BAR_TEXT));
wprintw(win, " "); wprintw(win, " ");
wattroff(win, COLOR_PAIR(WHITE_BLUE)); wattroff(win, COLOR_PAIR(BAR_TEXT));
} }
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
@ -597,9 +670,9 @@ void draw_window_bar(ToxWindow *self)
getyx(win, cur_y, cur_x); getyx(win, cur_y, cur_x);
wattron(win, COLOR_PAIR(WHITE_BLUE)); wattron(win, COLOR_PAIR(BAR_TEXT));
mvwhline(win, 0, cur_x, ' ', COLS - cur_x); mvwhline(win, 0, cur_x, ' ', COLS - cur_x);
wattroff(win, COLOR_PAIR(WHITE_BLUE)); wattroff(win, COLOR_PAIR(BAR_TEXT));
} }
/* /*
@ -706,6 +779,27 @@ void draw_active_window(Tox *m)
a->onDraw(a, m); a->onDraw(a, m);
wrefresh(a->window); wrefresh(a->window);
#ifdef GAMES
if (a->type == WINDOW_TYPE_GAME) {
int ch = getch();
if (ch == ERR) {
return;
}
if (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab) {
set_next_window(ch);
ch = KEY_F(2);
}
a->onKey(a, m, ch, false);
return;
}
#endif // GAMES
wint_t ch = 0; wint_t ch = 0;
int printable = get_current_char(&ch); int printable = get_current_char(&ch);
@ -792,15 +886,24 @@ int get_num_active_windows(void)
void kill_all_windows(Tox *m) void kill_all_windows(Tox *m)
{ {
for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) { ToxWindow *w = windows[i];
if (w == NULL) {
continue; continue;
} }
if (windows[i]->type == WINDOW_TYPE_CHAT) { if (w->type == WINDOW_TYPE_CHAT) {
kill_chat_window(windows[i], m); kill_chat_window(w, m);
} else if (windows[i]->type == WINDOW_TYPE_CONFERENCE) { } else if (w->type == WINDOW_TYPE_CONFERENCE) {
free_conference(windows[i], windows[i]->num); free_conference(w, w->num);
} }
#ifdef GAMES
else if (w->type == WINDOW_TYPE_GAME) {
game_kill(w);
}
#endif // GAMES
} }
/* TODO: use enum instead of magic indices */ /* TODO: use enum instead of magic indices */

View File

@ -43,6 +43,13 @@
#define TOP_BAR_HEIGHT 1 #define TOP_BAR_HEIGHT 1
#define WINDOW_BAR_HEIGHT 1 #define WINDOW_BAR_HEIGHT 1
typedef enum CustomPacket {
CUSTOM_PACKET_GAME_INVITE = 160,
CUSTOM_PACKET_GAME_DATA = 161,
} CustomPacket;
/* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */ /* ncurses colour pairs as FOREGROUND_BACKGROUND. No background defaults to black. */
typedef enum { typedef enum {
WHITE, WHITE,
@ -53,23 +60,26 @@ typedef enum {
YELLOW, YELLOW,
MAGENTA, MAGENTA,
BLACK, BLACK,
BLUE_BLACK,
BLACK_WHITE, BLACK_WHITE,
WHITE_BLACK,
WHITE_BLUE, WHITE_BLUE,
GREEN_BLUE, WHITE_GREEN,
CYAN_BLUE, BAR_TEXT,
PURPLE_BLUE, STATUS_ONLINE,
BLACK_BLUE, BAR_ACCENT,
RED_BLUE, PURPLE_BG,
YELLOW_BLUE, BLACK_BG,
STATUS_BUSY,
STATUS_AWAY,
BAR_NOTIFY,
} C_COLOURS; } C_COLOURS;
/* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */ /* tab alert types: lower types take priority (this relies on the order of C_COLOURS) */
typedef enum { typedef enum {
WINDOW_ALERT_NONE = 0, WINDOW_ALERT_NONE = 0,
WINDOW_ALERT_0 = GREEN_BLUE, WINDOW_ALERT_0 = STATUS_ONLINE,
WINDOW_ALERT_1 = CYAN_BLUE, WINDOW_ALERT_1 = BAR_ACCENT,
WINDOW_ALERT_2 = PURPLE_BLUE, WINDOW_ALERT_2 = PURPLE_BG,
} WINDOW_ALERTS; } WINDOW_ALERTS;
typedef enum { typedef enum {
@ -77,6 +87,10 @@ typedef enum {
WINDOW_TYPE_CHAT, WINDOW_TYPE_CHAT,
WINDOW_TYPE_CONFERENCE, WINDOW_TYPE_CONFERENCE,
WINDOW_TYPE_FRIEND_LIST, WINDOW_TYPE_FRIEND_LIST,
#ifdef GAMES
WINDOW_TYPE_GAME,
#endif
} WINDOW_TYPE; } WINDOW_TYPE;
/* Fixes text color problem on some terminals. /* Fixes text color problem on some terminals.
@ -129,6 +143,10 @@ typedef struct PromptBuf PromptBuf;
typedef struct ChatContext ChatContext; typedef struct ChatContext ChatContext;
typedef struct Help Help; typedef struct Help Help;
#ifdef GAMES
typedef struct GameData GameData;
#endif
struct ToxWindow { struct ToxWindow {
/* ncurses */ /* ncurses */
bool(*onKey)(ToxWindow *, Tox *, wint_t, bool); bool(*onKey)(ToxWindow *, Tox *, wint_t, bool);
@ -155,6 +173,11 @@ struct ToxWindow {
void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool); void(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t); void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
#ifdef GAMES
void(*onGameInvite)(ToxWindow *, Tox *, uint32_t, const uint8_t *, size_t);
void(*onGameData)(ToxWindow *, Tox *, uint32_t, const uint8_t *, size_t);
#endif // GAMES
#ifdef AUDIO #ifdef AUDIO
void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int); void(*onInvite)(ToxWindow *, ToxAV *, uint32_t, int);
@ -192,6 +215,10 @@ struct ToxWindow {
StatusBar *stb; StatusBar *stb;
Help *help; Help *help;
#ifdef GAMES
GameData *game;
#endif
WINDOW *window; WINDOW *window;
WINDOW *window_bar; WINDOW *window_bar;
}; };
@ -283,4 +310,5 @@ void draw_window_bar(ToxWindow *self);
call at least once per second */ call at least once per second */
void refresh_inactive_windows(void); void refresh_inactive_windows(void);
#endif /* WINDOWS_H */ #endif // WINWDOWS_H