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

Compare commits

..

106 Commits

Author SHA1 Message Date
22d60232fb Bump to version 0.8.4 2020-10-22 22:14:33 -04:00
e428879beb Fix clashing key bindings
Scrolling the groupchat peer list now uses ctrl+up and down
2020-10-13 19:52:43 -04:00
3015138a5a Manually attempt to decode input char sequences
This is currently a fallback method for when the terminal doesn't
detect ctrl arrow sequences, but it is generalized for future additions
2020-10-13 16:12:55 -04:00
9c06ad608b Add ability to skip words in input field with ctrl-left/right arrow 2020-10-10 11:21:15 -04:00
015dbd9a96 fix: Fix non-working bell notifications
The problem with the bell notifications only happens when Toxic is
compiled with X11 support, but does not run under X. This commit changes
toxic's behavior such that it behaves identically when not running under an
X session, no matter if it has been compiled with X11 support or not.
2020-08-24 16:36:52 +02:00
a7466c3142 Add option to disable LAN 2020-08-10 21:56:45 -04:00
f012007cc4 A couple small fixes 2020-05-19 21:44:35 -04:00
dcf3baf60f Make sure logger file is closed after tox_kill 2020-05-18 13:58:55 -04:00
4bda799a4b Add the ability to log to file (#84) 2020-05-16 23:12:54 -04:00
bdeae33d48 Add macOS build.
Lots of features disabled, because I can't get it to work yet. We'll
enable features one by one as we get them to work.
2020-05-05 00:47:08 +01:00
47591d5298 chore: Use global release-drafter config. 2020-05-05 00:25:10 +01:00
b5ace27a3e chore: Remove astyle from travis build.
The astyle check is now done by restyled.
2020-05-04 23:50:31 +01:00
b334622d36 Add release-drafter config. 2020-05-04 22:01:33 +01:00
4bfb344caa Add option for toxcore logging in stderr 2020-05-04 16:15:28 -04:00
16d96d6faf Add restyled config for toxic. 2020-05-04 20:54:58 +01:00
0ab2bad226 Add CODEOWNERS and settings.yml files. 2020-04-25 03:40:02 +00:00
68db926f9f Check that files are formatted correctly.
Fail the CI build if they are not.
2020-04-23 01:16:57 +00:00
b270c1e8b7 Remove extra semicolon in lock/unlock macros.
lock/unlock are always expanded in a context followed by a semicolon.
2020-04-23 01:12:34 +00:00
e7142e49fd Terminate notify before windows
This fixes a use after free bug
2020-04-22 20:54:53 -04:00
610906d07f fix segfault on starting video 2020-04-17 00:00:00 +00:00
6f72a191ba Revert "Add feature flags and missing #includes."
This reverts commit dd5fa236ae.

Also, set `-std=gnu99` in Bazel build.
2020-04-17 19:37:22 +00:00
dd5fa236ae Add feature flags and missing #includes.
These are needed to compile on some strict Linuxes.
2020-04-15 21:34:30 +00:00
51e1ab94b3 Add QR disabling options to INSTALL.md 2020-04-04 23:20:52 -03:00
ddc8c53abf Fix unused variable warnings and change all (void)s to UNUSED macro 2020-03-30 16:02:27 -04:00
46513017e3 Half-hearted attempt at making toxic work on osx.
Video has no chance without X11 for now.
2020-03-30 18:14:43 +00:00
98cb7f58c0 Fix unused parameter and unused result warnings 2020-03-15 14:57:00 -04:00
206bf407fd Use rules_cc instead of native cc_binary rules. 2020-03-12 12:23:57 +00:00
0a8ac4de3b Fix yamllint warning about missing document start marker.
YAML apparently likes to have this marker, so let's have it.
2020-03-10 14:57:12 +00:00
87d54acad0 Add sponsorship button 2020-03-07 13:19:10 -05:00
45ff6d8bac Migrate to Camel_Case enum types.
UPPER_CASE enum types are deprecated and will be removed in 0.3.0.
2020-03-07 18:11:41 +00:00
437dd8baeb Some misc fixes 2020-02-29 14:14:56 -05:00
b080236ee5 Fix typo 2020-02-10 17:16:56 -05:00
116bff8cef format fixes 2019-12-07 23:17:02 -05:00
ddeca171a0 Add input line text to history on UP and DOWN 2019-12-07 18:29:33 -03:00
127f9462e0 make friend add fail if friend is in the block list 2019-11-16 01:55:47 -05:00
4b5a9abbd4 Fix A/V bug preventing the caller from being properly informed of call termination 2019-07-12 11:51:08 -04:00
bb2257973e Load conference titles on startup for saved conferences (#43) 2019-06-30 14:51:13 -04:00
12b9cd2386 Fix header issues 2019-03-27 01:38:15 -04:00
2cbe8fa880 Use correct integer types to fix groupchat peer scrolling bug 2019-03-26 23:05:24 -04:00
2e39bee05a Check for openal lib before attempting to build with video support 2019-03-15 16:05:54 -04:00
05eda76643 Partially fix autocomplete behaviour for input with spaces 2019-02-17 17:25:34 -05:00
f7b73af9a7 Add config option for the notification timeout duration 2018-11-10 08:54:59 -05:00
73aaa44d12 Add config option to set data file auto-save frequency 2018-10-30 18:22:30 -04:00
e4abd8b36b Bump version to 0.8.3 2018-10-30 18:03:44 -04:00
9e3d4f3889 Check if pointer is null before accessing 2018-10-28 07:09:37 -04:00
b7d67c1d86 re-init AV for unblocked contacts 2018-10-20 02:56:37 -04:00
c4a11f8dc7 Refactor ToxWindows
ToxWindow constructor functions now return pointers instead of structs
ToxWindow windows array now holds pointers instead of structs
Refactor some logic in windows.c pertaining to keeping track of active windows
Use uint8_t for window array indexing
2018-10-19 17:43:06 -04:00
d18cc8cbc2 Fix two memory leaks and properly clean up friendlist window 2018-10-19 17:43:03 -04:00
ce6d4861fb Fix bug where autocomplete added a forward slash to files with no extension
Also slightly refactored the line complete functions
2018-10-17 18:00:09 -04:00
8f0e6026f0 Add enum for file_type() and a little cleanup 2018-10-17 18:00:08 -04:00
258736995d Remove ability to set note with status command 2018-10-17 18:00:08 -04:00
56e03a3f8b No longer require quotes to enclose paths 2018-10-17 18:00:08 -04:00
b6c746b5f5 No longer require quotes for commands that take strings as arguments 2018-10-17 18:00:07 -04:00
03673cbced Improve readability of INSTALL.md 2018-10-11 14:22:37 -04:00
0fea930c24 Convert deprecated uppercase enums to camelcase 2018-10-10 13:34:17 -04:00
94d22a8853 Allow empty status messages 2018-10-09 18:21:05 -04:00
63cc23401a Improve readability of defines 2018-10-08 13:39:04 -04:00
f90a774470 Make Toxic easier to port 2018-10-03 22:00:43 +00:00
e7c5fbc873 Cleanup xtra.c
Privatise some functions that don't need to be public
Fix struct names starting with _ char
Remove unnecessary memset of Xtra struct in init_xtra()
2018-09-27 03:41:58 -04:00
d62902ffb3 Use (void) in prototypes instead of ().
Also, removed `__inline` from an extern function (why was it there?).
Also, moved one extern declaration to a header file. There are lots of
these that need to be moved, but one thing at a time.
2018-09-13 23:47:47 +00:00
bebff3be0e Use (void) for empty parameter list in C. 2018-09-08 17:23:07 +00:00
2be4847b53 Ignore warning about unused result.
These should be fixed, but for now we leave it as warning, not error.
2018-09-06 13:46:24 +00:00
4557614443 Enable .travis.yml check and use non-markdown license. 2018-08-17 23:49:01 +00:00
5b30ecf2e4 Use @x11 instead of the longer @x11//:X11 to link against xlib. 2018-08-14 09:55:54 +00:00
2413ad2b59 Use bazel import libs instead of global -l flags.
Import libs currently also point to the global system installed versions,
but at least we have the freedom to import them properly later.
2018-08-05 19:17:57 +00:00
52855b805a Add astyle check to travis. 2018-07-18 17:19:43 +00:00
20b5e46850 Rename callback implementations to be of the form on_$event.
So it's easy to map from implementation to the event without looking at
the `tox_callback_*` calls.
2018-07-18 17:06:28 +00:00
f2b796940e Add missing braces to if/for/while blocks.
Astyle doesn't quite catch all of them.
2018-07-18 15:56:21 +00:00
a37bf300f9 Update astylerc to match toxcore & reformat all source files 2018-07-18 11:33:16 -04:00
cb524dcbc3 Merge branch 'iphydf-local-libconfig' 2018-07-16 10:18:44 -04:00
4144b868ce Use libconfig built in the WORKSPACE rather than system installed.
Improves hermeticity and reproducibility.
2018-07-15 15:34:19 +00:00
af11f16bef fix merge mistake take 2 2018-07-07 10:29:12 -04:00
1d27a496ef fix merge mistake 2018-07-06 15:43:32 -04:00
32bd9dc1a7 Merge branch 'TokTok-master' 2018-07-06 12:35:46 -04:00
3cd2bc7e3c Don't compile A/V code if AUDIO/VIDEO are not #defined. 2018-07-06 15:46:12 +00:00
43ca840658 Conditionally compile api.c and python_api.c contents.
Based on `-DPYTHON`, instead of based on which files are listed in the
source list. This simplifies people's lives when compiling from the
command line with `cc *.c`.
2018-07-06 14:42:33 +00:00
685837357b Add bazel build file for toxic. 2018-07-06 14:41:16 +00:00
812a13b0fb Merge branch 'fx-carton-master' 2018-06-17 20:27:57 -04:00
641fa471d2 Merge branch 'master' of https://github.com/fx-carton/toxic into fx-carton-master 2018-06-17 20:23:12 -04:00
8d5755f2d8 Merge branch 'nieldk-master' 2018-06-17 20:21:05 -04:00
af510b6666 Merge branch 'master' of https://github.com/nieldk/toxic into nieldk-master 2018-06-17 20:09:14 -04:00
46f646afcf Add an option to disable qr codes 2018-06-16 05:07:16 +02:00
68ce17a57f Temp fix for nick change spam on group join 2018-04-19 22:23:49 -04:00
a69fad15c1 Use default timestamp format if provided one is invalid 2018-04-10 17:47:21 -04:00
7621fe9a62 fix macOS build (#494) 2018-03-31 21:10:17 -04:00
f6d9bc3a74 Update check_features.mk
Changed cfg/checks/check_features.mk to enable individual Audio/Video checks by adding proper DISABLE_VI option which takes "0" or "1" for disable/enable video checks/building.
2018-03-19 22:31:31 +01:00
29aea0b42c Remove libtoxav from install instructions as it's now part of the toxcore lib 2018-03-03 23:55:54 -05:00
815c29ee31 Significantly reduce time before trying to re-send a message with no read receipt
(also some formatting cleanup)
2018-03-03 23:54:12 -05:00
3917f664bf Bump to v0.8.2 2018-03-01 19:14:14 -05:00
a223329815 Fix persistent groupchat loading 2018-02-27 18:46:56 -05:00
3fec11d5f9 Update readme 2018-02-26 03:03:12 -05:00
221edb0012 add a .travis file and some build fixes 2018-02-26 02:34:38 -05:00
2710ab6034 Re-implement group nick change notifications 2018-02-25 03:43:19 -05:00
bc3ffac0ba re-add group peer list sorting 2018-02-25 01:21:22 -05:00
29f55c5277 Update API
-Removed usage of deprecated API functions
-Integrated conference changes with a few regressions
2018-02-25 00:00:06 -05:00
a290f0f7f8 Update toxav API calls 2018-02-24 15:38:07 -05:00
5cd196a769 Fix autocomplete bug
Formatting was messed up due to not accounting for null byte in directory list
2018-02-20 20:54:26 -05:00
b14d983a8c Merge branch 'iphydf-opaque-options' 2018-01-20 15:20:25 -05:00
51f1daeec8 Treat Tox_Options as opaque struct: no deref, no alloc.
We allocate it inside toxcore and dereference it inside as well. This
allows us to change the layout of that struct and add new members.
2018-01-20 19:51:13 +00:00
b799c6a8d7 Merge branch 'iphydf-fix-typedefs' 2018-01-20 13:34:53 -05:00
b9f9546e2b Fix typedef enums and potential uninitialised value. 2018-01-20 18:30:35 +00:00
846bc4613e Fix comment 2017-11-20 07:07:24 -05:00
a5a1f6015d Merge branch 'calls_fix' 2017-11-20 07:03:22 -05:00
fe6a7074ea Dynamically allocate audio calls structure
This fixes a bug that caused a segfault when attempting to call a friend with a
friend number above the MAX_CALLS limit
2017-11-20 07:02:56 -05:00
db7c9fe426 Merge branch 'master' of https://github.com/JFreegman/toxic 2017-11-17 18:17:47 -05:00
0bd5b4ddee Repair operating system checks (#474) 2017-11-16 16:59:51 -05:00
101 changed files with 4273 additions and 2514 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
/.github/ @TokTok/admins

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
---
github: [JFreegman]

17
.github/settings.yml vendored Normal file
View File

@ -0,0 +1,17 @@
---
_extends: .github
repository:
name: toxic
description: An ncurses-based Tox client
topics: tox, console, chat
branches:
- name: "master"
protection:
required_status_checks:
contexts:
- Codacy/PR Quality Review
- CodeFactor
- Travis CI - Pull Request
- code-review/reviewable

4
.restyled.yaml Normal file
View File

@ -0,0 +1,4 @@
---
restylers:
- astyle:
arguments: ["--options=astylerc"]

60
.travis.yml Normal file
View File

@ -0,0 +1,60 @@
---
language: python
python: nightly
dist: xenial
os: linux
jobs:
include:
- env: JOB=linux
addons:
apt:
packages:
- libalut-dev
- libconfig-dev
- libnotify-dev
- libopenal-dev
- libopus-dev
- libqrencode-dev
- libvpx-dev
cache:
directories:
- $HOME/cache
install:
# Where to find libraries.
- export LD_LIBRARY_PATH=$HOME/cache/usr/lib
- export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
# c-sodium
- git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium ../libsodium
- test -f $HOME/cache/usr/lib/libsodium.so || (cd ../libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc))
# c-toxcore
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
- test -f $HOME/cache/usr/lib/libtoxcore.so || (cd ../c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
script:
- make ENABLE_PYTHON=1 -j$(nproc)
- env: JOB=macos
os: macos
language: c
cache:
directories:
- $HOME/cache
install:
- brew install
freealut
libconfig
libqrencode
libsodium
openal-soft
- export LDFLAGS="-L/usr/local/Cellar/openal-soft/1.20.1/lib"
- git clone --depth=1 https://github.com/TokTok/c-toxcore ../c-toxcore
- test -f /usr/local/lib/libtoxcore.dylib || (cd ../c-toxcore && cmake -B_build -H. && make -C_build install -j$(nproc))
script:
- make ENABLE_PYTHON=1 DISABLE_DESKTOP_NOTIFY=1 DISABLE_X11=1 DISABLE_AV=1 DISABLE_SOUND_NOTIFY=1 -j$(nproc)

41
BUILD.bazel Normal file
View File

@ -0,0 +1,41 @@
load("@rules_cc//cc:defs.bzl", "cc_binary")
load("//tools/project:build_defs.bzl", "project")
project()
cc_binary(
name = "toxic",
srcs = glob(
[
"src/*.c",
"src/*.h",
],
exclude = ["src/video*"],
) + select({
"//tools/config:linux": glob(["src/video*"]),
"//tools/config:osx": [],
}),
copts = [
"-std=gnu99",
"-DAUDIO",
"-DPACKAGE_DATADIR='\"data\"'",
"-DPYTHON",
"-DQRCODE",
] + select({
"//tools/config:linux": ["-DVIDEO"],
"//tools/config:osx": [],
}),
deps = [
"//c-toxcore",
"@curl",
"@libconfig",
"@libqrencode",
"@libvpx",
"@ncurses",
"@openal",
"@python3//:python",
] + select({
"//tools/config:linux": ["@x11"],
"//tools/config:osx": [],
}),
)

View File

@ -5,7 +5,7 @@
* [Documentation](#documentation) * [Documentation](#documentation)
* [Notes](#notes) * [Notes](#notes)
* [Compilation variables](#compilation-variables) * [Compilation variables](#compilation-variables)
* [Packaging](#packaging) * [Environment variables](#environment-variables)
## Dependencies ## Dependencies
| Name | Needed by | Debian package | | Name | Needed by | Debian package |
@ -15,8 +15,7 @@
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev | | [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make | | [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev| | [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev | | [libqrencode](https://fukuchi.org/works/qrencode/) | QRCODE | libqrencode-dev |
| [Tox Core AV](https://github.com/toktok/c-toxcore) | AUDIO | *None* |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev | | [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev | | [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev | | [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
@ -28,39 +27,42 @@
#### OS X Notes #### OS X Notes
Using [Homebrew](http://brew.sh): Using [Homebrew](http://brew.sh):
``` ```
brew install openal-soft freealut libconfig brew install curl qrencode openal-soft freealut libconfig libpng
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install libnotify brew install libnotify
export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig
make
``` ```
You can omit `libnotify` if you intend to build without desktop notifications enabled. You can omit `libnotify` if you intend to build without desktop notifications enabled.
## Compiling ## Compiling
``` ```
make PREFIX="/where/to/install" make
sudo make install PREFIX="/where/to/install" sudo make install
``` ```
#### Documentation #### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br /> **Note for developers**: asciidoc files and generated manpages will need to be committed together.<br />
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. **Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
## Notes ## Notes
#### Compilation variables #### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` * You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) * Default compile options can be overridden by using special variables:
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables: * `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
* `DISABLE_X11=1`build toxic without X11 support (needed for focus tracking) * `DISABLE_AV=1`Disable audio call support
* `DISABLE_AV=1` → build toxic without audio call support * `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support * `DISABLE_QRCODE` → Disable QR exporting support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support * `DISABLE_QRPNG` → Disable support for exporting QR as PNG
* Features excluded from the default build must be explicitly enabled using special variables: * `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
* `ENABLE_PYTHON=1`build toxic with Python scripting support * `ENABLE_PYTHON=1`Build toxic with Python scripting support
#### Packaging * `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`: #### Environment variables
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") * You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local") * The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br />
**Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information.

View File

View File

@ -3,13 +3,15 @@ CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk -include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig libqrencode LIBS = toxcore ncursesw libconfig libcurl
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all CFLAGS ?= -g
CFLAGS += -std=gnu99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64 CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"' CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS) CFLAGS += ${USER_CFLAGS}
LDFLAGS = $(USER_LDFLAGS) 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 group_commands.o groupchat.o help.o input.o OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
@ -19,26 +21,19 @@ OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check on wich system we are running # Check on wich system we are running
UNAME_S = $(shell uname -s) UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Linux) ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/systems/Linux.mk LDFLAGS += -ldl -lrt
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), DragonFly)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif endif
ifeq ($(UNAME_S), OpenBSD) ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif endif
ifeq ($(UNAME_S), NetBSD) ifeq ($(UNAME_S), NetBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif endif
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk -include $(CFG_DIR)/systems/Darwin.mk
endif endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running # Check on which platform we are running
UNAME_M = $(shell uname -m) UNAME_M = $(shell uname -m)

View File

@ -3,13 +3,12 @@
src="https://scan.coverity.com/projects/4975/badge.svg"/> src="https://scan.coverity.com/projects/4975/badge.svg"/>
</a> </a>
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/toktok/c-toxcore), and is now available as a standalone application. Toxic is a [Tox](https://tox.chat)-based instant messaging and video chat client.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png) [![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)
## Installation ## Installation
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br /> [See the install instructions](/INSTALL.md)
[Compile it yourself](/INSTALL.md)
## Settings ## Settings
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more. Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.

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.8.1' version = '0.8.4'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.8.1' release = '0.8.4'
# 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

@ -1,11 +1,26 @@
# Bracket Style Options
--style=kr --style=kr
--pad-header
--max-code-length=120 # Tab Options
--convert-tabs --indent=spaces=4
--indent-switches
--pad-oper # Indentation Options
--align-pointer=name --indent-switches
--align-reference=name
--preserve-date # Padding Options
--lineend=linux --pad-header
--break-blocks --break-blocks
--pad-oper
--unpad-paren
--align-pointer=name
--align-reference=name
# Formatting Options
--add-brackets
--convert-tabs
--max-code-length=120
# Other Options
--preserve-date
--formatted
--lineend=linux

View File

@ -1,5 +1,5 @@
# Variables for audio call support # Variables for audio call support
AUDIO_LIBS = libtoxav openal AUDIO_LIBS = openal
AUDIO_CFLAGS = -DAUDIO AUDIO_CFLAGS = -DAUDIO
ifneq (, $(findstring audio_device.o, $(OBJ))) ifneq (, $(findstring audio_device.o, $(OBJ)))
AUDIO_OBJ = audio_call.o AUDIO_OBJ = audio_call.o
@ -8,13 +8,13 @@ else
endif endif
# Check if we can build audio support # Check if we can build audio support
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error") CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
ifneq ($(CHECK_AUDIO_LIBS), error) ifneq ($(CHECK_AUDIO_LIBS), error)
LIBS += $(AUDIO_LIBS) LIBS += $(AUDIO_LIBS)
CFLAGS += $(AUDIO_CFLAGS) CFLAGS += $(AUDIO_CFLAGS)
OBJ += $(AUDIO_OBJ) OBJ += $(AUDIO_OBJ)
else ifneq ($(MAKECMDGOALS), clean) else ifneq ($(MAKECMDGOALS), clean)
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) MISSING_AUDIO_LIBS := $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without audio support) $(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support) $(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS)) $(warning WARNING -- $(MISSING_AUDIO_LIBS))

View File

@ -1,19 +1,19 @@
CHECKS_DIR = $(CFG_DIR)/checks CHECKS_DIR = $(CFG_DIR)/checks
# Check if we want build X11 support # Check if we want build X11 support
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi) X11 := $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled) ifneq ($(X11), disabled)
-include $(CHECKS_DIR)/x11.mk -include $(CHECKS_DIR)/x11.mk
endif endif
# Check if we want build audio support # Check if we want build audio support
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(AUDIO), disabled) ifneq ($(AUDIO), disabled)
-include $(CHECKS_DIR)/audio.mk -include $(CHECKS_DIR)/audio.mk
endif endif
# Check if we want build video support # Check if we want build video support
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi) VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(X11), disabled) ifneq ($(X11), disabled)
ifneq ($(AUDIO), disabled) ifneq ($(AUDIO), disabled)
ifneq ($(VIDEO), disabled) ifneq ($(VIDEO), disabled)
@ -23,36 +23,42 @@ endif
endif endif
# Check if we want build sound notifications support # Check if we want build sound notifications support
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(SND_NOTIFY), disabled) ifneq ($(SND_NOTIFY), disabled)
-include $(CHECKS_DIR)/sound_notifications.mk -include $(CHECKS_DIR)/sound_notifications.mk
endif endif
# Check if we want build desktop notifications support # Check if we want build desktop notifications support
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi) DESK_NOTIFY := $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(DESK_NOTIFY), disabled) ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk -include $(CHECKS_DIR)/desktop_notifications.mk
endif endif
# Check if we want build QR export support
QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(QR_CODE), disabled)
-include $(CHECKS_DIR)/qr.mk
endif
# Check if we want build QR exported as PNG support # Check if we want build QR exported as PNG support
QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi) QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(QR_PNG), disabled) ifneq ($(QR_PNG), disabled)
-include $(CHECKS_DIR)/qr_png.mk -include $(CHECKS_DIR)/qr_png.mk
endif endif
# Check if we want build Python scripting support # Check if we want build Python scripting support
PYTHON = $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi) PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(PYTHON), disabled) ifneq ($(PYTHON), disabled)
-include $(CHECKS_DIR)/python.mk -include $(CHECKS_DIR)/python.mk
endif endif
# Check if we can build Toxic # Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error) ifneq ($(CHECK_LIBS), error)
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS)) CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS)) LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
else ifneq ($(MAKECMDGOALS), clean) else ifneq ($(MAKECMDGOALS), clean)
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) MISSING_LIBS := $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning ERROR -- Cannot compile Toxic) $(warning ERROR -- Cannot compile Toxic)
$(warning ERROR -- You need these libraries) $(warning ERROR -- You need these libraries)
$(warning ERROR -- $(MISSING_LIBS)) $(warning ERROR -- $(MISSING_LIBS))

View File

@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
# Check if we can build desktop notifications support # Check if we can build desktop notifications support
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error") CHECK_DESK_NOTIFY_LIBS := $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error) ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
LIBS += $(DESK_NOTIFY_LIBS) LIBS += $(DESK_NOTIFY_LIBS)
CFLAGS += $(DESK_NOTIFY_CFLAGS) CFLAGS += $(DESK_NOTIFY_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean) else ifneq ($(MAKECMDGOALS), clean)
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done) MISSING_DESK_NOTIFY_LIBS := $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without desktop notifications support) $(warning WARNING -- Toxic will be compiled without desktop notifications support)
$(warning WARNING -- You need these libraries for desktop notifications support) $(warning WARNING -- You need these libraries for desktop notifications support)
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS)) $(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))

15
cfg/checks/qr.mk Normal file
View File

@ -0,0 +1,15 @@
# Variables for QR export support
QR_LIBS = libqrencode
QR_CFLAGS = -DQRCODE
# Check if we can build QR export support
CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error")
ifneq ($(CHECK_QR_LIBS), error)
LIBS += $(QR_LIBS)
CFLAGS += $(QR_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without QR export support)
$(warning WARNING -- You need these libraries for QR export support)
$(warning WARNING -- $(MISSING_QR_LIBS))
endif

View File

@ -1,5 +1,5 @@
# Variables for video call support # Variables for video call support
VIDEO_LIBS = libtoxav vpx x11 VIDEO_LIBS = openal vpx x11
VIDEO_CFLAGS = -DVIDEO VIDEO_CFLAGS = -DVIDEO
ifneq (, $(findstring video_device.o, $(OBJ))) ifneq (, $(findstring video_device.o, $(OBJ)))
VIDEO_OBJ = video_call.o VIDEO_OBJ = video_call.o

View File

@ -1,5 +1,5 @@
# Version # Version
TOXIC_VERSION = 0.8.1 TOXIC_VERSION = 0.8.4
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)
@ -23,10 +23,10 @@ SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
# Install directories # Install directories
PREFIX = /usr/local PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man MANDIR ?= $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications APPDIR = $(PREFIX)/share/applications
# Platform tools # Platform tools

View File

@ -6,7 +6,7 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o
LIBS := $(filter-out ncursesw, $(LIBS)) LIBS := $(filter-out ncursesw, $(LIBS))
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw. # OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g LDFLAGS += -lncurses -lalut -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
OSX_LIBRARIES = -lobjc -lresolv OSX_LIBRARIES = -lobjc -lresolv

View File

@ -1,4 +0,0 @@
# Specials options for freebsd systems
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw -lcurl
MANDIR = $(PREFIX)/man

View File

@ -1,4 +0,0 @@
# Specials options for linux systems
CFLAGS +=
LDFLAGS += -ldl -lrt -lcurl
MANDIR = $(PREFIX)/share/man

View File

@ -1,10 +1,10 @@
# Doc target # Doc target
doc: $(MANFILES:%=$(DOC_DIR)/%) doc: $(MANFILES:%=$(DOC_DIR)/%)
$(DOC_DIR)/%: $(DOC_DIR)/%.asc $(DOC_DIR)/%: $(DOC_DIR)/%.asc
@echo " MAN $(@F)" @echo " MAN $(@F)"
@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \ @a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \
-a manmanual="Toxic Manual" -a mansource=toxic \ -a manmanual="Toxic Manual" -a mansource=toxic \
-a manversion=__VERSION__ -a datadir=__DATADIR__ $< -a manversion=__VERSION__ -a datadir=__DATADIR__ $<
.PHONY: doc .PHONY: doc

View File

@ -14,11 +14,22 @@ help:
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support" @echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support" @echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" @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_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 " 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 " 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))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)" @echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
@echo
@echo "-- Environment Variables --"
@echo " CFLAGS: Add custom flags to default CFLAGS"
@echo " LDFLAGS: Add custom flags to default LDFLAGS"
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
.PHONY: help .PHONY: help

View File

@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic
if [ ! -e "$(DOC_DIR)/$$f" ]; then \ if [ ! -e "$(DOC_DIR)/$$f" ]; then \
continue ;\ continue ;\
fi ;\ fi ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
file=$$section/$$f ;\ file=$$section/$$f ;\
mkdir -p $$section ;\ mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\ install -m 0644 $(DOC_DIR)/$$f $$file ;\

View File

@ -16,7 +16,7 @@ uninstall:
@echo "Removing man pages" @echo "Removing man pages"
@for f in $(MANFILES) ; do \ @for f in $(MANFILES) ; do \
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\ section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
file=$$section/$$f ;\ file=$$section/$$f ;\
rm -f $$file $$file.gz ;\ rm -f $$file $$file.gz ;\
done done

View File

@ -78,6 +78,11 @@ instead of
Show help message Show help message
.RE .RE
.PP .PP
\-l, \-\-logging
.RS 4
Enable toxcore logging to stderr
.RE
.PP
\-n, \-\-nodes nodes\-file \-n, \-\-nodes nodes\-file
.RS 4 .RS 4
Use specified Use specified

View File

@ -40,6 +40,9 @@ OPTIONS
-h, --help:: -h, --help::
Show help message Show help message
-l, --logging::
Enable toxcore logging to stderr
-n, --nodes nodes-file:: -n, --nodes nodes-file::
Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json' Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'

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: 2016-09-20 .\" Date: 2018-10-27
.\" Manual: Toxic Manual .\" Manual: Toxic Manual
.\" Source: toxic __VERSION__ .\" Source: toxic __VERSION__
.\" Language: English .\" Language: English
.\" .\"
.TH "TOXIC\&.CONF" "5" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual" .TH "TOXIC\&.CONF" "5" "2018\-10\-27" "toxic __VERSION__" "Toxic Manual"
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
.\" * Define some portability stuff .\" * Define some portability stuff
.\" ----------------------------------------------------------------- .\" -----------------------------------------------------------------
@ -120,7 +120,12 @@ Enable friend connection change notifications\&. true or false
.PP .PP
\fBnodelist_update_freq\fR \fBnodelist_update_freq\fR
.RS 4 .RS 4
How often in days to update the DHT nodes list\&. (0 to disable updates) How often in days to update the DHT nodes list\&. (integer; 0 to disable)
.RE
.PP
\fBautosave_freq\fR
.RS 4
How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable)
.RE .RE
.PP .PP
\fBhistory_size\fR \fBhistory_size\fR
@ -128,6 +133,11 @@ How often in days to update the DHT nodes list\&. (0 to disable updates)
Maximum lines for chat window history\&. Integer value\&. (for example: 700) Maximum lines for chat window history\&. Integer value\&. (for example: 700)
.RE .RE
.PP .PP
\fBnotification_timeout\fR
.RS 4
Time in milliseconds to display a notification\&. (for example: 3000)
.RE
.PP
\fBline_join\fR \fBline_join\fR
.RS 4 .RS 4
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&. Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
@ -341,16 +351,6 @@ Key combination to scroll half page down\&.
Key combination to scroll to page bottom\&. Key combination to scroll to page bottom\&.
.RE .RE
.PP .PP
\fBpeer_list_up\fR
.RS 4
Key combination to scroll contacts list up\&.
.RE
.PP
\fBpeer_list_down\fR
.RS 4
Key combination to scroll contacts list down\&.
.RE
.PP
\fBtoggle_peerlist\fR \fBtoggle_peerlist\fR
.RS 4 .RS 4
Toggle the peer list on and off\&. Toggle the peer list on and off\&.

View File

@ -76,11 +76,17 @@ OPTIONS
Enable friend connection change notifications. true or false Enable friend connection change notifications. true or false
*nodelist_update_freq*;; *nodelist_update_freq*;;
How often in days to update the DHT nodes list. (0 to disable updates) How often in days to update the DHT nodes list. (integer; 0 to disable)
*autosave_freq*;;
How often in seconds to auto-save the Tox data file. (integer; 0 to disable)
*history_size*;; *history_size*;;
Maximum lines for chat window history. Integer value. (for example: 700) Maximum lines for chat window history. Integer value. (for example: 700)
*notification_timeout*;;
Time in milliseconds to display a notification. Integer value. (for example: 3000)
*line_join*;; *line_join*;;
Indicator for when someone connects or joins a group. Indicator for when someone connects or joins a group.
Three characters max for line_ settings. Three characters max for line_ settings.
@ -215,12 +221,6 @@ OPTIONS
*page_bottom*;; *page_bottom*;;
Key combination to scroll to page bottom. Key combination to scroll to page bottom.
*peer_list_up*;;
Key combination to scroll contacts list up.
*peer_list_down*;;
Key combination to scroll contacts list down.
*toggle_peerlist*;; *toggle_peerlist*;;
Toggle the peer list on and off. Toggle the peer list on and off.

View File

@ -47,9 +47,15 @@ ui = {
// How often in days to update the DHT nodes list. (0 to disable updates) // How often in days to update the DHT nodes list. (0 to disable updates)
nodeslist_update_freq=7; nodeslist_update_freq=7;
// How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves)
autosave_freq=600;
// maximum lines for chat window history // maximum lines for chat window history
history_size=700; history_size=700;
// time in milliseconds to display a notification
notification_timeout=3000;
// Indicator for display when someone connects or joins a group. // Indicator for display when someone connects or joins a group.
line_join="-->"; line_join="-->";
@ -116,8 +122,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";
peer_list_up="Ctrl+[";
peer_list_down="Ctrl+]";
toggle_peerlist="Ctrl+b"; toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T"; toggle_paste_mode="Ctrl+T";
}; };

View File

@ -36,7 +36,6 @@
#ifdef PYTHON #ifdef PYTHON
#include "python_api.h" #include "python_api.h"
#endif /* PYTHON */
Tox *user_tox; Tox *user_tox;
static WINDOW *cur_window; static WINDOW *cur_window;
@ -47,8 +46,9 @@ extern struct user_settings *user_settings;
void api_display(const char *const msg) void api_display(const char *const msg)
{ {
if (msg == NULL) if (msg == NULL) {
return; return;
}
self_window = get_active_window(); self_window = get_active_window();
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
@ -64,15 +64,16 @@ char *api_get_nick(void)
size_t len = tox_self_get_name_size(user_tox); size_t len = tox_self_get_name_size(user_tox);
uint8_t *name = malloc(len + 1); uint8_t *name = malloc(len + 1);
if (name == NULL) if (name == NULL) {
return NULL; return NULL;
}
tox_self_get_name(user_tox, name); tox_self_get_name(user_tox, name);
name[len] = '\0'; name[len] = '\0';
return (char *) name; return (char *) name;
} }
TOX_USER_STATUS api_get_status(void) Tox_User_Status api_get_status(void)
{ {
return tox_self_get_status(user_tox); return tox_self_get_status(user_tox);
} }
@ -82,8 +83,9 @@ char *api_get_status_message(void)
size_t len = tox_self_get_status_message_size(user_tox); size_t len = tox_self_get_status_message_size(user_tox);
uint8_t *status = malloc(len + 1); uint8_t *status = malloc(len + 1);
if (status == NULL) if (status == NULL) {
return NULL; return NULL;
}
tox_self_get_status_message(user_tox, status); tox_self_get_status_message(user_tox, status);
status[len] = '\0'; status[len] = '\0';
@ -92,14 +94,16 @@ char *api_get_status_message(void)
void api_send(const char *msg) void api_send(const char *msg)
{ {
if (msg == NULL || self_window->chatwin->cqueue == NULL) if (msg == NULL || self_window->chatwin->cqueue == NULL) {
return; return;
}
char *name = api_get_nick(); char *name = api_get_nick();
char timefrmt[TIME_STR_SIZE]; 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)); get_time_str(timefrmt, sizeof(timefrmt));
@ -145,9 +149,12 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
cur_window = window; cur_window = window;
self_window = self; self_window = self;
if ( argc != 1 ) { if (argc != 1) {
if ( argc < 1 ) error_str = "Path must be specified!"; if (argc < 1) {
else error_str = "Only one argument allowed!"; error_str = "Path must be specified.";
} else {
error_str = "Only one argument allowed.";
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return; return;
@ -155,8 +162,8 @@ void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
fp = fopen(argv[1], "r"); fp = fopen(argv[1], "r");
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, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return; return;
@ -174,8 +181,9 @@ void invoke_autoruns(WINDOW *window, ToxWindow *self)
DIR *d; DIR *d;
FILE *fp; FILE *fp;
if (user_settings->autorun_path[0] == '\0') if (user_settings->autorun_path[0] == '\0') {
return; return;
}
d = opendir(user_settings->autorun_path); d = opendir(user_settings->autorun_path);
@ -208,3 +216,4 @@ void invoke_autoruns(WINDOW *window, ToxWindow *self)
closedir(d); closedir(d);
} }
#endif /* PYTHON */

View File

@ -29,7 +29,7 @@
void api_display(const char *const msg); void api_display(const char *const msg);
FriendsList api_get_friendslist(void); FriendsList api_get_friendslist(void);
char *api_get_nick(void); char *api_get_nick(void);
TOX_USER_STATUS api_get_status(void); Tox_User_Status api_get_status(void);
char *api_get_status_message(void); char *api_get_status_message(void);
void api_send(const char *msg); void api_send(const char *msg);
void api_execute(const char *input, int mode); void api_execute(const char *input, int mode);
@ -38,5 +38,6 @@ int num_registered_handlers(void);
int help_max_width(void); int help_max_width(void);
void draw_handler_help(WINDOW *win); void draw_handler_help(WINDOW *win);
void invoke_autoruns(WINDOW *w, ToxWindow *self); void invoke_autoruns(WINDOW *w, ToxWindow *self);
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* #define API_H */ #endif /* API_H */

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,6 @@
#include "audio_device.h" #include "audio_device.h"
#define MAX_CALLS 10
typedef enum _AudioError { typedef enum _AudioError {
ae_None = 0, ae_None = 0,
ae_StartingCaptureDevice = 1 << 0, ae_StartingCaptureDevice = 1 << 0,
@ -65,7 +63,9 @@ struct CallControl {
ToxAV *av; ToxAV *av;
ToxWindow *prompt; ToxWindow *prompt;
Call calls[MAX_CALLS]; Call *calls;
uint32_t max_calls;
uint32_t call_state; uint32_t call_state;
bool pending_call; bool pending_call;
bool audio_enabled; bool audio_enabled;
@ -79,15 +79,17 @@ struct CallControl {
uint32_t video_bit_rate; uint32_t video_bit_rate;
int32_t video_frame_duration; int32_t video_frame_duration;
} CallControl; };
struct CallControl CallControl; extern struct CallControl CallControl;
/* You will have to pass pointer to first member of 'windows' declared in windows.c */ /* You will have to pass pointer to first member of 'windows' declared in windows.c */
ToxAV *init_audio(ToxWindow *self, Tox *tox); ToxAV *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio(); void terminate_audio(void);
int start_transmission(ToxWindow *self, Call *call); int start_transmission(ToxWindow *self, Call *call);
int stop_transmission(Call *call, uint32_t friend_number); int stop_transmission(Call *call, uint32_t friend_number);
void stop_current_call(ToxWindow *self); void stop_current_call(ToxWindow *self);
void init_friend_AV(uint32_t index);
void del_friend_AV(uint32_t index);
#endif /* AUDIO_CALL_H */ #endif /* AUDIO_CALL_H */

View File

@ -28,18 +28,14 @@
#include "line_info.h" #include "line_info.h"
#include "settings.h" #include "settings.h"
#include "misc_tools.h"
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h> #include <AL/al.h>
#include <AL/alc.h> #include <AL/alc.h>
/* compatibility with older versions of OpenAL */ /* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER #ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h> #include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */ #endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#include <stdbool.h> #include <stdbool.h>
#include <string.h> #include <string.h>
@ -97,19 +93,21 @@ void *thread_poll(void *);
#ifdef AUDIO #ifdef AUDIO
DeviceError init_devices(ToxAV *av_) DeviceError init_devices(ToxAV *av_)
#else #else
DeviceError init_devices() DeviceError init_devices(void)
#endif /* AUDIO */ #endif /* AUDIO */
{ {
get_devices_names(); get_devices_names();
// Start poll thread // Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0) if (pthread_mutex_init(&mutex, NULL) != 0) {
return de_InternalError; return de_InternalError;
}
pthread_t thread_id; pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) if (pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
return de_InternalError; return de_InternalError;
}
#ifdef AUDIO #ifdef AUDIO
av = av_; av = av_;
@ -118,7 +116,7 @@ DeviceError init_devices()
return (DeviceError) de_None; return (DeviceError) de_None;
} }
DeviceError terminate_devices() DeviceError terminate_devices(void)
{ {
/* Cleanup if needed */ /* Cleanup if needed */
lock; lock;
@ -127,47 +125,52 @@ DeviceError terminate_devices()
usleep(20000); usleep(20000);
if (pthread_mutex_destroy(&mutex) != 0) if (pthread_mutex_destroy(&mutex) != 0) {
return (DeviceError) de_InternalError; return (DeviceError) de_InternalError;
}
return (DeviceError) de_None; return (DeviceError) de_None;
} }
void get_devices_names() { void get_devices_names(void)
{
const char *stringed_device_list; const char *stringed_device_list;
size[input] = 0; size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) { if ((stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER); ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) { for (; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input]) {
devices_names[input][size[input]] = stringed_device_list; devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1; stringed_device_list += strlen(stringed_device_list) + 1;
} }
} }
size[output] = 0; size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER); stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else } else {
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER); stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
}
if (stringed_device_list) { if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER); ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) { for (; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output]) {
devices_names[output][size[output]] = stringed_device_list; devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1; stringed_device_list += strlen(stringed_device_list) + 1;
} }
} }
} }
DeviceError device_mute(DeviceType type, uint32_t device_idx) DeviceError device_mute(DeviceType type, uint32_t device_idx)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock; lock;
@ -187,7 +190,9 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
#ifdef AUDIO #ifdef AUDIO
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value) DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock; lock;
@ -208,7 +213,9 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
DeviceError set_primary_device(DeviceType type, int32_t selection) DeviceError set_primary_device(DeviceType type, int32_t selection)
{ {
if (size[type] <= selection || selection < 0) return de_InvalidSelection; if (size[type] <= selection || selection < 0) {
return de_InvalidSelection;
}
primary_device[type] = selection; primary_device[type] = selection;
@ -230,9 +237,13 @@ void get_primary_device_name(DeviceType type, char *buf, int size)
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate, DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
uint32_t frame_duration, uint8_t channels) uint32_t frame_duration, uint8_t channels)
{ {
if (size[type] <= selection || selection < 0) return de_InvalidSelection; if (size[type] <= selection || selection < 0) {
return de_InvalidSelection;
}
if (channels != 1 && channels != 2) return de_UnsupportedMode; if (channels != 1 && channels != 2) {
return de_UnsupportedMode;
}
lock; lock;
@ -245,10 +256,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
if (i == MAX_DEVICES) { if (i == MAX_DEVICES) {
unlock; unlock;
return de_AllDevicesBusy; return de_AllDevicesBusy;
} else *device_idx = i; } else {
*device_idx = i;
}
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if ( running[type][i] && running[type][i]->selection == selection ) { if (running[type][i] && running[type][i]->selection == selection) {
// printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl); // printf("a%d-%d:%p ", selection, i, running[type][i]->dhndl);
running[type][*device_idx] = running[type][i]; running[type][*device_idx] = running[type][i];
@ -281,7 +294,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
} else { } else {
device->dhndl = alcOpenDevice(devices_names[type][selection]); device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) { if (!device->dhndl) {
free(device); free(device);
running[type][*device_idx] = NULL; running[type][*device_idx] = NULL;
unlock; unlock;
@ -298,7 +311,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
uint16_t zeros[frame_size]; uint16_t zeros[frame_size];
memset(zeros, 0, frame_size * 2); memset(zeros, 0, frame_size * 2);
for ( i = 0; i < OPENAL_BUFS; ++i ) { for (i = 0; i < OPENAL_BUFS; ++i) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate); alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
} }
@ -324,7 +337,9 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
DeviceError close_device(DeviceType type, uint32_t device_idx) DeviceError close_device(DeviceType type, uint32_t device_idx)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
lock; lock;
Device *device = running[type][device_idx]; Device *device = running[type][device_idx];
@ -337,34 +352,45 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
running[type][device_idx] = NULL; running[type][device_idx] = NULL;
if ( !device->ref_count ) { if (!device->ref_count) {
if (type == input) { if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; if (!alcCaptureCloseDevice(device->dhndl)) {
rc = de_AlError;
}
} else { } else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); if (alcGetCurrentContext() != device->ctx) {
alcMakeContextCurrent(device->ctx);
}
alDeleteSources(1, &device->source); alDeleteSources(1, &device->source);
alDeleteBuffers(OPENAL_BUFS, device->buffers); alDeleteBuffers(OPENAL_BUFS, device->buffers);
alcMakeContextCurrent(NULL); alcMakeContextCurrent(NULL);
if ( device->ctx ) alcDestroyContext(device->ctx); if (device->ctx) {
alcDestroyContext(device->ctx);
}
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; if (!alcCloseDevice(device->dhndl)) {
rc = de_AlError;
}
} }
free(device); free(device);
} else device->ref_count--; } else {
device->ref_count--;
}
unlock; unlock;
return rc; return rc;
} }
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD) void *data, bool enable_VAD)
{ {
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
return de_InvalidSelection; return de_InvalidSelection;
}
lock; lock;
running[input][device_idx]->cb = callback; running[input][device_idx]->cb = callback;
@ -379,11 +405,15 @@ DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels, inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
uint32_t sample_rate) uint32_t sample_rate)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) {
return de_InvalidSelection;
}
Device *device = running[output][device_idx]; Device *device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive; if (!device || device->muted) {
return de_DeviceNotActive;
}
pthread_mutex_lock(device->mutex); pthread_mutex_lock(device->mutex);
@ -398,8 +428,9 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
alSourceUnqueueBuffers(device->source, processed, bufids); alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1); alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0]; bufid = bufids[0];
} else if (queued < 16) alGenBuffers(1, &bufid); } else if (queued < 16) {
else { alGenBuffers(1, &bufid);
} else {
pthread_mutex_unlock(device->mutex); pthread_mutex_unlock(device->mutex);
return de_Busy; return de_Busy;
} }
@ -412,19 +443,21 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
ALint state; ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state); alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if (state != AL_PLAYING) alSourcePlay(device->source); if (state != AL_PLAYING) {
alSourcePlay(device->source);
}
pthread_mutex_unlock(device->mutex); pthread_mutex_unlock(device->mutex);
return de_None; return de_None;
} }
void *thread_poll (void *arg) // TODO: maybe use thread for every input source void *thread_poll(void *arg) // TODO: maybe use thread for every input source
{ {
/* /*
* NOTE: We only need to poll input devices for data. * NOTE: We only need to poll input devices for data.
*/ */
(void)arg; UNUSED_VAR(arg);
uint32_t i; uint32_t i;
int32_t sample = 0; int32_t sample = 0;
@ -469,7 +502,9 @@ void *thread_poll (void *arg) // TODO: maybe use thread for every input source
continue; continue;
} }
if ( device->cb ) device->cb(frame, f_size, device->cb_data); if (device->cb) {
device->cb(frame, f_size, device->cb_data);
}
} }
unlock; unlock;
@ -487,7 +522,8 @@ void print_devices(ToxWindow *self, DeviceType 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, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i, devices_names[type][i]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i,
devices_names[type][i]);
} }
return; return;
@ -500,8 +536,9 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
void *get_device_callback_data(uint32_t device_idx) void *get_device_callback_data(uint32_t device_idx)
{ {
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) {
return NULL; return NULL;
}
return running[input][device_idx]->cb_data; return running[input][device_idx]->cb_data;
} }

View File

@ -31,7 +31,7 @@
#define OPENAL_BUFS 5 #define OPENAL_BUFS 5
#define MAX_DEVICES 32 #define MAX_DEVICES 32
#include <inttypes.h>
#include "windows.h" #include "windows.h"
typedef enum DeviceType { typedef enum DeviceType {
@ -52,17 +52,17 @@ typedef enum DeviceError {
de_AlError = -9, de_AlError = -9,
} DeviceError; } DeviceError;
typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data); typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data);
#ifdef AUDIO #ifdef AUDIO
DeviceError init_devices(ToxAV *av); DeviceError init_devices(ToxAV *av);
#else #else
DeviceError init_devices(); DeviceError init_devices(void);
#endif /* AUDIO */ #endif /* AUDIO */
void get_devices_names(); void get_devices_names(void);
DeviceError terminate_devices(); DeviceError terminate_devices(void);
/* Callback handles ready data from INPUT device */ /* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,

View File

@ -29,7 +29,7 @@
#include <sys/dir.h> #include <sys/dir.h>
#else #else
#include <dirent.h> #include <dirent.h>
#endif /* ifdef __APPLE__ */ #endif /* __APPLE__ */
#include "windows.h" #include "windows.h"
#include "toxic.h" #include "toxic.h"
@ -38,16 +38,18 @@
#include "execute.h" #include "execute.h"
#include "configdir.h" #include "configdir.h"
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size) static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size)
{ {
if (m) if (m) {
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE); execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
}
const char *L = (char *) list; const char *L = (char *) list;
int i; int i;
for (i = 0; i < n_items; ++i) for (i = 0; i < n_items; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */ line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
} }
@ -59,6 +61,8 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
*/ */
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n) static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
{ {
UNUSED_VAR(self);
if (n == 1) { if (n == 1) {
return snprintf(match, match_sz, "%s", matches[0]); return snprintf(match, match_sz, "%s", matches[0]);
} }
@ -83,53 +87,60 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
return snprintf(match, match_sz, "%s", matches[0]); return snprintf(match, match_sz, "%s", matches[0]);
} }
/* looks for all instances in list that begin with the last entered word in line according to pos, /*
then fills line with the complete word. e.g. "Hello jo" would complete the line * Looks for all instances in list that begin with the last entered word in line according to pos,
with "Hello john". If multiple matches, prints out all the matches and semi-completes line. * then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
list is a pointer to the list of strings being compared, n_items is the number of items *
in the list, and size is the size of each item in the list. * list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
Returns the difference between the old len and new len of line on success, -1 if error */ *
int complete_line(ToxWindow *self, const void *list, int n_items, int size) * dir_search should be true if the line being completed is a file path.
*
* Returns the difference between the old len and new len of line on success.
* Returns -1 on error.
*
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
*/
static int complete_line_helper(ToxWindow *self, const void *list, size_t n_items, size_t size, bool dir_search)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
return -1; return -1;
}
const char *L = (char *) list; if (ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE) {
return -1;
}
const char *L = (const char *) list;
const char *endchrs = " "; const char *endchrs = " ";
char ubuf[MAX_STR_SIZE]; char ubuf[MAX_STR_SIZE] = {0};
/* work with multibyte string copy of buf for simplicity */ /* work with multibyte string copy of buf for simplicity */
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
return -1; return -1;
}
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
#ifdef PYTHON
dir_search = dir_search || !strncmp(ubuf, "/run", strlen("/run"));
#endif
/* isolate substring from space behind pos to pos */ /* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE]; char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf); snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0'; tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' '); const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
char *sub = calloc(1, strlen(ubuf) + 1); char *sub = calloc(1, strlen(ubuf) + 1);
if (sub == NULL) if (sub == NULL) {
exit_toxic_err("failed in complete_line", FATALERR_MEMORY); exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY);
}
if (!s && !dir_search) { if (!s && !dir_search) {
strcpy(sub, tmp); strcpy(sub, tmp);
if (sub[0] != '/') if (sub[0] != '/') {
endchrs = ": "; endchrs = ": ";
}
} else if (s) { } else if (s) {
strcpy(sub, &s[1]); strcpy(sub, &s[1]);
@ -137,18 +148,19 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int sub_len = strlen(sub); int sub_len = strlen(sub);
int si = char_rfind(sub, '/', sub_len); int si = char_rfind(sub, '/', sub_len);
if (si || *sub == '/') if (si || *sub == '/') {
memmove(sub, &sub[si + 1], sub_len - si); memmove(sub, &sub[si + 1], sub_len - si);
}
} }
} }
if (!sub[0]) { if (!sub[0]) {
free(sub); free(sub);
return -1; return 0;
} }
int s_len = strlen(sub); int s_len = strlen(sub);
int n_matches = 0; size_t n_matches = 0;
char matches[n_items][MAX_STR_SIZE]; char matches[n_items][MAX_STR_SIZE];
int i = 0; int i = 0;
@ -157,17 +169,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
char str[MAX_CMDNAME_SIZE + 1]; char str[MAX_CMDNAME_SIZE + 1];
snprintf(str, sizeof(str), "%s", &L[i * size]); snprintf(str, sizeof(str), "%s", &L[i * size]);
if (strncasecmp(str, sub, s_len) == 0) if (strncasecmp(str, sub, s_len) == 0) {
strcpy(matches[n_matches++], str); strcpy(matches[n_matches++], str);
}
} }
free(sub); free(sub);
if (!n_matches) if (!n_matches) {
return -1; return -1;
}
if (!dir_search && n_matches > 1) if (!dir_search && n_matches > 1) {
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE); print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
}
char match[MAX_STR_SIZE]; char match[MAX_STR_SIZE];
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches); size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
@ -177,10 +192,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
} }
if (dir_search) { if (dir_search) {
if (n_matches == 1) if (n_matches == 1) {
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/"; endchrs = char_rfind(match, '.', match_len) ? "" : "/";
else } else {
endchrs = ""; endchrs = "";
}
} else if (n_matches > 1) { } else if (n_matches > 1) {
endchrs = ""; endchrs = "";
} }
@ -190,8 +206,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int strt = ctx->pos - s_len; int strt = ctx->pos - s_len;
int diff = match_len - s_len + n_endchrs; int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE) if (ctx->len + diff >= MAX_STR_SIZE) {
return -1; return -1;
}
char tmpend[MAX_STR_SIZE]; char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]); snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
@ -201,6 +218,31 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
} }
strcpy(&ubuf[strt], match); strcpy(&ubuf[strt], match);
/* If path points to a file with no extension don't append a forward slash */
if (dir_search && *endchrs == '/') {
const char *path_start = strchr(ubuf + 1, '/');
if (!path_start) {
path_start = strchr(ubuf + 1, ' ');
if (!path_start) {
return -1;
}
}
if (strlen(path_start) < 2) {
return -1;
}
++path_start;
if (file_type(path_start) == FILE_TYPE_REGULAR) {
endchrs = "";
diff -= n_endchrs;
}
}
strcpy(&ubuf[strt + match_len], endchrs); strcpy(&ubuf[strt + match_len], endchrs);
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend); strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
@ -219,8 +261,18 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
return diff; return diff;
} }
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/ int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen) {
return complete_line_helper(self, list, n_items, size, false);
}
static int complete_path(ToxWindow *self, const void *list, size_t n_items, size_t size)
{
return complete_line_helper(self, list, n_items, size, true);
}
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -228,45 +280,51 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha
get_home_dir(homedir, sizeof(homedir)); get_home_dir(homedir, sizeof(homedir));
char newline[MAX_STR_SIZE]; char newline[MAX_STR_SIZE];
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1); snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen]); snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
wchar_t wline[MAX_STR_SIZE]; wchar_t wline[MAX_STR_SIZE];
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) {
return; return;
}
int newlen = wcslen(wline); int newlen = wcslen(wline);
if (ctx->len + newlen >= MAX_STR_SIZE) if (ctx->len + newlen >= MAX_STR_SIZE) {
return; return;
}
wmemcpy(ctx->line, wline, newlen + 1); wmemcpy(ctx->line, wline, newlen + 1);
ctx->pos = newlen; ctx->pos = newlen;
ctx->len = ctx->pos; ctx->len = ctx->pos;
} }
/* attempts to match /command "<incomplete-dir>" line to matching directories. /* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
if only one match, auto-complete line. *
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */ * Returns the diff between old len and new len of ctx->line on success.
* Returns -1 if no matches or more than one match.
*/
#define MAX_DIRS 512 #define MAX_DIRS 512
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd) int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
{ {
char b_path[MAX_STR_SIZE]; char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE]; char b_name[MAX_STR_SIZE];
char b_cmd[MAX_STR_SIZE]; char b_cmd[MAX_STR_SIZE];
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */ const wchar_t *tmpline = &line[wcslen(cmd) + 1]; /* start after "/command " */
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1) {
return -1; return -1;
}
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) {
return -1; return -1;
}
if (b_path[0] == '~') if (b_path[0] == '~') {
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2); complete_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
}
int si = char_rfind(b_path, '/', strlen(b_path)); int si = char_rfind(b_path, '/', strlen(b_path));
@ -284,8 +342,9 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
int b_name_len = strlen(b_name); int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path); DIR *dp = opendir(b_path);
if (dp == NULL) if (dp == NULL) {
return -1; return -1;
}
char dirnames[MAX_DIRS][NAME_MAX + 1]; char dirnames[MAX_DIRS][NAME_MAX + 1];
struct dirent *entry; struct dirent *entry;
@ -301,13 +360,14 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
closedir(dp); closedir(dp);
if (dircount == 0) if (dircount == 0) {
return -1; return -1;
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX);
} }
return complete_line(self, dirnames, dircount, NAME_MAX); if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX + 1);
}
return complete_path(self, dirnames, dircount, NAME_MAX + 1);
} }

View File

@ -23,20 +23,25 @@
#ifndef AUTOCOMPLETE_H #ifndef AUTOCOMPLETE_H
#define AUTOCOMPLETE_H #define AUTOCOMPLETE_H
/* looks for all instances in list that begin with the last entered word in line according to pos, /*
then fills line with the complete word. e.g. "Hello jo" would complete the line * Looks for all instances in list that begin with the last entered word in line according to pos,
with "Hello john". If multiple matches, prints out all the matches and semi-completes line. * then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
*
* Returns the difference between the old len and new len of line on success.
* Returns -1 on error.
*/
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
list is a pointer to the list of strings being compared, n_items is the number of items /* Attempts to match /command "<incomplete-dir>" line to matching directories.
in the list, and size is the size of each item in the list. * If there is only one match the line is auto-completed.
*
Returns the difference between the old len and new len of line on success, -1 if error */ * Returns the diff between old len and new len of ctx->line on success.
int complete_line(ToxWindow *self, const void *list, int n_items, int size); * Returns -1 if no matches or more than one match.
*/
/* attempts to match /command "<incomplete-dir>" line to matching directories.
if only one match, auto-complete line.
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd); int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
#endif /* #define AUTOCOMPLETE_H */ #endif /* AUTOCOMPLETE_H */

View File

@ -52,12 +52,13 @@ static void avatar_clear(void)
*/ */
int avatar_send(Tox *m, uint32_t friendnum) int avatar_send(Tox *m, uint32_t friendnum)
{ {
TOX_ERR_FILE_SEND err; Tox_Err_File_Send err;
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
if (Avatar.size == 0) if (Avatar.size == 0) {
return 0; return 0;
}
if (err != TOX_ERR_FILE_SEND_OK) { if (err != TOX_ERR_FILE_SEND_OK) {
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err); fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
@ -66,13 +67,15 @@ int avatar_send(Tox *m, uint32_t friendnum)
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR); struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
if (!ft) if (!ft) {
return -1; return -1;
}
ft->file = fopen(Avatar.path, "r"); ft->file = fopen(Avatar.path, "r");
if (ft->file == NULL) if (ft->file == NULL) {
return -1; return -1;
}
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name); snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
ft->file_size = Avatar.size; ft->file_size = Avatar.size;
@ -86,8 +89,9 @@ static void avatar_send_all(Tox *m)
size_t i; size_t i;
for (i = 0; i < Friends.max_idx; ++i) { for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) {
avatar_send(m, Friends.list[i].num); avatar_send(m, Friends.list[i].num);
}
} }
} }
@ -98,15 +102,17 @@ static void avatar_send_all(Tox *m)
*/ */
int avatar_set(Tox *m, const char *path, size_t path_len) int avatar_set(Tox *m, const char *path, size_t path_len)
{ {
if (path_len == 0 || path_len >= sizeof(Avatar.path)) if (path_len == 0 || path_len >= sizeof(Avatar.path)) {
return -1; return -1;
}
FILE *fp = fopen(path, "rb"); FILE *fp = fopen(path, "rb");
if (fp == NULL) if (fp == NULL) {
return -1; return -1;
}
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) { if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
fclose(fp); fclose(fp);
@ -117,8 +123,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len)
off_t size = file_size(path); off_t size = file_size(path);
if (size == 0 || size > MAX_AVATAR_FILE_SIZE) if (size == 0 || size > MAX_AVATAR_FILE_SIZE) {
return -1; return -1;
}
get_file_name(Avatar.name, sizeof(Avatar.name), path); get_file_name(Avatar.name, sizeof(Avatar.name), path);
Avatar.name_len = strlen(Avatar.name); Avatar.name_len = strlen(Avatar.name);
@ -142,7 +149,7 @@ void avatar_unset(Tox *m)
avatar_send_all(m); avatar_send_all(m);
} }
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control) void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control)
{ {
switch (control) { switch (control) {
case TOX_FILE_CONTROL_RESUME: case TOX_FILE_CONTROL_RESUME:
@ -166,8 +173,9 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length) void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
{ {
if (ft->state != FILE_TRANSFER_STARTED) if (ft->state != FILE_TRANSFER_STARTED) {
return; return;
}
if (length == 0) { if (length == 0) {
close_file_transfer(NULL, m, ft, -1, NULL, silent); close_file_transfer(NULL, m, ft, -1, NULL, silent);
@ -196,11 +204,12 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position,
return; return;
} }
TOX_ERR_FILE_SEND_CHUNK err; Tox_Err_File_Send_Chunk err;
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err); tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err); fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
}
ft->position += send_length; ft->position += send_length;
ft->last_keep_alive = get_unix_time(); ft->last_keep_alive = get_unix_time();

View File

@ -47,6 +47,6 @@ int avatar_set(Tox *m, const char *path, size_t length);
void avatar_unset(Tox *m); void avatar_unset(Tox *m);
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length); void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control); void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control);
#endif /* AVATARS_H */ #endif /* AVATARS_H */

View File

@ -34,6 +34,7 @@
#include "configdir.h" #include "configdir.h"
#include "curl_util.h" #include "curl_util.h"
#include "settings.h" #include "settings.h"
#include "prompt.h"
extern struct arg_opts arg_opts; extern struct arg_opts arg_opts;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
@ -108,7 +109,10 @@ static struct DHT_Nodes {
/* Determine if a node is offline by comparing the age of the nodeslist /* Determine if a node is offline by comparing the age of the nodeslist
* to the last time the node was successfully pinged. * to the last time the node was successfully pinged.
*/ */
#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping)) static bool node_is_offline(unsigned long long int last_ping)
{
return last_ping + NODE_OFFLINE_TIMOUT <= last_ping;
}
/* Return true if nodeslist pointed to by fp needs to be updated. /* Return true if nodeslist pointed to by fp needs to be updated.
* This will be the case if the file is empty, has an invalid format, * This will be the case if the file is empty, has an invalid format,
@ -377,7 +381,7 @@ static int extract_node(const char *line, struct Node *node)
long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN); long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) { if (last_pinged <= 0 || node_is_offline(last_pinged)) {
return -3; return -3;
} }
@ -426,6 +430,8 @@ static int extract_node(const char *line, struct Node *node)
/* Loads the DHT nodeslist to memory from json encoded nodes file. */ /* Loads the DHT nodeslist to memory from json encoded nodes file. */
void *load_nodeslist_thread(void *data) void *load_nodeslist_thread(void *data)
{ {
UNUSED_VAR(data);
char nodes_path[PATH_MAX]; char nodes_path[PATH_MAX];
get_nodeslist_path(nodes_path, sizeof(nodes_path)); get_nodeslist_path(nodes_path, sizeof(nodes_path));
@ -556,7 +562,7 @@ static void DHT_bootstrap(Tox *m)
continue; continue;
} }
TOX_ERR_BOOTSTRAP err; Tox_Err_Bootstrap err;
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err); tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) { if (err != TOX_ERR_BOOTSTRAP_OK) {
@ -577,7 +583,7 @@ static void DHT_bootstrap(Tox *m)
void do_tox_connection(Tox *m) void do_tox_connection(Tox *m)
{ {
static time_t last_bootstrap_time = 0; static time_t last_bootstrap_time = 0;
bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE; bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE;
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) { if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
DHT_bootstrap(m); DHT_bootstrap(m);

View File

@ -39,4 +39,4 @@ void do_tox_connection(Tox *m);
*/ */
int load_DHT_nodeslist(void); int load_DHT_nodeslist(void);
#endif /* BOOTSTRAP_H */ #endif /* BOOTSTRAP_H */

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,6 @@
set msg to NULL if we don't want to display a message */ set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL); void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self, Tox *m); void kill_chat_window(ToxWindow *self, Tox *m);
ToxWindow new_chat(Tox *m, int32_t friendnum); ToxWindow *new_chat(Tox *m, int32_t friendnum);
#endif /* end of include guard: CHAT_H */ #endif /* end of include guard: CHAT_H */

View File

@ -38,6 +38,8 @@ extern FriendsList Friends;
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])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
return; return;
@ -80,6 +82,8 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
return; return;
@ -92,7 +96,7 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
return; return;
} }
TOX_ERR_CONFERENCE_INVITE err; Tox_Err_Conference_Invite err;
if (!tox_conference_invite(m, self->num, groupnum, &err)) { if (!tox_conference_invite(m, self->num, groupnum, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
@ -104,6 +108,10 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(argc);
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, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return; return;
@ -123,16 +131,16 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return; return;
} }
TOX_ERR_CONFERENCE_JOIN err; Tox_Err_Conference_Join err;
uint32_t groupnum = tox_conference_join(m, self->num, (uint8_t *) groupkey, length, &err); uint32_t groupnum = tox_conference_join(m, self->num, (const uint8_t *) groupkey, 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, "Group chat instance failed to initialize (error %d)", err); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
return; return;
} }
if (init_groupchat_win(prompt, m, groupnum, type) == -1) { if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_conference_delete(m, groupnum, NULL); tox_conference_delete(m, groupnum, NULL);
return; return;
@ -142,6 +150,8 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
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);
if (argc < 1) { if (argc < 1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
return; return;
@ -167,16 +177,17 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
} }
if ((ft->file = fopen(ft->file_path, "a")) == NULL) { if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
const char *msg = "File transfer failed: Invalid file path."; const char *msg = "File transfer failed: Invalid download path.";
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error); close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
return; return;
} }
TOX_ERR_FILE_CONTROL err; Tox_Err_File_Control err;
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err); tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
if (err != TOX_ERR_FILE_CONTROL_OK) if (err != TOX_ERR_FILE_CONTROL_OK) {
goto on_recv_error; goto on_recv_error;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
@ -217,6 +228,8 @@ on_recv_error:
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
const char *errmsg = NULL; const char *errmsg = NULL;
if (argc < 1) { if (argc < 1) {
@ -224,16 +237,9 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return; return;
} }
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE]; char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]); snprintf(path, sizeof(path), "%s", argv[1]);
int path_len = strlen(path) - 1; int path_len = strlen(path);
path[path_len] = '\0';
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
@ -258,12 +264,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
char file_name[TOX_MAX_FILENAME_LENGTH]; char file_name[TOX_MAX_FILENAME_LENGTH];
size_t namelen = get_file_name(file_name, sizeof(file_name), path); size_t namelen = get_file_name(file_name, sizeof(file_name), path);
TOX_ERR_FILE_SEND err; Tox_Err_File_Send err;
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL, uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
(uint8_t *) file_name, namelen, &err); (uint8_t *) file_name, namelen, &err);
if (err != TOX_ERR_FILE_SEND_OK) if (err != TOX_ERR_FILE_SEND_OK) {
goto on_send_error; goto on_send_error;
}
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA); struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);

View File

@ -49,4 +49,4 @@ void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */ #endif /* VIDEO */
#endif /* #define CHAT_COMMANDS_H */ #endif /* CHAT_COMMANDS_H */

View File

@ -48,8 +48,9 @@ void get_home_dir(char *home, int size)
} else { } else {
hmstr = getenv("HOME"); hmstr = getenv("HOME");
if (hmstr == NULL) if (hmstr == NULL) {
return; return;
}
snprintf(buf, sizeof(buf), "%s", hmstr); snprintf(buf, sizeof(buf), "%s", hmstr);
hmstr = buf; hmstr = buf;
@ -77,8 +78,9 @@ char *get_user_config_dir(void)
len = strlen(home) + strlen("/Library/Application Support") + 1; len = strlen(home) + strlen("/Library/Application Support") + 1;
user_config_dir = malloc(len); user_config_dir = malloc(len);
if (user_config_dir == NULL) if (user_config_dir == NULL) {
return NULL; return NULL;
}
snprintf(user_config_dir, len, "%s/Library/Application Support", home); snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */ # else /* __APPLE__ */
@ -89,8 +91,9 @@ char *get_user_config_dir(void)
len = strlen(home) + strlen("/.config") + 1; len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len); user_config_dir = malloc(len);
if (user_config_dir == NULL) if (user_config_dir == NULL) {
return NULL; return NULL;
}
snprintf(user_config_dir, len, "%s/.config", home); snprintf(user_config_dir, len, "%s/.config", home);
} else { } else {
@ -112,14 +115,16 @@ int create_user_config_dirs(char *path)
struct stat buf; struct stat buf;
int mkdir_err = mkdir(path, 0700); int mkdir_err = mkdir(path, 0700);
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
return -1; return -1;
}
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1); char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1); char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
if (fullpath == NULL || logpath == NULL) if (fullpath == NULL || logpath == NULL) {
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY); exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
}
strcpy(fullpath, path); strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR); strcat(fullpath, CONFIGDIR);

View File

@ -53,4 +53,4 @@ void get_home_dir(char *home, int size);
*/ */
int create_user_config_dirs(char *path); int create_user_config_dirs(char *path);
#endif /* #define CONFIGDIR_H */ #endif /* CONFIGDIR_H */

View File

@ -36,8 +36,9 @@
*/ */
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type) int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
{ {
if (proxy_type == TOX_PROXY_TYPE_NONE) if (proxy_type == TOX_PROXY_TYPE_NONE) {
return 0; return 0;
}
if (proxy_address == NULL || port == 0) { if (proxy_address == NULL || port == 0) {
return -1; return -1;

View File

@ -52,4 +52,4 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin
*/ */
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer); size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
#endif /* CURL_UTIL_H */ #endif /* CURL_UTIL_H */

View File

@ -52,7 +52,9 @@ static struct cmd_func global_commands[] = {
{ "/help", cmd_prompt_help }, { "/help", cmd_prompt_help },
{ "/log", cmd_log }, { "/log", cmd_log },
{ "/myid", cmd_myid }, { "/myid", cmd_myid },
#ifdef QRCODE
{ "/myqr", cmd_myqr }, { "/myqr", cmd_myqr },
#endif /* QRCODE */
{ "/nick", cmd_nick }, { "/nick", cmd_nick },
{ "/note", cmd_note }, { "/note", cmd_note },
{ "/nospam", cmd_nospam }, { "/nospam", cmd_nospam },
@ -105,52 +107,104 @@ static struct cmd_func group_commands[] = {
{ NULL, NULL }, { NULL, NULL },
}; };
/* Parses input command and puts args into arg array.
Returns number of arguments on success, -1 on failure. */ #ifdef PYTHON
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE]) #define SPECIAL_COMMANDS 6
#else
#define SPECIAL_COMMANDS 5
#endif /* PYTHON */
/* Special commands are commands that only take one argument even if it contains spaces */
static const char special_commands[SPECIAL_COMMANDS][MAX_CMDNAME_SIZE] = {
"/avatar",
"/nick",
"/note",
#ifdef PYTHON
"/run",
#endif /* PYTHON */
"/sendfile",
"/title",
};
/* Returns true if input command is in the special_commands array. */
static bool is_special_command(const char *input)
{ {
const int s = char_find(0, input, ' ');
for (int i = 0; i < SPECIAL_COMMANDS; ++i) {
if (strncmp(input, special_commands[i], s) == 0) {
return true;
}
}
return false;
}
/* Parses commands in the special_commands array. Unlike parse_command, this function
* does not split the input string at spaces.
*
* Returns the number of arguments.
*/
static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE])
{
int len = strlen(input);
int s = char_find(0, input, ' ');
memcpy(args[0], input, s);
args[0][s++] = '\0'; // increment to remove space after "/command "
if (s >= len) {
return 1; // No additional args
}
memcpy(args[1], input + s, len - s);
args[1][len - s] = '\0';
return 2;
}
/* Parses input command and puts args into arg array.
*
* Returns the number of arguments.
*/
static int parse_command(const char *input, char (*args)[MAX_STR_SIZE])
{
if (is_special_command(input)) {
return parse_special_command(input, args);
}
char *cmd = strdup(input); char *cmd = strdup(input);
if (cmd == NULL) if (cmd == NULL) {
exit_toxic_err("failed in parse_command", FATALERR_MEMORY); exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
}
int num_args = 0; int num_args = 0;
int i = 0; /* index of last char in an argument */
/* characters wrapped in double quotes count as one arg */ /* characters wrapped in double quotes count as one arg */
while (num_args < MAX_NUM_ARGS) { while (num_args < MAX_NUM_ARGS) {
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */ int i = char_find(0, cmd, ' '); // index of last char in an argument
memcpy(args[num_args], cmd, i);
args[num_args++][i] = '\0';
if (*cmd == '\"') { if (cmd[i] == '\0') { // no more args
qt_ofst = 1;
i = char_find(1, cmd, '\"');
if (cmd[i] == '\0') {
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
free(cmd);
return -1;
}
} else {
i = char_find(0, cmd, ' ');
}
memcpy(args[num_args], cmd, i + qt_ofst);
args[num_args++][i + qt_ofst] = '\0';
if (cmd[i] == '\0') /* no more args */
break; break;
}
char tmp[MAX_STR_SIZE]; char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]); snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
strcpy(cmd, tmp); /* tmp will always fit inside cmd */ strcpy(cmd, tmp); // tmp will always fit inside cmd
} }
free(cmd); free(cmd);
return num_args; return num_args;
} }
/* Matches command to respective function. Returns 0 on match, 1 on no match */ /* Matches command to respective function.
*
* Returns 0 on match.
* Returns 1 on no match
*/
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands, static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
char (*args)[MAX_STR_SIZE]) char (*args)[MAX_STR_SIZE])
{ {
@ -168,40 +222,47 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode) void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
{ {
if (string_is_empty(input)) if (string_is_empty(input)) {
return; return;
}
char args[MAX_NUM_ARGS][MAX_STR_SIZE]; char args[MAX_NUM_ARGS][MAX_STR_SIZE];
int num_args = parse_command(w, self, input, args); int num_args = parse_command(input, args);
if (num_args == -1) if (num_args <= 0) {
return; return;
}
/* Try to match input command to command functions. If non-global command mode is specified, /* Try to match input command to command functions. If non-global command mode is specified,
try specified mode's commands first, then upon failure try global commands. * try specified mode's commands first, then upon failure try global commands.
*
Note: Global commands must come last in case of duplicate command names */ * Note: Global commands must come last in case of duplicate command names
*/
switch (mode) { switch (mode) {
case CHAT_COMMAND_MODE: case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, chat_commands, args) == 0) if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
return; return;
}
break; break;
case GROUPCHAT_COMMAND_MODE: case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0) if (do_command(w, self, m, num_args, group_commands, args) == 0) {
return; return;
}
break; break;
} }
if (do_command(w, self, m, num_args, global_commands, args) == 0) if (do_command(w, self, m, num_args, global_commands, args) == 0) {
return; return;
}
#ifdef PYTHON #ifdef PYTHON
if (do_plugin_command(num_args, args) == 0) if (do_plugin_command(num_args, args) == 0) {
return; return;
}
#endif #endif

View File

@ -36,4 +36,4 @@ enum {
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode); void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
#endif /* #define EXECUTE_H */ #endif /* EXECUTE_H */

View File

@ -45,8 +45,9 @@ void init_progress_bar(char *progline)
strcpy(progline, "0% ["); strcpy(progline, "0% [");
int i; int i;
for (i = 0; i < NUM_PROG_MARKS; ++i) for (i = 0; i < NUM_PROG_MARKS; ++i) {
strcat(progline, "-"); strcat(progline, "-");
}
strcat(progline, "] 0.0 B/s"); strcat(progline, "] 0.0 B/s");
} }
@ -54,8 +55,9 @@ void init_progress_bar(char *progline)
/* prints a progress bar for file transfers. */ /* prints a progress bar for file transfers. */
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id) void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
{ {
if (bps < 0 || pct_done < 0 || pct_done > 100) if (bps < 0 || pct_done < 0 || pct_done > 100) {
return; return;
}
char pct_str[24]; char pct_str[24];
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done); snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
@ -67,14 +69,17 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
int n = pct_done / (100 / NUM_PROG_MARKS); int n = pct_done / (100 / NUM_PROG_MARKS);
int i, j; int i, j;
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i) {
strcat(prog_line, "="); strcat(prog_line, "=");
}
if (pct_done < 100) if (pct_done < 100) {
strcpy(prog_line + n, ">"); strcpy(prog_line + n, ">");
}
for (j = i; j < NUM_PROG_MARKS - 1; ++j) for (j = i; j < NUM_PROG_MARKS - 1; ++j) {
strcat(prog_line, "-"); strcat(prog_line, "-");
}
char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7]; char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str); snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
@ -82,14 +87,16 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
line_info_set(self, line_id, full_line); line_info_set(self, line_id, full_line);
} }
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft) static void refresh_progress_helper(ToxWindow *self, struct FileTransfer *ft)
{ {
if (ft->state == FILE_TRANSFER_INACTIVE) if (ft->state == FILE_TRANSFER_INACTIVE) {
return; return;
}
/* Timeout must be set to 1 second to show correct bytes per second */ /* Timeout must be set to 1 second to show correct bytes per second */
if (!timed_out(ft->last_line_progress, 1)) if (!timed_out(ft->last_line_progress, 1)) {
return; return;
}
double remain = ft->file_size - ft->position; double remain = ft->file_size - ft->position;
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100; double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
@ -100,13 +107,13 @@ static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer
} }
/* refreshes active file transfer status bars. */ /* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum) void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum)
{ {
size_t i; size_t i;
for (i = 0; i < MAX_FILES; ++i) { for (i = 0; i < MAX_FILES; ++i) {
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]); refresh_progress_helper(self, &Friends.list[friendnum].file_receiver[i]);
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]); refresh_progress_helper(self, &Friends.list[friendnum].file_sender[i]);
} }
} }
@ -120,13 +127,15 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
for (i = 0; i < MAX_FILES; ++i) { for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i]; struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum) {
return ft_send; return ft_send;
}
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i]; struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum) {
return ft_recv; return ft_recv;
}
} }
return NULL; return NULL;
@ -138,8 +147,9 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction) FILE_TRANSFER_DIRECTION direction)
{ {
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
return NULL; return NULL;
}
size_t i; size_t i;
@ -148,8 +158,9 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
&Friends.list[friendnum].file_sender[i] : &Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i]; &Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
return ft; return ft;
}
} }
return NULL; return NULL;
@ -215,11 +226,13 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum, struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
FILE_TRANSFER_DIRECTION direction, uint8_t type) FILE_TRANSFER_DIRECTION direction, uint8_t type)
{ {
if (direction == FILE_TRANSFER_RECV) if (direction == FILE_TRANSFER_RECV) {
return new_file_receiver(window, friendnum, filenum, type); return new_file_receiver(window, friendnum, filenum, type);
}
if (direction == FILE_TRANSFER_SEND) if (direction == FILE_TRANSFER_SEND) {
return new_file_sender(window, friendnum, filenum, type); return new_file_sender(window, friendnum, filenum, type);
}
return NULL; return NULL;
} }
@ -233,23 +246,28 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message, void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
Notification sound_type) Notification sound_type)
{ {
if (!ft) if (!ft) {
return; return;
}
if (ft->state == FILE_TRANSFER_INACTIVE) if (ft->state == FILE_TRANSFER_INACTIVE) {
return; return;
}
if (ft->file) if (ft->file) {
fclose(ft->file); fclose(ft->file);
}
if (CTRL >= 0) if (CTRL >= 0) {
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL); tox_file_control(m, ft->friendnum, ft->filenum, (Tox_File_Control) CTRL, NULL);
}
if (message && self) { if (message && self) {
if (self->active_box != -1 && sound_type != silent) if (self->active_box != -1 && sound_type != silent) {
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message); box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
else } else {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
} }
@ -272,6 +290,7 @@ void kill_all_file_transfers(Tox *m)
{ {
size_t i; size_t i;
for (i = 0; i < Friends.max_idx; ++i) for (i = 0; i < Friends.max_idx; ++i) {
kill_all_file_transfers_friend(m, Friends.list[i].num); kill_all_file_transfers_friend(m, Friends.list[i].num);
}
} }

View File

@ -75,7 +75,7 @@ void init_progress_bar(char *progline);
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id); void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
/* refreshes active file transfer status bars. */ /* refreshes active file transfer status bars. */
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum); void refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnum);
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum. /* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
* Returns NULL if filenum is invalid. * Returns NULL if filenum is invalid.
@ -108,4 +108,4 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
void kill_all_file_transfers(Tox *m); void kill_all_file_transfers(Tox *m);
#endif /* #define FILE_TRANSFERS_H */ #endif /* FILE_TRANSFERS_H */

View File

@ -84,8 +84,9 @@ static void realloc_friends(int n)
ToxicFriend *f = realloc(Friends.list, n * sizeof(ToxicFriend)); ToxicFriend *f = realloc(Friends.list, n * sizeof(ToxicFriend));
uint32_t *f_idx = realloc(Friends.index, n * sizeof(uint32_t)); uint32_t *f_idx = realloc(Friends.index, n * sizeof(uint32_t));
if (f == NULL || f_idx == NULL) if (f == NULL || f_idx == NULL) {
exit_toxic_err("failed in realloc_friends", FATALERR_MEMORY); exit_toxic_err("failed in realloc_friends", FATALERR_MEMORY);
}
Friends.list = f; Friends.list = f;
Friends.index = f_idx; Friends.index = f_idx;
@ -104,24 +105,26 @@ static void realloc_blocklist(int n)
BlockedFriend *b = realloc(Blocked.list, n * sizeof(BlockedFriend)); BlockedFriend *b = realloc(Blocked.list, n * sizeof(BlockedFriend));
uint32_t *b_idx = realloc(Blocked.index, n * sizeof(uint32_t)); uint32_t *b_idx = realloc(Blocked.index, n * sizeof(uint32_t));
if (b == NULL || b_idx == NULL) if (b == NULL || b_idx == NULL) {
exit_toxic_err("failed in realloc_blocklist", FATALERR_MEMORY); exit_toxic_err("failed in realloc_blocklist", FATALERR_MEMORY);
}
Blocked.list = b; Blocked.list = b;
Blocked.index = b_idx; Blocked.index = b_idx;
} }
void kill_friendlist(void) void kill_friendlist(ToxWindow *self)
{ {
int i; for (size_t i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) {
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL)
free(Friends.list[i].group_invite.key); free(Friends.list[i].group_invite.key);
}
} }
realloc_blocklist(0); realloc_blocklist(0);
realloc_friends(0); realloc_friends(0);
free(self->help);
del_window(self);
} }
/* Saves the blocklist to path. If there are no items in the blocklist the /* Saves the blocklist to path. If there are no items in the blocklist the
@ -215,13 +218,15 @@ static void sort_blocklist_index(void);
int load_blocklist(char *path) int load_blocklist(char *path)
{ {
if (path == NULL) if (path == NULL) {
return -1; return -1;
}
FILE *fp = fopen(path, "rb"); FILE *fp = fopen(path, "rb");
if (fp == NULL) if (fp == NULL) {
return -1; return -1;
}
off_t len = file_size(path); off_t len = file_size(path);
@ -298,11 +303,14 @@ void sort_friendlist_index(void)
uint32_t n = 0; uint32_t n = 0;
for (i = 0; i < Friends.max_idx; ++i) { for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active) if (Friends.list[i].active) {
Friends.index[n++] = Friends.list[i].num; Friends.index[n++] = Friends.list[i].num;
}
} }
qsort(Friends.index, Friends.num_friends, sizeof(uint32_t), index_name_cmp); if (Friends.num_friends > 0) {
qsort(Friends.index, Friends.num_friends, sizeof(uint32_t), index_name_cmp);
}
} }
static int index_name_cmp_block(const void *n1, const void *n2) static int index_name_cmp_block(const void *n1, const void *n2)
@ -316,8 +324,9 @@ static void sort_blocklist_index(void)
uint32_t n = 0; uint32_t n = 0;
for (i = 0; i < Blocked.max_idx; ++i) { for (i = 0; i < Blocked.max_idx; ++i) {
if (Blocked.list[i].active) if (Blocked.list[i].active) {
Blocked.index[n++] = Blocked.list[i].num; Blocked.index[n++] = Blocked.list[i].num;
}
} }
qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block); qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block);
@ -334,14 +343,20 @@ static void update_friend_last_online(uint32_t num, time_t timestamp)
&Friends.list[num].last_online.tm); &Friends.list[num].last_online.tm);
} }
static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *str, static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Message_Type type, const char *str,
size_t length) size_t length)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
return; UNUSED_VAR(type);
UNUSED_VAR(length);
if (Friends.list[num].chatwin != -1) if (num >= Friends.max_idx) {
return; return;
}
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num)); Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -359,18 +374,22 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESS
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status) static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_Connection connection_status)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
if (num >= Friends.max_idx) {
return; return;
}
if (connection_status == TOX_CONNECTION_NONE) { if (connection_status == TOX_CONNECTION_NONE) {
--Friends.num_online; --Friends.num_online;
} else if (Friends.list[num].connection_status == TOX_CONNECTION_NONE) { } else if (Friends.list[num].connection_status == TOX_CONNECTION_NONE) {
++Friends.num_online; ++Friends.num_online;
if (avatar_send(m, num) == -1) if (avatar_send(m, num) == -1) {
fprintf(stderr, "avatar_send failed for friend %d\n", num); fprintf(stderr, "avatar_send failed for friend %d\n", num);
}
} }
Friends.list[num].connection_status = connection_status; Friends.list[num].connection_status = connection_status;
@ -381,8 +400,12 @@ static void friendlist_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num,
static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const char *nick, size_t length) static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const char *nick, size_t length)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
UNUSED_VAR(length);
if (num >= Friends.max_idx) {
return; return;
}
/* save old name for log renaming */ /* save old name for log renaming */
char oldname[TOXIC_MAX_NAME_LENGTH + 1]; char oldname[TOXIC_MAX_NAME_LENGTH + 1];
@ -398,24 +421,32 @@ static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const
strcpy(newnamecpy, Friends.list[num].name); strcpy(newnamecpy, Friends.list[num].name);
tox_self_get_address(m, (uint8_t *) myid); tox_self_get_address(m, (uint8_t *) myid);
if (strcmp(oldname, newnamecpy) != 0) if (strcmp(oldname, newnamecpy) != 0) {
rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin); rename_logfile(oldname, newnamecpy, myid, Friends.list[num].pub_key, Friends.list[num].chatwin);
}
sort_friendlist_index(); sort_friendlist_index();
} }
static void friendlist_onStatusChange(ToxWindow *self, Tox *m, uint32_t num, TOX_USER_STATUS status) static void friendlist_onStatusChange(ToxWindow *self, Tox *m, uint32_t num, Tox_User_Status status)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
UNUSED_VAR(m);
if (num >= Friends.max_idx) {
return; return;
}
Friends.list[num].status = status; Friends.list[num].status = status;
} }
static void friendlist_onStatusMessageChange(ToxWindow *self, uint32_t num, const char *note, size_t length) static void friendlist_onStatusMessageChange(ToxWindow *self, uint32_t num, const char *note, size_t length)
{ {
if (length > TOX_MAX_STATUS_MESSAGE_LENGTH || num >= Friends.max_idx) UNUSED_VAR(self);
if (length > TOX_MAX_STATUS_MESSAGE_LENGTH || num >= Friends.max_idx) {
return; return;
}
snprintf(Friends.list[num].statusmsg, sizeof(Friends.list[num].statusmsg), "%s", note); snprintf(Friends.list[num].statusmsg, sizeof(Friends.list[num].statusmsg), "%s", note);
Friends.list[num].statusmsg_len = strlen(Friends.list[num].statusmsg); Friends.list[num].statusmsg_len = strlen(Friends.list[num].statusmsg);
@ -423,14 +454,17 @@ static void friendlist_onStatusMessageChange(ToxWindow *self, uint32_t num, cons
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort) void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
{ {
UNUSED_VAR(self);
realloc_friends(Friends.max_idx + 1); realloc_friends(Friends.max_idx + 1);
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend)); memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
uint32_t i; uint32_t i;
for (i = 0; i <= Friends.max_idx; ++i) { for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active) if (Friends.list[i].active) {
continue; continue;
}
++Friends.num_friends; ++Friends.num_friends;
@ -441,17 +475,19 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
Friends.list[i].status = TOX_USER_STATUS_NONE; Friends.list[i].status = TOX_USER_STATUS_NONE;
Friends.list[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON; Friends.list[i].logging_on = (bool) user_settings->autolog == AUTOLOG_ON;
TOX_ERR_FRIEND_GET_PUBLIC_KEY pkerr; Tox_Err_Friend_Get_Public_Key pkerr;
tox_friend_get_public_key(m, num, (uint8_t *) Friends.list[i].pub_key, &pkerr); tox_friend_get_public_key(m, num, (uint8_t *) Friends.list[i].pub_key, &pkerr);
if (pkerr != TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK) if (pkerr != TOX_ERR_FRIEND_GET_PUBLIC_KEY_OK) {
fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr); fprintf(stderr, "tox_friend_get_public_key failed (error %d)\n", pkerr);
}
TOX_ERR_FRIEND_GET_LAST_ONLINE loerr; Tox_Err_Friend_Get_Last_Online loerr;
time_t t = tox_friend_get_last_online(m, num, &loerr); time_t t = tox_friend_get_last_online(m, num, &loerr);
if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) {
t = 0; t = 0;
}
update_friend_last_online(i, t); update_friend_last_online(i, t);
@ -460,18 +496,24 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname); snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname);
Friends.list[i].namelength = strlen(Friends.list[i].name); Friends.list[i].namelength = strlen(Friends.list[i].name);
if (i == Friends.max_idx) if (i == Friends.max_idx) {
++Friends.max_idx; ++Friends.max_idx;
}
if (sort) if (sort) {
sort_friendlist_index(); sort_friendlist_index();
}
#ifdef AUDIO
init_friend_AV(i);
#endif
return; return;
} }
} }
/* puts blocked friend back in friendlist. fnum is new friend number, bnum is blocked number */ /* Puts blocked friend back in friendlist. fnum is new friend number, bnum is blocked number. */
static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum) static void friendlist_add_blocked(uint32_t fnum, uint32_t bnum)
{ {
realloc_friends(Friends.max_idx + 1); realloc_friends(Friends.max_idx + 1);
memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend)); memset(&Friends.list[Friends.max_idx], 0, sizeof(ToxicFriend));
@ -479,8 +521,9 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
int i; int i;
for (i = 0; i <= Friends.max_idx; ++i) { for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active) if (Friends.list[i].active) {
continue; continue;
}
++Friends.num_friends; ++Friends.num_friends;
@ -494,12 +537,16 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
memcpy(Friends.list[i].name, Blocked.list[bnum].name, Friends.list[i].namelength + 1); memcpy(Friends.list[i].name, Blocked.list[bnum].name, Friends.list[i].namelength + 1);
memcpy(Friends.list[i].pub_key, Blocked.list[bnum].pub_key, TOX_PUBLIC_KEY_SIZE); memcpy(Friends.list[i].pub_key, Blocked.list[bnum].pub_key, TOX_PUBLIC_KEY_SIZE);
if (i == Friends.max_idx) if (i == Friends.max_idx) {
++Friends.max_idx; ++Friends.max_idx;
}
sort_blocklist_index(); sort_blocklist_index();
sort_friendlist_index(); sort_friendlist_index();
#ifdef AUDIO
init_friend_AV(i);
#endif
return; return;
} }
} }
@ -507,11 +554,18 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
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)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
return; UNUSED_VAR(file_size);
UNUSED_VAR(filename);
UNUSED_VAR(name_length);
if (Friends.list[num].chatwin != -1) if (num >= Friends.max_idx) {
return; return;
}
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num)); Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -532,11 +586,18 @@ static void friendlist_onFileRecv(ToxWindow *self, Tox *m, uint32_t num, uint32_
static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key, static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8_t type, const char *group_pub_key,
uint16_t length) uint16_t length)
{ {
if (num >= Friends.max_idx) UNUSED_VAR(self);
return; UNUSED_VAR(type);
UNUSED_VAR(group_pub_key);
UNUSED_VAR(length);
if (Friends.list[num].chatwin != -1) if (num >= Friends.max_idx) {
return; return;
}
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num)); Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -553,14 +614,16 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
} }
/* move friendlist/blocklist cursor up and down */ /* move friendlist/blocklist cursor up and down */
static void select_friend(ToxWindow *self, wint_t key, int *selected, int num) static void select_friend(wint_t key, int *selected, int num)
{ {
if (num <= 0) if (num <= 0) {
return; return;
}
if (key == KEY_UP) { if (key == KEY_UP) {
if (--(*selected) < 0) if (--(*selected) < 0) {
*selected = num - 1; *selected = num - 1;
}
} else if (key == KEY_DOWN) { } else if (key == KEY_DOWN) {
*selected = (*selected + 1) % num; *selected = (*selected + 1) % num;
} }
@ -568,7 +631,7 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
static void delete_friend(Tox *m, uint32_t f_num) static void delete_friend(Tox *m, uint32_t f_num)
{ {
TOX_ERR_FRIEND_DELETE err; Tox_Err_Friend_Delete err;
if (tox_friend_delete(m, f_num, &err) != true) { if (tox_friend_delete(m, f_num, &err) != true) {
fprintf(stderr, "tox_friend_delete failed with error %d\n", err); fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
@ -577,8 +640,9 @@ static void delete_friend(Tox *m, uint32_t f_num)
--Friends.num_friends; --Friends.num_friends;
if (Friends.list[f_num].connection_status != TOX_CONNECTION_NONE) if (Friends.list[f_num].connection_status != TOX_CONNECTION_NONE) {
--Friends.num_online; --Friends.num_online;
}
/* close friend's chatwindow if it's currently open */ /* close friend's chatwindow if it's currently open */
if (Friends.list[f_num].chatwin >= 0) { if (Friends.list[f_num].chatwin >= 0) {
@ -586,34 +650,41 @@ static void delete_friend(Tox *m, uint32_t f_num)
if (toxwin != NULL) { if (toxwin != NULL) {
kill_chat_window(toxwin, m); kill_chat_window(toxwin, m);
set_active_window(1); /* keep friendlist focused */ set_active_window_index(1); /* keep friendlist focused */
} }
} }
if (Friends.list[f_num].group_invite.key != NULL) if (Friends.list[f_num].group_invite.key != NULL) {
free(Friends.list[f_num].group_invite.key); free(Friends.list[f_num].group_invite.key);
}
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend)); memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
int i; int i;
for (i = Friends.max_idx; i > 0; --i) { for (i = Friends.max_idx; i > 0; --i) {
if (Friends.list[i - 1].active) if (Friends.list[i - 1].active) {
break; break;
}
} }
Friends.max_idx = i; Friends.max_idx = i;
realloc_friends(i); realloc_friends(i);
#ifdef AUDIO
del_friend_AV(i);
#endif
/* make sure num_selected stays within Friends.num_friends range */ /* make sure num_selected stays within Friends.num_friends range */
if (Friends.num_friends && Friends.num_selected == Friends.num_friends) if (Friends.num_friends && Friends.num_selected == Friends.num_friends) {
--Friends.num_selected; --Friends.num_selected;
}
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
/* activates delete friend popup */ /* activates delete friend popup */
static void del_friend_activate(ToxWindow *self, Tox *m, uint32_t f_num) static void del_friend_activate(uint32_t f_num)
{ {
PendingDelete.popup = newwin(3, 22 + TOXIC_MAX_NAME_LENGTH, 8, 8); PendingDelete.popup = newwin(3, 22 + TOXIC_MAX_NAME_LENGTH, 8, 8);
PendingDelete.active = true; PendingDelete.active = true;
@ -623,7 +694,7 @@ static void del_friend_activate(ToxWindow *self, Tox *m, uint32_t f_num)
static void delete_blocked_friend(uint32_t bnum); static void delete_blocked_friend(uint32_t bnum);
/* deactivates delete friend popup and deletes friend if instructed */ /* deactivates delete friend popup and deletes friend if instructed */
static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key) static void del_friend_deactivate(Tox *m, wint_t key)
{ {
if (key == 'y') { if (key == 'y') {
if (blocklist_view == 0) { if (blocklist_view == 0) {
@ -643,8 +714,9 @@ static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key)
static void draw_del_popup(void) static void draw_del_popup(void)
{ {
if (!PendingDelete.active) if (!PendingDelete.active) {
return; return;
}
wattron(PendingDelete.popup, A_BOLD); wattron(PendingDelete.popup, A_BOLD);
box(PendingDelete.popup, ACS_VLINE, ACS_HLINE); box(PendingDelete.popup, ACS_VLINE, ACS_HLINE);
@ -656,10 +728,11 @@ static void draw_del_popup(void)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (blocklist_view == 0) if (blocklist_view == 0) {
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name); wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
else } else {
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name); wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
}
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -677,8 +750,9 @@ static void delete_blocked_friend(uint32_t bnum)
int i; int i;
for (i = Blocked.max_idx; i > 0; --i) { for (i = Blocked.max_idx; i > 0; --i) {
if (Blocked.list[i - 1].active) if (Blocked.list[i - 1].active) {
break; break;
}
} }
--Blocked.num_blocked; --Blocked.num_blocked;
@ -686,15 +760,17 @@ static void delete_blocked_friend(uint32_t bnum)
realloc_blocklist(i); realloc_blocklist(i);
save_blocklist(BLOCK_FILE); save_blocklist(BLOCK_FILE);
if (Blocked.num_blocked && Blocked.num_selected == Blocked.num_blocked) if (Blocked.num_blocked && Blocked.num_selected == Blocked.num_blocked) {
--Blocked.num_selected; --Blocked.num_selected;
}
} }
/* deletes contact from friendlist and puts in blocklist */ /* deletes contact from friendlist and puts in blocklist */
void block_friend(Tox *m, uint32_t fnum) void block_friend(Tox *m, uint32_t fnum)
{ {
if (Friends.num_friends <= 0) if (Friends.num_friends <= 0) {
return; return;
}
realloc_blocklist(Blocked.max_idx + 1); realloc_blocklist(Blocked.max_idx + 1);
memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend)); memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend));
@ -702,8 +778,9 @@ void block_friend(Tox *m, uint32_t fnum)
int i; int i;
for (i = 0; i <= Blocked.max_idx; ++i) { for (i = 0; i <= Blocked.max_idx; ++i) {
if (Blocked.list[i].active) if (Blocked.list[i].active) {
continue; continue;
}
Blocked.list[i].active = true; Blocked.list[i].active = true;
Blocked.list[i].num = i; Blocked.list[i].num = i;
@ -714,8 +791,9 @@ void block_friend(Tox *m, uint32_t fnum)
++Blocked.num_blocked; ++Blocked.num_blocked;
if (i == Blocked.max_idx) if (i == Blocked.max_idx) {
++Blocked.max_idx; ++Blocked.max_idx;
}
delete_friend(m, fnum); delete_friend(m, fnum);
save_blocklist(BLOCK_FILE); save_blocklist(BLOCK_FILE);
@ -729,10 +807,11 @@ void block_friend(Tox *m, uint32_t fnum)
/* removes friend from blocklist, puts back in friendlist */ /* removes friend from blocklist, puts back in friendlist */
static void unblock_friend(Tox *m, uint32_t bnum) static void unblock_friend(Tox *m, uint32_t bnum)
{ {
if (Blocked.num_blocked <= 0) if (Blocked.num_blocked <= 0) {
return; return;
}
TOX_ERR_FRIEND_ADD err; Tox_Err_Friend_Add err;
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) {
@ -740,60 +819,69 @@ static void unblock_friend(Tox *m, uint32_t bnum)
return; return;
} }
friendlist_add_blocked(m, friendnum, bnum); friendlist_add_blocked(friendnum, bnum);
delete_blocked_friend(bnum); delete_blocked_friend(bnum);
sort_blocklist_index(); sort_blocklist_index();
sort_friendlist_index(); sort_friendlist_index();
} }
static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) /*
* Return true if input is recognized by handler
*/
static bool friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
if (self->help->active) { if (self->help->active) {
help_onKey(self, key); help_onKey(self, key);
return; return true;
} }
if (key == 'h') { if (key == 'h') {
help_init_menu(self); help_init_menu(self);
return; return true;
} }
if (!blocklist_view && !Friends.num_friends && (key != KEY_RIGHT && key != KEY_LEFT)) if (!blocklist_view && !Friends.num_friends && (key != KEY_RIGHT && key != KEY_LEFT)) {
return; return true;
}
if (blocklist_view && !Blocked.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT)) if (blocklist_view && !Blocked.num_blocked && (key != KEY_RIGHT && key != KEY_LEFT)) {
return; return true;
}
int f = 0; int f = 0;
if (blocklist_view == 1 && Blocked.num_blocked) if (blocklist_view == 1 && Blocked.num_blocked) {
f = Blocked.index[Blocked.num_selected]; f = Blocked.index[Blocked.num_selected];
else if (Friends.num_friends) } else if (Friends.num_friends) {
f = Friends.index[Friends.num_selected]; f = Friends.index[Friends.num_selected];
}
/* lock screen and force decision on deletion popup */ /* lock screen and force decision on deletion popup */
if (PendingDelete.active) { if (PendingDelete.active) {
if (key == 'y' || key == 'n') if (key == 'y' || key == 'n') {
del_friend_deactivate(self, m, key); del_friend_deactivate(m, key);
}
return; return true;
} }
if (key == ltr) if (key == ltr) {
return; return true;
}
switch (key) { switch (key) {
case '\r': case '\r':
if (blocklist_view) if (blocklist_view) {
break; break;
}
/* Jump to chat window if already open */ /* Jump to chat window if already open */
if (Friends.list[f].chatwin != -1) { if (Friends.list[f].chatwin != -1) {
set_active_window(Friends.list[f].chatwin); set_active_window_index(Friends.list[f].chatwin);
} else if (get_num_active_windows() < MAX_WINDOWS_NUM) { } else if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[f].chatwin = add_window(m, new_chat(m, Friends.list[f].num)); Friends.list[f].chatwin = add_window(m, new_chat(m, Friends.list[f].num));
set_active_window(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, NULL, NULL, NULL, SYS_MSG, 0, RED, msg);
@ -803,14 +891,15 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
break; break;
case KEY_DC: case KEY_DC:
del_friend_activate(self, m, f); del_friend_activate(f);
break; break;
case 'b': case 'b':
if (!blocklist_view) if (!blocklist_view) {
block_friend(m, f); block_friend(m, f);
else } else {
unblock_friend(m, f); unblock_friend(m, f);
}
break; break;
@ -820,26 +909,32 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
break; break;
default: default:
if (blocklist_view == 0) if (blocklist_view == 0) {
select_friend(self, key, &Friends.num_selected, Friends.num_friends); select_friend(key, &Friends.num_selected, Friends.num_friends);
else } else {
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked); select_friend(key, &Blocked.num_selected, Blocked.num_blocked);
}
break; break;
} }
return true;
} }
#define FLIST_OFST 6 /* Accounts for space at top and bottom */ #define FLIST_OFST 6 /* Accounts for space at top and bottom */
static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2) static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
{ {
UNUSED_VAR(m);
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, " Blocked: "); wprintw(self->window, " Blocked: ");
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
wprintw(self->window, "%d\n\n", Blocked.num_blocked); wprintw(self->window, "%d\n\n", Blocked.num_blocked);
if ((y2 - FLIST_OFST) <= 0) if ((y2 - FLIST_OFST) <= 0) {
return; return;
}
uint32_t selected_num = 0; uint32_t selected_num = 0;
@ -868,15 +963,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wprintw(self->window, "x"); wprintw(self->window, "x");
wattroff(self->window, COLOR_PAIR(RED)); wattroff(self->window, COLOR_PAIR(RED));
if (f_selected) if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
wprintw(self->window, " %s\n", Blocked.list[f].name); wprintw(self->window, " %s\n", Blocked.list[f].name);
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
if (f_selected) if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE)); wattroff(self->window, COLOR_PAIR(BLUE));
}
} }
wprintw(self->window, "\n"); wprintw(self->window, "\n");
@ -891,15 +988,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
int i; int i;
for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) {
wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff); wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
}
} }
wnoutrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active) {
help_onDraw(self); help_onDraw(self);
}
} }
static void friendlist_onDraw(ToxWindow *self, Tox *m) static void friendlist_onDraw(ToxWindow *self, Tox *m)
@ -933,8 +1032,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends); wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends);
if ((y2 - FLIST_OFST) <= 0) if ((y2 - FLIST_OFST) <= 0) {
return; return;
}
uint32_t selected_num = 0; uint32_t selected_num = 0;
@ -973,8 +1073,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
} }
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection_status = Friends.list[f].connection_status; Tox_Connection connection_status = Friends.list[f].connection_status;
TOX_USER_STATUS status = Friends.list[f].status; Tox_User_Status status = Friends.list[f].status;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
if (connection_status != TOX_CONNECTION_NONE) { if (connection_status != TOX_CONNECTION_NONE) {
@ -998,8 +1098,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%s ", ONLINE_CHAR); wprintw(self->window, "%s ", ONLINE_CHAR);
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD); wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
if (f_selected) if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
@ -1007,8 +1108,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
if (f_selected) if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE)); wattroff(self->window, COLOR_PAIR(BLUE));
}
/* Reset Friends.list[f].statusmsg on window resize */ /* Reset Friends.list[f].statusmsg on window resize */
if (fix_statuses) { if (fix_statuses) {
@ -1041,8 +1143,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
Friends.list[f].statusmsg_len = maxlen; Friends.list[f].statusmsg_len = maxlen;
} }
if (Friends.list[f].statusmsg_len > 0) if (Friends.list[f].statusmsg_len > 0) {
wprintw(self->window, " %s", Friends.list[f].statusmsg); wprintw(self->window, " %s", Friends.list[f].statusmsg);
}
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -1050,8 +1153,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
} else { } else {
wprintw(self->window, "%s ", OFFLINE_CHAR); wprintw(self->window, "%s ", OFFLINE_CHAR);
if (f_selected) if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE)); wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD); wattron(self->window, A_BOLD);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
@ -1059,8 +1163,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wattroff(self->window, A_BOLD); wattroff(self->window, A_BOLD);
if (f_selected) if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE)); wattroff(self->window, COLOR_PAIR(BLUE));
}
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
time_t last_seen = Friends.list[f].last_online.last_on; time_t last_seen = Friends.list[f].last_online.last_on;
@ -1108,15 +1213,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
int i; int i;
for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) for (i = 0; i < TOX_PUBLIC_KEY_SIZE; ++i) {
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff); wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
}
} }
wnoutrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active) {
help_onDraw(self); help_onDraw(self);
}
} }
void disable_chatwin(uint32_t f_num) void disable_chatwin(uint32_t f_num)
@ -1127,19 +1234,19 @@ void disable_chatwin(uint32_t f_num)
#ifdef AUDIO #ifdef AUDIO
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{ {
assert(0); UNUSED_VAR(self);
if ( friend_number >= Friends.max_idx) if (friend_number >= Friends.max_idx) {
return; return;
}
assert(0);
Tox *m = toxav_get_tox(av); Tox *m = toxav_get_tox(av);
if (Friends.list[friend_number].chatwin == -1) { if (Friends.list[friend_number].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) { if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
set_active_window(Friends.list[friend_number].chatwin); set_active_window_index(Friends.list[friend_number].chatwin);
} }
} else { } else {
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
@ -1155,49 +1262,84 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
} }
#endif /* AUDIO */ #endif /* AUDIO */
ToxWindow new_friendlist(void) /* Returns a friend's status */
Tox_User_Status get_friend_status(uint32_t friendnumber)
{ {
ToxWindow ret; return Friends.list[friendnumber].status;
memset(&ret, 0, sizeof(ret)); }
ret.active = true; /* Returns a friend's connection status */
ret.is_friendlist = true; Tox_Connection get_friend_connection_status(uint32_t friendnumber)
{
return Friends.list[friendnumber].connection_status;
}
ret.onKey = &friendlist_onKey; /*
ret.onDraw = &friendlist_onDraw; * Returns true if friend associated with `public_key` is in the block list.
ret.onFriendAdded = &friendlist_onFriendAdded; *
ret.onMessage = &friendlist_onMessage; * `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
ret.onConnectionChange = &friendlist_onConnectionChange; */
ret.onNickChange = &friendlist_onNickChange; bool friend_is_blocked(const char *public_key)
ret.onStatusChange = &friendlist_onStatusChange; {
ret.onStatusMessageChange = &friendlist_onStatusMessageChange; for (size_t i = 0; i < Blocked.max_idx; ++i) {
ret.onFileRecv = &friendlist_onFileRecv; if (!Blocked.list[i].active) {
ret.onGroupInvite = &friendlist_onGroupInvite; continue;
}
if (memcmp(public_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE) == 0) {
return true;
}
}
return false;
}
ToxWindow *new_friendlist(void)
{
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
if (ret == NULL) {
exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY);
}
ret->is_friendlist = true;
ret->onKey = &friendlist_onKey;
ret->onDraw = &friendlist_onDraw;
ret->onFriendAdded = &friendlist_onFriendAdded;
ret->onMessage = &friendlist_onMessage;
ret->onConnectionChange = &friendlist_onConnectionChange;
ret->onNickChange = &friendlist_onNickChange;
ret->onStatusChange = &friendlist_onStatusChange;
ret->onStatusMessageChange = &friendlist_onStatusMessageChange;
ret->onFileRecv = &friendlist_onFileRecv;
ret->onGroupInvite = &friendlist_onGroupInvite;
#ifdef AUDIO #ifdef AUDIO
ret.onInvite = &friendlist_onAV; ret->onInvite = &friendlist_onAV;
ret.onRinging = &friendlist_onAV; ret->onRinging = &friendlist_onAV;
ret.onStarting = &friendlist_onAV; ret->onStarting = &friendlist_onAV;
ret.onEnding = &friendlist_onAV; ret->onEnding = &friendlist_onAV;
ret.onError = &friendlist_onAV; ret->onError = &friendlist_onAV;
ret.onStart = &friendlist_onAV; ret->onStart = &friendlist_onAV;
ret.onCancel = &friendlist_onAV; ret->onCancel = &friendlist_onAV;
ret.onReject = &friendlist_onAV; ret->onReject = &friendlist_onAV;
ret.onEnd = &friendlist_onAV; ret->onEnd = &friendlist_onAV;
ret.is_call = false; ret->is_call = false;
ret.device_selection[0] = ret.device_selection[1] = -1; ret->device_selection[0] = ret->device_selection[1] = -1;
#endif /* AUDIO */ #endif /* AUDIO */
ret.num = -1; ret->num = -1;
ret.active_box = -1; ret->active_box = -1;
Help *help = calloc(1, sizeof(Help)); Help *help = calloc(1, sizeof(Help));
if (help == NULL) if (help == NULL) {
exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY); exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY);
}
ret.help = help; ret->help = help;
strcpy(ret.name, "contacts"); strcpy(ret->name, "contacts");
return ret; return ret;
} }

View File

@ -51,10 +51,10 @@ typedef struct {
uint32_t num; uint32_t num;
int chatwin; int chatwin;
bool active; bool active;
TOX_CONNECTION connection_status; Tox_Connection connection_status;
bool is_typing; bool is_typing;
bool logging_on; /* saves preference for friend irrespective of global settings */ bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status; Tox_User_Status status;
struct LastOnline last_online; struct LastOnline last_online;
struct GroupChatInvite group_invite; struct GroupChatInvite group_invite;
@ -81,14 +81,23 @@ typedef struct {
ToxicFriend *list; ToxicFriend *list;
} FriendsList; } FriendsList;
ToxWindow new_friendlist(void); ToxWindow *new_friendlist(void);
void disable_chatwin(uint32_t f_num); void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name); int get_friendnum(uint8_t *name);
int load_blocklist(char *data); int load_blocklist(char *data);
void kill_friendlist(void); void kill_friendlist(ToxWindow *self);
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort); void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
Tox_User_Status get_friend_status(uint32_t friendnumber);
Tox_Connection get_friend_connection_status(uint32_t friendnumber);
/* sorts friendlist_index first by connection status then alphabetically */ /* sorts friendlist_index first by connection status then alphabetically */
void sort_friendlist_index(void); void sort_friendlist_index(void);
/*
* Returns true if friend associated with `public_key` is in the block list.
*
* `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
*/
bool friend_is_blocked(const char *public_key);
#endif /* end of include guard: FRIENDLIST_H */ #endif /* end of include guard: FRIENDLIST_H */

View File

@ -41,11 +41,12 @@
extern char *DATA_FILE; extern char *DATA_FILE;
extern ToxWindow *prompt; extern ToxWindow *prompt;
extern FriendsList Friends; extern FriendsList Friends;
extern FriendRequests FrndRequests;
/* command functions */ /* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return; return;
@ -63,7 +64,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return; return;
} }
TOX_ERR_FRIEND_ADD err; Tox_Err_Friend_Add err;
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) {
@ -71,7 +72,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return; return;
} else { } else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend request accepted.");
on_friendadded(m, friendnum, true); on_friend_added(m, friendnum, true);
} }
memset(&FrndRequests.request[req], 0, sizeof(struct friend_request)); memset(&FrndRequests.request[req], 0, sizeof(struct friend_request));
@ -79,8 +80,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int i; int i;
for (i = FrndRequests.max_idx; i > 0; --i) { for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active) if (FrndRequests.request[i - 1].active) {
break; break;
}
} }
FrndRequests.max_idx = i; FrndRequests.max_idx = i;
@ -92,8 +94,8 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
{ {
const char *errmsg; const char *errmsg;
TOX_ERR_FRIEND_ADD err; Tox_Err_Friend_Add err;
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err); uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err);
switch (err) { switch (err) {
case TOX_ERR_FRIEND_ADD_TOO_LONG: case TOX_ERR_FRIEND_ADD_TOO_LONG:
@ -126,14 +128,14 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
case TOX_ERR_FRIEND_ADD_OK: case TOX_ERR_FRIEND_ADD_OK:
errmsg = "Friend request sent."; errmsg = "Friend request sent.";
on_friendadded(m, f_num, true); on_friend_added(m, f_num, true);
break; break;
case TOX_ERR_FRIEND_ADD_NULL: case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */ /* fallthrough */
default: default:
errmsg = "Faile to add friend: Unknown error."; errmsg = "Failed to add friend: Unknown error.";
break; break;
} }
@ -142,6 +144,8 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
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])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Tox ID or address required.");
return; return;
@ -193,6 +197,11 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
id_bin[i] = x; id_bin[i] = x;
} }
if (friend_is_blocked(id_bin)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Friend is in your block list.");
return;
}
cmd_add_helper(self, m, id_bin, msg); cmd_add_helper(self, m, id_bin, msg);
} else { /* assume id is a username@domain address and do http name server lookup */ } else { /* assume id is a username@domain address and do http name server lookup */
name_lookup(self, m, id_bin, id, msg); name_lookup(self, m, id_bin, id, msg);
@ -201,21 +210,17 @@ void cmd_add(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
if (argc < 2 || strlen(argv[1]) < 3) { UNUSED_VAR(window);
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 is not set."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Avatar has been unset.");
return; return;
} }
if (argv[1][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Path must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes */
char path[MAX_STR_SIZE]; char path[MAX_STR_SIZE];
snprintf(path, sizeof(path), "%s", &argv[1][1]); snprintf(path, sizeof(path), "%s", argv[1]);
int len = strlen(path) - 1; 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
@ -238,12 +243,18 @@ void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
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])
{ {
UNUSED_VAR(m);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
line_info_clear(self->chatwin->hst); line_info_clear(self->chatwin->hst);
force_refresh(window); force_refresh(window);
} }
void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Require: <ip> <port> <key>");
return; return;
@ -267,7 +278,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
return; return;
} }
TOX_ERR_BOOTSTRAP err; Tox_Err_Bootstrap err;
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err); tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err); tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
@ -291,6 +302,9 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Request ID required.");
return; return;
@ -313,8 +327,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
int i; int i;
for (i = FrndRequests.max_idx; i > 0; --i) { for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active) if (FrndRequests.request[i - 1].active) {
break; break;
}
} }
FrndRequests.max_idx = i; FrndRequests.max_idx = i;
@ -323,6 +338,8 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
return; return;
@ -335,11 +352,11 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
uint8_t type; uint8_t type;
if (!strcasecmp(argv[1], "audio")) if (!strcasecmp(argv[1], "audio")) {
type = TOX_CONFERENCE_TYPE_AV; type = TOX_CONFERENCE_TYPE_AV;
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 group types are: text | audio"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return; return;
} }
@ -349,7 +366,7 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
return; return;
} }
TOX_ERR_CONFERENCE_NEW err; Tox_Err_Conference_New err;
uint32_t groupnum = tox_conference_new(m, &err); uint32_t groupnum = tox_conference_new(m, &err);
@ -358,7 +375,7 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
return; return;
} }
if (init_groupchat_win(prompt, m, groupnum, type) == -1) { if (init_groupchat_win(m, groupnum, type, NULL, 0) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_conference_delete(m, groupnum, NULL); tox_conference_delete(m, groupnum, NULL);
return; return;
@ -369,14 +386,17 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
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])
{ {
UNUSED_VAR(window);
const char *msg; const char *msg;
struct chatlog *log = self->chatwin->log; struct chatlog *log = self->chatwin->log;
if (argc == 0) { if (argc == 0) {
if (log->log_on) if (log->log_on) {
msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)"; msg = "Logging for this window is ON; type \"/log off\" to disable. (Logs are not encrypted)";
else } else {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return; return;
@ -403,8 +423,9 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg); line_info_add(self, NULL, 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->is_chat) if (self->is_chat) {
Friends.list[self->num].logging_on = false; Friends.list[self->num].logging_on = false;
}
log_disable(log); log_disable(log);
@ -419,6 +440,10 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
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])
{ {
UNUSED_VAR(window);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
char id_string[TOX_ADDRESS_SIZE * 2 + 1]; char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE]; char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id); tox_self_get_address(m, (uint8_t *) bin_id);
@ -431,8 +456,11 @@ void cmd_myid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", id_string);
} }
#ifdef QRCODE
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
char id_string[TOX_ADDRESS_SIZE * 2 + 1]; char id_string[TOX_ADDRESS_SIZE * 2 + 1];
char bin_id[TOX_ADDRESS_SIZE]; char bin_id[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) bin_id); tox_self_get_address(m, (uint8_t *) bin_id);
@ -488,25 +516,20 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
#endif /* QRPNG */ #endif /* QRPNG */
} }
#endif /* QRCODE */
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return; return;
} }
char nick[MAX_STR_SIZE]; char nick[MAX_STR_SIZE];
size_t len = 0; snprintf(nick, sizeof(nick), "%s", argv[1]);
size_t len = strlen(nick);
if (argv[1][0] == '\"') { /* remove opening and closing quotes */
snprintf(nick, sizeof(nick), "%s", &argv[1][1]);
len = strlen(nick) - 1;
nick[len] = '\0';
} else {
snprintf(nick, sizeof(nick), "%s", argv[1]);
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
@ -524,24 +547,14 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
return; return;
} }
if (argv[1][0] != '\"') { prompt_update_statusmessage(prompt, m, argv[1]);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
return;
}
/* remove opening and closing quotes and replace linebreaks with spaces */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[1][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
strsubst(msg, '\n', ' ');
prompt_update_statusmessage(prompt, m, msg);
} }
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -571,16 +584,31 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
help_init_menu(self); help_init_menu(self);
} }
void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_quit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(argc);
UNUSED_VAR(argv);
UNUSED_VAR(self);
exit_toxic_success(m); exit_toxic_success(m);
} }
void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
UNUSED_VAR(argc);
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending friend requests.");
return; return;
@ -590,8 +618,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
int count = 0; int count = 0;
for (i = 0; i < FrndRequests.max_idx; ++i) { for (i = 0; i < FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active) if (!FrndRequests.request[i].active) {
continue; continue;
}
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0}; char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
@ -604,36 +633,36 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d : %s", i, id);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", FrndRequests.request[i].msg); line_info_add(self, NULL, 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
}
} }
} }
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
bool have_note = false; UNUSED_VAR(window);
const char *errmsg; const char *errmsg;
lock_status (); lock_status();
if (argc >= 2) { if (argc < 1) {
have_note = true;
} else 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, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish; goto finish;
} }
const char *status_str = argv[1]; const char *status_str = argv[1];
TOX_USER_STATUS status; Tox_User_Status status;
if (!strcasecmp(status_str, "online")) if (!strcasecmp(status_str, "online")) {
status = TOX_USER_STATUS_NONE; status = TOX_USER_STATUS_NONE;
else if (!strcasecmp(status_str, "away")) } else if (!strcasecmp(status_str, "away")) {
status = TOX_USER_STATUS_AWAY; status = TOX_USER_STATUS_AWAY;
else if (!strcasecmp(status_str, "busy")) } else if (!strcasecmp(status_str, "busy")) {
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, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish; goto finish;
@ -641,25 +670,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
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);
if (have_note) {
if (argv[2][0] != '\"') {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Note must be enclosed in quotes.");
goto finish;
}
/* remove opening and closing quotes */
char msg[MAX_STR_SIZE];
snprintf(msg, sizeof(msg), "%s", &argv[2][1]);
int len = strlen(msg) - 1;
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s: \"%s\".", status_str, msg);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
}
finish: finish:
unlock_status (); unlock_status();
} }

View File

@ -35,7 +35,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef QRCODE
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* QRCODE */
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
@ -58,6 +60,6 @@ void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv
#ifdef PYTHON #ifdef PYTHON
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 #endif /* PYTHON */
#endif /* #define GLOBAL_COMMANDS_H */ #endif /* GLOBAL_COMMANDS_H */

View File

@ -30,13 +30,15 @@
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
TOX_ERR_CONFERENCE_TITLE err; UNUSED_VAR(window);
Tox_Err_Conference_Title err;
char title[MAX_STR_SIZE]; char title[MAX_STR_SIZE];
if (argc < 1) { if (argc < 1) {
size_t tlen = tox_conference_get_title_size(m, self->num, &err); size_t tlen = tox_conference_get_title_size(m, self->num, &err);
if (err != TOX_ERR_CONFERENCE_TITLE_OK) { if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return; return;
} }
@ -52,15 +54,8 @@ void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
return; return;
} }
if (argv[1][0] != '\"') { snprintf(title, sizeof(title), "%s", argv[1]);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes."); int len = strlen(title);
return;
}
/* remove opening and closing quotes */
snprintf(title, sizeof(title), "%s", &argv[1][1]);
int len = strlen(title) - 1;
title[len] = '\0';
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);

View File

@ -28,4 +28,4 @@
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]); void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* GROUP_COMMANDS_H */ #endif /* GROUP_COMMANDS_H */

View File

@ -30,6 +30,7 @@
#include <time.h> #include <time.h>
#include <wchar.h> #include <wchar.h>
#include <unistd.h> #include <unistd.h>
#include <inttypes.h>
#ifdef AUDIO #ifdef AUDIO
#ifdef __APPLE__ #ifdef __APPLE__
@ -41,9 +42,9 @@
/* compatibility with older versions of OpenAL */ /* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER #ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h> #include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */ #endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */ #endif /* __APPLE__ */
#endif /* AUDIO */ #endif /* AUDIO */
#include "windows.h" #include "windows.h"
#include "toxic.h" #include "toxic.h"
@ -69,15 +70,17 @@ static int max_groupchat_index = 0;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
extern struct Winthread Winthread; extern struct Winthread Winthread;
#if defined(AUDIO) && defined(PYTHON) #ifdef PYTHON
#define AC_NUM_GROUP_COMMANDS 25 #define AC_NUM_GROUP_COMMANDS_PYTHON 1
#elif AUDIO
#define AC_NUM_GROUP_COMMANDS 24
#elif PYTHON
#define AC_NUM_GROUP_COMMANDS 21
#else #else
#define AC_NUM_GROUP_COMMANDS 20 #define AC_NUM_GROUP_COMMANDS_PYTHON 0
#endif /* AUDIO */ #endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_GROUP_COMMANDS_QRCODE 1
#else
#define AC_NUM_GROUP_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_GROUP_COMMANDS (19 + AC_NUM_GROUP_COMMANDS_PYTHON + AC_NUM_GROUP_COMMANDS_QRCODE)
/* Array of groupchat command names used for tab completion. */ /* Array of groupchat command names used for tab completion. */
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = { static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -93,7 +96,9 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" }, { "/help" },
{ "/log" }, { "/log" },
{ "/myid" }, { "/myid" },
#ifdef QRCODE
{ "/myqr" }, { "/myqr" },
#endif /* QRCODE */
{ "/nick" }, { "/nick" },
{ "/note" }, { "/note" },
{ "/nospam" }, { "/nospam" },
@ -109,46 +114,7 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
#endif /* PYTHON */ #endif /* PYTHON */
}; };
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type) static ToxWindow *new_group_chat(uint32_t groupnum);
{
if (groupnum > MAX_GROUPCHAT_NUM) {
return -1;
}
ToxWindow self = new_group_chat(m, groupnum);
int i;
for (i = 0; i <= max_groupchat_index; ++i) {
if (!groupchats[i].active) {
groupchats[i].chatwin = add_window(m, self);
groupchats[i].active = true;
groupchats[i].num_peers = 0;
groupchats[i].type = type;
groupchats[i].start_time = get_unix_time();
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
set_active_window(groupchats[i].chatwin);
if (i == max_groupchat_index)
++max_groupchat_index;
return 0;
}
}
return -1;
}
static void kill_groupchat_window(ToxWindow *self) static void kill_groupchat_window(ToxWindow *self)
{ {
@ -165,27 +131,63 @@ static void kill_groupchat_window(ToxWindow *self)
del_window(self); del_window(self);
} }
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum) int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title,
size_t title_length)
{ {
tox_conference_delete(m, groupnum, NULL); if (groupnum > MAX_GROUPCHAT_NUM) {
return -1;
}
free(groupchats[groupnum].peer_names); ToxWindow *self = new_group_chat(groupnum);
free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths); for (int i = 0; i <= max_groupchat_index; ++i) {
free(groupchats[groupnum].oldpeer_name_lengths); if (!groupchats[i].active) {
groupchats[i].chatwin = add_window(m, self);
groupchats[i].active = true;
groupchats[i].num_peers = 0;
groupchats[i].type = type;
groupchats[i].start_time = get_unix_time();
set_active_window_index(groupchats[i].chatwin);
set_window_title(self, title, title_length);
if (i == max_groupchat_index) {
++max_groupchat_index;
}
return 0;
}
}
kill_groupchat_window(self);
return -1;
}
void free_groupchat(ToxWindow *self, uint32_t groupnum)
{
free(groupchats[groupnum].name_list);
free(groupchats[groupnum].peer_list);
memset(&groupchats[groupnum], 0, sizeof(GroupChat)); memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i; int i;
for (i = max_groupchat_index; i > 0; --i) { for (i = max_groupchat_index; i > 0; --i) {
if (groupchats[i - 1].active) if (groupchats[i - 1].active) {
break; break;
}
} }
max_groupchat_index = i; max_groupchat_index = i;
kill_groupchat_window(self); kill_groupchat_window(self);
} }
static void delete_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
{
tox_conference_delete(m, groupnum, NULL);
free_groupchat(self, groupnum);
}
/* destroys and re-creates groupchat window with or without the peerlist */ /* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self) void redraw_groupchat_win(ToxWindow *self)
{ {
@ -199,8 +201,9 @@ void redraw_groupchat_win(ToxWindow *self)
getmaxyx(stdscr, y2, x2); getmaxyx(stdscr, y2, x2);
y2 -= 2; y2 -= 2;
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
return; return;
}
if (ctx->sidebar) { if (ctx->sidebar) {
delwin(ctx->sidebar); delwin(ctx->sidebar);
@ -226,10 +229,13 @@ void redraw_groupchat_win(ToxWindow *self)
} }
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
TOX_MESSAGE_TYPE type, const char *msg, size_t len) Tox_Message_Type type, const char *msg, size_t len)
{ {
if (self->num != groupnum) UNUSED_VAR(len);
if (self->num != groupnum) {
return; return;
}
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -248,10 +254,11 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum,
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) { if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL); sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
if (self->active_box != -1) if (self->active_box != -1) {
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg); box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
else } else {
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg); box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
}
nick_clr = RED; nick_clr = RED;
} else { } else {
@ -271,8 +278,9 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t group
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (self->num != groupnum) if (self->num != groupnum) {
return; return;
}
set_window_title(self, title, length); set_window_title(self, title, length);
@ -280,8 +288,9 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t group
get_time_str(timefrmt, sizeof(timefrmt)); 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(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) {
return; return;
}
char nick[TOX_MAX_NAME_LENGTH]; char nick[TOX_MAX_NAME_LENGTH];
get_group_nick_truncate(m, nick, peernum, groupnum); get_group_nick_truncate(m, nick, peernum, groupnum);
@ -292,212 +301,169 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t group
write_to_log(tmp_event, nick, ctx->log, true); write_to_log(tmp_event, nick, ctx->log, true);
} }
/* Puts two copies of peerlist/lengths in chat instance */ static void group_update_name_list(uint32_t groupnum)
static void copy_peernames(Tox *m, uint32_t gnum, size_t npeers)
{ {
/* Assumes these are initiated in init_groupchat_win */ GroupChat *chat = &groupchats[groupnum];
free(groupchats[gnum].peer_names);
free(groupchats[gnum].oldpeer_names);
free(groupchats[gnum].peer_name_lengths);
free(groupchats[gnum].oldpeer_name_lengths);
int N = TOX_MAX_NAME_LENGTH; if (!chat->active) {
return;
groupchats[gnum].peer_names = calloc(1, sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = calloc(1, sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
exit_toxic_err("failed in copy_peernames()", FATALERR_MEMORY);
} }
uint16_t u_len = strlen(UNKNOWN_NAME); if (chat->name_list) {
free(chat->name_list);
}
int i; chat->name_list = malloc(sizeof(char *) * chat->num_peers * TOX_MAX_NAME_LENGTH);
for (i = 0; i < npeers; ++i) { if (chat->name_list == NULL) {
uint8_t name[TOX_MAX_NAME_LENGTH]; exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY);
TOX_ERR_CONFERENCE_PEER_QUERY err; }
size_t n_len = tox_conference_peer_get_name_size(m, gnum, i, &err); uint32_t i, count = 0;
for (i = 0; i < chat->max_idx; ++i) {
if (chat->peer_list[i].active) {
memcpy(&chat->name_list[count * TOX_MAX_NAME_LENGTH], chat->peer_list[i].name, chat->peer_list[i].name_length + 1);
++count;
}
}
qsort(chat->name_list, count, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
}
/* Reallocates groupnum's peer list. Increase is true if the list needs to grow.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
static int realloc_peer_list(GroupChat *chat, uint32_t num_peers)
{
if (!chat) {
return -1;
}
if (num_peers == 0) {
free(chat->peer_list);
chat->peer_list = NULL;
return 0;
}
struct GroupPeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct GroupPeer));
if (!tmp_list) {
return -1;
}
chat->peer_list = tmp_list;
return 0;
}
static void update_peer_list(Tox *m, uint32_t groupnum, uint32_t num_peers)
{
GroupChat *chat = &groupchats[groupnum];
if (!chat->active) {
return;
}
realloc_peer_list(chat, num_peers);
uint32_t i;
for (i = 0; i < num_peers; ++i) {
GroupPeer *peer = &chat->peer_list[i];
Tox_Err_Conference_Peer_Query err;
size_t length = tox_conference_peer_get_name_size(m, groupnum, i, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK || length >= TOX_MAX_NAME_LENGTH) {
continue;
}
tox_conference_peer_get_name(m, groupnum, i, (uint8_t *) peer->name, &err);
peer->name[length] = 0;
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len); continue;
groupchats[gnum].peer_names[i * N + u_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = u_len;
} else {
tox_conference_peer_get_name(m, gnum, i, name, NULL);
n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
memcpy(&groupchats[gnum].peer_names[i * N], name, n_len);
groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len;
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
}
}
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
}
struct group_add_thrd {
Tox *m;
ToxWindow *self;
uint32_t peernum;
uint32_t groupnum;
time_t timestamp;
pthread_t tid;
pthread_attr_t attr;
};
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
void *group_add_wait(void *data)
{
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
ToxWindow *self = thrd->self;
Tox *m = thrd->m;
char peername[TOX_MAX_NAME_LENGTH];
/* keep polling for a name that differs from the default until we run out of time */
while (true) {
usleep(100000);
pthread_mutex_lock(&Winthread.lock);
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
pthread_mutex_unlock(&Winthread.lock);
break;
} }
pthread_mutex_unlock(&Winthread.lock); peer->active = true;
peer->name_length = length;
peer->peernumber = i;
} }
const char *event = "has joined the room"; group_update_name_list(groupnum);
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
pthread_mutex_lock(&Winthread.lock);
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
write_to_log(event, (char *) peername, self->chatwin->log, true);
pthread_mutex_unlock(&Winthread.lock);
pthread_attr_destroy(&thrd->attr);
free(thrd);
pthread_exit(NULL);
} }
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum)
TOX_CONFERENCE_STATE_CHANGE change)
{ {
if (self->num != groupnum) if (self->num != groupnum) {
return; return;
}
if (groupnum > max_groupchat_index) if (groupnum > max_groupchat_index) {
return; return;
}
TOX_ERR_CONFERENCE_PEER_QUERY err; GroupChat *chat = &groupchats[groupnum];
if (!chat->active) {
return;
}
Tox_Err_Conference_Peer_Query err;
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err); uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
uint32_t old_num = chat->num_peers;
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) { if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
groupchats[groupnum].num_peers = num_peers; chat->num_peers = num_peers;
} else { } else {
num_peers = groupchats[groupnum].num_peers; num_peers = old_num;
} }
if (peernum > num_peers) chat->max_idx = num_peers;
update_peer_list(m, groupnum, num_peers);
}
static void groupchat_onGroupPeerNameChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
const char *name, size_t length)
{
UNUSED_VAR(length);
if (self->num != groupnum) {
return; return;
/* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN) {
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(oldpeername));
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
oldpeername[old_n_len] = '\0';
} }
/* Update name/len lists */ GroupChat *chat = &groupchats[groupnum];
copy_peernames(m, groupnum, num_peers);
/* get current peername then sort namelist */ if (!chat->active) {
uint8_t peername[TOX_MAX_NAME_LENGTH]; return;
if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0';
} }
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr); uint32_t i;
ChatContext *ctx = self->chatwin; for (i = 0; i < chat->max_idx; ++i) {
GroupPeer *peer = &chat->peer_list[i];
const char *event; // Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting)
char timefrmt[TIME_STR_SIZE]; if (peer->active && peer->peernumber == peernum && peer->name_length > 0) {
get_time_str(timefrmt, sizeof(timefrmt)); ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE];
switch (change) { get_time_str(timefrmt, sizeof(timefrmt));
case TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN:
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
break;
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
thrd->m = m;
thrd->peernum = peernum;
thrd->groupnum = groupnum;
thrd->self = self;
thrd->timestamp = get_unix_time();
if (pthread_attr_init(&thrd->attr) != 0) {
free(thrd);
return;
}
if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
pthread_attr_destroy(&thrd->attr);
free(thrd);
return;
}
if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
pthread_attr_destroy(&thrd->attr);
free(thrd);
return;
}
break;
case TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT:
event = "has left the room";
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
if (groupchats[self->num].side_pos > 0)
--groupchats[self->num].side_pos;
write_to_log(event, (char *) oldpeername, ctx->log, true);
break;
case TOX_CONFERENCE_STATE_CHANGE_PEER_NAME_CHANGE:
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
return;
/* ignore initial name change (TODO: this is a bad way to do this) */
if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
return;
event = " is now known as ";
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32]; char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername); snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name);
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
write_to_log(tmp_event, peer->name, ctx->log, true);
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
break; break;
}
} }
sound_notify(self, silent, NT_WNDALERT_2, NULL); groupchat_onGroupNameListChange(self, m, groupnum);
} }
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action) static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
@ -507,14 +473,17 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
return; return;
} }
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, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
} }
} }
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) /*
* Return true if input is recognized by handler
*/
static bool groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -522,47 +491,56 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x); getyx(self->window, y, x);
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0) UNUSED_VAR(y);
return;
if (x2 <= 0 || y2 <= 0) {
return false;
}
if (self->help->active) { if (self->help->active) {
help_onKey(self, key); help_onKey(self, key);
return; return true;
} }
if (ctx->pastemode && key == '\r') if (ctx->pastemode && key == '\r') {
key = '\n'; key = '\n';
}
if (ltr || key == '\n') { /* char is printable */ if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2); input_new_char(self, key, x, x2);
return; return true;
} }
if (line_info_onKey(self, key)) if (line_info_onKey(self, key)) {
return; return true;
}
if (input_handle(self, key, x, y, x2, y2)) if (input_handle(self, key, x, x2)) {
return; return true;
}
bool input_ret = false;
if (key == '\t') { /* TAB key: auto-completes peer name or command */ if (key == '\t') { /* TAB key: auto-completes peer name or command */
input_ret = true;
if (ctx->len > 0) { if (ctx->len > 0) {
int diff; int diff;
/* TODO: make this not suck */ /* TODO: make this not suck */
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) { if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers, diff = complete_line(self, groupchats[self->num].name_list, groupchats[self->num].num_peers,
TOX_MAX_NAME_LENGTH); TOX_MAX_NAME_LENGTH);
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { } else if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
} }
#ifdef PYTHON #ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0) { else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run"); diff = dir_match(self, m, ctx->line, L"/run");
} }
#endif #endif
else { else {
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE); diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
} }
@ -578,15 +556,21 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else { } else {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
} }
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */ } else if (key == T_KEY_C_DOWN) { /* Scroll peerlist up and down one position */
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST; input_ret = true;
const int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L) if (groupchats[self->num].side_pos < (int64_t) groupchats[self->num].num_peers - L) {
++groupchats[self->num].side_pos; ++groupchats[self->num].side_pos;
} else if (key == user_settings->key_peer_list_up) { }
if (groupchats[self->num].side_pos > 0) } else if (key == T_KEY_C_UP) {
input_ret = true;
if (groupchats[self->num].side_pos > 0) {
--groupchats[self->num].side_pos; --groupchats[self->num].side_pos;
}
} else if (key == '\r') { } else if (key == '\r') {
input_ret = true;
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line)) { if (!wstring_is_empty(ctx->line)) {
@ -596,20 +580,21 @@ static void groupchat_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) {
memset(&line, 0, sizeof(line)); memset(&line, 0, sizeof(line));
}
if (line[0] == '/') { if (line[0] == '/') {
if (strcmp(line, "/close") == 0) { if (strcmp(line, "/close") == 0) {
close_groupchat(self, m, self->num); delete_groupchat(self, m, self->num);
return; return true;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) { } else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me ")); send_group_action(self, ctx, m, line + strlen("/me "));
} else { } else {
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
} }
} else { } else {
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, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
@ -621,15 +606,20 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
reset_buf(ctx); reset_buf(ctx);
} }
return input_ret;
} }
static void groupchat_onDraw(ToxWindow *self, Tox *m) static void groupchat_onDraw(ToxWindow *self, Tox *m)
{ {
UNUSED_VAR(m);
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0) if (x2 <= 0 || y2 <= 0) {
return; return;
}
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -641,8 +631,9 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
curs_set(1); curs_set(1);
if (ctx->len > 0) if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
wclear(ctx->sidebar); wclear(ctx->sidebar);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2); mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
@ -652,19 +643,19 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE); mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
int num_peers = groupchats[self->num].num_peers; uint32_t num_peers = groupchats[self->num].num_peers;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wmove(ctx->sidebar, 0, 1); wmove(ctx->sidebar, 0, 1);
wattron(ctx->sidebar, A_BOLD); wattron(ctx->sidebar, A_BOLD);
wprintw(ctx->sidebar, "Peers: %d\n", num_peers); wprintw(ctx->sidebar, "Peers: %"PRIu32"\n", num_peers);
wattroff(ctx->sidebar, A_BOLD); wattroff(ctx->sidebar, A_BOLD);
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE); mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1); mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT; int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
int i; uint32_t i;
for (i = 0; i < num_peers && i < maxlines; ++i) { for (i = 0; i < num_peers && i < maxlines; ++i) {
wmove(ctx->sidebar, i + 2, 1); wmove(ctx->sidebar, i + 2, 1);
@ -678,7 +669,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int maxlen = SIDEBAR_WIDTH - 2; int maxlen = SIDEBAR_WIDTH - 2;
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen); memcpy(tmpnck, &groupchats[self->num].name_list[peer * TOX_MAX_NAME_LENGTH], maxlen);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
tmpnck[maxlen] = '\0'; tmpnck[maxlen] = '\0';
@ -689,14 +680,17 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int y, x; int y, x;
getyx(self->window, y, x); getyx(self->window, y, x);
(void) x;
UNUSED_VAR(x);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x); wmove(self->window, y + 1, new_x);
wnoutrefresh(self->window); wnoutrefresh(self->window);
if (self->help->active) if (self->help->active) {
help_onDraw(self); help_onDraw(self);
}
} }
static void groupchat_onInit(ToxWindow *self, Tox *m) static void groupchat_onInit(ToxWindow *self, Tox *m)
@ -704,8 +698,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0) if (x2 <= 0 || y2 <= 0) {
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES); exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
}
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -716,8 +711,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
ctx->hst = calloc(1, sizeof(struct history)); ctx->hst = calloc(1, sizeof(struct history));
ctx->log = calloc(1, sizeof(struct chatlog)); ctx->log = calloc(1, sizeof(struct chatlog));
if (ctx->log == NULL || ctx->hst == NULL) if (ctx->log == NULL || ctx->hst == NULL) {
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY); exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
}
line_info_init(ctx->hst); line_info_init(ctx->hst);
@ -725,8 +721,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
char myid[TOX_ADDRESS_SIZE]; char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid); tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1) if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
} }
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE); execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
@ -735,35 +732,39 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} }
ToxWindow new_group_chat(Tox *m, uint32_t groupnum) static ToxWindow *new_group_chat(uint32_t groupnum)
{ {
ToxWindow ret; ToxWindow *ret = calloc(1, sizeof(ToxWindow));
memset(&ret, 0, sizeof(ret));
ret.active = true; if (ret == NULL) {
ret.is_groupchat = true; exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
}
ret.onKey = &groupchat_onKey; ret->is_groupchat = true;
ret.onDraw = &groupchat_onDraw;
ret.onInit = &groupchat_onInit;
ret.onGroupMessage = &groupchat_onGroupMessage;
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum); ret->onKey = &groupchat_onKey;
ret->onDraw = &groupchat_onDraw;
ret->onInit = &groupchat_onInit;
ret->onGroupMessage = &groupchat_onGroupMessage;
ret->onGroupNameListChange = &groupchat_onGroupNameListChange;
ret->onGroupPeerNameChange = &groupchat_onGroupPeerNameChange;
ret->onGroupTitleChange = &groupchat_onGroupTitleChange;
snprintf(ret->name, sizeof(ret->name), "Group %u", groupnum);
ChatContext *chatwin = calloc(1, sizeof(ChatContext)); ChatContext *chatwin = calloc(1, sizeof(ChatContext));
Help *help = calloc(1, sizeof(Help)); Help *help = calloc(1, sizeof(Help));
if (chatwin == NULL || help == NULL) if (chatwin == NULL || help == NULL) {
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY); exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
}
ret.chatwin = chatwin; ret->chatwin = chatwin;
ret.help = help; ret->help = help;
ret.num = groupnum; ret->num = groupnum;
ret.show_peerlist = true; ret->show_peerlist = true;
ret.active_box = -1; ret->active_box = -1;
return ret; return ret;
} }

View File

@ -31,25 +31,34 @@
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2 #define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3 #define GROUP_EVENT_WAIT 3
typedef struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peernumber;
} GroupPeer;
typedef struct { typedef struct {
int chatwin; int chatwin;
bool active; bool active;
uint8_t type; uint8_t type;
uint32_t num_peers;
int side_pos; /* current position of the sidebar - used for scrolling up and down */ int side_pos; /* current position of the sidebar - used for scrolling up and down */
time_t start_time; time_t start_time;
uint8_t *peer_names;
uint8_t *oldpeer_names; GroupPeer *peer_list;
uint16_t *peer_name_lengths; uint32_t max_idx;
uint16_t *oldpeer_name_lengths;
char *name_list;
uint32_t num_peers;
} GroupChat; } GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum); /* Frees all Toxic associated data structures for a groupchat (does not call tox_conference_delete() ) */
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type); void free_groupchat(ToxWindow *self, uint32_t groupnum);
int init_groupchat_win(Tox *m, uint32_t groupnum, uint8_t type, const char *title, size_t title_length);
/* destroys and re-creates groupchat window with or without the peerlist */ /* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self); void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, uint32_t groupnum); #endif /* GROUPCHAT_H */
#endif /* #define GROUPCHAT_H */

View File

@ -40,14 +40,16 @@
void help_init_menu(ToxWindow *self) void help_init_menu(ToxWindow *self)
{ {
if (self->help->win) if (self->help->win) {
delwin(self->help->win); delwin(self->help->win);
}
int y2, x2; int y2, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) {
return; return;
}
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3); self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
self->help->active = true; self->help->active = true;
@ -62,14 +64,16 @@ static void help_exit(ToxWindow *self)
static void help_init_window(ToxWindow *self, int height, int width) static void help_init_window(ToxWindow *self, int height, int width)
{ {
if (self->help->win) if (self->help->win) {
delwin(self->help->win); delwin(self->help->win);
}
int y2, x2; int y2, x2;
getmaxyx(stdscr, y2, x2); getmaxyx(stdscr, y2, x2);
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
return; return;
}
height = MIN(height, y2); height = MIN(height, y2);
width = MIN(width, x2); width = MIN(width, x2);
@ -134,7 +138,8 @@ static void help_draw_bottom_menu(WINDOW *win)
{ {
int y2, x2; int y2, x2;
getmaxyx(win, y2, x2); getmaxyx(win, y2, x2);
(void) x2;
UNUSED_VAR(x2);
wmove(win, y2 - 2, 1); wmove(win, y2 - 2, 1);
@ -173,11 +178,13 @@ 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, " /group <type> : Create a group chat where type: text | audio\n"); wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n"); wprintw(win, " /myid : Print your Tox ID\n");
#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");
#else #else
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n"); wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
#endif /* QRPNG */ #endif /* QRPNG */
#endif /* QRCODE */
wprintw(win, " /clear : Clear window history\n"); wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n"); wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n"); wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -272,7 +279,7 @@ static void help_draw_keys(ToxWindow *self)
wprintw(win, " Page Up and Page Down : Scroll window history one line\n"); wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n"); wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
wprintw(win, " Ctrl+H : Move to the bottom of window history\n"); wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n"); wprintw(win, " Ctrl+up and Ctrl+down : Scroll peer list in groupchats\n");
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n"); wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
wprintw(win, " Ctrl+J : Insert new line\n"); wprintw(win, " Ctrl+J : Insert new line\n");
wprintw(win, " Ctrl+T : Toggle paste mode\n\n"); wprintw(win, " Ctrl+T : Toggle paste mode\n\n");
@ -320,7 +327,7 @@ static void help_draw_plugin(ToxWindow *self)
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wnoutrefresh(win); wnoutrefresh(win);
} }
#endif #endif /* PYTHON */
static void help_draw_contacts(ToxWindow *self) static void help_draw_contacts(ToxWindow *self)
{ {
@ -390,7 +397,7 @@ void help_onKey(ToxWindow *self, wint_t key)
help_init_window(self, 4 + num_registered_handlers(), help_max_width()); help_init_window(self, 4 + num_registered_handlers(), help_max_width());
self->help->type = HELP_PLUGIN; self->help->type = HELP_PLUGIN;
break; break;
#endif #endif /* PYTHON */
case 'f': case 'f':
help_init_window(self, 10, 80); help_init_window(self, 10, 80);

View File

@ -42,4 +42,4 @@ void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self); void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key); void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define HELP_H */ #endif /* HELP_H */

View File

@ -38,13 +38,14 @@
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
/* add a char to input field and buffer */ /* add a char to input field and buffer */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
/* this is the only place we need to do this check */ /* this is the only place we need to do this check */
if (key == '\n') if (key == '\n') {
key = L''; key = L'';
}
int cur_len = wcwidth(key); int cur_len = wcwidth(key);
@ -77,42 +78,45 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0; int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
if (ctx->start && (x >= mx_x - cur_len)) if (ctx->start && (x >= mx_x - cur_len)) {
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start) } else if (ctx->start) {
ctx->start = MAX(0, ctx->start - cur_len); ctx->start = MAX(0, ctx->start - cur_len);
}
} }
/* delete a char via delete key from input field and buffer */ /* delete a char via delete key from input field and buffer */
static void input_delete(ToxWindow *self) static void input_delete(ToxWindow *self)
{ {
if (del_char_buf_frnt(self->chatwin) == -1) if (del_char_buf_frnt(self->chatwin) == -1) {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
}
} }
/* delete last typed word */ /* delete last typed word */
static void input_del_word(ToxWindow *self, int x, int mx_x) static void input_del_word(ToxWindow *self)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (del_word_buf(ctx) == -1) { if (del_word_buf(ctx) == -1) {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
return;
} }
} }
/* deletes entire line before cursor from input field and buffer */ /* deletes entire line before cursor from input field and buffer */
static void input_discard(ToxWindow *self) static void input_discard(ToxWindow *self)
{ {
if (discard_buf(self->chatwin) == -1) if (discard_buf(self->chatwin) == -1) {
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
}
} }
/* deletes entire line after cursor from input field and buffer */ /* deletes entire line after cursor from input field and buffer */
static void input_kill(ChatContext *ctx) static void input_kill(ChatContext *ctx)
{ {
if (kill_buf(ctx) == -1) if (kill_buf(ctx) == -1) {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL); sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
} }
static void input_yank(ToxWindow *self, int x, int mx_x) static void input_yank(ToxWindow *self, int x, int mx_x)
@ -134,7 +138,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
} }
/* moves cursor/line position to end of line in input field and buffer */ /* moves cursor/line position to end of line in input field and buffer */
static void input_mv_end(ToxWindow *self, int y, int mx_x) static void input_mv_end(ToxWindow *self, int mx_x)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -149,8 +153,9 @@ static void input_mv_home(ToxWindow *self)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0) if (ctx->pos <= 0) {
return; return;
}
ctx->pos = 0; ctx->pos = 0;
ctx->start = 0; ctx->start = 0;
@ -161,18 +166,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0) if (ctx->pos <= 0) {
return; return;
}
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0; int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
--ctx->pos; --ctx->pos;
if (ctx->start && (x >= mx_x - cur_len)) if (ctx->start > 0 && (x >= mx_x - cur_len)) {
int s_len = wcwidth(ctx->line[ctx->start - 1]);
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len)); ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
else if (ctx->start) } else if (ctx->start > 0) {
ctx->start = MAX(0, ctx->start - cur_len); ctx->start = MAX(0, ctx->start - cur_len);
}
}
/* moves the cursor to the beginning of the previous word in input field and buffer */
static void input_skip_left(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0) {
return;
}
int count = 0;
do {
--ctx->pos;
count += wcwidth(ctx->line[ctx->pos]);
} while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' '));
if (ctx->start > 0 && (x >= mx_x - count)) {
int s_len = wcwidth(ctx->line[ctx->start - 1]);
ctx->start = MAX(0, ctx->start - 1 + (s_len - count));
} else if (ctx->start > 0) {
ctx->start = MAX(0, ctx->start - count);
}
} }
/* moves cursor/line position right in input field and buffer */ /* moves cursor/line position right in input field and buffer */
@ -180,8 +211,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len) if (ctx->pos >= ctx->len) {
return; return;
}
++ctx->pos; ++ctx->pos;
@ -193,6 +225,29 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
} }
} }
/* moves the cursor to the end of the next word in input field and buffer */
static void input_skip_right(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len) {
return;
}
int count = 0;
do {
count += wcwidth(ctx->line[ctx->pos]);
++ctx->pos;
} while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' '));
int newpos = x + count;
if (newpos >= mx_x) {
ctx->start += (1 + (newpos - mx_x));
}
}
/* puts a line history item in input field and buffer */ /* puts a line history item in input field and buffer */
static void input_history(ToxWindow *self, wint_t key, int mx_x) static void input_history(ToxWindow *self, wint_t key, int mx_x)
{ {
@ -205,7 +260,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
/* Handles non-printable input keys that behave the same for all types of chat windows. /* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */ return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y) bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
{ {
bool match = true; bool match = true;
@ -232,7 +287,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
break; break;
case T_KEY_C_W: case T_KEY_C_W:
input_del_word(self, x, mx_x); input_del_word(self);
break; break;
case KEY_HOME: case KEY_HOME:
@ -242,7 +297,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
case KEY_END: case KEY_END:
case T_KEY_C_E: case T_KEY_C_E:
input_mv_end(self, y, mx_x); input_mv_end(self, mx_x);
break; break;
case KEY_LEFT: case KEY_LEFT:
@ -262,6 +317,14 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
force_refresh(self->chatwin->history); force_refresh(self->chatwin->history);
break; break;
case T_KEY_C_LEFT:
input_skip_left(self, x, mx_x);
break;
case T_KEY_C_RIGHT:
input_skip_right(self, x, mx_x);
break;
default: default:
match = false; match = false;
break; break;

View File

@ -24,10 +24,10 @@
#define INPUT_H #define INPUT_H
/* add a char to input field and buffer for given chatcontext */ /* add a char to input field and buffer for given chatcontext */
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x);
/* Handles non-printable input keys that behave the same for all types of chat windows. /* Handles non-printable input keys that behave the same for all types of chat windows.
return true if key matches a function, false otherwise */ return true if key matches a function, false otherwise */
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y); bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x);
#endif /* #define INPUT_H */ #endif /* INPUT_H */

View File

@ -40,8 +40,9 @@ void line_info_init(struct history *hst)
{ {
hst->line_root = calloc(1, sizeof(struct line_info)); hst->line_root = calloc(1, sizeof(struct line_info));
if (hst->line_root == NULL) if (hst->line_root == NULL) {
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY); exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
}
hst->line_start = hst->line_root; hst->line_start = hst->line_root;
hst->line_end = hst->line_start; hst->line_end = hst->line_start;
@ -53,8 +54,9 @@ void line_info_reset_start(ToxWindow *self, struct history *hst)
{ {
struct line_info *line = hst->line_end; struct line_info *line = hst->line_end;
if (line->prev == NULL) if (line->prev == NULL) {
return; return;
}
int y2, x2; int y2, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
@ -88,8 +90,9 @@ void line_info_cleanup(struct history *hst)
int i; int i;
for (i = 0; i < hst->queue_sz; ++i) { for (i = 0; i < hst->queue_sz; ++i) {
if (hst->queue[i]) if (hst->queue[i]) {
free(hst->queue[i]); free(hst->queue[i]);
}
} }
free(hst); free(hst);
@ -114,15 +117,17 @@ static void line_info_root_fwd(struct history *hst)
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */ /* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
static struct line_info *line_info_ret_queue(struct history *hst) static struct line_info *line_info_ret_queue(struct history *hst)
{ {
if (hst->queue_sz <= 0) if (hst->queue_sz <= 0) {
return NULL; return NULL;
}
struct line_info *line = hst->queue[0]; struct line_info *line = hst->queue[0];
int i; int i;
for (i = 0; i < hst->queue_sz; ++i) for (i = 0; i < hst->queue_sz; ++i) {
hst->queue[i] = hst->queue[i + 1]; hst->queue[i] = hst->queue[i + 1];
}
--hst->queue_sz; --hst->queue_sz;
@ -137,18 +142,21 @@ static struct line_info *line_info_ret_queue(struct history *hst)
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type, int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...) uint8_t bold, uint8_t colour, const char *msg, ...)
{ {
if (!self) if (!self) {
return -1; return -1;
}
struct history *hst = self->chatwin->hst; struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) {
return -1; return -1;
}
struct line_info *new_line = calloc(1, sizeof(struct line_info)); struct line_info *new_line = calloc(1, sizeof(struct line_info));
if (new_line == NULL) if (new_line == NULL) {
exit_toxic_err("failed in line_info_add", FATALERR_MEMORY); exit_toxic_err("failed in line_info_add", FATALERR_MEMORY);
}
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0}; char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
@ -206,8 +214,9 @@ int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const
int i; int i;
for (i = 0; frmt_msg[i]; ++i) { for (i = 0; frmt_msg[i]; ++i) {
if (frmt_msg[i] == '\n') if (frmt_msg[i] == '\n') {
++new_line->newlines; ++new_line->newlines;
}
} }
} }
@ -245,11 +254,13 @@ static void line_info_check_queue(ToxWindow *self)
struct history *hst = self->chatwin->hst; struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst); struct line_info *line = line_info_ret_queue(hst);
if (line == NULL) if (line == NULL) {
return; return;
}
if (hst->start_id > user_settings->history_size) if (hst->start_id > user_settings->history_size) {
line_info_root_fwd(hst); line_info_root_fwd(hst);
}
line->prev = hst->line_end; line->prev = hst->line_end;
hst->line_end->next = line; hst->line_end->next = line;
@ -259,10 +270,12 @@ static void line_info_check_queue(ToxWindow *self)
int y, y2, x, x2; int y, y2, x, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
getyx(self->chatwin->history, y, x); getyx(self->chatwin->history, y, x);
(void) x;
if (x2 <= SIDEBAR_WIDTH) UNUSED_VAR(x);
if (x2 <= SIDEBAR_WIDTH) {
return; return;
}
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */ int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst)); int lines = 1 + line->newlines + (line->len / (x2 - offst));
@ -284,8 +297,9 @@ void line_info_print(ToxWindow *self)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
if (ctx == NULL) if (ctx == NULL) {
return; return;
}
struct history *hst = ctx->hst; struct history *hst = ctx->hst;
@ -293,19 +307,25 @@ void line_info_print(ToxWindow *self)
line_info_check_queue(self); line_info_check_queue(self);
WINDOW *win = ctx->history; WINDOW *win = ctx->history;
wclear(win); wclear(win);
int y2, x2; int y2, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (x2 <= SIDEBAR_WIDTH) if (x2 <= SIDEBAR_WIDTH) {
return; return;
}
if (self->is_groupchat) if (self->is_groupchat) {
wmove(win, 0, 0); wmove(win, 0, 0);
else } else {
wmove(win, 2, 0); wmove(win, 2, 0);
}
struct line_info *line = hst->line_start->next; struct line_info *line = hst->line_start->next;
int numlines = 0; int numlines = 0;
while (line && numlines++ <= y2) { while (line && numlines++ <= y2) {
@ -325,10 +345,11 @@ void line_info_print(ToxWindow *self)
int nameclr = GREEN; int nameclr = GREEN;
if (line->colour) if (line->colour) {
nameclr = line->colour; nameclr = line->colour;
else if (type == IN_MSG) } else if (type == IN_MSG) {
nameclr = CYAN; nameclr = CYAN;
}
wattron(win, COLOR_PAIR(nameclr)); wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1); wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
@ -339,26 +360,29 @@ void line_info_print(ToxWindow *self)
while (msg) { while (msg) {
char *line = strsep(&msg, "\n"); char *line = strsep(&msg, "\n");
if (line[0] == '>') if (line[0] == '>') {
wattron(win, COLOR_PAIR(GREEN)); wattron(win, COLOR_PAIR(GREEN));
else if (line[0] == '<') } else if (line[0] == '<') {
wattron(win, COLOR_PAIR(RED)); wattron(win, COLOR_PAIR(RED));
}
wprintw(win, "%s%c", line, msg ? '\n' : '\0'); wprintw(win, "%s%c", line, msg ? '\n' : '\0');
if (line[0] == '>') if (line[0] == '>') {
wattroff(win, COLOR_PAIR(GREEN)); wattroff(win, COLOR_PAIR(GREEN));
else if (line[0] == '<') } else if (line[0] == '<') {
wattroff(win, COLOR_PAIR(RED)); wattroff(win, COLOR_PAIR(RED));
}
// change the \0 set by strsep back to \n // change the \0 set by strsep back to \n
if (msg) if (msg) {
msg[-1] = '\n'; msg[-1] = '\n';
}
} }
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) { if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
wattron(win, COLOR_PAIR(RED)); wattron(win, COLOR_PAIR(RED));
wprintw(win, " x", line->msg); wprintw(win, " x");
wattroff(win, COLOR_PAIR(RED)); wattroff(win, COLOR_PAIR(RED));
if (line->noread_flag == false) { if (line->noread_flag == false) {
@ -406,19 +430,23 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(BLUE)); wattroff(win, COLOR_PAIR(BLUE));
} }
if (line->bold) if (line->bold) {
wattron(win, A_BOLD); wattron(win, A_BOLD);
}
if (line->colour) if (line->colour) {
wattron(win, COLOR_PAIR(line->colour)); wattron(win, COLOR_PAIR(line->colour));
}
wprintw(win, "%s\n", line->msg); wprintw(win, "%s\n", line->msg);
if (line->bold) if (line->bold) {
wattroff(win, A_BOLD); wattroff(win, A_BOLD);
}
if (line->colour) if (line->colour) {
wattroff(win, COLOR_PAIR(line->colour)); wattroff(win, COLOR_PAIR(line->colour));
}
break; break;
@ -427,8 +455,9 @@ void line_info_print(ToxWindow *self)
wprintw(win, "$ "); wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN)); wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0]) if (line->msg[0]) {
wprintw(win, "%s", line->msg); wprintw(win, "%s", line->msg);
}
wprintw(win, "\n"); wprintw(win, "\n");
break; break;
@ -492,8 +521,9 @@ void line_info_print(ToxWindow *self)
} }
/* keep calling until queue is empty */ /* keep calling until queue is empty */
if (hst->queue_sz > 0) if (hst->queue_sz > 0) {
line_info_print(self); line_info_print(self);
}
} }
/* puts msg in specified line_info msg buffer */ /* puts msg in specified line_info msg buffer */
@ -518,40 +548,50 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
static void line_info_scroll_up(struct history *hst) static void line_info_scroll_up(struct history *hst)
{ {
if (hst->line_start->prev) if (hst->line_start->prev) {
hst->line_start = hst->line_start->prev; hst->line_start = hst->line_start->prev;
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); } else {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
} }
static void line_info_scroll_down(struct history *hst) static void line_info_scroll_down(struct history *hst)
{ {
if (hst->line_start->next) if (hst->line_start->next) {
hst->line_start = hst->line_start->next; hst->line_start = hst->line_start->next;
else sound_notify(NULL, notif_error, NT_ALWAYS, NULL); } else {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
} }
static void line_info_page_up(ToxWindow *self, struct history *hst) static void line_info_page_up(ToxWindow *self, struct history *hst)
{ {
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
(void) x2;
UNUSED_VAR(x2);
int jump_dist = y2 / 2; int jump_dist = y2 / 2;
int i; int i;
for (i = 0; i < jump_dist && hst->line_start->prev; ++i) for (i = 0; i < jump_dist && hst->line_start->prev; ++i) {
hst->line_start = hst->line_start->prev; hst->line_start = hst->line_start->prev;
}
} }
static void line_info_page_down(ToxWindow *self, struct history *hst) static void line_info_page_down(ToxWindow *self, struct history *hst)
{ {
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
(void) x2;
UNUSED_VAR(x2);
int jump_dist = y2 / 2; int jump_dist = y2 / 2;
int i; int i;
for (i = 0; i < jump_dist && hst->line_start->next; ++i) for (i = 0; i < jump_dist && hst->line_start->next; ++i) {
hst->line_start = hst->line_start->next; hst->line_start = hst->line_start->next;
}
} }
bool line_info_onKey(ToxWindow *self, wint_t key) bool line_info_onKey(ToxWindow *self, wint_t key)

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 */
enum { typedef enum {
SYS_MSG, SYS_MSG,
IN_MSG, IN_MSG,
OUT_MSG, OUT_MSG,
@ -100,4 +100,4 @@ void line_info_reset_start(ToxWindow *self, struct history *hst);
void line_info_init(struct history *hst); void line_info_init(struct history *hst);
bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */ bool line_info_onKey(ToxWindow *self, wint_t key); /* returns true if key is a match */
#endif /* #define LINE_INFO_H */ #endif /* LINE_INFO_H */

View File

@ -45,8 +45,9 @@ extern struct user_settings *user_settings;
Returns 0 on success, -1 if the path is too long */ Returns 0 on success, -1 if the path is too long */
static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype) static int get_log_path(char *dest, int destsize, char *name, const char *selfkey, const char *otherkey, int logtype)
{ {
if (!valid_nick(name)) if (!valid_nick(name)) {
name = UNKNOWN_NAME; name = UNKNOWN_NAME;
}
const char *namedash = logtype == LOG_PROMPT ? "" : "-"; const char *namedash = logtype == LOG_PROMPT ? "" : "-";
const char *set_path = user_settings->chatlogs_path; const char *set_path = user_settings->chatlogs_path;
@ -85,10 +86,11 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
return -1; return -1;
} }
if (!string_is_empty(set_path)) if (!string_is_empty(set_path)) {
snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id); snprintf(dest, destsize, "%s%s-%s%s%s.log", set_path, self_id, name, namedash, other_id);
else } else {
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id); snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
}
free(user_config_dir); free(user_config_dir);
@ -98,19 +100,22 @@ static int get_log_path(char *dest, int destsize, char *name, const char *selfke
/* Opens log file or creates a new one */ /* Opens log file or creates a new one */
static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype) static int init_logging_session(char *name, const char *selfkey, const char *otherkey, struct chatlog *log, int logtype)
{ {
if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) if (selfkey == NULL || (logtype == LOG_CHAT && otherkey == NULL)) {
return -1; return -1;
}
char log_path[MAX_STR_SIZE]; char log_path[MAX_STR_SIZE];
if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) if (get_log_path(log_path, sizeof(log_path), name, selfkey, otherkey, logtype) == -1) {
return -1; return -1;
}
log->file = fopen(log_path, "a+"); log->file = fopen(log_path, "a+");
snprintf(log->path, sizeof(log->path), "%s", log_path); snprintf(log->path, sizeof(log->path), "%s", log_path);
if (log->file == NULL) if (log->file == NULL) {
return -1; return -1;
}
return 0; return 0;
} }
@ -119,8 +124,9 @@ static int init_logging_session(char *name, const char *selfkey, const char *oth
void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event) void write_to_log(const char *msg, const char *name, struct chatlog *log, bool event)
{ {
if (!log->log_on) if (!log->log_on) {
return; return;
}
if (log->file == NULL) { if (log->file == NULL) {
log->log_on = false; log->log_on = false;
@ -129,10 +135,11 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
char name_frmt[TOXIC_MAX_NAME_LENGTH + 3]; char name_frmt[TOXIC_MAX_NAME_LENGTH + 3];
if (event) if (event) {
snprintf(name_frmt, sizeof(name_frmt), "* %s", name); snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else } else {
snprintf(name_frmt, sizeof(name_frmt), "%s:", name); snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
}
const char *t = user_settings->log_timestamp_format; const char *t = user_settings->log_timestamp_format;
char s[MAX_STR_SIZE]; char s[MAX_STR_SIZE];
@ -147,8 +154,9 @@ void write_to_log(const char *msg, const char *name, struct chatlog *log, bool e
void log_disable(struct chatlog *log) void log_disable(struct chatlog *log)
{ {
if (log->file != NULL) if (log->file != NULL) {
fclose(log->file); fclose(log->file);
}
memset(log, 0, sizeof(struct chatlog)); memset(log, 0, sizeof(struct chatlog));
} }
@ -157,8 +165,9 @@ int log_enable(char *name, const char *selfkey, const char *otherkey, struct cha
{ {
log->log_on = true; log->log_on = true;
if (log->file != NULL) if (log->file != NULL) {
return 0; return 0;
}
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) { if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
log_disable(log); log_disable(log);
@ -171,18 +180,21 @@ int log_enable(char *name, const char *selfkey, const char *otherkey, struct cha
/* Loads previous history from chat log */ /* Loads previous history from chat log */
void load_chat_history(ToxWindow *self, struct chatlog *log) void load_chat_history(ToxWindow *self, struct chatlog *log)
{ {
if (log->file == NULL) if (log->file == NULL) {
return; return;
}
off_t sz = file_size(log->path); off_t sz = file_size(log->path);
if (sz <= 0) if (sz <= 0) {
return; return;
}
char *hstbuf = malloc(sz + 1); char *hstbuf = malloc(sz + 1);
if (hstbuf == NULL) if (hstbuf == NULL) {
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY); exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
}
if (fseek(log->file, 0L, SEEK_SET) == -1) { if (fseek(log->file, 0L, SEEK_SET) == -1) {
free(hstbuf); free(hstbuf);
@ -204,8 +216,9 @@ void load_chat_history(ToxWindow *self, struct chatlog *log)
/* start at end and backtrace L lines or to the beginning of buffer */ /* start at end and backtrace L lines or to the beginning of buffer */
for (start = sz - 1; start >= 0 && count < L; --start) { for (start = sz - 1; start >= 0 && count < L; --start) {
if (hstbuf[start] == '\n') if (hstbuf[start] == '\n') {
++count; ++count;
}
} }
const char *line = strtok(&hstbuf[start + 1], "\n"); const char *line = strtok(&hstbuf[start + 1], "\n");
@ -238,33 +251,40 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
log_on = log->log_on; log_on = log->log_on;
} }
if (log_on) if (log_on) {
log_disable(log); log_disable(log);
}
char newpath[MAX_STR_SIZE]; char newpath[MAX_STR_SIZE];
char oldpath[MAX_STR_SIZE]; char oldpath[MAX_STR_SIZE];
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) {
goto on_error; goto on_error;
}
if (!file_exists(oldpath)) if (!file_exists(oldpath)) {
return 0; return 0;
}
if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) if (get_log_path(newpath, sizeof(newpath), dest, selfkey, otherkey, LOG_CHAT) == -1) {
goto on_error; goto on_error;
}
if (rename(oldpath, newpath) != 0) if (rename(oldpath, newpath) != 0) {
goto on_error; goto on_error;
}
if (log_on) if (log_on) {
log_enable(dest, selfkey, otherkey, log, LOG_CHAT); log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
}
return 0; return 0;
on_error: on_error:
if (log_on) if (log_on) {
log_enable(src, selfkey, otherkey, log, LOG_CHAT); log_enable(src, selfkey, otherkey, log, LOG_CHAT);
}
return -1; return -1;
} }

View File

@ -30,7 +30,7 @@ struct chatlog {
bool log_on; /* specific to current chat window */ bool log_on; /* specific to current chat window */
}; };
enum { typedef enum {
LOG_GROUP, LOG_GROUP,
LOG_PROMPT, LOG_PROMPT,
LOG_CHAT, LOG_CHAT,
@ -56,4 +56,4 @@ void load_chat_history(ToxWindow *self, struct chatlog *log);
Returns 0 on success or if no log exists, -1 on failure. */ Returns 0 on success or if no log exists, -1 on failure. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum); int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
#endif /* #define LOG_H */ #endif /* LOG_H */

View File

@ -50,8 +50,9 @@ void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type,
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg)); struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL) if (new_m == NULL) {
exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY); exit_toxic_err("failed in cqueue_message", FATALERR_MEMORY);
}
snprintf(new_m->message, sizeof(new_m->message), "%s", msg); snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
new_m->len = len; new_m->len = len;
@ -118,8 +119,9 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
struct cqueue_msg *next = msg->next; struct cqueue_msg *next = msg->next;
if (msg->prev == NULL) { /* root */ if (msg->prev == NULL) { /* root */
if (next) if (next) {
next->prev = NULL; next->prev = NULL;
}
free(msg); free(msg);
q->root = next; q->root = next;
@ -133,7 +135,7 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
} }
} }
#define CQUEUE_TRY_SEND_INTERVAL 60 #define CQUEUE_TRY_SEND_INTERVAL 10
/* Tries to send the oldest unsent message in queue. */ /* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m) void cqueue_try_send(ToxWindow *self, Tox *m)
@ -141,18 +143,15 @@ void cqueue_try_send(ToxWindow *self, Tox *m)
struct chat_queue *q = self->chatwin->cqueue; struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root; struct cqueue_msg *msg = q->root;
if (!msg) if (!msg) {
return; return;
}
if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) if (msg->receipt != 0 && !timed_out(msg->last_send_try, CQUEUE_TRY_SEND_INTERVAL)) {
return; return;
}
uint32_t receipt = 0; Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
msg->receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
TOX_MESSAGE_TYPE type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
msg->last_send_try = get_unix_time(); msg->last_send_try = get_unix_time();
msg->receipt = receipt;
return;
} }

View File

@ -48,4 +48,4 @@ void cqueue_try_send(ToxWindow *self, Tox *m);
/* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */ /* removes message with matching receipt from queue, writes to log and updates line to show the message was received. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt); void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
#endif /* #define MESSAGE_QUEUE_H */ #endif /* MESSAGE_QUEUE_H */

View File

@ -26,10 +26,8 @@
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include <dirent.h> #include <dirent.h>
#if SYSTEM == BSD
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#endif /* BSD! */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/stat.h> #include <sys/stat.h>
@ -42,6 +40,13 @@
extern ToxWindow *prompt; extern ToxWindow *prompt;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
void clear_screen(void)
{
if (system("clear") != 0) {
fprintf(stderr, "Warning: system() failed to clear screen\n");
}
}
void hst_to_net(uint8_t *num, uint16_t numbytes) void hst_to_net(uint8_t *num, uint16_t numbytes)
{ {
#ifndef WORDS_BIGENDIAN #ifndef WORDS_BIGENDIAN
@ -86,25 +91,30 @@ void get_time_str(char *buf, int bufsize)
} }
const char *t = user_settings->timestamp_format; const char *t = user_settings->timestamp_format;
strftime(buf, bufsize, t, get_time());
if (strftime(buf, bufsize, t, get_time()) == 0) {
strftime(buf, bufsize, TIMESTAMP_DEFAULT, get_time());
}
} }
/* 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)
{ {
if (!secs) if (!secs) {
return; return;
}
long int seconds = secs % 60; long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60; long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600; long int hours = secs / 3600;
if (!minutes && !hours) if (!minutes && !hours) {
snprintf(buf, bufsize, "%.2ld", seconds); snprintf(buf, bufsize, "%.2ld", seconds);
else if (!hours) } else if (!hours) {
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds); snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
else } else {
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds); snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
} }
/* /*
@ -116,11 +126,12 @@ void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
*/ */
int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size) int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size_t output_size)
{ {
if (output_size == 0 || hex_len != output_size * 2) if (output_size == 0 || hex_len != output_size * 2) {
return -1; return -1;
}
for (size_t i = 0; i < output_size; ++i) { for (size_t i = 0; i < output_size; ++i) {
sscanf(hex_string, "%2hhx", &output[i]); sscanf(hex_string, "%2hhx", (unsigned char *)&output[i]);
hex_string += 2; hex_string += 2;
} }
@ -129,18 +140,20 @@ int hex_string_to_bin(const char *hex_string, size_t hex_len, char *output, size
int hex_string_to_bytes(char *buf, int size, const char *keystr) int hex_string_to_bytes(char *buf, int size, const char *keystr)
{ {
if (size % 2 != 0) if (size % 2 != 0) {
return -1; return -1;
}
int i, res; int i, res;
const char *pos = keystr; const char *pos = keystr;
for (i = 0; i < size; ++i) { for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]); res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
pos += 2; pos += 2;
if (res == EOF || res < 1) if (res == EOF || res < 1) {
return -1; return -1;
}
} }
return 0; return 0;
@ -153,13 +166,15 @@ int hex_string_to_bytes(char *buf, int size, const char *keystr)
*/ */
int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size) int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_t output_size)
{ {
if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) if (bin_id_size != TOX_ADDRESS_SIZE || output_size < (TOX_ADDRESS_SIZE * 2 + 1)) {
return -1; return -1;
}
size_t i; size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) for (i = 0; i < TOX_ADDRESS_SIZE; ++i) {
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
}
return 0; return 0;
} }
@ -167,8 +182,9 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
/* Returns 1 if the string is empty, 0 otherwise */ /* Returns 1 if the string is empty, 0 otherwise */
int string_is_empty(const char *string) int string_is_empty(const char *string)
{ {
if (!string) if (!string) {
return true; return true;
}
return string[0] == '\0'; return string[0] == '\0';
} }
@ -176,8 +192,9 @@ int string_is_empty(const char *string)
/* Returns 1 if the string is empty, 0 otherwise */ /* Returns 1 if the string is empty, 0 otherwise */
int wstring_is_empty(const wchar_t *string) int wstring_is_empty(const wchar_t *string)
{ {
if (!string) if (!string) {
return true; return true;
}
return string[0] == L'\0'; return string[0] == L'\0';
} }
@ -187,11 +204,13 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
{ {
size_t len = mbstowcs(NULL, string, 0) + 1; size_t len = mbstowcs(NULL, string, 0) + 1;
if (n < len) if (n < len) {
return -1; return -1;
}
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) {
return -1; return -1;
}
return len; return len;
} }
@ -201,11 +220,13 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
{ {
size_t len = wcstombs(NULL, string, 0) + 1; size_t len = wcstombs(NULL, string, 0) + 1;
if (n < len) if (n < len) {
return -1; return -1;
}
if ((len = wcstombs(buf, string, n)) == (size_t) - 1) if ((len = wcstombs(buf, string, n)) == (size_t) - 1) {
return -1; return -1;
}
return len; return len;
} }
@ -224,8 +245,9 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
- must not contain a newline or tab seqeunce */ - must not contain a newline or tab seqeunce */
int valid_nick(const char *nick) int valid_nick(const char *nick)
{ {
if (!nick[0] || nick[0] == ' ') if (!nick[0] || nick[0] == ' ') {
return 0; return 0;
}
int i; int i;
@ -237,7 +259,9 @@ int valid_nick(const char *nick)
|| nick[i] == '\v' || nick[i] == '\v'
|| nick[i] == '\r') || nick[i] == '\r')
{
return 0; return 0;
}
} }
return 1; return 1;
@ -249,8 +273,9 @@ void filter_str(char *str, size_t len)
size_t i; size_t i;
for (i = 0; i < len; ++i) { for (i = 0; i < len; ++i) {
if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') if (str[i] == '\n' || str[i] == '\r' || str[i] == '\t' || str[i] == '\v' || str[i] == '\0') {
str[i] = ' '; str[i] = ' ';
}
} }
} }
@ -262,22 +287,26 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
int len = strlen(pathname) - 1; int len = strlen(pathname) - 1;
char *path = strdup(pathname); char *path = strdup(pathname);
if (path == NULL) if (path == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
while (len >= 0 && pathname[len] == '/') while (len >= 0 && pathname[len] == '/') {
path[len--] = '\0'; path[len--] = '\0';
}
char *finalname = strdup(path); char *finalname = strdup(path);
if (finalname == NULL) if (finalname == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY); exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
const char *basenm = strrchr(path, '/'); const char *basenm = strrchr(path, '/');
if (basenm != NULL) { if (basenm != NULL) {
if (basenm[1]) if (basenm[1]) {
strcpy(finalname, &basenm[1]); strcpy(finalname, &basenm[1]);
}
} }
snprintf(namebuf, bufsize, "%s", finalname); snprintf(namebuf, bufsize, "%s", finalname);
@ -294,13 +323,15 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
*/ */
size_t get_base_dir(const char *path, size_t path_len, char *dir) size_t get_base_dir(const char *path, size_t path_len, char *dir)
{ {
if (path_len == 0 || path == NULL) if (path_len == 0 || path == NULL) {
return 0; return 0;
}
size_t dir_len = char_rfind(path, '/', path_len); size_t dir_len = char_rfind(path, '/', path_len);
if (dir_len != 0 && dir_len < path_len) if (dir_len != 0 && dir_len < path_len) {
++dir_len; /* Leave trailing slash */ ++dir_len; /* Leave trailing slash */
}
memcpy(dir, path, dir_len); memcpy(dir, path, dir_len);
dir[dir_len] = '\0'; dir[dir_len] = '\0';
@ -313,8 +344,9 @@ void str_to_lower(char *str)
{ {
int i; int i;
for (i = 0; str[i]; ++i) for (i = 0; str[i]; ++i) {
str[i] = tolower(str[i]); str[i] = tolower(str[i]);
}
} }
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary. /* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
@ -322,7 +354,7 @@ void str_to_lower(char *str)
Returns nick len */ Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{ {
TOX_ERR_FRIEND_QUERY err; Tox_Err_Friend_Query err;
size_t len = tox_friend_get_name_size(m, friendnum, &err); size_t len = tox_friend_get_name_size(m, friendnum, &err);
if (err != TOX_ERR_FRIEND_QUERY_OK) { if (err != TOX_ERR_FRIEND_QUERY_OK) {
@ -348,7 +380,7 @@ on_error:
/* same as get_nick_truncate but for groupchats */ /* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum) int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
{ {
TOX_ERR_CONFERENCE_PEER_QUERY err; Tox_Err_Conference_Peer_Query err;
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err); size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) { if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
@ -400,8 +432,9 @@ int char_find(int idx, const char *s, char ch)
int i = idx; int i = idx;
for (i = idx; s[i]; ++i) { for (i = idx; s[i]; ++i) {
if (s[i] == ch) if (s[i] == ch) {
break; break;
}
} }
return i; return i;
@ -418,8 +451,9 @@ int char_rfind(const char *s, char ch, int len)
int i = 0; int i = 0;
for (i = len; i > 0; --i) { for (i = len; i > 0; --i) {
if (s[i] == ch) if (s[i] == ch) {
break; break;
}
} }
return i; return i;
@ -454,13 +488,41 @@ bool file_exists(const char *path)
return stat(path, &s) == 0; return stat(path, &s) == 0;
} }
/*
* Checks the file type path points to and returns a File_Type enum value.
*
* Returns FILE_TYPE_DIRECTORY if path points to a directory.
* Returns FILE_TYPE_REGULAR if path points to a regular file.
* Returns FILE_TYPE_OTHER on any other result, including an invalid path.
*/
File_Type file_type(const char *path)
{
struct stat s;
if (stat(path, &s) == -1) {
return FILE_TYPE_OTHER;
}
switch (s.st_mode & S_IFMT) {
case S_IFDIR:
return FILE_TYPE_DIRECTORY;
case S_IFREG:
return FILE_TYPE_REGULAR;
default:
return FILE_TYPE_OTHER;
}
}
/* returns file size. If file doesn't exist returns 0. */ /* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path) off_t file_size(const char *path)
{ {
struct stat st; struct stat st;
if (stat(path, &st) == -1) if (stat(path, &st) == -1) {
return 0; return 0;
}
return st.st_size; return st.st_size;
} }
@ -469,17 +531,19 @@ off_t file_size(const char *path)
Returns 0 if they are the same, 1 if they differ, and -1 on error. Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */ On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp) int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
{ {
char buf[size]; char buf[size];
if (fread(buf, size, 1, fp) != 1) if (fread(buf, size, 1, fp) != 1) {
return -1; return -1;
}
int ret = memcmp(signature, buf, size); int ret = memcmp(signature, buf, size);
if (fseek(fp, 0L, SEEK_SET) == -1) if (fseek(fp, 0L, SEEK_SET) == -1) {
return -1; return -1;
}
return ret == 0 ? 0 : 1; return ret == 0 ? 0 : 1;
} }
@ -487,12 +551,17 @@ int check_file_signature(const char *signature, size_t size, FILE *fp)
/* sets window title in tab bar. */ /* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len) void set_window_title(ToxWindow *self, const char *title, int len)
{ {
if (len <= 0 || !title) {
return;
}
char cpy[TOXIC_MAX_NAME_LENGTH + 1]; char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->is_groupchat) /* keep groupnumber in title */ if (self->is_groupchat) { /* keep groupnumber in title */
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title); snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
else } else {
snprintf(cpy, sizeof(cpy), "%s", title); snprintf(cpy, sizeof(cpy), "%s", title);
}
if (len > MAX_WINDOW_NAME_LENGTH) { if (len > MAX_WINDOW_NAME_LENGTH) {
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "..."); strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");

View File

@ -39,6 +39,17 @@
#define net_to_host(x, y) hst_to_net(x, y) #define net_to_host(x, y) hst_to_net(x, y)
#endif #endif
#define UNUSED_VAR(x) ((void) x)
typedef enum File_Type {
FILE_TYPE_REGULAR,
FILE_TYPE_DIRECTORY,
FILE_TYPE_OTHER,
} File_Type;
void clear_screen(void);
void hst_to_net(uint8_t *num, uint16_t numbytes); void hst_to_net(uint8_t *num, uint16_t numbytes);
/* /*
@ -78,13 +89,13 @@ int string_is_empty(const char *string);
/* Same as above but for wide character strings */ /* Same as above but for wide character strings */
int wstring_is_empty(const wchar_t *string); int wstring_is_empty(const wchar_t *string);
/* convert a multibyte string to a wide character string (must provide buffer) */ /* converts a multibyte string to a wide character string (must provide buffer) */
int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n); int char_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
/* converts wide character string into a multibyte string and puts in buf. */ /* converts wide character string into a multibyte string and puts in buf. */
int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n); int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n);
/* convert a multibyte string to a wide character string and puts in buf) */ /* converts a multibyte string to a wide character string and puts in buf) */
int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n); int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n);
/* Returns 1 if connection has timed out, 0 otherwise */ /* Returns 1 if connection has timed out, 0 otherwise */
@ -146,6 +157,15 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */ /* checks if a file exists. Returns true or false */
bool file_exists(const char *path); bool file_exists(const char *path);
/*
* Checks the file type path points to and returns a File_Type enum value.
*
* Returns FILE_TYPE_DIRECTORY if path points to a directory.
* Returns FILE_TYPE_REGULAR if path points to a regular file.
* Returns FILE_TYPE_OTHER on any other result, including an invalid path.
*/
File_Type file_type(const char *path);
/* returns file size. If file doesn't exist returns 0. */ /* returns file size. If file doesn't exist returns 0. */
off_t file_size(const char *path); off_t file_size(const char *path);
@ -153,7 +173,7 @@ off_t file_size(const char *path);
Returns 0 if they are the same, 1 if they differ, and -1 on error. Returns 0 if they are the same, 1 if they differ, and -1 on error.
On success this function will seek back to the beginning of fp */ On success this function will seek back to the beginning of fp */
int check_file_signature(const char *signature, size_t size, FILE *fp); int check_file_signature(const unsigned char *signature, size_t size, FILE *fp);
/* sets window title in tab bar. */ /* sets window title in tab bar. */
void set_window_title(ToxWindow *self, const char *title, int len); void set_window_title(ToxWindow *self, const char *title, int len);
@ -170,4 +190,4 @@ bool is_ip4_address(const char *address);
*/ */
bool is_ip6_address(const char *address); bool is_ip6_address(const char *address);
#endif /* #define MISC_TOOLS_H */ #endif /* MISC_TOOLS_H */

View File

@ -42,7 +42,7 @@ extern struct Winthread Winthread;
#define MAX_DOMAIN_SIZE 32 #define MAX_DOMAIN_SIZE 32
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3 #define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
struct Nameservers { static struct Nameservers {
int lines; int lines;
char names[MAX_SERVERS][MAX_DOMAIN_SIZE]; char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_SERVERS][SERVER_KEY_SIZE]; char keys[MAX_SERVERS][SERVER_KEY_SIZE];
@ -97,42 +97,49 @@ static int load_nameserver_list(const char *path)
{ {
FILE *fp = fopen(path, "r"); FILE *fp = fopen(path, "r");
if (fp == NULL) if (fp == NULL) {
return -2; return -2;
}
char line[MAX_SERVER_LINE]; char line[MAX_SERVER_LINE];
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) { while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
int linelen = strlen(line); int linelen = strlen(line);
if (linelen < SERVER_KEY_SIZE * 2 + 5) if (linelen < SERVER_KEY_SIZE * 2 + 5) {
continue; continue;
}
if (line[linelen - 1] == '\n') if (line[linelen - 1] == '\n') {
line[--linelen] = '\0'; line[--linelen] = '\0';
}
const char *name = strtok(line, " "); const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " "); const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL) if (name == NULL || keystr == NULL) {
continue; continue;
}
if (strlen(keystr) != SERVER_KEY_SIZE * 2) if (strlen(keystr) != SERVER_KEY_SIZE * 2) {
continue; continue;
}
snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name); snprintf(Nameservers.names[Nameservers.lines], sizeof(Nameservers.names[Nameservers.lines]), "%s", name);
int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr); int res = hex_string_to_bytes(Nameservers.keys[Nameservers.lines], SERVER_KEY_SIZE, keystr);
if (res == -1) if (res == -1) {
continue; continue;
}
++Nameservers.lines; ++Nameservers.lines;
} }
fclose(fp); fclose(fp);
if (Nameservers.lines < 1) if (Nameservers.lines < 1) {
return -3; return -3;
}
return 0; return 0;
} }
@ -145,8 +152,9 @@ static int load_nameserver_list(const char *path)
*/ */
static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz) static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *dombuf, size_t dombuf_sz)
{ {
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) {
return -1; return -1;
}
char tmpaddr[MAX_STR_SIZE]; char tmpaddr[MAX_STR_SIZE];
char *tmpname = NULL; char *tmpname = NULL;
@ -156,8 +164,9 @@ static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *
tmpname = strtok(tmpaddr, "@"); tmpname = strtok(tmpaddr, "@");
tmpdom = strtok(NULL, ""); tmpdom = strtok(NULL, "");
if (tmpname == NULL || tmpdom == NULL) if (tmpname == NULL || tmpdom == NULL) {
return -1; return -1;
}
str_to_lower(tmpdom); str_to_lower(tmpdom);
snprintf(namebuf, namebuf_sz, "%s", tmpname); snprintf(namebuf, namebuf_sz, "%s", tmpname);
@ -197,29 +206,35 @@ static int process_response(struct Recv_Curl_Data *recv_data)
{ {
size_t prefix_size = strlen(ID_PREFIX); size_t prefix_size = strlen(ID_PREFIX);
if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) if (recv_data->length < TOX_ADDRESS_SIZE * 2 + prefix_size) {
return -1; return -1;
}
const char *IDstart = strstr(recv_data->data, ID_PREFIX); const char *IDstart = strstr(recv_data->data, ID_PREFIX);
if (IDstart == NULL) if (IDstart == NULL) {
return -1; return -1;
}
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) {
return -1; return -1;
}
char ID_string[TOX_ADDRESS_SIZE * 2 + 1]; char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2); memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
ID_string[TOX_ADDRESS_SIZE * 2] = 0; ID_string[TOX_ADDRESS_SIZE * 2] = 0;
if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) if (hex_string_to_bin(ID_string, strlen(ID_string), t_data.id_bin, sizeof(t_data.id_bin)) == -1) {
return -1; return -1;
}
return 0; return 0;
} }
void *lookup_thread_func(void *data) void *lookup_thread_func(void *data)
{ {
UNUSED_VAR(data);
ToxWindow *self = t_data.self; ToxWindow *self = t_data.self;
char input_domain[MAX_STR_SIZE]; char input_domain[MAX_STR_SIZE];
@ -234,10 +249,11 @@ void *lookup_thread_func(void *data)
char real_domain[MAX_DOMAIN_SIZE]; char real_domain[MAX_DOMAIN_SIZE];
if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) { if (!get_domain_match(nameserver_key, real_domain, sizeof(real_domain), input_domain)) {
if (!strcasecmp(input_domain, "utox.org")) if (!strcasecmp(input_domain, "utox.org")) {
lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic."); lookup_error(self, "utox.org uses deprecated DNS-based lookups and is no longer supported by Toxic.");
else } else {
lookup_error(self, "Name server domain not found."); lookup_error(self, "Name server domain not found.");
}
kill_lookup_thread(); kill_lookup_thread();
} }
@ -278,7 +294,7 @@ void *lookup_thread_func(void *data)
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type); int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);
if (proxy_ret != 0) { if (proxy_ret != 0) {
lookup_error(self, "Failed to set proxy (error %d)\n"); lookup_error(self, "Failed to set proxy (error %d)\n", proxy_ret);
goto on_exit; goto on_exit;
} }

View File

@ -47,12 +47,12 @@
/* compatibility with older versions of OpenAL */ /* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER #ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h> #include <AL/alext.h>
#endif #endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif #endif /* __APPLE__ */
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */ #include <AL/alut.h> /* freealut packet */
#endif #endif /* SOUND_NOTIFY */
#endif /* AUDIO */ #endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
#include <libnotify/notify.h> #include <libnotify/notify.h>
@ -64,7 +64,7 @@
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
struct Control { static struct Control {
time_t cooldown; time_t cooldown;
time_t notif_timeout; time_t notif_timeout;
@ -79,12 +79,12 @@ struct Control {
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
} Control = {0}; } Control = {0};
struct _ActiveNotifications { static struct _ActiveNotifications {
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
uint32_t source; uint32_t source;
uint32_t buffer; uint32_t buffer;
bool looping; bool looping;
#endif #endif /* SOUND_NOTIFY */
bool active; bool active;
int *id_indicator; int *id_indicator;
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
@ -93,7 +93,7 @@ struct _ActiveNotifications {
char title[64]; char title[64];
size_t size; size_t size;
time_t n_timeout; time_t n_timeout;
#endif #endif /* BOX_NOTIFY */
} actives[ACTIVE_NOTIFS_MAX]; } actives[ACTIVE_NOTIFS_MAX];
/**********************************************************************************/ /**********************************************************************************/
/**********************************************************************************/ /**********************************************************************************/
@ -104,21 +104,24 @@ struct _ActiveNotifications {
/* coloured tab notifications: primary notification type */ /* coloured tab notifications: primary notification type */
static void tab_notify(ToxWindow *self, uint64_t flags) static void tab_notify(ToxWindow *self, uint64_t flags)
{ {
if (self == NULL) if (self == NULL) {
return; return;
}
if (flags & NT_WNDALERT_0) if (flags & NT_WNDALERT_0) {
self->alert = WINDOW_ALERT_0; self->alert = WINDOW_ALERT_0;
else if ( (flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0) ) } else if ((flags & NT_WNDALERT_1) && (!self->alert || self->alert > WINDOW_ALERT_0)) {
self->alert = WINDOW_ALERT_1; self->alert = WINDOW_ALERT_1;
else if ( (flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1) ) } else if ((flags & NT_WNDALERT_2) && (!self->alert || self->alert > WINDOW_ALERT_1)) {
self->alert = WINDOW_ALERT_2; self->alert = WINDOW_ALERT_2;
}
} }
static bool notifications_are_disabled(uint64_t flags) static bool notifications_are_disabled(uint64_t flags)
{ {
if (user_settings->alerts != ALERTS_ENABLED) if (user_settings->alerts != ALERTS_ENABLED) {
return true; return true;
}
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time()); bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
#ifdef X11 #ifdef X11
@ -128,14 +131,14 @@ static bool notifications_are_disabled(uint64_t flags)
#endif #endif
} }
static void control_lock() static void control_lock(void)
{ {
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_lock(Control.poll_mutex); pthread_mutex_lock(Control.poll_mutex);
#endif #endif
} }
static void control_unlock() static void control_unlock(void)
{ {
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_unlock(Control.poll_mutex); pthread_mutex_unlock(Control.poll_mutex);
@ -157,11 +160,13 @@ static bool device_opened = false;
time_t last_opened_update = 0; time_t last_opened_update = 0;
/* Opens primary device. Returns true on succe*/ /* Opens primary device. Returns true on succe*/
void m_open_device() void m_open_device(void)
{ {
last_opened_update = get_unix_time(); last_opened_update = get_unix_time();
if (device_opened) return; if (device_opened) {
return;
}
/* Blah error check */ /* Blah error check */
open_primary_device(output, &Control.device_idx, 48000, 20, 1); open_primary_device(output, &Control.device_idx, 48000, 20, 1);
@ -169,9 +174,11 @@ void m_open_device()
device_opened = true; device_opened = true;
} }
void m_close_device() void m_close_device(void)
{ {
if (!device_opened) return; if (!device_opened) {
return;
}
close_device(output, Control.device_idx); close_device(output, Control.device_idx);
@ -179,7 +186,7 @@ void m_close_device()
} }
/* Terminate all sounds but wait for them to finish first */ /* Terminate all sounds but wait for them to finish first */
void graceful_clear() void graceful_clear(void)
{ {
control_lock(); control_lock();
@ -196,17 +203,20 @@ void graceful_clear()
actives[i].box = NULL; actives[i].box = NULL;
} }
#endif #endif /* BOX_NOTIFY */
if (actives[i].id_indicator) if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
}
if ( actives[i].looping ) { if (actives[i].looping) {
stop_sound(i); stop_sound(i);
} else { } else {
if (!is_playing(actives[i].source)) if (!is_playing(actives[i].source)) {
memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
else break; } else {
break;
}
} }
} }
} }
@ -225,7 +235,7 @@ void graceful_clear()
void *do_playing(void *_p) void *do_playing(void *_p)
{ {
(void)_p; UNUSED_VAR(_p);
while (true) { while (true) {
control_lock(); control_lock();
@ -241,7 +251,9 @@ void *do_playing(void *_p)
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) { for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].looping) has_looping = true; if (actives[i].looping) {
has_looping = true;
}
test_active_notify = actives[i].active && !actives[i].looping; test_active_notify = actives[i].active && !actives[i].looping;
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
@ -249,8 +261,9 @@ void *do_playing(void *_p)
#endif #endif
if (test_active_notify) { if (test_active_notify) {
if (actives[i].id_indicator) if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
}
if (!is_playing(actives[i].source)) { if (!is_playing(actives[i].source)) {
/* Close */ /* Close */
@ -267,8 +280,9 @@ void *do_playing(void *_p)
notify_notification_close(actives[i].box, &ignore); notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL; actives[i].box = NULL;
if (actives[i].id_indicator) if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
}
if (!actives[i].looping && !is_playing(actives[i].source)) { if (!actives[i].looping && !is_playing(actives[i].source)) {
/* stop source if not looping or playing, just terminate box */ /* stop source if not looping or playing, just terminate box */
@ -279,7 +293,7 @@ void *do_playing(void *_p)
} }
} }
#endif #endif /* BOX_NOTIFY */
} }
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/ /* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
@ -303,7 +317,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++); for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) { if (i == ACTIVE_NOTIFS_MAX) {
return -1; /* Full */ return -1; /* Full */
} }
@ -320,7 +334,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
#elif BOX_NOTIFY #elif BOX_NOTIFY
void *do_playing(void *_p) void *do_playing(void *_p)
{ {
(void)_p; UNUSED_VAR(_p);
while (true) { while (true) {
control_lock(); control_lock();
@ -338,8 +352,9 @@ void *do_playing(void *_p)
notify_notification_close(actives[i].box, &ignore); notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL; actives[i].box = NULL;
if (actives[i].id_indicator) if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
}
memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
} }
@ -352,7 +367,7 @@ void *do_playing(void *_p)
pthread_exit(NULL); pthread_exit(NULL);
} }
void graceful_clear() void graceful_clear(void)
{ {
int i; int i;
control_lock(); control_lock();
@ -364,15 +379,16 @@ void graceful_clear()
actives[i].box = NULL; actives[i].box = NULL;
} }
if (actives[i].id_indicator) if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */ *actives[i].id_indicator = -1; /* reset indicator value */
}
memset(&actives[i], 0, sizeof(struct _ActiveNotifications)); memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
} }
control_unlock(); control_unlock();
} }
#endif #endif /* SOUND_NOTIFY */
/**********************************************************************************/ /**********************************************************************************/
/**********************************************************************************/ /**********************************************************************************/
@ -391,19 +407,20 @@ int init_notify(int login_cooldown, int notification_timeout)
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) {
return -1; return -1;
}
Control.poll_active = 1; Control.poll_active = 1;
pthread_t thread; pthread_t thread;
if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0 ) { if (pthread_create(&thread, NULL, do_playing, NULL) != 0 || pthread_detach(thread) != 0) {
pthread_mutex_destroy(Control.poll_mutex); pthread_mutex_destroy(Control.poll_mutex);
Control.poll_active = 0; Control.poll_active = 0;
return -1; return -1;
} }
#endif #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
Control.cooldown = time(NULL) + login_cooldown; Control.cooldown = time(NULL) + login_cooldown;
@ -414,12 +431,12 @@ int init_notify(int login_cooldown, int notification_timeout)
return 1; return 1;
} }
void terminate_notify() void terminate_notify(void)
{ {
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) #if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
control_lock(); control_lock();
if ( !Control.poll_active ) { if (!Control.poll_active) {
control_unlock(); control_unlock();
return; return;
} }
@ -428,12 +445,14 @@ void terminate_notify()
control_unlock(); control_unlock();
graceful_clear(); graceful_clear();
#endif #endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
int i = 0; int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]); for (; i < SOUNDS_SIZE; i ++) {
free(Control.sounds[i]);
}
alutExit(); alutExit();
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
@ -446,7 +465,9 @@ void terminate_notify()
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char *value) int set_sound(Notification sound, const char *value)
{ {
if (sound == silent) return 0; if (sound == silent) {
return 0;
}
free(Control.sounds[sound]); free(Control.sounds[sound]);
@ -487,11 +508,14 @@ int play_notify_sound(Notification notif, uint64_t flags)
{ {
int rc = -1; int rc = -1;
if (flags & NT_BEEP) beep(); if (flags & NT_BEEP) {
beep();
}
if (notif != silent) { if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] ) if (!Control.poll_active || !Control.sounds[notif]) {
return -1; return -1;
}
rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0); rc = play_sound_internal(notif, flags & NT_LOOP ? 1 : 0);
} }
@ -502,7 +526,7 @@ int play_notify_sound(Notification notif, uint64_t flags)
void stop_sound(int id) void stop_sound(int id)
{ {
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) { if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active) {
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
if (actives[id].box) { if (actives[id].box) {
@ -510,19 +534,20 @@ void stop_sound(int id)
notify_notification_close(actives[id].box, &ignore); notify_notification_close(actives[id].box, &ignore);
} }
#endif #endif /* BOX_NOTIFY */
if (actives[id].id_indicator) if (actives[id].id_indicator) {
*actives[id].id_indicator = -1; *actives[id].id_indicator = -1;
}
// alSourcei(actives[id].source, AL_LOOPING, false); // alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source); alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source); alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1, &actives[id].buffer); alDeleteBuffers(1, &actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications)); memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
} }
} }
#endif #endif /* SOUND_NOTIFY */
static int m_play_sound(Notification notif, uint64_t flags) static int m_play_sound(Notification notif, uint64_t flags)
{ {
@ -530,48 +555,45 @@ static int m_play_sound(Notification notif, uint64_t flags)
return play_notify_sound(notif, flags); return play_notify_sound(notif, flags);
#else #else
if (notif != silent) if (notif != silent) {
beep(); beep();
}
return -1; return -1;
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
} }
#ifdef BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void *data)
{
}
#endif
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator) int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator)
{ {
tab_notify(self, flags); tab_notify(self, flags);
if (notifications_are_disabled(flags)) if (notifications_are_disabled(flags)) {
return -1; return -1;
}
int id = -1; int id = -1;
control_lock(); control_lock();
if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) if (self && (!self->stb || self->stb->status != TOX_USER_STATUS_BUSY)) {
id = m_play_sound(notif, flags); id = m_play_sound(notif, flags);
else if (flags & NT_ALWAYS) } else if (flags & NT_ALWAYS) {
id = m_play_sound(notif, flags); id = m_play_sound(notif, flags);
}
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) #if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
if (id == -1) { if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++); for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) { if (id == ACTIVE_NOTIFS_MAX) {
control_unlock(); control_unlock();
return -1; /* Full */ return -1; /* Full */
} }
} }
#endif #endif /* defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY) */
if ( id_indicator && id != -1 ) { if (id_indicator && id != -1) {
actives[id].id_indicator = id_indicator; actives[id].id_indicator = id_indicator;
*id_indicator = id; *id_indicator = id;
} }
@ -585,10 +607,13 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
{ {
tab_notify(self, flags); tab_notify(self, flags);
if (notifications_are_disabled(flags)) if (notifications_are_disabled(flags)) {
return -1; return -1;
}
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1; if (id < 0 || id >= ACTIVE_NOTIFS_MAX) {
return -1;
}
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
control_lock(); control_lock();
@ -617,8 +642,9 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
return id; return id;
#else #else
if (notif != silent) if (notif != silent) {
beep(); beep();
}
return 0; return 0;
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
@ -644,7 +670,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) { if (id == ACTIVE_NOTIFS_MAX) {
control_unlock(); control_unlock();
return -1; /* Full */ return -1; /* Full */
} }
@ -652,27 +678,33 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi
actives[id].active = 1; actives[id].active = 1;
actives[id].id_indicator = id_indicator; actives[id].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id; if (id_indicator) {
*id_indicator = id;
}
} }
#else #else
if (id == -1) if (id == -1) {
return -1; return -1;
}
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); if (strlen(title) > 23) {
strcpy(actives[id].title + 20, "...");
}
va_list __ARGS__; va_list __ARGS__;
va_start (__ARGS__, format); va_start(__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__); va_end(__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size++; actives[id].size++;
@ -687,7 +719,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi
return id; return id;
#else #else
return sound_notify(self, notif, flags, id_indicator); return sound_notify(self, notif, flags, id_indicator);
#endif /* BOX_NOTIFY */ #endif /* BOX_NOTIFY */
} }
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...) int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...)
@ -699,8 +731,9 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1) if (sound_notify2(self, notif, flags, id) == -1) {
return -1; return -1;
}
control_lock(); control_lock();
@ -710,12 +743,13 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
} }
va_list __ARGS__; va_list __ARGS__;
va_start (__ARGS__, format); va_start(__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__); va_end(__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].size++; actives[id].size++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
@ -739,15 +773,16 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
return id; return id;
#else #else
return sound_notify2(self, notif, flags, id); return sound_notify2(self, notif, flags, id);
#endif #endif /* BOX_NOTIFY */
} }
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...) int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...)
{ {
tab_notify(self, flags); tab_notify(self, flags);
if (notifications_are_disabled(flags)) if (notifications_are_disabled(flags)) {
return -1; return -1;
}
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
@ -757,7 +792,7 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++); for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) { if (id == ACTIVE_NOTIFS_MAX) {
control_unlock(); control_unlock();
return -1; /* Full */ return -1; /* Full */
} }
@ -769,15 +804,18 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title); snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "..."); if (strlen(title) > 23) {
strcpy(actives[id].title + 20, "...");
}
va_list __ARGS__; va_list __ARGS__;
va_start (__ARGS__, format); va_start(__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__); vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__); va_end(__ARGS__);
if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) if (strlen(actives[id].messages[0]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "..."); strcpy(actives[id].messages[0] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].active = 1; actives[id].active = 1;
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL); actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
@ -793,32 +831,34 @@ int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const
return id; return id;
#else #else
return -1; return -1;
#endif #endif /* BOX_NOTIFY */
} }
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...) int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
{ {
tab_notify(self, flags); tab_notify(self, flags);
if (notifications_are_disabled(flags)) if (notifications_are_disabled(flags)) {
return -1; return -1;
}
#ifdef BOX_NOTIFY #ifdef BOX_NOTIFY
control_lock(); control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1 ) { if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= MAX_BOX_MSG_LEN + 1) {
control_unlock(); control_unlock();
return -1; return -1;
} }
va_list __ARGS__; va_list __ARGS__;
va_start (__ARGS__, format); va_start(__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__); vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__); va_end(__ARGS__);
if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) if (strlen(actives[id].messages[actives[id].size]) > MAX_BOX_MSG_LEN - 3) {
strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "..."); strcpy(actives[id].messages[actives[id].size] + MAX_BOX_MSG_LEN - 3, "...");
}
actives[id].size ++; actives[id].size ++;
actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000; actives[id].n_timeout = get_unix_time() + Control.notif_timeout / 1000;
@ -842,5 +882,5 @@ int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *form
return id; return id;
#else #else
return -1; return -1;
#endif #endif /* BOX_NOTIFY */
} }

View File

@ -23,7 +23,7 @@
#ifndef NOTIFY_H #ifndef NOTIFY_H
#define NOTIFY_H #define NOTIFY_H
#include <inttypes.h> #include <stdint.h>
#include "windows.h" #include "windows.h"
typedef enum _Notification { typedef enum _Notification {
@ -60,7 +60,7 @@ typedef enum _Flags {
} Flags; } Flags;
int init_notify(int login_cooldown, int notification_timeout); int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify(); void terminate_notify(void);
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator); int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id); int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id);

View File

@ -45,7 +45,7 @@ void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *
#endif /* __OBJC__ */ #endif /* __OBJC__ */
int osx_video_init(char **device_names, int *size); int osx_video_init(char **device_names, int *size);
void osx_video_release(); void osx_video_release(void);
/* Start device */ /* Start device */
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height); int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
/* Stop device */ /* Stop device */
@ -54,4 +54,4 @@ void osx_video_close_device(uint32_t device_idx);
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height); int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height);
#endif /* OSX_VIDEO_H */ #endif /* OSX_VIDEO_H */

View File

@ -144,8 +144,9 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
device_names[i] = video_input_name; device_names[i] = video_input_name;
} }
if ( i <= 0 ) if (i <= 0) {
return nil; return nil;
}
*size = i; *size = i;
@ -174,7 +175,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
NSError *error = NULL; NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error]; AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
if ( error != NULL ) { if (error != NULL) {
[input release]; [input release];
return -1; return -1;
} }
@ -193,7 +194,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
AVCaptureInputPort *port = [input.ports objectAtIndex: 0]; AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
CMFormatDescriptionRef format_description = port.formatDescription; CMFormatDescriptionRef format_description = port.formatDescription;
if ( format_description ) { if (format_description) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description); CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
*width = dimensions.width; *width = dimensions.width;
*height = dimensions.height; *height = dimensions.height;
@ -303,8 +304,9 @@ int osx_video_init(char **device_names, int *size)
{ {
_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size]; _OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
if ( _OSXVideo == nil ) if (_OSXVideo == nil) {
return -1; return -1;
}
return 0; return 0;
} }
@ -317,8 +319,9 @@ void osx_video_release()
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height) int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
{ {
if ( _OSXVideo == nil ) if (_OSXVideo == nil) {
return -1; return -1;
}
return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height]; return [_OSXVideo openVideoDeviceIndex: selection Width: width Height: height];
} }
@ -330,8 +333,9 @@ void osx_video_close_device(uint32_t device_idx)
int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height) int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, uint16_t *height)
{ {
if ( _OSXVideo == nil ) if (_OSXVideo == nil) {
return -1; return -1;
}
return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height]; return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
} }
@ -339,4 +343,4 @@ int osx_video_read_device(uint8_t *y, uint8_t *u, uint8_t *v, uint16_t *width, u
* End of C-interface for OSXVideo * End of C-interface for OSXVideo
*/ */
#endif /* __OBJC__ */ #endif /* __OBJC__ */

View File

@ -49,19 +49,27 @@ extern struct Winthread Winthread;
extern FriendsList Friends; extern FriendsList Friends;
FriendRequests FrndRequests; FriendRequests FrndRequests;
#if defined(PYTHON) && defined(VIDEO) #ifdef AUDIO
#define AC_NUM_GLOB_COMMANDS 23 #define AC_NUM_GLOB_COMMANDS_AUDIO 2
#elif defined(PYTHON) && defined(AUDIO)
#define AC_NUM_GLOB_COMMANDS 21
#elif VIDEO
#define AC_NUM_GLOB_COMMANDS 22
#elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20
#elif PYTHON
#define AC_NUM_GLOB_COMMANDS 19
#else #else
#define AC_NUM_GLOB_COMMANDS 18 #define AC_NUM_GLOB_COMMANDS_AUDIO 0
#endif #endif /* AUDIO */
#ifdef VIDEO
#define AC_NUM_GLOB_COMMANDS_VIDEO 2
#else
#define AC_NUM_GLOB_COMMANDS_VIDEO 0
#endif /* VIDEO */
#ifdef PYTHON
#define AC_NUM_GLOB_COMMANDS_PYTHON 1
#else
#define AC_NUM_GLOB_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_GLOB_COMMANDS_QRCODE 1
#else
#define AC_NUM_GLOB_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_GLOB_COMMANDS (17 + AC_NUM_GLOB_COMMANDS_AUDIO + AC_NUM_GLOB_COMMANDS_VIDEO + AC_NUM_GLOB_COMMANDS_PYTHON + AC_NUM_GLOB_COMMANDS_QRCODE)
/* Array of global command names used for tab completion. */ /* Array of global command names used for tab completion. */
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = { static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -76,7 +84,9 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" }, { "/help" },
{ "/log" }, { "/log" },
{ "/myid" }, { "/myid" },
#ifdef QRCODE
{ "/myqr" }, { "/myqr" },
#endif /* QRCODE */
{ "/nick" }, { "/nick" },
{ "/note" }, { "/note" },
{ "/nospam" }, { "/nospam" },
@ -127,8 +137,10 @@ void kill_prompt_window(ToxWindow *self)
} }
/* callback: Updates own connection status in prompt statusbar */ /* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata) void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata)
{ {
UNUSED_VAR(m);
UNUSED_VAR(userdata);
StatusBar *statusbar = prompt->stb; StatusBar *statusbar = prompt->stb;
statusbar->connection = connection_status; statusbar->connection = connection_status;
} }
@ -149,26 +161,35 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
size_t len = strlen(statusbar->statusmsg); size_t len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len; statusbar->statusmsg_len = len;
TOX_ERR_SET_INFO err; Tox_Err_Set_Info err;
tox_self_set_status_message(m, (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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set note (error %d)\n", err);
}
} }
/* Updates own status in prompt statusbar */ /* Updates own status in prompt statusbar */
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status) void prompt_update_status(ToxWindow *prompt, Tox_User_Status status)
{ {
StatusBar *statusbar = prompt->stb; StatusBar *statusbar = prompt->stb;
statusbar->status = status; statusbar->status = status;
} }
/* Returns our own connection status */
Tox_Connection prompt_selfConnectionStatus(void)
{
StatusBar *statusbar = prompt->stb;
return statusbar->connection;
}
/* Adds friend request to pending friend requests. /* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full. */ Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data) static int add_friend_request(const char *public_key, const char *data)
{ {
if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) if (FrndRequests.max_idx >= MAX_FRIEND_REQUESTS) {
return -1; return -1;
}
int i; int i;
@ -178,8 +199,9 @@ static int add_friend_request(const char *public_key, const char *data)
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE); memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data); snprintf(FrndRequests.request[i].msg, sizeof(FrndRequests.request[i].msg), "%s", data);
if (i == FrndRequests.max_idx) if (i == FrndRequests.max_idx) {
++FrndRequests.max_idx; ++FrndRequests.max_idx;
}
++FrndRequests.num_requests; ++FrndRequests.num_requests;
@ -190,7 +212,10 @@ static int add_friend_request(const char *public_key, const char *data)
return -1; return -1;
} }
static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) /*
* Return true if input is recognized by handler
*/
static bool prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -198,38 +223,51 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x); getyx(self->window, y, x);
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0) UNUSED_VAR(y);
return;
if (ctx->pastemode && key == '\r') if (x2 <= 0 || y2 <= 0) {
return false;
}
if (ctx->pastemode && key == '\r') {
key = '\n'; key = '\n';
}
/* ignore non-menu related input if active */ /* ignore non-menu related input if active */
if (self->help->active) { if (self->help->active) {
help_onKey(self, key); help_onKey(self, key);
return; return true;
} }
if (ltr || key == '\n') { /* char is printable */ if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2); input_new_char(self, key, x, x2);
return; return true;
} }
if (line_info_onKey(self, key)) if (line_info_onKey(self, key)) {
return; return true;
}
input_handle(self, key, x, y, x2, y2); if (input_handle(self, key, x, x2)) {
return true;
}
int input_ret = false;
if (key == '\t') { /* TAB key: auto-completes command */ if (key == '\t') { /* TAB key: auto-completes command */
input_ret = true;
if (ctx->len > 1 && ctx->line[0] == '/') { if (ctx->len > 1 && ctx->line[0] == '/') {
int diff = -1; int diff = -1;
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) if (wcsncmp(ctx->line, L"/avatar ", wcslen(L"/avatar ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
}
#ifdef PYTHON #ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0) else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run"); diff = dir_match(self, m, ctx->line, L"/run");
}
#endif #endif
@ -240,8 +278,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{"busy"}, {"busy"},
}; };
diff = complete_line(self, status_cmd_list, 3, 8); diff = complete_line(self, status_cmd_list, 3, 8);
} else } else {
diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE); diff = complete_line(self, glob_cmd_list, AC_NUM_GLOB_COMMANDS, MAX_CMDNAME_SIZE);
}
if (diff != -1) { if (diff != -1) {
if (x + diff > x2 - 1) { if (x + diff > x2 - 1) {
@ -255,6 +294,8 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
sound_notify(self, notif_error, 0, NULL); sound_notify(self, notif_error, 0, NULL);
} }
} else if (key == '\r') { } else if (key == '\r') {
input_ret = true;
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line)) { if (!wstring_is_empty(ctx->line)) {
@ -263,8 +304,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
char line[MAX_STR_SIZE] = {0}; char line[MAX_STR_SIZE] = {0};
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1) {
memset(&line, 0, sizeof(line)); memset(&line, 0, sizeof(line));
}
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line); line_info_add(self, NULL, 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);
@ -274,6 +316,8 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
reset_buf(ctx); reset_buf(ctx);
} }
return input_ret;
} }
static void prompt_onDraw(ToxWindow *self, Tox *m) static void prompt_onDraw(ToxWindow *self, Tox *m)
@ -281,8 +325,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
return; return;
}
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -294,8 +339,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
curs_set(1); curs_set(1);
if (ctx->len > 0) if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]); mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
@ -303,7 +349,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wmove(statusbar->topline, 0, 0); wmove(statusbar->topline, 0, 0);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection = statusbar->connection; Tox_Connection connection = statusbar->connection;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
if (connection != TOX_CONNECTION_NONE) { if (connection != TOX_CONNECTION_NONE) {
@ -311,7 +357,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
const char *status_text = "ERROR"; const char *status_text = "ERROR";
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
TOX_USER_STATUS status = statusbar->status; Tox_User_Status status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
switch (status) { switch (status) {
@ -355,7 +401,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m); size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t *) statusmsg); tox_self_get_status_message(m, (uint8_t *) statusmsg);
statusmsg[slen] = '\0'; statusmsg[slen] = '\0';
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg); statusbar->statusmsg_len = strlen(statusbar->statusmsg);
@ -375,8 +421,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
statusbar->statusmsg_len = maxlen; statusbar->statusmsg_len = maxlen;
} }
if (statusbar->statusmsg[0]) if (statusbar->statusmsg[0]) {
wprintw(statusbar->topline, " : %s", statusbar->statusmsg); wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
}
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -384,26 +431,29 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int y, x; int y, x;
getyx(self->window, y, x); getyx(self->window, y, x);
(void) x;
UNUSED_VAR(x);
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x); wmove(self->window, y + 1, new_x);
wnoutrefresh(self->window); wnoutrefresh(self->window);
if (self->help->active) if (self->help->active) {
help_onDraw(self); help_onDraw(self);
}
} }
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, TOX_CONNECTION connection_status) static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, Tox_Connection connection_status)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */ char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
get_nick_truncate(m, nick, friendnum); get_nick_truncate(m, nick, friendnum);
if (!nick[0]) if (!nick[0]) {
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME); snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
}
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
@ -418,28 +468,33 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg); line_info_add(self, timefrmt, 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) {
box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, box_notify2(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has come online", nick ); "%s has come online", nick);
else } else {
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick ); "Toxic", "%s has come online", nick);
}
} 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, timefrmt, 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) {
box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box, box_notify2(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, self->active_box,
"%s has gone offline", nick ); "%s has gone offline", nick);
else } else {
box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, box_notify(self, user_log_out, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has gone offline", nick ); "Toxic", "%s has gone offline", nick);
}
} }
} }
static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length) static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, const char *data, size_t length)
{ {
UNUSED_VAR(m);
UNUSED_VAR(length);
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
@ -460,15 +515,16 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL); sound_notify(self, generic_message, NT_WNDALERT_1 | NT_NOTIFWND, NULL);
} }
void prompt_init_statusbar(ToxWindow *self, Tox *m) void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run)
{ {
int x2, y2; int x2, y2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES); exit_toxic_err("failed in prompt_init_statusbar", FATALERR_CURSES);
}
(void) y2; UNUSED_VAR(y2);
/* Init statusbar info */ /* Init statusbar info */
StatusBar *statusbar = self->stb; StatusBar *statusbar = self->stb;
@ -484,12 +540,12 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
size_t s_len = tox_self_get_status_message_size(m); size_t s_len = tox_self_get_status_message_size(m);
tox_self_get_status_message(m, (uint8_t *) statusmsg); tox_self_get_status_message(m, (uint8_t *) statusmsg);
TOX_USER_STATUS status = tox_self_get_status(m); Tox_User_Status status = tox_self_get_status(m);
nick[n_len] = '\0'; nick[n_len] = '\0';
statusmsg[s_len] = '\0'; statusmsg[s_len] = '\0';
if (s_len == 0 || !strncmp(statusmsg, "Toxing on Toxic", strlen("Toxing on Toxic"))) { if (first_time_run) {
snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic"); snprintf(statusmsg, sizeof(statusmsg), "Toxing on Toxic");
s_len = strlen(statusmsg); s_len = strlen(statusmsg);
statusmsg[s_len] = '\0'; statusmsg[s_len] = '\0';
@ -512,7 +568,7 @@ static void print_welcome_msg(ToxWindow *self)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 1, BLUE, " |_| \\___/_/\\_\\___\\____| v." TOXICVER);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
const char *msg = "Welcome to Toxic, a free, open source Tox-based instant messenging 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, NULL, 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, NULL, NULL, NULL, SYS_MSG, 1, CYAN, msg);
@ -525,8 +581,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
int y2, x2; int y2, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES); exit_toxic_err("failed in prompt_onInit", FATALERR_CURSES);
}
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
@ -535,8 +592,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
ctx->log = calloc(1, sizeof(struct chatlog)); ctx->log = calloc(1, sizeof(struct chatlog));
ctx->hst = calloc(1, sizeof(struct history)); ctx->hst = calloc(1, sizeof(struct history));
if (ctx->log == NULL || ctx->hst == NULL) if (ctx->log == NULL || ctx->hst == NULL) {
exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY); exit_toxic_err("failed in prompt_onInit", FATALERR_MEMORY);
}
line_info_init(ctx->hst); line_info_init(ctx->hst);
@ -544,46 +602,51 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
char myid[TOX_ADDRESS_SIZE]; char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid); tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
} }
scrollok(ctx->history, 0); scrollok(ctx->history, 0);
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) if (user_settings->show_welcome_msg == SHOW_WELCOME_MSG_ON) {
print_welcome_msg(self); print_welcome_msg(self);
}
} }
ToxWindow new_prompt(void) ToxWindow *new_prompt(void)
{ {
ToxWindow ret; ToxWindow *ret = calloc(1, sizeof(ToxWindow));
memset(&ret, 0, sizeof(ret));
ret.num = -1; if (ret == NULL) {
ret.active = true; exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
ret.is_prompt = true; }
ret.onKey = &prompt_onKey; ret->num = -1;
ret.onDraw = &prompt_onDraw; ret->is_prompt = true;
ret.onInit = &prompt_onInit;
ret.onConnectionChange = &prompt_onConnectionChange;
ret.onFriendRequest = &prompt_onFriendRequest;
strcpy(ret.name, "home"); ret->onKey = &prompt_onKey;
ret->onDraw = &prompt_onDraw;
ret->onInit = &prompt_onInit;
ret->onConnectionChange = &prompt_onConnectionChange;
ret->onFriendRequest = &prompt_onFriendRequest;
strcpy(ret->name, "home");
ChatContext *chatwin = calloc(1, sizeof(ChatContext)); ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar)); StatusBar *stb = calloc(1, sizeof(StatusBar));
Help *help = calloc(1, sizeof(Help)); Help *help = calloc(1, sizeof(Help));
if (stb == NULL || chatwin == NULL || help == NULL) if (stb == NULL || chatwin == NULL || help == NULL) {
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY); exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
}
ret.chatwin = chatwin; ret->chatwin = chatwin;
ret.stb = stb; ret->stb = stb;
ret.help = help; ret->help = help;
ret.active_box = -1; ret->active_box = -1;
return ret; return ret;
} }

View File

@ -40,17 +40,22 @@ typedef struct {
struct friend_request request[MAX_FRIEND_REQUESTS]; struct friend_request request[MAX_FRIEND_REQUESTS];
} FriendRequests; } FriendRequests;
ToxWindow new_prompt(void); extern FriendRequests FrndRequests;
ToxWindow *new_prompt(void);
void prep_prompt_win(void); void prep_prompt_win(void);
void prompt_init_statusbar(ToxWindow *self, Tox *m); void prompt_init_statusbar(ToxWindow *self, Tox *m, bool first_time_run);
void prompt_update_nick(ToxWindow *prompt, const char *nick); void prompt_update_nick(ToxWindow *prompt, const char *nick);
void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg); void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusmsg);
void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status); void prompt_update_status(ToxWindow *prompt, Tox_User_Status status);
void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected); void prompt_update_connectionstatus(ToxWindow *prompt, bool is_connected);
void kill_prompt_window(ToxWindow *self); void kill_prompt_window(ToxWindow *self);
/* callback: Updates own connection status in prompt statusbar */ /* callback: Updates own connection status in prompt statusbar */
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata); void on_self_connection_status(Tox *m, Tox_Connection connection_status, void *userdata);
/* Returns our own connection status */
Tox_Connection prompt_selfConnectionStatus(void);
#endif /* end of include guard: PROMPT_H */ #endif /* end of include guard: PROMPT_H */

View File

@ -23,7 +23,6 @@
#ifdef PYTHON #ifdef PYTHON
#include <Python.h> #include <Python.h>
#include "api.h" #include "api.h"
#endif /* PYTHON */
#include "execute.h" #include "execute.h"
@ -40,8 +39,9 @@ static PyObject *python_api_display(PyObject *self, PyObject *args)
{ {
const char *msg; const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL; return NULL;
}
api_display(msg); api_display(msg);
return Py_None; return Py_None;
@ -52,13 +52,15 @@ static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
char *name; char *name;
PyObject *ret; PyObject *ret;
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, "")) {
return NULL; return NULL;
}
name = api_get_nick(); name = api_get_nick();
if (name == NULL) if (name == NULL) {
return NULL; return NULL;
}
ret = Py_BuildValue("s", name); ret = Py_BuildValue("s", name);
free(name); free(name);
@ -67,10 +69,11 @@ static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
static PyObject *python_api_get_status(PyObject *self, PyObject *args) static PyObject *python_api_get_status(PyObject *self, PyObject *args)
{ {
PyObject *ret; PyObject *ret = NULL;
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, "")) {
return NULL; return NULL;
}
switch (api_get_status()) { switch (api_get_status()) {
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
@ -94,13 +97,15 @@ static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
char *status; char *status;
PyObject *ret; PyObject *ret;
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, "")) {
return NULL; return NULL;
}
status = api_get_status_message(); status = api_get_status_message();
if (status == NULL) if (status == NULL) {
return NULL; return NULL;
}
ret = Py_BuildValue("s", status); ret = Py_BuildValue("s", status);
free(status); free(status);
@ -114,15 +119,17 @@ static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
PyObject *cur, *ret; PyObject *cur, *ret;
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1]; char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (!PyArg_ParseTuple(args, "")) if (!PyArg_ParseTuple(args, "")) {
return NULL; return NULL;
}
friends = api_get_friendslist(); friends = api_get_friendslist();
ret = PyList_New(0); ret = PyList_New(0);
for (i = 0; i < friends.num_friends; i++) { for (i = 0; i < friends.num_friends; i++) {
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff); snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
}
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0'; pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf); cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
@ -136,8 +143,9 @@ static PyObject *python_api_send(PyObject *self, PyObject *args)
{ {
const char *msg; const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL; return NULL;
}
api_send(msg); api_send(msg);
return Py_None; return Py_None;
@ -148,8 +156,9 @@ static PyObject *python_api_execute(PyObject *self, PyObject *args)
int mode; int mode;
const char *command; const char *command;
if (!PyArg_ParseTuple(args, "si", &command, &mode)) if (!PyArg_ParseTuple(args, "si", &command, &mode)) {
return NULL; return NULL;
}
api_execute(command, mode); api_execute(command, mode);
return Py_None; return Py_None;
@ -162,8 +171,9 @@ static PyObject *python_api_register(PyObject *self, PyObject *args)
const char *command, *help; const char *command, *help;
PyObject *callback; PyObject *callback;
if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) {
return NULL; return NULL;
}
if (!PyCallable_Check(callback)) { if (!PyCallable_Check(callback)) {
PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable"); PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable");
@ -187,21 +197,24 @@ static PyObject *python_api_register(PyObject *self, PyObject *args)
Py_XINCREF(callback); Py_XINCREF(callback);
cur->next = malloc(sizeof(struct python_registered_func)); cur->next = malloc(sizeof(struct python_registered_func));
if (cur->next == NULL) if (cur->next == NULL) {
return PyErr_NoMemory(); return PyErr_NoMemory();
}
command_len = strlen(command); command_len = strlen(command);
cur->next->name = malloc(command_len + 1); cur->next->name = malloc(command_len + 1);
if (cur->next->name == NULL) if (cur->next->name == NULL) {
return PyErr_NoMemory(); return PyErr_NoMemory();
}
strncpy(cur->next->name, command, command_len + 1); strncpy(cur->next->name, command, command_len + 1);
help_len = strlen(help); help_len = strlen(help);
cur->next->help = malloc(help_len + 1); cur->next->help = malloc(help_len + 1);
if (cur->next->help == NULL) if (cur->next->help == NULL) {
return PyErr_NoMemory(); return PyErr_NoMemory();
}
strncpy(cur->next->help, help, help_len + 1); strncpy(cur->next->help, help, help_len + 1);
cur->next->callback = callback; cur->next->callback = callback;
@ -253,8 +266,9 @@ void terminate_python(void)
{ {
struct python_registered_func *cur, *old; struct python_registered_func *cur, *old;
if (python_commands.name != NULL) if (python_commands.name != NULL) {
free(python_commands.name); free(python_commands.name);
}
for (cur = python_commands.next; cur != NULL;) { for (cur = python_commands.next; cur != NULL;) {
old = cur; old = cur;
@ -285,19 +299,22 @@ int do_python_command(int num_args, char (*args)[MAX_STR_SIZE])
struct python_registered_func *cur; struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) { for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name == NULL) if (cur->name == NULL) {
continue; continue;
}
if (!strcmp(args[0], cur->name)) { if (!strcmp(args[0], cur->name)) {
args_strings = PyList_New(0); args_strings = PyList_New(0);
for (i = 1; i < num_args; i++) for (i = 1; i < num_args; i++) {
PyList_Append(args_strings, Py_BuildValue("s", args[i])); PyList_Append(args_strings, Py_BuildValue("s", args[i]));
}
callback_args = PyTuple_Pack(1, args_strings); callback_args = PyTuple_Pack(1, args_strings);
if (PyObject_CallObject(cur->callback, callback_args) == NULL) if (PyObject_CallObject(cur->callback, callback_args) == NULL) {
api_display("Exception raised in callback function"); api_display("Exception raised in callback function");
}
return 0; return 0;
} }
@ -312,8 +329,9 @@ int python_num_registered_handlers(void)
struct python_registered_func *cur; struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) { for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) if (cur->name != NULL) {
n++; n++;
}
} }
return n; return n;
@ -341,7 +359,9 @@ void python_draw_handler_help(WINDOW *win)
struct python_registered_func *cur; struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) { for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) if (cur->name != NULL) {
wprintw(win, " %-29s: %.50s\n", cur->name, cur->help); wprintw(win, " %-29s: %.50s\n", cur->name, cur->help);
}
} }
} }
#endif /* PYTHON */

View File

@ -36,4 +36,4 @@ int python_num_registered_handlers(void);
int python_help_max_width(void); int python_help_max_width(void);
void python_draw_handler_help(WINDOW *win); void python_draw_handler_help(WINDOW *win);
#endif /* #define PYTHON_API_H */ #endif /* PYTHON_API_H */

View File

@ -20,6 +20,8 @@
* *
*/ */
#ifdef QRCODE
#include <stdlib.h> #include <stdlib.h>
#include <qrencode.h> #include <qrencode.h>
#include <stdbool.h> #include <stdbool.h>
@ -50,8 +52,9 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
{ {
FILE *fp = fopen(outfile, "wb"); FILE *fp = fopen(outfile, "wb");
if (fp == NULL) if (fp == NULL) {
return -1; return -1;
}
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
@ -63,14 +66,16 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
size_t width = qr_obj->width; size_t width = qr_obj->width;
size_t i, j; size_t i, j;
for (i = 0; i < width + BORDER_LEN * 2; ++i) for (i = 0; i < width + BORDER_LEN * 2; ++i) {
fprintf(fp, "%s", CHAR_1); fprintf(fp, "%s", CHAR_1);
}
fprintf(fp, "\n"); fprintf(fp, "\n");
for (i = 0; i < width; i += 2) { for (i = 0; i < width; i += 2) {
for (j = 0; j < BORDER_LEN; ++j) for (j = 0; j < BORDER_LEN; ++j) {
fprintf(fp, "%s", CHAR_1); fprintf(fp, "%s", CHAR_1);
}
const unsigned char *row_1 = qr_obj->data + width * i; const unsigned char *row_1 = qr_obj->data + width * i;
const unsigned char *row_2 = row_1 + width; const unsigned char *row_2 = row_1 + width;
@ -79,18 +84,20 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
bool x = row_1[j] & 1; bool x = row_1[j] & 1;
bool y = (i + 1) < width ? (row_2[j] & 1) : false; bool y = (i + 1) < width ? (row_2[j] & 1) : false;
if (x && y) if (x && y) {
fprintf(fp, " "); fprintf(fp, " ");
else if (x) } else if (x) {
fprintf(fp, "%s", CHAR_2); fprintf(fp, "%s", CHAR_2);
else if (y) } else if (y) {
fprintf(fp, "%s", CHAR_3); fprintf(fp, "%s", CHAR_3);
else } else {
fprintf(fp, "%s", CHAR_1); fprintf(fp, "%s", CHAR_1);
}
} }
for (j = 0; j < BORDER_LEN; ++j) for (j = 0; j < BORDER_LEN; ++j) {
fprintf(fp, "%s", CHAR_1); fprintf(fp, "%s", CHAR_1);
}
fprintf(fp, "\n"); fprintf(fp, "\n");
} }
@ -208,3 +215,5 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile)
return 0; return 0;
} }
#endif /* QRPNG */ #endif /* QRPNG */
#endif /* QRCODE */

View File

@ -23,6 +23,8 @@
#ifndef QR_CODE #ifndef QR_CODE
#define QR_CODE #define QR_CODE
#ifdef QRCODE
#define QRCODE_FILENAME_EXT ".QRcode" #define QRCODE_FILENAME_EXT ".QRcode"
/* Converts a tox ID string into a QRcode and prints it into the given filename. /* Converts a tox ID string into a QRcode and prints it into the given filename.
@ -42,4 +44,6 @@ int ID_to_QRcode_txt(const char *tox_id, const char *outfile);
int ID_to_QRcode_png(const char *tox_id, const char *outfile); int ID_to_QRcode_png(const char *tox_id, const char *outfile);
#endif /* QRPNG */ #endif /* QRPNG */
#endif /* QRCODE */
#endif /* QR_CODE */ #endif /* QR_CODE */

View File

@ -58,11 +58,13 @@ static struct ui_strings {
const char *native_colors; const char *native_colors;
const char *autolog; const char *autolog;
const char *history_size; const char *history_size;
const char *notification_timeout;
const char *show_typing_self; const char *show_typing_self;
const char *show_typing_other; const char *show_typing_other;
const char *show_welcome_msg; const char *show_welcome_msg;
const char *show_connection_msg; const char *show_connection_msg;
const char *nodeslist_update_freq; const char *nodeslist_update_freq;
const char *autosave_freq;
const char *line_join; const char *line_join;
const char *line_quit; const char *line_quit;
@ -85,11 +87,13 @@ static struct ui_strings {
"native_colors", "native_colors",
"autolog", "autolog",
"history_size", "history_size",
"notification_timeout",
"show_typing_self", "show_typing_self",
"show_typing_other", "show_typing_other",
"show_welcome_msg", "show_welcome_msg",
"show_connection_msg", "show_connection_msg",
"nodeslist_update_freq", "nodeslist_update_freq",
"autosave_freq",
"line_join", "line_join",
"line_quit", "line_quit",
"line_alert", "line_alert",
@ -112,11 +116,13 @@ static void ui_defaults(struct user_settings *settings)
settings->bell_on_invite = 0; settings->bell_on_invite = 0;
settings->colour_theme = DFLT_COLS; settings->colour_theme = DFLT_COLS;
settings->history_size = 700; settings->history_size = 700;
settings->notification_timeout = 3000;
settings->show_typing_self = SHOW_TYPING_ON; settings->show_typing_self = SHOW_TYPING_ON;
settings->show_typing_other = SHOW_TYPING_ON; settings->show_typing_other = SHOW_TYPING_ON;
settings->show_welcome_msg = SHOW_WELCOME_MSG_ON; settings->show_welcome_msg = SHOW_WELCOME_MSG_ON;
settings->show_connection_msg = SHOW_CONNECTION_MSG_ON; settings->show_connection_msg = SHOW_CONNECTION_MSG_ON;
settings->nodeslist_update_freq = 7; settings->nodeslist_update_freq = 7;
settings->autosave_freq = 600;
snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN); snprintf(settings->line_join, LINE_HINT_MAX + 1, "%s", LINE_JOIN);
snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT); snprintf(settings->line_quit, LINE_HINT_MAX + 1, "%s", LINE_QUIT);
@ -124,10 +130,10 @@ static void ui_defaults(struct user_settings *settings)
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL); snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
settings->mplex_away = MPLEX_ON; settings->mplex_away = MPLEX_ON;
snprintf (settings->mplex_away_note, snprintf(settings->mplex_away_note,
sizeof (settings->mplex_away_note), sizeof(settings->mplex_away_note),
"%s", "%s",
MPLEX_AWAY_NOTE); MPLEX_AWAY_NOTE);
} }
static const struct keys_strings { static const struct keys_strings {
@ -139,8 +145,6 @@ static const struct keys_strings {
const char *half_page_up; const char *half_page_up;
const char *half_page_down; const char *half_page_down;
const char *page_bottom; const char *page_bottom;
const char *peer_list_up;
const char *peer_list_down;
const char *toggle_peerlist; const char *toggle_peerlist;
const char *toggle_pastemode; const char *toggle_pastemode;
} key_strings = { } key_strings = {
@ -152,8 +156,6 @@ static const struct keys_strings {
"half_page_up", "half_page_up",
"half_page_down", "half_page_down",
"page_bottom", "page_bottom",
"peer_list_up",
"peer_list_down",
"toggle_peerlist", "toggle_peerlist",
"toggle_paste_mode", "toggle_paste_mode",
}; };
@ -168,8 +170,6 @@ static void key_defaults(struct user_settings *settings)
settings->key_half_page_up = T_KEY_C_F; settings->key_half_page_up = T_KEY_C_F;
settings->key_half_page_down = T_KEY_C_V; settings->key_half_page_down = T_KEY_C_V;
settings->key_page_bottom = T_KEY_C_H; settings->key_page_bottom = T_KEY_C_H;
settings->key_peer_list_up = T_KEY_C_LB;
settings->key_peer_list_down = T_KEY_C_RB;
settings->key_toggle_peerlist = T_KEY_C_B; settings->key_toggle_peerlist = T_KEY_C_B;
settings->key_toggle_pastemode = T_KEY_C_T; settings->key_toggle_pastemode = T_KEY_C_T;
} }
@ -253,15 +253,18 @@ static int key_parse(const char **bind)
int len = strlen(*bind); int len = strlen(*bind);
if (len > 5) { if (len > 5) {
if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M') /* ctrl+m cannot be used */ if (strncasecmp(*bind, "ctrl+", 5) == 0 && toupper(bind[0][5]) != 'M') { /* ctrl+m cannot be used */
return toupper(bind[0][5]) - 'A' + 1; return toupper(bind[0][5]) - 'A' + 1;
}
} }
if (strncasecmp(*bind, "tab", 3) == 0) if (strncasecmp(*bind, "tab", 3) == 0) {
return T_KEY_TAB; return T_KEY_TAB;
}
if (strncasecmp(*bind, "page", 4) == 0) if (strncasecmp(*bind, "page", 4) == 0) {
return len == 6 ? KEY_PPAGE : KEY_NPAGE; return len == 6 ? KEY_PPAGE : KEY_NPAGE;
}
return -1; return -1;
} }
@ -304,8 +307,9 @@ int settings_load(struct user_settings *s, const char *patharg)
if (!file_exists(path)) { if (!file_exists(path)) {
FILE *fp = fopen(path, "w"); FILE *fp = fopen(path, "w");
if (fp == NULL) if (fp == NULL) {
return -1; return -1;
}
fclose(fp); fclose(fp);
} }
@ -324,18 +328,18 @@ int settings_load(struct user_settings *s, const char *patharg)
int time = 24; int time = 24;
if ( config_setting_lookup_int(setting, ui_strings.time_format, &time) ) { if (config_setting_lookup_int(setting, ui_strings.time_format, &time)) {
if (time == 12) { if (time == 12) {
snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p"); snprintf(s->timestamp_format, sizeof(s->timestamp_format), "%s", "%I:%M:%S %p");
snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]"); snprintf(s->log_timestamp_format, sizeof(s->log_timestamp_format), "%s", "%Y/%m/%d [%I:%M:%S %p]");
} }
} }
if ( config_setting_lookup_string(setting, ui_strings.timestamp_format, &str) ) { if (config_setting_lookup_string(setting, ui_strings.timestamp_format, &str)) {
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.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);
} }
@ -365,82 +369,89 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg); config_setting_lookup_bool(setting, ui_strings.show_connection_msg, &s->show_connection_msg);
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size); config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_int(setting, ui_strings.notification_timeout, &s->notification_timeout);
config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq); config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_freq);
config_setting_lookup_int(setting, ui_strings.autosave_freq, &s->autosave_freq);
if ( config_setting_lookup_string(setting, ui_strings.line_join, &str) ) { if (config_setting_lookup_string(setting, ui_strings.line_join, &str)) {
snprintf(s->line_join, sizeof(s->line_join), "%s", str); snprintf(s->line_join, sizeof(s->line_join), "%s", str);
} }
if ( config_setting_lookup_string(setting, ui_strings.line_quit, &str) ) { if (config_setting_lookup_string(setting, ui_strings.line_quit, &str)) {
snprintf(s->line_quit, sizeof(s->line_quit), "%s", str); snprintf(s->line_quit, sizeof(s->line_quit), "%s", str);
} }
if ( config_setting_lookup_string(setting, ui_strings.line_alert, &str) ) { if (config_setting_lookup_string(setting, ui_strings.line_alert, &str)) {
snprintf(s->line_alert, sizeof(s->line_alert), "%s", str); snprintf(s->line_alert, sizeof(s->line_alert), "%s", str);
} }
if ( config_setting_lookup_string(setting, ui_strings.line_normal, &str) ) { if (config_setting_lookup_string(setting, ui_strings.line_normal, &str)) {
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str); snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
} }
config_setting_lookup_bool (setting, ui_strings.mplex_away, &s->mplex_away); config_setting_lookup_bool(setting, ui_strings.mplex_away, &s->mplex_away);
if (config_setting_lookup_string (setting, ui_strings.mplex_away_note, &str)) { if (config_setting_lookup_string(setting, ui_strings.mplex_away_note, &str)) {
snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", str); snprintf(s->mplex_away_note, sizeof(s->mplex_away_note), "%s", str);
} }
} }
/* paths */ /* paths */
if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) { if ((setting = config_lookup(cfg, tox_strings.self)) != NULL) {
if ( config_setting_lookup_string(setting, tox_strings.download_path, &str) ) { if (config_setting_lookup_string(setting, tox_strings.download_path, &str)) {
snprintf(s->download_path, sizeof(s->download_path), "%s", str); snprintf(s->download_path, sizeof(s->download_path), "%s", str);
int len = strlen(s->download_path); int len = strlen(s->download_path);
/* make sure path ends with a '/' */ /* make sure path ends with a '/' */
if (len >= sizeof(s->download_path) - 2) if (len >= sizeof(s->download_path) - 2) {
s->download_path[0] = '\0'; s->download_path[0] = '\0';
else if (s->download_path[len - 1] != '/') } else if (s->download_path[len - 1] != '/') {
strcat(&s->download_path[len - 1], "/"); strcat(&s->download_path[len - 1], "/");
}
} }
if ( config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str) ) { if (config_setting_lookup_string(setting, tox_strings.chatlogs_path, &str)) {
snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str); snprintf(s->chatlogs_path, sizeof(s->chatlogs_path), "%s", str);
int len = strlen(s->chatlogs_path); int len = strlen(s->chatlogs_path);
if (len >= sizeof(s->chatlogs_path) - 2) if (len >= sizeof(s->chatlogs_path) - 2) {
s->chatlogs_path[0] = '\0'; s->chatlogs_path[0] = '\0';
else if (s->chatlogs_path[len - 1] != '/') } else if (s->chatlogs_path[len - 1] != '/') {
strcat(&s->chatlogs_path[len - 1], "/"); strcat(&s->chatlogs_path[len - 1], "/");
}
} }
if ( config_setting_lookup_string(setting, tox_strings.avatar_path, &str) ) { if (config_setting_lookup_string(setting, tox_strings.avatar_path, &str)) {
snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str); snprintf(s->avatar_path, sizeof(s->avatar_path), "%s", str);
int len = strlen(str); int len = strlen(str);
if (len >= sizeof(s->avatar_path)) if (len >= sizeof(s->avatar_path)) {
s->avatar_path[0] = '\0'; s->avatar_path[0] = '\0';
}
} }
#ifdef PYTHON #ifdef PYTHON
if ( config_setting_lookup_string(setting, tox_strings.autorun_path, &str) ) { if (config_setting_lookup_string(setting, tox_strings.autorun_path, &str)) {
snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str); snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str);
int len = strlen(str); int len = strlen(str);
if (len >= sizeof(s->autorun_path) - 2) if (len >= sizeof(s->autorun_path) - 2) {
s->autorun_path[0] = '\0'; s->autorun_path[0] = '\0';
else if (s->autorun_path[len - 1] != '/') } else if (s->autorun_path[len - 1] != '/') {
strcat(&s->autorun_path[len - 1], "/"); strcat(&s->autorun_path[len - 1], "/");
}
} }
#endif #endif
if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) { if (config_setting_lookup_string(setting, tox_strings.password_eval, &str)) {
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str); snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
int len = strlen(str); int len = strlen(str);
if (len >= sizeof(s->password_eval)) if (len >= sizeof(s->password_eval)) {
s->password_eval[0] = '\0'; s->password_eval[0] = '\0';
}
} }
} }
@ -448,38 +459,41 @@ int settings_load(struct user_settings *s, const char *patharg)
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) { if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char *tmp = NULL; const char *tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp)) {
set_key_binding(&s->key_next_tab, &tmp); set_key_binding(&s->key_next_tab, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) if (config_setting_lookup_string(setting, key_strings.prev_tab, &tmp)) {
set_key_binding(&s->key_prev_tab, &tmp); set_key_binding(&s->key_prev_tab, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.scroll_line_up, &tmp)) {
set_key_binding(&s->key_scroll_line_up, &tmp); set_key_binding(&s->key_scroll_line_up, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) if (config_setting_lookup_string(setting, key_strings.scroll_line_down, &tmp)) {
set_key_binding(&s->key_scroll_line_down, &tmp); set_key_binding(&s->key_scroll_line_down, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.half_page_up, &tmp)) {
set_key_binding(&s->key_half_page_up, &tmp); set_key_binding(&s->key_half_page_up, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) if (config_setting_lookup_string(setting, key_strings.half_page_down, &tmp)) {
set_key_binding(&s->key_half_page_down, &tmp); set_key_binding(&s->key_half_page_down, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) if (config_setting_lookup_string(setting, key_strings.page_bottom, &tmp)) {
set_key_binding(&s->key_page_bottom, &tmp); set_key_binding(&s->key_page_bottom, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp)) {
set_key_binding(&s->key_peer_list_up, &tmp);
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &tmp))
set_key_binding(&s->key_peer_list_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
set_key_binding(&s->key_toggle_peerlist, &tmp); set_key_binding(&s->key_toggle_peerlist, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp)) {
set_key_binding(&s->key_toggle_pastemode, &tmp); set_key_binding(&s->key_toggle_pastemode, &tmp);
}
} }
#ifdef AUDIO #ifdef AUDIO
@ -499,52 +513,60 @@ int settings_load(struct user_settings *s, const char *patharg)
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) { if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) || if ((config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
!set_sound(notif_error, str) ) { !set_sound(notif_error, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) || if (!config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str) ) { !set_sound(user_log_in, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav"); set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) || if (!config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str) ) { !set_sound(user_log_out, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav"); set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) || if (!config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str) ) { !set_sound(call_incoming, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav"); set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) || if (!config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str) ) { !set_sound(call_outgoing, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav"); set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) || if (!config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str) ) { !set_sound(generic_message, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav"); set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) || if (!config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str) ) { !set_sound(transfer_pending, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav"); set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
}
} }
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) || if (!config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str) ) { !set_sound(transfer_completed, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav"); set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
} }
} else { } else {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav"); set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");

View File

@ -49,11 +49,13 @@ struct user_settings {
int colour_theme; /* boolean (0 for default toxic colours) */ int colour_theme; /* boolean (0 for default toxic colours) */
int history_size; /* int between MIN_HISTORY and MAX_HISTORY */ int history_size; /* int between MIN_HISTORY and MAX_HISTORY */
int notification_timeout;
int show_typing_self; /* boolean */ int show_typing_self; /* boolean */
int show_typing_other; /* boolean */ int show_typing_other; /* boolean */
int show_welcome_msg; /* boolean */ int show_welcome_msg; /* boolean */
int show_connection_msg; /* boolean */ int show_connection_msg; /* boolean */
int nodeslist_update_freq; /* int (<= 0 to disable updates) */ int nodeslist_update_freq; /* int (<= 0 to disable updates) */
int autosave_freq; /* int (<= 0 to disable autosave) */
char line_join[LINE_HINT_MAX + 1]; char line_join[LINE_HINT_MAX + 1];
char line_quit[LINE_HINT_MAX + 1]; char line_quit[LINE_HINT_MAX + 1];
@ -73,8 +75,6 @@ struct user_settings {
int key_half_page_up; int key_half_page_up;
int key_half_page_down; int key_half_page_down;
int key_page_bottom; int key_page_bottom;
int key_peer_list_up;
int key_peer_list_down;
int key_toggle_peerlist; int key_toggle_peerlist;
int key_toggle_pastemode; int key_toggle_pastemode;
@ -88,7 +88,7 @@ struct user_settings {
#endif #endif
}; };
enum { enum settings_values {
AUTOLOG_OFF = 0, AUTOLOG_OFF = 0,
AUTOLOG_ON = 1, AUTOLOG_ON = 1,
@ -114,7 +114,7 @@ enum {
MPLEX_OFF = 0, MPLEX_OFF = 0,
MPLEX_ON = 1, MPLEX_ON = 1,
} settings_values; };
#define LINE_JOIN "-->" #define LINE_JOIN "-->"
#define LINE_QUIT "<--" #define LINE_QUIT "<--"
@ -125,4 +125,5 @@ enum {
#define MPLEX_AWAY_NOTE "Detached from screen" #define MPLEX_AWAY_NOTE "Detached from screen"
int settings_load(struct user_settings *s, const char *patharg); int settings_load(struct user_settings *s, const char *patharg);
#endif /* #define SETTINGS_H */
#endif /* SETTINGS_H */

View File

@ -33,7 +33,7 @@
#include <tox/tox.h> #include <tox/tox.h>
#include "global_commands.h" #include "execute.h"
#include "windows.h" #include "windows.h"
#include "term_mplex.h" #include "term_mplex.h"
#include "toxic.h" #include "toxic.h"
@ -69,7 +69,7 @@ static char buffer [BUFFER_SIZE];
static bool auto_away_active = false; static bool auto_away_active = false;
static mplex_status mplex = MPLEX_NONE; static mplex_status mplex = MPLEX_NONE;
static TOX_USER_STATUS prev_status = TOX_USER_STATUS_NONE; static Tox_User_Status prev_status = TOX_USER_STATUS_NONE;
static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = ""; static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = "";
/* mutex for access to status data, for sync between: /* mutex for access to status data, for sync between:
@ -80,108 +80,118 @@ static char prev_note [TOX_MAX_STATUS_MESSAGE_LENGTH] = "";
static pthread_mutex_t status_lock; static pthread_mutex_t status_lock;
static pthread_t mplex_tid; static pthread_t mplex_tid;
void lock_status () void lock_status(void)
{ {
pthread_mutex_lock (&status_lock); pthread_mutex_lock(&status_lock);
} }
void unlock_status () void unlock_status(void)
{ {
pthread_mutex_unlock (&status_lock); pthread_mutex_unlock(&status_lock);
} }
static char *read_into_dyn_buffer (FILE *stream) static char *read_into_dyn_buffer(FILE *stream)
{ {
const char *input_ptr = NULL; const char *input_ptr = NULL;
char *dyn_buffer = NULL; char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */ int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) { while ((input_ptr = fgets(buffer, BUFFER_SIZE, stream)) != NULL) {
int length = dyn_buffer_size + strlen (input_ptr); int length = dyn_buffer_size + strlen(input_ptr);
if (dyn_buffer) if (dyn_buffer) {
dyn_buffer = (char *) realloc (dyn_buffer, length); dyn_buffer = (char *) realloc(dyn_buffer, length);
else } else {
dyn_buffer = (char *) malloc (length); dyn_buffer = (char *) malloc(length);
}
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); strcpy(dyn_buffer + dyn_buffer_size - 1, input_ptr);
dyn_buffer_size = length; dyn_buffer_size = length;
} }
return dyn_buffer; return dyn_buffer;
} }
static char *extract_socket_path (const char *info) static char *extract_socket_path(const char *info)
{ {
const char *search_str = " Socket"; const char *search_str = " Socket";
const char *pos = strstr (info, search_str); const char *pos = strstr(info, search_str);
char *end = NULL; char *end = NULL;
char *path = NULL; char *path = NULL;
if (!pos) if (!pos) {
return NULL; return NULL;
}
pos += strlen (search_str); pos += strlen(search_str);
pos = strchr (pos, PATH_SEP_C); pos = strchr(pos, PATH_SEP_C);
if (!pos) if (!pos) {
return NULL; return NULL;
}
end = strchr (pos, '\n'); end = strchr(pos, '\n');
if (!end) if (!end) {
return NULL; return NULL;
}
*end = '\0'; *end = '\0';
end = strrchr (pos, '.'); end = strrchr(pos, '.');
if (!end) if (!end) {
return NULL; return NULL;
}
path = (char *) malloc (end - pos + 1); path = (char *) malloc(end - pos + 1);
*end = '\0'; *end = '\0';
return strcpy (path, pos); return strcpy(path, pos);
} }
static int detect_gnu_screen () static int detect_gnu_screen(void)
{ {
FILE *session_info_stream = NULL; FILE *session_info_stream = NULL;
char *socket_name = NULL, *socket_path = NULL; char *socket_name = NULL, *socket_path = NULL;
char *dyn_buffer = NULL; char *dyn_buffer = NULL;
socket_name = getenv ("STY"); socket_name = getenv("STY");
if (!socket_name) if (!socket_name) {
goto nomplex; goto nomplex;
}
session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); session_info_stream = popen("env LC_ALL=C screen -ls", "r");
if (!session_info_stream) if (!session_info_stream) {
goto nomplex; goto nomplex;
}
dyn_buffer = read_into_dyn_buffer (session_info_stream); dyn_buffer = read_into_dyn_buffer(session_info_stream);
if (!dyn_buffer) if (!dyn_buffer) {
goto nomplex; goto nomplex;
}
pclose (session_info_stream); pclose(session_info_stream);
session_info_stream = NULL; session_info_stream = NULL;
socket_path = extract_socket_path (dyn_buffer); socket_path = extract_socket_path(dyn_buffer);
if (!socket_path) if (!socket_path) {
goto nomplex; goto nomplex;
}
free (dyn_buffer); free(dyn_buffer);
dyn_buffer = NULL; dyn_buffer = NULL;
if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data)) if (strlen(socket_path) + strlen(PATH_SEP_S) + strlen(socket_name) >= sizeof(mplex_data)) {
goto nomplex; goto nomplex;
}
strcpy (mplex_data, socket_path); strcpy(mplex_data, socket_path);
strcat (mplex_data, PATH_SEP_S); strcat(mplex_data, PATH_SEP_S);
strcat (mplex_data, socket_name); strcat(mplex_data, socket_name);
free (socket_path); free(socket_path);
socket_path = NULL; socket_path = NULL;
mplex = MPLEX_SCREEN; mplex = MPLEX_SCREEN;
@ -189,33 +199,38 @@ static int detect_gnu_screen ()
nomplex: nomplex:
if (session_info_stream) if (session_info_stream) {
pclose (session_info_stream); pclose(session_info_stream);
}
if (dyn_buffer) if (dyn_buffer) {
free (dyn_buffer); free(dyn_buffer);
}
if (socket_path) if (socket_path) {
free(socket_path); free(socket_path);
}
return 0; return 0;
} }
static int detect_tmux () static int detect_tmux(void)
{ {
char *tmux_env = getenv ("TMUX"), *pos; char *tmux_env = getenv("TMUX"), *pos;
if (!tmux_env) if (!tmux_env) {
return 0; return 0;
}
/* find second separator */ /* find second separator */
pos = strrchr (tmux_env, ','); pos = strrchr(tmux_env, ',');
if (!pos) if (!pos) {
return 0; return 0;
}
/* store the session id for later use */ /* store the session id for later use */
snprintf (mplex_data, sizeof(mplex_data), "$%s", pos + 1); snprintf(mplex_data, sizeof(mplex_data), "$%s", pos + 1);
mplex = MPLEX_TMUX; mplex = MPLEX_TMUX;
return 1; return 1;
} }
@ -228,92 +243,102 @@ static int detect_tmux ()
Returns 1 if present, 0 otherwise. This value can be used to determine Returns 1 if present, 0 otherwise. This value can be used to determine
whether an auto-away detection timer is needed. whether an auto-away detection timer is needed.
*/ */
static int detect_mplex () static int detect_mplex(void)
{ {
/* try screen, and if fails try tmux */ /* try screen, and if fails try tmux */
return detect_gnu_screen () || detect_tmux (); return detect_gnu_screen() || detect_tmux();
} }
/* Detects gnu screen session attached/detached by examining permissions of /* Detects gnu screen session attached/detached by examining permissions of
the session's unix socket. the session's unix socket.
*/ */
static int gnu_screen_is_detached () static int gnu_screen_is_detached(void)
{ {
if (mplex != MPLEX_SCREEN) if (mplex != MPLEX_SCREEN) {
return 0; return 0;
}
struct stat sb; struct stat sb;
if (stat (mplex_data, &sb) != 0) if (stat(mplex_data, &sb) != 0) {
return 0; return 0;
}
/* execution permission (x) means attached */ /* execution permission (x) means attached */
return ! (sb.st_mode & S_IXUSR); return !(sb.st_mode & S_IXUSR);
} }
/* Detects tmux attached/detached by getting session data and finding the /* Detects tmux attached/detached by getting session data and finding the
current session's entry. current session's entry.
*/ */
static int tmux_is_detached () static int tmux_is_detached(void)
{ {
if (mplex != MPLEX_TMUX) if (mplex != MPLEX_TMUX) {
return 0; return 0;
}
FILE *session_info_stream = NULL; FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL; char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos; char *entry_pos;
int detached; int detached;
const int numstr_len = strlen (mplex_data); const int numstr_len = strlen(mplex_data);
/* get the number of attached clients for each session */ /* get the number of attached clients for each session */
session_info_stream = popen ("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r"); session_info_stream = popen("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r");
if (!session_info_stream) if (!session_info_stream) {
goto fail; goto fail;
}
dyn_buffer = read_into_dyn_buffer (session_info_stream); dyn_buffer = read_into_dyn_buffer(session_info_stream);
if (!dyn_buffer) if (!dyn_buffer) {
goto fail; goto fail;
}
pclose (session_info_stream); pclose(session_info_stream);
session_info_stream = NULL; session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */ /* prepare search string, for finding the current session's entry */
search_str = (char *) malloc (numstr_len + 2); search_str = (char *) malloc(numstr_len + 2);
search_str[0] = '\n'; search_str[0] = '\n';
strcpy (search_str + 1, mplex_data); strcpy(search_str + 1, mplex_data);
/* do the search */ /* do the search */
if (strncmp (dyn_buffer, search_str + 1, numstr_len) == 0) if (strncmp(dyn_buffer, search_str + 1, numstr_len) == 0) {
entry_pos = dyn_buffer; entry_pos = dyn_buffer;
else } else {
entry_pos = strstr (dyn_buffer, search_str); entry_pos = strstr(dyn_buffer, search_str);
}
if (! entry_pos) if (! entry_pos) {
goto fail; goto fail;
}
entry_pos = strchr (entry_pos, ' ') + 1; entry_pos = strchr(entry_pos, ' ') + 1;
detached = strncmp (entry_pos, "0\n", 2) == 0; detached = strncmp(entry_pos, "0\n", 2) == 0;
free (search_str); free(search_str);
search_str = NULL; search_str = NULL;
free (dyn_buffer); free(dyn_buffer);
dyn_buffer = NULL; dyn_buffer = NULL;
return detached; return detached;
fail: fail:
if (session_info_stream) if (session_info_stream) {
pclose (session_info_stream); pclose(session_info_stream);
}
if (dyn_buffer) if (dyn_buffer) {
free (dyn_buffer); free(dyn_buffer);
}
if (search_str) if (search_str) {
free (search_str); free(search_str);
}
return 0; return 0;
} }
@ -327,24 +352,25 @@ fail:
sample its state and update away status according to attached/detached state sample its state and update away status according to attached/detached state
of the mplex. of the mplex.
*/ */
static int mplex_is_detached () static int mplex_is_detached(void)
{ {
return gnu_screen_is_detached () || tmux_is_detached (); return gnu_screen_is_detached() || tmux_is_detached();
} }
static void mplex_timer_handler (Tox *m) static void mplex_timer_handler(Tox *m)
{ {
TOX_USER_STATUS current_status, new_status; Tox_User_Status current_status, new_status;
const char *new_note; const char *new_note;
if (mplex == MPLEX_NONE) if (mplex == MPLEX_NONE) {
return; return;
}
int detached = mplex_is_detached (); int detached = mplex_is_detached();
pthread_mutex_lock (&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
current_status = tox_self_get_status (m); current_status = tox_self_get_status(m);
pthread_mutex_unlock (&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) { if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) {
auto_away_active = false; auto_away_active = false;
@ -354,25 +380,27 @@ static void mplex_timer_handler (Tox *m)
auto_away_active = true; auto_away_active = true;
prev_status = current_status; prev_status = current_status;
new_status = TOX_USER_STATUS_AWAY; new_status = TOX_USER_STATUS_AWAY;
pthread_mutex_lock (&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m); size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t *) prev_note); tox_self_get_status_message(m, (uint8_t *) prev_note);
prev_note[slen] = '\0'; prev_note[slen] = '\0';
pthread_mutex_unlock (&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
new_note = user_settings->mplex_away_note; new_note = user_settings->mplex_away_note;
} else } else {
return; return;
}
char argv[3][MAX_STR_SIZE]; char status_str[MAX_STR_SIZE];
strcpy (argv[0], "/status"); char note_str[MAX_STR_SIZE];
strcpy (argv[1], (new_status == TOX_USER_STATUS_AWAY ? "away" : const char *status = new_status == TOX_USER_STATUS_AWAY ? "away" :
new_status == TOX_USER_STATUS_BUSY ? "busy" : "online")); new_status == TOX_USER_STATUS_BUSY ? "busy" : "online";
argv[2][0] = '\"'; snprintf(status_str, sizeof(status_str), "/status %s", status);
strcpy (argv[2] + 1, new_note); snprintf(note_str, sizeof(status_str), "/note %s", new_note);
strcat (argv[2], "\"");
pthread_mutex_lock (&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
cmd_status (prompt->chatwin->history, prompt, m, 2, argv); execute(prompt->chatwin->history, prompt, m, status_str, GLOBAL_COMMAND_MODE);
pthread_mutex_unlock (&Winthread.lock); execute(prompt->chatwin->history, prompt, m, note_str, GLOBAL_COMMAND_MODE);
pthread_mutex_unlock(&Winthread.lock);
} }
/* Time in seconds between calls to mplex_timer_handler */ /* Time in seconds between calls to mplex_timer_handler */
@ -388,20 +416,24 @@ void *mplex_timer_thread(void *data)
} }
} }
int init_mplex_away_timer (Tox *m) int init_mplex_away_timer(Tox *m)
{ {
if (! detect_mplex ()) if (! detect_mplex()) {
return 0; return 0;
}
if (! user_settings->mplex_away) if (! user_settings->mplex_away) {
return 0; return 0;
}
/* status access mutex */ /* status access mutex */
if (pthread_mutex_init (&status_lock, NULL) != 0) if (pthread_mutex_init(&status_lock, NULL) != 0) {
return -1; return -1;
}
if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0) if (pthread_create(&mplex_tid, NULL, mplex_timer_thread, (void *) m) != 0) {
return -1; return -1;
}
return 0; return 0;
} }

View File

@ -24,12 +24,12 @@
#define TERM_MPLEX_H #define TERM_MPLEX_H
/* Checks if Toxic runs inside a terminal multiplexer (GNU screen or tmux). If /* Checks if Toxic runs inside a terminal multiplexer (GNU screen or tmux). If
yes, it initializes a timer which periodically checks the attached/detached * yes, it initializes a timer which periodically checks the attached/detached
state of the terminal and updates away status accordingly. * state of the terminal and updates away status accordingly.
*/ */
int init_mplex_away_timer (Tox *m); int init_mplex_away_timer(Tox *m);
void lock_status (); void lock_status(void);
void unlock_status (); void unlock_status(void);
#endif /* #define TERM_MPLEX_H */ #endif /* TERM_MPLEX_H */

View File

@ -52,6 +52,7 @@
#include "friendlist.h" #include "friendlist.h"
#include "prompt.h" #include "prompt.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "groupchat.h"
#include "file_transfers.h" #include "file_transfers.h"
#include "line_info.h" #include "line_info.h"
#include "settings.h" #include "settings.h"
@ -93,7 +94,6 @@ ToxWindow *prompt = NULL;
#define DATANAME "toxic_profile.tox" #define DATANAME "toxic_profile.tox"
#define BLOCKNAME "toxic_blocklist" #define BLOCKNAME "toxic_blocklist"
#define AUTOSAVE_FREQ 600
#define MIN_PASSWORD_LEN 6 #define MIN_PASSWORD_LEN 6
#define MAX_PASSWORD_LEN 64 #define MAX_PASSWORD_LEN 64
@ -114,6 +114,8 @@ static time_t last_signal_time;
static void catch_SIGINT(int sig) static void catch_SIGINT(int sig)
{ {
UNUSED_VAR(sig);
time_t cur_time = get_unix_time(); time_t cur_time = get_unix_time();
if (difftime(cur_time, last_signal_time) <= 1) { if (difftime(cur_time, last_signal_time) <= 1) {
@ -125,7 +127,12 @@ static void catch_SIGINT(int sig)
static void catch_SIGSEGV(int sig) static void catch_SIGSEGV(int sig)
{ {
freopen("/dev/tty", "w", stderr); // make sure stderr is enabled since we may have disabled it UNUSED_VAR(sig);
if (!freopen("/dev/tty", "w", stderr)) { // make sure stderr is enabled since we may have disabled it
fprintf(stderr, "Warning: Failed to enable stderr\n");
}
endwin(); endwin();
fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n"); fprintf(stderr, "Caught SIGSEGV: Aborting toxic session.\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -133,6 +140,8 @@ static void catch_SIGSEGV(int sig)
static void flag_window_resize(int sig) static void flag_window_resize(int sig)
{ {
UNUSED_VAR(sig);
Winthread.flag_resize = 1; Winthread.flag_resize = 1;
} }
@ -165,9 +174,11 @@ void exit_toxic_success(Tox *m)
{ {
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
memset(&user_password, 0, sizeof(struct user_password)); memset(&user_password, 0, sizeof(struct user_password));
terminate_notify();
kill_all_file_transfers(m); kill_all_file_transfers(m);
kill_all_windows(m); kill_all_windows(m);
terminate_notify();
#ifdef AUDIO #ifdef AUDIO
#ifdef VIDEO #ifdef VIDEO
@ -182,6 +193,12 @@ void exit_toxic_success(Tox *m)
free_global_data(); free_global_data();
tox_kill(m); tox_kill(m);
if (arg_opts.log_fp != NULL) {
fclose(arg_opts.log_fp);
arg_opts.log_fp = NULL;
}
endwin(); endwin();
curl_global_cleanup(); curl_global_cleanup();
@ -198,12 +215,32 @@ void exit_toxic_success(Tox *m)
void exit_toxic_err(const char *errmsg, int errcode) void exit_toxic_err(const char *errmsg, int errcode)
{ {
free_global_data(); free_global_data();
freopen("/dev/tty", "w", stderr);
if (!freopen("/dev/tty", "w", stderr)) {
fprintf(stderr, "Warning: Failed to open stderr\n");
}
endwin(); endwin();
fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg); fprintf(stderr, "Toxic session aborted with error code %d (%s)\n", errcode, errmsg);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
void cb_toxcore_logger(Tox *m, TOX_LOG_LEVEL level, const char *file, uint32_t line, const char *func,
const char *message, void *user_data)
{
UNUSED_VAR(user_data);
UNUSED_VAR(file);
UNUSED_VAR(m);
if (user_data) {
FILE *fp = (FILE *)user_data;
fprintf(fp, "[%d] %u:%s() - %s\n", level, line, func, message);
fflush(fp);
} else {
fprintf(stderr, "[%d] %u:%s() - %s\n", level, line, func, message);
}
}
static void init_term(void) static void init_term(void)
{ {
#if HAVE_WIDECHAR #if HAVE_WIDECHAR
@ -228,8 +265,9 @@ static void init_term(void)
start_color(); start_color();
if (user_settings->colour_theme == NATIVE_COLS) { if (user_settings->colour_theme == NATIVE_COLS) {
if (assume_default_colors(-1, -1) == OK) if (assume_default_colors(-1, -1) == OK) {
bg_color = -1; bg_color = -1;
}
} }
init_pair(0, COLOR_WHITE, COLOR_BLACK); init_pair(0, COLOR_WHITE, COLOR_BLACK);
@ -266,13 +304,15 @@ static void queue_init_message(const char *msg, ...)
char **new_msgs = realloc(init_messages.msgs, sizeof(char *) * init_messages.num); char **new_msgs = realloc(init_messages.msgs, sizeof(char *) * init_messages.num);
if (new_msgs == NULL) if (new_msgs == NULL) {
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY); exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
}
new_msgs[i] = malloc(MAX_STR_SIZE); new_msgs[i] = malloc(MAX_STR_SIZE);
if (new_msgs[i] == NULL) if (new_msgs[i] == NULL) {
exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY); exit_toxic_err("Failed in queue_init_message", FATALERR_MEMORY);
}
snprintf(new_msgs[i], MAX_STR_SIZE, "%s", frmt_msg); snprintf(new_msgs[i], MAX_STR_SIZE, "%s", frmt_msg);
init_messages.msgs = new_msgs; init_messages.msgs = new_msgs;
@ -281,13 +321,15 @@ static void queue_init_message(const char *msg, ...)
/* called after messages have been printed to prompt and are no longer needed */ /* called after messages have been printed to prompt and are no longer needed */
static void cleanup_init_messages(void) static void cleanup_init_messages(void)
{ {
if (init_messages.num <= 0) if (init_messages.num <= 0) {
return; return;
}
int i; int i;
for (i = 0; i < init_messages.num; ++i) for (i = 0; i < init_messages.num; ++i) {
free(init_messages.msgs[i]); free(init_messages.msgs[i]);
}
free(init_messages.msgs); free(init_messages.msgs);
} }
@ -296,8 +338,9 @@ static void print_init_messages(ToxWindow *toxwin)
{ {
int i; int i;
for (i = 0; i < init_messages.num; ++i) for (i = 0; i < init_messages.num; ++i) {
line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]); line_info_add(toxwin, NULL, NULL, NULL, SYS_MSG, 0, 0, init_messages.msgs[i]);
}
} }
static void load_friendlist(Tox *m) static void load_friendlist(Tox *m)
@ -305,12 +348,63 @@ static void load_friendlist(Tox *m)
size_t i; size_t i;
size_t numfriends = tox_self_get_friend_list_size(m); size_t numfriends = tox_self_get_friend_list_size(m);
for (i = 0; i < numfriends; ++i) for (i = 0; i < numfriends; ++i) {
friendlist_onFriendAdded(NULL, m, i, false); friendlist_onFriendAdded(NULL, m, i, false);
}
sort_friendlist_index(); sort_friendlist_index();
} }
static void load_conferences(Tox *m)
{
size_t num_chats = tox_conference_get_chatlist_size(m);
if (num_chats == 0) {
return;
}
uint32_t chatlist[num_chats];
tox_conference_get_chatlist(m, chatlist);
for (size_t i = 0; i < num_chats; ++i) {
uint32_t groupnum = chatlist[i];
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
tox_conference_delete(m, groupnum, NULL);
continue;
}
Tox_Err_Conference_Get_Type err;
Tox_Conference_Type type = tox_conference_get_type(m, groupnum, &err);
if (err != TOX_ERR_CONFERENCE_GET_TYPE_OK) {
tox_conference_delete(m, groupnum, NULL);
continue;
}
Tox_Err_Conference_Title t_err;
size_t length = tox_conference_get_title_size(m, groupnum, &t_err);
uint8_t title[MAX_STR_SIZE];
if (t_err != TOX_ERR_CONFERENCE_TITLE_OK || length >= sizeof(title)) {
length = 0;
} else {
tox_conference_get_title(m, groupnum, title, &t_err);
if (t_err != TOX_ERR_CONFERENCE_TITLE_OK) {
length = 0;
}
}
title[length] = 0;
if (init_groupchat_win(m, groupnum, type, (const char *) title, length) == -1) {
tox_conference_delete(m, groupnum, NULL);
continue;
}
}
}
/* return length of password on success, 0 on failure */ /* return length of password on success, 0 on failure */
static int password_prompt(char *buf, int size) static int password_prompt(char *buf, int size)
{ {
@ -323,8 +417,9 @@ static int password_prompt(char *buf, int size)
nflags.c_lflag &= ~ECHO; nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL; nflags.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
return 0; return 0;
}
const char *p = fgets(buf, size, stdin); const char *p = fgets(buf, size, stdin);
int len = strlen(buf); int len = strlen(buf);
@ -332,15 +427,16 @@ static int password_prompt(char *buf, int size)
/* re-enable terminal echo */ /* re-enable terminal echo */
tcsetattr(fileno(stdin), TCSANOW, &oflags); tcsetattr(fileno(stdin), TCSANOW, &oflags);
if (p == NULL || len <= 1) if (p == NULL || len <= 1) {
return 0; return 0;
}
/* eat overflowed stdin and return error */ /* eat overflowed stdin and return error */
if (buf[--len] != '\n') { if (buf[--len] != '\n') {
int ch; int ch;
while ((ch = getchar()) != '\n' && ch > 0) while ((ch = getchar()) != '\n' && ch > 0) {
; }
return 0; return 0;
} }
@ -398,20 +494,22 @@ static void first_time_encrypt(const char *msg)
char ch[256] = {0}; char ch[256] = {0};
do { do {
system("clear"); clear_screen();
printf("%s ", msg); printf("%s ", msg);
fflush(stdout); fflush(stdout);
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) {
break; break;
}
} while (fgets(ch, sizeof(ch), stdin)); } while (fgets(ch, sizeof(ch), stdin));
printf("\n"); printf("\n");
if (ch[0] == 'q' || ch[0] == 'Q') if (ch[0] == 'q' || ch[0] == 'Q') {
exit(0); exit(0);
}
if (ch[0] == 'y' || ch[0] == 'Y') { if (ch[0] == 'y' || ch[0] == 'Y') {
int len = 0; int len = 0;
@ -425,8 +523,9 @@ static void first_time_encrypt(const char *msg)
len = password_prompt(user_password.pass, sizeof(user_password.pass)); len = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = len; user_password.len = len;
if (strcasecmp(user_password.pass, "q") == 0) if (strcasecmp(user_password.pass, "q") == 0) {
exit(0); exit(0);
}
if (string_is_empty(passconfirm) && (len < MIN_PASSWORD_LEN || len > MAX_PASSWORD_LEN)) { if (string_is_empty(passconfirm) && (len < MIN_PASSWORD_LEN || len > MAX_PASSWORD_LEN)) {
printf("Password must be between %d and %d characters long. ", MIN_PASSWORD_LEN, MAX_PASSWORD_LEN); printf("Password must be between %d and %d characters long. ", MIN_PASSWORD_LEN, MAX_PASSWORD_LEN);
@ -454,7 +553,7 @@ static void first_time_encrypt(const char *msg)
user_password.data_is_encrypted = true; user_password.data_is_encrypted = true;
} }
system("clear"); clear_screen();
} }
/* Store Tox profile data to path. /* Store Tox profile data to path.
@ -498,7 +597,7 @@ int store_data(Tox *m, const char *path)
return -1; return -1;
} }
TOX_ERR_ENCRYPTION err; Tox_Err_Encryption err;
tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len, tox_pass_encrypt((uint8_t *) data, data_len, (uint8_t *) user_password.pass, user_password.len,
(uint8_t *) enc_data, &err); (uint8_t *) enc_data, &err);
@ -540,22 +639,23 @@ int store_data(Tox *m, const char *path)
static void init_tox_callbacks(Tox *m) static void init_tox_callbacks(Tox *m)
{ {
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange); tox_callback_self_connection_status(m, on_self_connection_status);
tox_callback_friend_connection_status(m, on_connectionchange); tox_callback_friend_connection_status(m, on_friend_connection_status);
tox_callback_friend_typing(m, on_typing_change); tox_callback_friend_typing(m, on_friend_typing);
tox_callback_friend_request(m, on_request); tox_callback_friend_request(m, on_friend_request);
tox_callback_friend_message(m, on_message); tox_callback_friend_message(m, on_friend_message);
tox_callback_friend_name(m, on_nickchange); tox_callback_friend_name(m, on_friend_name);
tox_callback_friend_status(m, on_statuschange); tox_callback_friend_status(m, on_friend_status);
tox_callback_friend_status_message(m, on_statusmessagechange); tox_callback_friend_status_message(m, on_friend_status_message);
tox_callback_friend_read_receipt(m, on_read_receipt); tox_callback_friend_read_receipt(m, on_friend_read_receipt);
tox_callback_conference_invite(m, on_groupinvite); tox_callback_conference_invite(m, on_conference_invite);
tox_callback_conference_message(m, on_groupmessage); tox_callback_conference_message(m, on_conference_message);
tox_callback_conference_namelist_change(m, on_group_namelistchange); tox_callback_conference_peer_list_changed(m, on_conference_peer_list_changed);
tox_callback_conference_title(m, on_group_titlechange); tox_callback_conference_peer_name(m, on_conference_peer_name);
tox_callback_conference_title(m, on_conference_title);
tox_callback_file_recv(m, on_file_recv); tox_callback_file_recv(m, on_file_recv);
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_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);
} }
@ -563,30 +663,41 @@ static void init_tox_options(struct Tox_Options *tox_opts)
{ {
tox_options_default(tox_opts); tox_options_default(tox_opts);
tox_opts->ipv6_enabled = !arg_opts.use_ipv4; tox_options_set_ipv6_enabled(tox_opts, !arg_opts.use_ipv4);
tox_opts->udp_enabled = !arg_opts.force_tcp; tox_options_set_udp_enabled(tox_opts, !arg_opts.force_tcp);
tox_opts->proxy_type = arg_opts.proxy_type; tox_options_set_proxy_type(tox_opts, arg_opts.proxy_type);
tox_opts->tcp_port = arg_opts.tcp_port; tox_options_set_tcp_port(tox_opts, arg_opts.tcp_port);
tox_options_set_local_discovery_enabled(tox_opts, !arg_opts.disable_local_discovery);
if (!tox_opts->ipv6_enabled) if (arg_opts.logging) {
tox_options_set_log_callback(tox_opts, cb_toxcore_logger);
if (arg_opts.log_fp != NULL) {
tox_options_set_log_user_data(tox_opts, arg_opts.log_fp);
}
}
if (!tox_options_get_ipv6_enabled(tox_opts)) {
queue_init_message("Forcing IPv4 connection"); queue_init_message("Forcing IPv4 connection");
}
if (tox_opts->tcp_port) if (tox_options_get_tcp_port(tox_opts)) {
queue_init_message("TCP relaying enabled on port %d", tox_opts->tcp_port); queue_init_message("TCP relaying enabled on port %d", tox_options_get_tcp_port(tox_opts));
}
if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) { if (tox_options_get_proxy_type(tox_opts) != TOX_PROXY_TYPE_NONE) {
tox_opts->proxy_port = arg_opts.proxy_port; tox_options_set_proxy_port(tox_opts, arg_opts.proxy_port);
tox_opts->proxy_host = arg_opts.proxy_address; tox_options_set_proxy_host(tox_opts, arg_opts.proxy_address);
const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP"; const char *ps = tox_options_get_proxy_type(tox_opts) == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
char tmp[sizeof(arg_opts.proxy_address) + MAX_STR_SIZE]; char tmp[sizeof(arg_opts.proxy_address) + MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port); snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port);
queue_init_message("%s", tmp); queue_init_message("%s", tmp);
} }
if (!tox_opts->udp_enabled) { if (!tox_options_get_udp_enabled(tox_opts)) {
queue_init_message("UDP disabled"); queue_init_message("UDP disabled");
} else if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) { } else if (tox_options_get_proxy_type(tox_opts) != TOX_PROXY_TYPE_NONE) {
const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address."; const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address.";
queue_init_message("%s", msg); queue_init_message("%s", msg);
msg = "Use the -t option to disable UDP."; msg = "Use the -t option to disable UDP.";
@ -597,7 +708,7 @@ static void init_tox_options(struct Tox_Options *tox_opts)
/* Returns a new Tox object on success. /* Returns a new Tox object on success.
* If object fails to initialize the toxic process will terminate. * If object fails to initialize the toxic process will terminate.
*/ */
static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW *new_err) static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, Tox_Err_New *new_err)
{ {
Tox *m = NULL; Tox *m = NULL;
@ -626,20 +737,22 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT); exit_toxic_err("failed in load_tox", FATALERR_ENCRYPT);
} }
if (arg_opts.unencrypt_data && is_encrypted) if (arg_opts.unencrypt_data && is_encrypted) {
queue_init_message("Data file '%s' has been unencrypted", data_path); queue_init_message("Data file '%s' has been unencrypted", data_path);
else if (arg_opts.unencrypt_data) } else if (arg_opts.unencrypt_data) {
queue_init_message("Warning: passed --unencrypt-data option with unencrypted data file '%s'", data_path); queue_init_message("Warning: passed --unencrypt-data option with unencrypted data file '%s'", data_path);
}
if (is_encrypted) { if (is_encrypted) {
if (!arg_opts.unencrypt_data) if (!arg_opts.unencrypt_data) {
user_password.data_is_encrypted = true; user_password.data_is_encrypted = true;
}
size_t pwlen = 0; size_t pwlen = 0;
int pweval = user_settings->password_eval[0]; int pweval = user_settings->password_eval[0];
if (!pweval) { if (!pweval) {
system("clear"); // TODO: is this portable? clear_screen();
printf("Enter password (q to quit) "); printf("Enter password (q to quit) ");
} }
@ -663,21 +776,20 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
} }
if (pwlen < MIN_PASSWORD_LEN) { if (pwlen < MIN_PASSWORD_LEN) {
system("clear"); clear_screen();
sleep(1); sleep(1);
printf("Invalid password. Try again. "); printf("Invalid password. Try again. ");
pweval = 0; pweval = 0;
continue; continue;
} }
TOX_ERR_DECRYPTION pwerr; Tox_Err_Decryption pwerr;
tox_pass_decrypt((uint8_t *) data, len, (uint8_t *) user_password.pass, pwlen, tox_pass_decrypt((uint8_t *) data, len, (uint8_t *) user_password.pass, pwlen,
(uint8_t *) plain, &pwerr); (uint8_t *) plain, &pwerr);
if (pwerr == TOX_ERR_DECRYPTION_OK) { if (pwerr == TOX_ERR_DECRYPTION_OK) {
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE; tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_opts->savedata_data = (uint8_t *) plain; tox_options_set_savedata_data(tox_opts, (uint8_t *) plain, plain_len);
tox_opts->savedata_length = plain_len;
m = tox_new(tox_opts, new_err); m = tox_new(tox_opts, new_err);
@ -688,7 +800,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
break; break;
} else if (pwerr == TOX_ERR_DECRYPTION_FAILED) { } else if (pwerr == TOX_ERR_DECRYPTION_FAILED) {
system("clear"); clear_screen();
sleep(1); sleep(1);
printf("Invalid password. Try again. "); printf("Invalid password. Try again. ");
pweval = 0; pweval = 0;
@ -698,9 +810,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
} }
} }
} else { /* data is not encrypted */ } else { /* data is not encrypted */
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE; tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_opts->savedata_data = (uint8_t *) data; tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len);
tox_opts->savedata_length = len;
m = tox_new(tox_opts, new_err); m = tox_new(tox_opts, new_err);
@ -712,18 +823,21 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
fclose(fp); fclose(fp);
} else { /* Data file does not/should not exist */ } else { /* Data file does not/should not exist */
if (file_exists(data_path)) if (file_exists(data_path)) {
exit_toxic_err("failed in load_tox", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
}
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE; tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_NONE);
m = tox_new(tox_opts, new_err); m = tox_new(tox_opts, new_err);
if (m == NULL) if (m == NULL) {
return NULL; return NULL;
}
if (store_data(m, data_path) == -1) if (store_data(m, data_path) == -1) {
exit_toxic_err("failed in load_tox", FATALERR_FILEOP); exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
}
} }
return m; return m;
@ -731,31 +845,41 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
static Tox *load_toxic(char *data_path) static Tox *load_toxic(char *data_path)
{ {
struct Tox_Options tox_opts; Tox_Err_Options_New options_new_err;
init_tox_options(&tox_opts); struct Tox_Options *tox_opts = tox_options_new(&options_new_err);
TOX_ERR_NEW new_err; if (!tox_opts) {
Tox *m = load_tox(data_path, &tox_opts, &new_err); exit_toxic_err("tox_options_new returned fatal error", options_new_err);
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_opts.ipv6_enabled) {
queue_init_message("Falling back to ipv4");
tox_opts.ipv6_enabled = false;
m = load_tox(data_path, &tox_opts, &new_err);
} }
if (!m) init_tox_options(tox_opts);
exit_toxic_err("tox_new returned fatal error", new_err);
if (new_err != TOX_ERR_NEW_OK) Tox_Err_New new_err;
Tox *m = load_tox(data_path, tox_opts, &new_err);
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_options_get_ipv6_enabled(tox_opts)) {
queue_init_message("Falling back to ipv4");
tox_options_set_ipv6_enabled(tox_opts, false);
m = load_tox(data_path, tox_opts, &new_err);
}
if (!m) {
exit_toxic_err("tox_new returned fatal error", new_err);
}
if (new_err != TOX_ERR_NEW_OK) {
queue_init_message("tox_new returned non-fatal error %d", new_err); queue_init_message("tox_new returned non-fatal error %d", new_err);
}
init_tox_callbacks(m); init_tox_callbacks(m);
load_friendlist(m); load_friendlist(m);
load_blocklist(BLOCK_FILE); load_blocklist(BLOCK_FILE);
if (tox_self_get_name_size(m) == 0) if (tox_self_get_name_size(m) == 0) {
tox_self_set_name(m, (uint8_t *) "Toxic User", strlen("Toxic User"), NULL); tox_self_set_name(m, (uint8_t *) "Toxic User", strlen("Toxic User"), NULL);
}
tox_options_free(tox_opts);
return m; return m;
} }
@ -814,8 +938,9 @@ void *thread_cqueue(void *data)
ToxWindow *toxwin = get_window_ptr(i); ToxWindow *toxwin = get_window_ptr(i);
if (toxwin != NULL && toxwin->is_chat if (toxwin != NULL && toxwin->is_chat
&& tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE) && get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE) {
cqueue_try_send(toxwin, m); cqueue_try_send(toxwin, m);
}
} }
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -837,7 +962,7 @@ void *thread_av(void *data)
usleep(toxav_iteration_interval(av) * 1000); usleep(toxav_iteration_interval(av) * 1000);
} }
} }
#endif /* AUDIO */ #endif /* AUDIO */
static void print_usage(void) static void print_usage(void)
{ {
@ -849,6 +974,8 @@ static void print_usage(void)
fprintf(stderr, " -e, --encrypt-data Encrypt an unencrypted data file\n"); fprintf(stderr, " -e, --encrypt-data Encrypt an unencrypted data file\n");
fprintf(stderr, " -f, --file Use specified data file\n"); fprintf(stderr, " -f, --file Use specified data file\n");
fprintf(stderr, " -h, --help Show this message and exit\n"); fprintf(stderr, " -h, --help Show this message and exit\n");
fprintf(stderr, " -l, --logging Enable toxcore logging: Requires [log_path | stderr]\n");
fprintf(stderr, " -L, --no-lan Disable local discovery\n");
fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n"); fprintf(stderr, " -n, --nodes Use specified DHTnodes file\n");
fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n"); fprintf(stderr, " -o, --noconnect Do not connect to the DHT network\n");
fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n"); fprintf(stderr, " -p, --SOCKS5-proxy Use SOCKS5 proxy: Requires [IP] [port]\n");
@ -879,12 +1006,14 @@ static void parse_args(int argc, char *argv[])
set_default_opts(); set_default_opts();
static struct option long_opts[] = { static struct option long_opts[] = {
{"file", required_argument, 0, 'f'},
{"ipv4", no_argument, 0, '4'}, {"ipv4", no_argument, 0, '4'},
{"debug", no_argument, 0, 'b'}, {"debug", no_argument, 0, 'b'},
{"default-locale", no_argument, 0, 'd'}, {"default-locale", no_argument, 0, 'd'},
{"config", required_argument, 0, 'c'}, {"config", required_argument, 0, 'c'},
{"encrypt-data", no_argument, 0, 'e'}, {"encrypt-data", no_argument, 0, 'e'},
{"file", required_argument, 0, 'f'},
{"logging", required_argument, 0, 'l'},
{"no-lan", no_argument, 0, 'L'},
{"nodes", required_argument, 0, 'n'}, {"nodes", required_argument, 0, 'n'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{"noconnect", no_argument, 0, 'o'}, {"noconnect", no_argument, 0, 'o'},
@ -898,7 +1027,7 @@ static void parse_args(int argc, char *argv[])
{NULL, no_argument, NULL, 0}, {NULL, no_argument, NULL, 0},
}; };
const char *opts_str = "4bdehotuxvc:f:n:r:p:P:T:"; const char *opts_str = "4bdehLotuxvc:f:l:n:r:p:P:T:";
int opt, indexptr; int opt, indexptr;
long int port = 0; long int port = 0;
@ -916,8 +1045,9 @@ static void parse_args(int argc, char *argv[])
case 'c': case 'c':
snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg); snprintf(arg_opts.config_path, sizeof(arg_opts.config_path), "%s", optarg);
if (!file_exists(arg_opts.config_path)) if (!file_exists(arg_opts.config_path)) {
queue_init_message("Config file not found"); queue_init_message("Config file not found");
}
break; break;
@ -933,22 +1063,26 @@ static void parse_args(int argc, char *argv[])
case 'f': case 'f':
arg_opts.use_custom_data = 1; arg_opts.use_custom_data = 1;
if (DATA_FILE) if (DATA_FILE) {
free(DATA_FILE); free(DATA_FILE);
}
if (BLOCK_FILE) if (BLOCK_FILE) {
free(BLOCK_FILE); free(BLOCK_FILE);
}
DATA_FILE = malloc(strlen(optarg) + 1); DATA_FILE = malloc(strlen(optarg) + 1);
strcpy(DATA_FILE, optarg); strcpy(DATA_FILE, optarg);
if (DATA_FILE == NULL) if (DATA_FILE == NULL) {
exit_toxic_err("failed in parse_args", FATALERR_MEMORY); exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
}
BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1); BLOCK_FILE = malloc(strlen(optarg) + strlen("-blocklist") + 1);
if (BLOCK_FILE == NULL) if (BLOCK_FILE == NULL) {
exit_toxic_err("failed in parse_args", FATALERR_MEMORY); exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
}
strcpy(BLOCK_FILE, optarg); strcpy(BLOCK_FILE, optarg);
strcat(BLOCK_FILE, "-blocklist"); strcat(BLOCK_FILE, "-blocklist");
@ -957,6 +1091,32 @@ static void parse_args(int argc, char *argv[])
break; break;
case 'l':
if (optarg) {
arg_opts.logging = true;
if (strcmp(optarg, "stderr") != 0) {
arg_opts.log_fp = fopen(optarg, "w");
if (arg_opts.log_fp != NULL) {
queue_init_message("Toxcore logging enabled to %s", optarg);
} else {
arg_opts.debug = true;
queue_init_message("Failed to open log file %s. Falling back to stderr.", optarg);
}
} else {
arg_opts.debug = true;
queue_init_message("Toxcore logging enabled to stderr");
}
}
break;
case 'L':
arg_opts.disable_local_discovery = 1;
queue_init_message("Local discovery disabled");
break;
case 'n': case 'n':
snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg); snprintf(arg_opts.nodes_path, sizeof(arg_opts.nodes_path), "%s", optarg);
break; break;
@ -970,13 +1130,15 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind - 1][0] == '-') if (++optind > argc || argv[optind - 1][0] == '-') {
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
}
port = strtol(argv[optind - 1], NULL, 10); port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE) {
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
}
arg_opts.proxy_port = port; arg_opts.proxy_port = port;
break; break;
@ -985,13 +1147,15 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind - 1][0] == '-') if (++optind > argc || argv[optind - 1][0] == '-') {
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
}
port = strtol(argv[optind - 1], NULL, 10); port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE) {
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
}
arg_opts.proxy_port = port; arg_opts.proxy_port = port;
break; break;
@ -999,8 +1163,9 @@ static void parse_args(int argc, char *argv[])
case 'r': case 'r':
snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg); snprintf(arg_opts.nameserver_path, sizeof(arg_opts.nameserver_path), "%s", optarg);
if (!file_exists(arg_opts.nameserver_path)) if (!file_exists(arg_opts.nameserver_path)) {
queue_init_message("nameserver list not found"); queue_init_message("nameserver list not found");
}
break; break;
@ -1011,8 +1176,9 @@ static void parse_args(int argc, char *argv[])
case 'T': case 'T':
port = strtol(optarg, NULL, 10); port = strtol(optarg, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE) {
port = 14191; port = 14191;
}
arg_opts.tcp_port = port; arg_opts.tcp_port = port;
break; break;
@ -1045,28 +1211,34 @@ static int rename_old_profile(const char *user_config_dir)
char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1]; char old_data_file[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_NAME) + 1];
snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME); snprintf(old_data_file, sizeof(old_data_file), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_NAME);
if (!file_exists(old_data_file)) if (!file_exists(old_data_file)) {
return 0; return 0;
}
if (file_exists(DATA_FILE)) if (file_exists(DATA_FILE)) {
return 0; return 0;
}
if (rename(old_data_file, DATA_FILE) != 0) if (rename(old_data_file, DATA_FILE) != 0) {
return -1; return -1;
}
queue_init_message("Data file has been moved to %s", DATA_FILE); queue_init_message("Data file has been moved to %s", DATA_FILE);
char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1]; char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1];
snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME); snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME);
if (!file_exists(old_data_blocklist)) if (!file_exists(old_data_blocklist)) {
return 0; return 0;
}
if (file_exists(BLOCK_FILE)) if (file_exists(BLOCK_FILE)) {
return 0; return 0;
}
if (rename(old_data_blocklist, BLOCK_FILE) != 0) if (rename(old_data_blocklist, BLOCK_FILE) != 0) {
return -1; return -1;
}
return 0; return 0;
} }
@ -1077,13 +1249,15 @@ static int rename_old_profile(const char *user_config_dir)
*/ */
static void init_default_data_files(void) static void init_default_data_files(void)
{ {
if (arg_opts.use_custom_data) if (arg_opts.use_custom_data) {
return; return;
}
char *user_config_dir = get_user_config_dir(); char *user_config_dir = get_user_config_dir();
if (user_config_dir == NULL) if (user_config_dir == NULL) {
exit_toxic_err("failed in init_default_data_files()", FATALERR_FILEOP); exit_toxic_err("failed in init_default_data_files()", FATALERR_FILEOP);
}
int config_err = create_user_config_dirs(user_config_dir); int config_err = create_user_config_dirs(user_config_dir);
@ -1091,14 +1265,16 @@ static void init_default_data_files(void)
DATA_FILE = strdup(DATANAME); DATA_FILE = strdup(DATANAME);
BLOCK_FILE = strdup(BLOCKNAME); BLOCK_FILE = strdup(BLOCKNAME);
if (DATA_FILE == NULL || BLOCK_FILE == NULL) if (DATA_FILE == NULL || BLOCK_FILE == NULL) {
exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY); exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY);
}
} else { } else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1); DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1); BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL) if (DATA_FILE == NULL || BLOCK_FILE == NULL) {
exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY); exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY);
}
strcpy(DATA_FILE, user_config_dir); strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR); strcat(DATA_FILE, CONFIGDIR);
@ -1110,8 +1286,9 @@ static void init_default_data_files(void)
} }
/* For backwards compatibility with old toxic profile names. TODO: remove this some day */ /* For backwards compatibility with old toxic profile names. TODO: remove this some day */
if (rename_old_profile(user_config_dir) == -1) if (rename_old_profile(user_config_dir) == -1) {
queue_init_message("Warning: Profile backwards compatibility failed."); queue_init_message("Warning: Profile backwards compatibility failed.");
}
free(user_config_dir); free(user_config_dir);
} }
@ -1120,6 +1297,8 @@ static void init_default_data_files(void)
#ifdef X11 #ifdef X11
void DnD_callback(const char *asdv, DropType dt) void DnD_callback(const char *asdv, DropType dt)
{ {
UNUSED_VAR(asdv);
UNUSED_VAR(dt);
// if (dt != DT_plain) // if (dt != DT_plain)
// return; // return;
@ -1131,11 +1310,17 @@ void DnD_callback(const char *asdv, DropType dt)
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
parse_args(argc, argv); parse_args(argc, argv);
/* Use the -b flag to enable stderr */ /* Use the -b flag to enable stderr */
if (!arg_opts.debug) if (!arg_opts.debug) {
freopen("/dev/null", "w", stderr); if (!freopen("/dev/null", "w", stderr)) {
fprintf(stderr, "Warning: failed to enable stderr\n");
}
}
if (arg_opts.encrypt_data && arg_opts.unencrypt_data) { if (arg_opts.encrypt_data && arg_opts.unencrypt_data) {
arg_opts.encrypt_data = 0; arg_opts.encrypt_data = 0;
@ -1143,24 +1328,23 @@ int main(int argc, char **argv)
queue_init_message("Warning: Using --unencrypt-data and --encrypt-data simultaneously has no effect"); queue_init_message("Warning: Using --unencrypt-data and --encrypt-data simultaneously has no effect");
} }
/* Make sure all written files are read/writeable only by the current user. */
umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
init_default_data_files(); init_default_data_files();
bool datafile_exists = file_exists(DATA_FILE); bool datafile_exists = file_exists(DATA_FILE);
if (!datafile_exists && !arg_opts.unencrypt_data) if (!datafile_exists && !arg_opts.unencrypt_data) {
first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)"); first_time_encrypt("Creating new data file. Would you like to encrypt it? Y/n (q to quit)");
else if (arg_opts.encrypt_data) } else if (arg_opts.encrypt_data) {
first_time_encrypt("Encrypt existing data file? Y/n (q to quit)"); first_time_encrypt("Encrypt existing data file? Y/n (q to quit)");
}
/* init user_settings struct and load settings from conf file */ /* init user_settings struct and load settings from conf file */
user_settings = calloc(1, sizeof(struct user_settings)); user_settings = calloc(1, sizeof(struct user_settings));
if (user_settings == NULL) if (user_settings == NULL) {
exit_toxic_err("failed in main", FATALERR_MEMORY); exit_toxic_err("failed in main", FATALERR_MEMORY);
}
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
@ -1181,32 +1365,37 @@ int main(int argc, char **argv)
#ifdef X11 #ifdef X11
if (init_xtra(DnD_callback) == -1) if (init_xtra(DnD_callback) == -1) {
queue_init_message("X failed to initialize"); queue_init_message("X failed to initialize");
}
#endif #endif /* X11 */
Tox *m = load_toxic(DATA_FILE); Tox *m = load_toxic(DATA_FILE);
if (arg_opts.encrypt_data && !datafile_exists) if (arg_opts.encrypt_data && !datafile_exists) {
arg_opts.encrypt_data = 0; arg_opts.encrypt_data = 0;
}
init_term(); init_term();
prompt = init_windows(m); prompt = init_windows(m);
prompt_init_statusbar(prompt, m); prompt_init_statusbar(prompt, m, !datafile_exists);
load_conferences(m);
/* thread for ncurses stuff */ /* thread for ncurses stuff */
if (pthread_mutex_init(&Winthread.lock, NULL) != 0) if (pthread_mutex_init(&Winthread.lock, NULL) != 0) {
exit_toxic_err("failed in main", FATALERR_MUTEX_INIT); exit_toxic_err("failed in main", FATALERR_MUTEX_INIT);
}
if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) if (pthread_create(&Winthread.tid, NULL, thread_winref, (void *) m) != 0) {
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
}
/* thread for message queue */ /* thread for message queue */
if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) if (pthread_create(&cqueue_thread.tid, NULL, thread_cqueue, (void *) m) != 0) {
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
}
#ifdef AUDIO #ifdef AUDIO
@ -1218,16 +1407,18 @@ int main(int argc, char **argv)
#endif /* VIDEO */ #endif /* VIDEO */
/* AV thread */ /* AV thread */
if (pthread_create(&av_thread.tid, NULL, thread_av, (void *) av) != 0) if (pthread_create(&av_thread.tid, NULL, thread_av, (void *) av) != 0) {
exit_toxic_err("failed in main", FATALERR_THREAD_CREATE); exit_toxic_err("failed in main", FATALERR_THREAD_CREATE);
}
set_primary_device(input, user_settings->audio_in_dev); set_primary_device(input, user_settings->audio_in_dev);
set_primary_device(output, user_settings->audio_out_dev); set_primary_device(output, user_settings->audio_out_dev);
#elif SOUND_NOTIFY #elif SOUND_NOTIFY
if ( init_devices() == de_InternalError ) if (init_devices() == de_InternalError) {
queue_init_message("Failed to init audio devices"); queue_init_message("Failed to init audio devices");
}
#endif /* AUDIO */ #endif /* AUDIO */
@ -1238,11 +1429,12 @@ int main(int argc, char **argv)
#endif /* PYTHON */ #endif /* PYTHON */
init_notify(60, 3000); init_notify(60, user_settings->notification_timeout);
/* screen/tmux auto-away timer */ /* screen/tmux auto-away timer */
if (init_mplex_away_timer(m) == -1) if (init_mplex_away_timer(m) == -1) {
queue_init_message("Failed to init mplex auto-away."); queue_init_message("Failed to init mplex auto-away.");
}
int nodeslist_ret = load_DHT_nodeslist(); int nodeslist_ret = load_DHT_nodeslist();
@ -1258,7 +1450,7 @@ int main(int argc, char **argv)
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */ /* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
char avatarstr[PATH_MAX + 11]; char avatarstr[PATH_MAX + 11];
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); snprintf(avatarstr, sizeof(avatarstr), "/avatar %s", user_settings->avatar_path);
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
time_t last_save = get_unix_time(); time_t last_save = get_unix_time();
@ -1268,11 +1460,12 @@ int main(int argc, char **argv)
time_t cur_time = get_unix_time(); time_t cur_time = get_unix_time();
if (timed_out(last_save, AUTOSAVE_FREQ)) { if (user_settings->autosave_freq > 0 && timed_out(last_save, user_settings->autosave_freq)) {
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, NULL, 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

@ -61,8 +61,6 @@
#define T_KEY_PREV 0x0F /* ctrl-o */ #define T_KEY_PREV 0x0F /* ctrl-o */
#define T_KEY_C_E 0x05 /* ctrl-e */ #define T_KEY_C_E 0x05 /* ctrl-e */
#define T_KEY_C_A 0x01 /* ctrl-a */ #define T_KEY_C_A 0x01 /* ctrl-a */
#define T_KEY_C_RB 0x1D /* ctrl-] */
#define T_KEY_C_LB 0x1B /* ctrl-[ */
#define T_KEY_C_V 0x16 /* ctrl-v */ #define T_KEY_C_V 0x16 /* ctrl-v */
#define T_KEY_C_F 0x06 /* ctrl-f */ #define T_KEY_C_F 0x06 /* ctrl-f */
#define T_KEY_C_H 0x08 /* ctrl-h */ #define T_KEY_C_H 0x08 /* ctrl-h */
@ -71,6 +69,10 @@
#define T_KEY_C_W 0x17 /* ctrl-w */ #define T_KEY_C_W 0x17 /* ctrl-w */
#define T_KEY_C_B 0x02 /* ctrl-b */ #define T_KEY_C_B 0x02 /* ctrl-b */
#define T_KEY_C_T 0x14 /* ctrl-t */ #define T_KEY_C_T 0x14 /* ctrl-t */
#define T_KEY_C_LEFT 0x221 /* ctrl-left arrow */
#define T_KEY_C_RIGHT 0x230 /* ctrl-right arrow */
#define T_KEY_C_UP 0x236 /* ctrl-up arrow */
#define T_KEY_C_DOWN 0x20D /* ctrl-down arrow */
#define T_KEY_TAB 0x09 /* TAB key */ #define T_KEY_TAB 0x09 /* TAB key */
#define ONLINE_CHAR "*" #define ONLINE_CHAR "*"
@ -96,8 +98,8 @@ typedef enum _FATAL_ERRS {
Uncomment if necessary */ Uncomment if necessary */
/* #define URXVT_FIX */ /* #define URXVT_FIX */
void lock_status (); void lock_status(void);
void unlock_status (); void unlock_status(void);
void exit_toxic_success(Tox *m); void exit_toxic_success(Tox *m);
void exit_toxic_err(const char *errmsg, int errcode); void exit_toxic_err(const char *errmsg, int errcode);
@ -105,31 +107,32 @@ void exit_toxic_err(const char *errmsg, int errcode);
int store_data(Tox *m, const char *path); int store_data(Tox *m, const char *path);
/* callbacks */ /* callbacks */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata); void on_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata);
void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION status, void *userdata); void on_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection status, void *userdata);
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, void on_friend_message(Tox *m, uint32_t friendnumber, Tox_Message_Type type, const uint8_t *string, size_t length,
void *userdata); void *userdata);
void on_action(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); void on_friend_name(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); void on_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata);
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata); void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata); void on_friend_added(Tox *m, uint32_t friendnumber, bool sort);
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort); void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type, const uint8_t *message, size_t length, void *userdata);
const uint8_t *message, size_t length, void *userdata); void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key,
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key, size_t length, void *userdata);
size_t length, void *userdata); void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata);
void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change, void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
void *userdata); size_t length, void *userdata);
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length, void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata); void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
void *userdata); void *userdata);
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data, void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *userdata); size_t length, void *userdata);
void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata); void on_file_recv_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, Tox_File_Control control,
void *userdata);
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size, void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *userdata); const uint8_t *filename, size_t filename_length, void *userdata);
void on_typing_change(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_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);
#endif /* #define TOXIC_H */ #endif /* TOXIC_H */

View File

@ -33,8 +33,9 @@
/* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */ /* Adds char to line at pos. Return 0 on success, -1 if line buffer is full */
int add_char_to_buf(ChatContext *ctx, wint_t ch) int add_char_to_buf(ChatContext *ctx, wint_t ch)
{ {
if (ctx->len >= MAX_STR_SIZE - 1) if (ctx->len >= MAX_STR_SIZE - 1) {
return -1; return -1;
}
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos); wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
ctx->line[ctx->pos++] = ch; ctx->line[ctx->pos++] = ch;
@ -46,8 +47,9 @@ int add_char_to_buf(ChatContext *ctx, wint_t ch)
/* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */ /* Deletes the character before pos. Return 0 on success, -1 if nothing to delete */
int del_char_buf_bck(ChatContext *ctx) int del_char_buf_bck(ChatContext *ctx)
{ {
if (ctx->pos <= 0) if (ctx->pos <= 0) {
return -1; return -1;
}
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos); wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
--ctx->pos; --ctx->pos;
@ -59,8 +61,9 @@ int del_char_buf_bck(ChatContext *ctx)
/* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */ /* Deletes the character at pos. Return 0 on success, -1 if nothing to delete. */
int del_char_buf_frnt(ChatContext *ctx) int del_char_buf_frnt(ChatContext *ctx)
{ {
if (ctx->pos >= ctx->len) if (ctx->pos >= ctx->len) {
return -1; return -1;
}
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1); wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
ctx->line[--ctx->len] = L'\0'; ctx->line[--ctx->len] = L'\0';
@ -72,8 +75,9 @@ int del_char_buf_frnt(ChatContext *ctx)
Return 0 on success, -1 if noting to discard. */ Return 0 on success, -1 if noting to discard. */
int discard_buf(ChatContext *ctx) int discard_buf(ChatContext *ctx)
{ {
if (ctx->pos <= 0) if (ctx->pos <= 0) {
return -1; return -1;
}
ctx->yank_len = ctx->pos; ctx->yank_len = ctx->pos;
wmemcpy(ctx->yank, ctx->line, ctx->yank_len); wmemcpy(ctx->yank, ctx->line, ctx->yank_len);
@ -92,8 +96,9 @@ int discard_buf(ChatContext *ctx)
Return 0 on success, -1 if nothing to kill. */ Return 0 on success, -1 if nothing to kill. */
int kill_buf(ChatContext *ctx) int kill_buf(ChatContext *ctx)
{ {
if (ctx->len <= ctx->pos) if (ctx->len <= ctx->pos) {
return -1; return -1;
}
ctx->yank_len = ctx->len - ctx->pos; ctx->yank_len = ctx->len - ctx->pos;
wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len); wmemcpy(ctx->yank, &ctx->line[ctx->pos], ctx->yank_len);
@ -109,11 +114,13 @@ int kill_buf(ChatContext *ctx)
Return 0 on success, -1 if yank buffer is empty or too long */ Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx) int yank_buf(ChatContext *ctx)
{ {
if (!ctx->yank[0]) if (!ctx->yank[0]) {
return -1; return -1;
}
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE) if (ctx->yank_len + ctx->len >= MAX_STR_SIZE) {
return -1; return -1;
}
wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos); wmemmove(&ctx->line[ctx->pos + ctx->yank_len], &ctx->line[ctx->pos], ctx->len - ctx->pos);
wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len); wmemcpy(&ctx->line[ctx->pos], ctx->yank, ctx->yank_len);
@ -130,8 +137,9 @@ int yank_buf(ChatContext *ctx)
Return 0 on success, -1 if nothing to delete */ Return 0 on success, -1 if nothing to delete */
int del_word_buf(ChatContext *ctx) int del_word_buf(ChatContext *ctx)
{ {
if (ctx->len == 0 || ctx->pos == 0) if (ctx->len == 0 || ctx->pos == 0) {
return -1; return -1;
}
int i = ctx->pos, count = 0; int i = ctx->pos, count = 0;
@ -169,17 +177,20 @@ void reset_buf(ChatContext *ctx)
/* Removes trailing spaces and newlines from line. */ /* Removes trailing spaces and newlines from line. */
void rm_trailing_spaces_buf(ChatContext *ctx) void rm_trailing_spaces_buf(ChatContext *ctx)
{ {
if (ctx->len <= 0) if (ctx->len <= 0) {
return; return;
}
if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'') if (ctx->line[ctx->len - 1] != ' ' && ctx->line[ctx->len - 1] != L'') {
return; return;
}
int i; int i;
for (i = ctx->len - 1; i >= 0; --i) { for (i = ctx->len - 1; i >= 0; --i) {
if (ctx->line[i] != ' ' && ctx->line[i] != L'') if (ctx->line[i] != ' ' && ctx->line[i] != L'') {
break; break;
}
} }
ctx->len = i + 1; ctx->len = i + 1;
@ -195,8 +206,9 @@ static void shift_hist_back(ChatContext *ctx)
int i; int i;
int n = MAX_LINE_HIST - HIST_PURGE; int n = MAX_LINE_HIST - HIST_PURGE;
for (i = 0; i < n; ++i) for (i = 0; i < n; ++i) {
wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE); wmemcpy(ctx->ln_history[i], ctx->ln_history[i + HIST_PURGE], MAX_STR_SIZE);
}
ctx->hst_tot = n; ctx->hst_tot = n;
} }
@ -204,11 +216,13 @@ static void shift_hist_back(ChatContext *ctx)
/* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */ /* adds a line to the ln_history buffer at hst_pos and sets hst_pos to end of history. */
void add_line_to_hist(ChatContext *ctx) void add_line_to_hist(ChatContext *ctx)
{ {
if (ctx->len >= MAX_STR_SIZE) if (ctx->len >= MAX_STR_SIZE) {
return; return;
}
if (ctx->hst_tot >= MAX_LINE_HIST) if (ctx->hst_tot >= MAX_LINE_HIST) {
shift_hist_back(ctx); shift_hist_back(ctx);
}
++ctx->hst_tot; ++ctx->hst_tot;
ctx->hst_pos = ctx->hst_tot; ctx->hst_pos = ctx->hst_tot;
@ -222,6 +236,12 @@ void add_line_to_hist(ChatContext *ctx)
resets line if at end of history */ resets line if at end of history */
void fetch_hist_item(ChatContext *ctx, int key_dir) void fetch_hist_item(ChatContext *ctx, int key_dir)
{ {
if (wcscmp(ctx->line, L"\0") != 0
&& ctx->hst_pos == ctx->hst_tot) {
add_line_to_hist(ctx);
ctx->hst_pos--;
}
if (key_dir == KEY_UP) { if (key_dir == KEY_UP) {
if (--ctx->hst_pos < 0) { if (--ctx->hst_pos < 0) {
ctx->hst_pos = 0; ctx->hst_pos = 0;
@ -247,16 +267,20 @@ void strsubst(char *str, char old, char new)
{ {
int i; int i;
for (i = 0; str[i] != '\0'; ++i) for (i = 0; str[i] != '\0'; ++i) {
if (str[i] == old) if (str[i] == old) {
str[i] = new; str[i] = new;
}
}
} }
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new) void wstrsubst(wchar_t *str, wchar_t old, wchar_t new)
{ {
int i; int i;
for (i = 0; str[i] != L'\0'; ++i) for (i = 0; str[i] != L'\0'; ++i) {
if (str[i] == old) if (str[i] == old) {
str[i] = new; str[i] = new;
}
}
} }

View File

@ -70,4 +70,4 @@ void fetch_hist_item(ChatContext *ctx, int key_dir);
void strsubst(char *str, char old, char new); void strsubst(char *str, char old, char new);
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new); void wstrsubst(wchar_t *str, wchar_t old, wchar_t new);
#endif /* #define TOXIC_STRINGS_H */ #endif /* TOXIC_STRINGS_H */

View File

@ -28,6 +28,7 @@
#include "global_commands.h" #include "global_commands.h"
#include "line_info.h" #include "line_info.h"
#include "notify.h" #include "notify.h"
#include "misc_tools.h"
#include <stdbool.h> #include <stdbool.h>
#include <curses.h> #include <curses.h>
@ -37,59 +38,63 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#ifdef VIDEO
#define default_video_bit_rate 5000 #define default_video_bit_rate 5000
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number, void on_video_receive_frame(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height, uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data ); void *user_data);
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data);
uint32_t video_bit_rate, void *user_data);
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, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
} }
ToxAV *init_video(ToxWindow *self, Tox *tox) ToxAV *init_video(ToxWindow *self, Tox *tox)
{ {
UNUSED_VAR(tox);
CallControl.video_errors = ve_None; CallControl.video_errors = ve_None;
CallControl.video_enabled = true; CallControl.video_enabled = true;
CallControl.video_bit_rate = 0; CallControl.video_bit_rate = 0;
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, NULL, 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init video devices");
return NULL; return NULL;
} }
toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl); toxav_callback_video_receive_frame(CallControl.av, on_video_receive_frame, &CallControl);
toxav_callback_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl); toxav_callback_video_bit_rate(CallControl.av, on_video_bit_rate, &CallControl);
return CallControl.av; return CallControl.av;
} }
void terminate_video() void terminate_video(void)
{ {
int i; int i;
for (i = 0; i < MAX_CALLS; ++i) { for (i = 0; i < CallControl.max_calls; ++i) {
Call *this_call = &CallControl.calls[i]; Call *this_call = &CallControl.calls[i];
stop_video_transmission(this_call, i); stop_video_transmission(this_call, i);
if ( this_call->vout_idx != -1 ) if (this_call->vout_idx != -1) {
close_video_device(vdt_output, this_call->vout_idx); close_video_device(vdt_output, this_call->vout_idx);
}
} }
terminate_video_devices(); terminate_video_devices();
@ -100,21 +105,22 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y,
{ {
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */ uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
Call *this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_SEND_FRAME error; Toxav_Err_Send_Frame error;
/* Drop frame if video sending is disabled */ /* Drop frame if video sending is disabled */
if ( CallControl.video_bit_rate == 0 || this_call->vin_idx == -1 ) { if (CallControl.video_bit_rate == 0 || this_call->vin_idx == -1) {
line_info_add(CallControl.prompt, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video frame dropped."); line_info_add(CallControl.prompt, NULL, 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, NULL, 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, NULL, 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare video frame");
}
} }
} }
@ -123,29 +129,31 @@ void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
UNUSED_VAR(friend_number);
write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data); write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
} }
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 transmission"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
return -1; return -1;
} }
CallControl.video_bit_rate = default_video_bit_rate; CallControl.video_bit_rate = default_video_bit_rate;
if ( toxav_bit_rate_set(CallControl.av, self->num, -1, CallControl.video_bit_rate, NULL) == false ) { if (toxav_video_set_bit_rate(CallControl.av, self->num, CallControl.video_bit_rate, NULL) == false) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
return -1; return -1;
} }
if ( open_primary_video_device(vdt_input, &call->vin_idx) != vde_None ) { if (open_primary_video_device(vdt_input, &call->vin_idx) != vde_None) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open input video device!"); line_info_add(self, NULL, 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input video handler!");
return -1; return -1;
} }
@ -156,9 +164,9 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
int stop_video_transmission(Call *call, int friend_number) int stop_video_transmission(Call *call, int friend_number)
{ {
CallControl.video_bit_rate = 0; CallControl.video_bit_rate = 0;
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL); toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
if ( call->vin_idx != -1 ) { if (call->vin_idx != -1) {
close_video_device(vdt_input, call->vin_idx); close_video_device(vdt_input, call->vin_idx);
call->vin_idx = -1; call->vin_idx = -1;
} }
@ -176,28 +184,33 @@ int stop_video_transmission(Call *call, int friend_number)
/* /*
* Callbacks * Callbacks
*/ */
void receive_video_frame_cb(ToxAV *av, uint32_t friend_number, void on_video_receive_frame(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height, uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
UNUSED_VAR(av);
write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data); write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
} }
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data)
uint32_t video_bit_rate, void *user_data)
{ {
UNUSED_VAR(av);
UNUSED_VAR(user_data);
CallControl.video_bit_rate = video_bit_rate; CallControl.video_bit_rate = video_bit_rate;
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL); toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
} }
void callback_recv_video_starting(uint32_t friend_number) void callback_recv_video_starting(uint32_t friend_number)
{ {
Call *this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
if ( this_call->vout_idx != -1 ) if (this_call->vout_idx != -1) {
return; return;
}
open_primary_video_device(vdt_output, &this_call->vout_idx); open_primary_video_device(vdt_output, &this_call->vout_idx);
} }
@ -210,23 +223,24 @@ void callback_recv_video_end(uint32_t friend_number)
} }
void callback_video_starting(uint32_t friend_number) void callback_video_starting(uint32_t friend_number)
{ {
ToxWindow *windows = CallControl.prompt;
Call *this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK; Toxav_Err_Call_Control error = TOXAV_ERR_CALL_CONTROL_OK;
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error); toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error);
if (error == TOXAV_ERR_CALL_CONTROL_OK) { if (error == TOXAV_ERR_CALL_CONTROL_OK) {
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].is_call && windows[i].num == friend_number ) { ToxWindow *window = get_window_ptr(i);
if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) {
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!"); if (window != NULL && window->is_call && window->num == friend_number) {
if (0 != start_video_transmission(window, CallControl.av, this_call)) {
line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return; return;
} }
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting."); line_info_add(window, NULL, NULL, NULL, SYS_MSG, 0, 0, "Video capture starting.");
} }
} }
} }
@ -246,57 +260,70 @@ void callback_video_end(uint32_t friend_number)
*/ */
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
UNUSED_VAR(argv);
const char *error_str; const char *error_str;
Call *this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if ( argc != 0 ) { if (argc != 0) {
error_str = "Unknown arguments."; error_str = "Unknown arguments.";
goto on_error; goto on_error;
} }
if ( !CallControl.av ) { if (!CallControl.av) {
error_str = "ToxAV not supported!"; error_str = "ToxAV not supported!";
goto on_error; goto on_error;
} }
if ( !self->stb->connection ) { if (!self->stb->connection) {
error_str = "Friend is offline."; error_str = "Friend is offline.";
goto on_error; goto on_error;
} }
if ( !self->is_call ) { if (!self->is_call) {
error_str = "Not in call!"; error_str = "Not in call!";
goto on_error; goto on_error;
} }
if ( this_call->vin_idx == -1 ) if (this_call->vin_idx == -1) {
callback_video_starting(self->num); callback_video_starting(self->num);
else } else {
callback_video_end(self->num); callback_video_end(self->num);
}
return; return;
on_error: on_error:
print_err (self, error_str); print_err(self, error_str);
} }
void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
const char *error_str; const char *error_str;
if ( argc != 1 ) { if (argc != 1) {
if ( argc < 1 ) error_str = "Type must be specified!"; if (argc < 1) {
else error_str = "Only one argument allowed!"; error_str = "Type must be specified!";
} else {
error_str = "Only one argument allowed!";
}
goto on_error; goto on_error;
} }
VideoDeviceType type; VideoDeviceType type;
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */ if (strcasecmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input; type = vdt_input;
}
else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */ else if (strcasecmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output; type = vdt_output;
}
else { else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -307,29 +334,38 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c
return; return;
on_error: on_error:
print_err (self, error_str); print_err(self, error_str);
} }
/* This changes primary video device only */ /* This changes primary video device only */
void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
const char *error_str; const char *error_str;
if ( argc != 2 ) { if (argc != 2) {
if ( argc < 1 ) error_str = "Type must be specified!"; if (argc < 1) {
else if ( argc < 2 ) error_str = "Must have id!"; error_str = "Type must be specified!";
else error_str = "Only two arguments allowed!"; } else if (argc < 2) {
error_str = "Must have id!";
} else {
error_str = "Only two arguments allowed!";
}
goto on_error; goto on_error;
} }
VideoDeviceType type; VideoDeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */ if (strcmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input; type = vdt_input;
}
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ else if (strcmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output; type = vdt_output;
}
else { else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -340,40 +376,49 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
char *end; char *end;
long int selection = strtol(argv[2], &end, 10); long int selection = strtol(argv[2], &end, 10);
if ( *end ) { if (*end) {
error_str = "Invalid input"; error_str = "Invalid input";
goto on_error; goto on_error;
} }
if ( set_primary_video_device(type, selection) == vde_InvalidSelection ) { if (set_primary_video_device(type, selection) == vde_InvalidSelection) {
error_str = "Invalid selection!"; error_str = "Invalid selection!";
goto on_error; goto on_error;
} }
return; return;
on_error: on_error:
print_err (self, error_str); print_err(self, error_str);
} }
void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
UNUSED_VAR(window);
UNUSED_VAR(m);
const char *error_str; const char *error_str;
if ( argc != 2 ) { if (argc != 2) {
if ( argc < 1 ) error_str = "Type must be specified!"; if (argc < 1) {
else if ( argc < 2 ) error_str = "Must have id!"; error_str = "Type must be specified!";
else error_str = "Only two arguments allowed!"; } else if (argc < 2) {
error_str = "Must have id!";
} else {
error_str = "Only two arguments allowed!";
}
goto on_error; goto on_error;
} }
VideoDeviceType type; VideoDeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */ if (strcmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input; type = vdt_input;
}
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */ else if (strcmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output; type = vdt_output;
}
else { else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -384,23 +429,23 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
char *end; char *end;
long int selection = strtol(argv[2], &end, 10); long int selection = strtol(argv[2], &end, 10);
if ( *end ) { if (*end) {
error_str = "Invalid input"; error_str = "Invalid input";
goto on_error; goto on_error;
} }
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) { if (video_selection_valid(type, selection) == vde_InvalidSelection) {
error_str = "Invalid selection!"; error_str = "Invalid selection!";
goto on_error; goto on_error;
} }
/* If call is active, change device */ /* If call is active, change device */
if ( self->is_call ) { if (self->is_call) {
Call *this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) { if (this_call->ttas) {
if ( type == vdt_output ) { if (type == vdt_output) {
} else { } else {
/* TODO: check for failure */ /* TODO: check for failure */
close_video_device(vdt_input, this_call->vin_idx); close_video_device(vdt_input, this_call->vin_idx);
@ -414,13 +459,16 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
return; return;
on_error: on_error:
print_err (self, error_str); print_err(self, error_str);
} }
void stop_video_stream(ToxWindow *self) void stop_video_stream(ToxWindow *self)
{ {
Call *this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if (this_call && this_call->vin_idx != -1) if (this_call && this_call->vin_idx != -1) {
stop_video_transmission(this_call, self->num); stop_video_transmission(this_call, self->num);
}
} }
#endif /* VIDEO */

View File

@ -31,7 +31,7 @@
/* You will have to pass pointer to first member of 'windows' declared in windows.c */ /* You will have to pass pointer to first member of 'windows' declared in windows.c */
ToxAV *init_video(ToxWindow *self, Tox *tox); ToxAV *init_video(ToxWindow *self, Tox *tox);
void terminate_video(); void terminate_video(void);
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call); int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call);
int stop_video_transmission(Call *call, int friend_number); int stop_video_transmission(Call *call, int friend_number);
void stop_video_stream(ToxWindow *self); void stop_video_stream(ToxWindow *self);

View File

@ -24,28 +24,29 @@
#include "video_call.h" #include "video_call.h"
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <vpx/vpx_image.h> #include <vpx/vpx_image.h>
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
#import "osx_video.h" #import "osx_video.h"
#else #else
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
#if defined(__linux__) #if defined(__OpenBSD__) || defined(__NetBSD__)
#include <linux/videodev2.h>
#else
#include <sys/videoio.h> #include <sys/videoio.h>
#endif #else
#endif #include <linux/videodev2.h>
#endif /* defined(__OpenBSD__) || defined(__NetBSD__) */
#endif /* __OSX__ || __APPLE__ */
#include "line_info.h" #include "line_info.h"
#include "settings.h" #include "settings.h"
#include "misc_tools.h"
#include <errno.h> #include <errno.h>
@ -55,6 +56,8 @@
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#ifdef VIDEO
#define inline__ inline __attribute__((always_inline)) #define inline__ inline __attribute__((always_inline))
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
@ -69,7 +72,7 @@ typedef struct VideoDevice {
void *cb_data; /* Data to be passed to callback */ void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */ int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || SYSTEM == BSD #if !(defined(__OSX__) || defined(__APPLE__))
int fd; /* File descriptor of video device selected/opened */ int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt; struct v4l2_format fmt;
struct VideoBuffer *buffers; struct VideoBuffer *buffers;
@ -96,13 +99,11 @@ static int size[2]; /* Size of above containers */
VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */ VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_video_device[2]; /* Primary device */ uint32_t primary_video_device[2]; /* Primary device */
#ifdef VIDEO
static ToxAV *av = NULL; static ToxAV *av = NULL;
#endif /* VIDEO */
/* q_mutex */ /* q_mutex */
#define lock pthread_mutex_lock(&video_mutex); #define lock pthread_mutex_lock(&video_mutex)
#define unlock pthread_mutex_unlock(&video_mutex); #define unlock pthread_mutex_unlock(&video_mutex)
pthread_mutex_t video_mutex; pthread_mutex_t video_mutex;
bool video_thread_running = true, bool video_thread_running = true,
@ -136,7 +137,7 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
} }
} }
#if defined(__linux__) || SYSTEM == BSD #if !(defined(__OSX__) || defined(__APPLE__))
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height) uint8_t *input, uint16_t width, uint16_t height)
{ {
@ -185,10 +186,13 @@ VideoDeviceError init_video_devices()
{ {
size[vdt_input] = 0; size[vdt_input] = 0;
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
if (osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0) {
return vde_InternalError; return vde_InternalError;
#else /* not __OSX__*/ }
#else /* not __OSX__ || __APPLE__ */
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) { for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
int fd; int fd;
@ -197,14 +201,14 @@ VideoDeviceError init_video_devices()
fd = open(device_address, O_RDWR | O_NONBLOCK, 0); fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
if ( fd == -1 ) { if (fd == -1) {
break; break;
} else { } else {
struct v4l2_capability cap; struct v4l2_capability cap;
char *video_input_name; char *video_input_name;
/* Query V4L for capture capabilities */ /* Query V4L for capture capabilities */
if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) { if (-1 != ioctl(fd, VIDIOC_QUERYCAP, &cap)) {
video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4); video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4);
strcpy(video_input_name, (char *)cap.card); strcpy(video_input_name, (char *)cap.card);
strcat(video_input_name, " ("); strcat(video_input_name, " (");
@ -230,13 +234,15 @@ VideoDeviceError init_video_devices()
video_devices_names[vdt_output][0] = video_output_name; video_devices_names[vdt_output][0] = video_output_name;
// Start poll thread // Start poll thread
if ( pthread_mutex_init(&video_mutex, NULL) != 0 ) if (pthread_mutex_init(&video_mutex, NULL) != 0) {
return vde_InternalError; return vde_InternalError;
}
pthread_t thread_id; pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 ) if (pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) {
return vde_InternalError; return vde_InternalError;
}
#ifdef VIDEO #ifdef VIDEO
av = av_; av = av_;
@ -245,7 +251,7 @@ VideoDeviceError init_video_devices()
return (VideoDeviceError) vde_None; return (VideoDeviceError) vde_None;
} }
VideoDeviceError terminate_video_devices() VideoDeviceError terminate_video_devices(void)
{ {
/* Cleanup if needed */ /* Cleanup if needed */
lock; lock;
@ -260,12 +266,13 @@ VideoDeviceError terminate_video_devices()
free((void *)video_devices_names[vdt_input][i]); free((void *)video_devices_names[vdt_input][i]);
} }
if ( pthread_mutex_destroy(&video_mutex) != 0 ) if (pthread_mutex_destroy(&video_mutex) != 0) {
return (VideoDeviceError) vde_InternalError; return (VideoDeviceError) vde_InternalError;
}
#ifdef __OSX__ #if defined(__OSX__) || defined(__APPLE__)
osx_video_release(); osx_video_release();
#endif /* __OSX__ */ #endif /* __OSX__ || __APPLE__ */
return (VideoDeviceError) vde_None; return (VideoDeviceError) vde_None;
} }
@ -273,16 +280,18 @@ VideoDeviceError terminate_video_devices()
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx, VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
VideoDataHandleCallback callback, void *data) VideoDataHandleCallback callback, void *data)
{ {
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] ) if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]) {
return vde_InvalidSelection; return vde_InvalidSelection;
}
#else /* not __OSX__ */ #else /* not __OSX__ || __APPLE__ */
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]
|| !video_devices_running[vdt_input][device_idx]->fd ) || !video_devices_running[vdt_input][device_idx]->fd) {
return vde_InvalidSelection; return vde_InvalidSelection;
}
#endif #endif
@ -297,7 +306,9 @@ VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection) VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection)
{ {
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection; if (size[type] <= selection || selection < 0) {
return vde_InvalidSelection;
}
primary_video_device[type] = selection; primary_video_device[type] = selection;
@ -316,14 +327,16 @@ void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx) VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx)
{ {
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection; if (size[type] <= selection || selection < 0) {
return vde_InvalidSelection;
}
lock; lock;
uint32_t i, temp_idx = -1; uint32_t i, temp_idx = -1;
for (i = 0; i < MAX_DEVICES; ++i) { for (i = 0; i < MAX_DEVICES; ++i) {
if ( !video_devices_running[type][i] ) { if (!video_devices_running[type][i]) {
temp_idx = i; temp_idx = i;
break; break;
} }
@ -335,7 +348,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
} }
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if ( video_devices_running[type][i] && video_devices_running[type][i]->selection == selection ) { if (video_devices_running[type][i] && video_devices_running[type][i]->selection == selection) {
video_devices_running[type][temp_idx] = video_devices_running[type][i]; video_devices_running[type][temp_idx] = video_devices_running[type][i];
video_devices_running[type][i]->ref_count++; video_devices_running[type][i]->ref_count++;
@ -348,29 +361,31 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice)); VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
device->selection = selection; device->selection = selection;
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) { if (pthread_mutex_init(device->mutex, NULL) != 0) {
free(device); free(device);
unlock; unlock;
return vde_InternalError; return vde_InternalError;
} }
if ( type == vdt_input ) { if (type == vdt_input) {
video_thread_paused = true; video_thread_paused = true;
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) {
free(device); free(device);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
#else /* not __OSX__*/
#else /* not __OSX__ || __APPLE__ */
/* Open selected device */ /* Open selected device */
char device_address[] = "/dev/videoXX"; char device_address[] = "/dev/videoXX";
snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection); snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection);
device->fd = open(device_address, O_RDWR); device->fd = open(device_address, O_RDWR);
if ( device->fd == -1 ) { if (device->fd == -1) {
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
@ -378,7 +393,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
/* Obtain video device capabilities */ /* Obtain video device capabilities */
struct v4l2_capability cap; struct v4l2_capability cap;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) { if (-1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap)) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -392,7 +407,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if ( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) { if (-1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt)) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -409,14 +424,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP; req.memory = V4L2_MEMORY_MMAP;
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) { if (-1 == xioctl(device->fd, VIDIOC_REQBUFS, &req)) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
if ( req.count < 2 ) { if (req.count < 2) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -433,7 +448,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; buf.index = i;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf) ) { if (-1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf)) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -447,9 +462,10 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
MAP_SHARED /* recommended */, MAP_SHARED /* recommended */,
device->fd, buf.m.offset); device->fd, buf.m.offset);
if ( MAP_FAILED == device->buffers[i].start ) { if (MAP_FAILED == device->buffers[i].start) {
for (i = 0; i < buf.index; ++i) for (i = 0; i < buf.index; ++i) {
munmap(device->buffers[i].start, device->buffers[i].length); munmap(device->buffers[i].start, device->buffers[i].length);
}
close(device->fd); close(device->fd);
free(device); free(device);
@ -470,9 +486,10 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
buf.index = i; buf.index = i;
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
for (i = 0; i < device->n_buffers; ++i) for (i = 0; i < device->n_buffers; ++i) {
munmap(device->buffers[i].start, device->buffers[i].length); munmap(device->buffers[i].start, device->buffers[i].length);
}
close(device->fd); close(device->fd);
free(device); free(device);
@ -484,7 +501,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* Turn on video stream */ /* Turn on video stream */
if ( -1 == xioctl(device->fd, VIDIOC_STREAMON, &type) ) { if (-1 == xioctl(device->fd, VIDIOC_STREAMON, &type)) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -493,7 +510,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
#endif #endif
/* Create X11 window associated to device */ /* Create X11 window associated to device */
if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) { if ((device->x_display = XOpenDisplay(NULL)) == NULL) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -501,9 +518,9 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display); int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, if (!(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen), device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
BlackPixel(device->x_display, screen))) ) { BlackPixel(device->x_display, screen)))) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -512,7 +529,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
XStoreName(device->x_display, device->x_window, "Video Preview"); XStoreName(device->x_display, device->x_window, "Video Preview");
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask); XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { if ((device->x_gc = DefaultGC(device->x_display, screen)) == NULL) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -533,7 +550,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
} else { /* vdt_output */ } else { /* vdt_output */
/* Create X11 window associated to device */ /* Create X11 window associated to device */
if ( (device->x_display = XOpenDisplay(NULL)) == NULL ) { if ((device->x_display = XOpenDisplay(NULL)) == NULL) {
close_video_device(vdt_output, temp_idx); close_video_device(vdt_output, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -541,8 +558,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display); int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, if (!(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) { 100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen)))) {
close_video_device(vdt_output, temp_idx); close_video_device(vdt_output, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -551,7 +568,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
XStoreName(device->x_display, device->x_window, "Video Receive"); XStoreName(device->x_display, device->x_window, "Video Receive");
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask); XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { if ((device->x_gc = DefaultGC(device->x_display, screen)) == NULL) {
close_video_device(vdt_output, temp_idx); close_video_device(vdt_output, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -575,21 +592,27 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
return vde_None; return vde_None;
} }
__inline VideoDeviceError write_video_out(uint16_t width, uint16_t height, VideoDeviceError write_video_out(uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
UNUSED_VAR(user_data);
VideoDevice *device = video_devices_running[vdt_output][0]; VideoDevice *device = video_devices_running[vdt_output][0];
if ( !device ) return vde_DeviceNotActive; if (!device) {
return vde_DeviceNotActive;
}
if ( !device->x_window ) return vde_DeviceNotActive; if (!device->x_window) {
return vde_DeviceNotActive;
}
pthread_mutex_lock(device->mutex); pthread_mutex_lock(device->mutex);
/* Resize X11 window to correct size */ /* Resize X11 window to correct size */
if ( device->video_width != width || device->video_height != height ) { if (device->video_width != width || device->video_height != height) {
device->video_width = width; device->video_width = width;
device->video_height = height; device->video_height = height;
XResizeWindow(device->x_display, device->x_window, width, height); XResizeWindow(device->x_display, device->x_window, width, height);
@ -634,12 +657,12 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
return vde_None; return vde_None;
} }
void *video_thread_poll (void *arg) // TODO: maybe use thread for every input source void *video_thread_poll(void *arg) // TODO: maybe use thread for every input source
{ {
/* /*
* NOTE: We only need to poll input devices for data. * NOTE: We only need to poll input devices for data.
*/ */
(void)arg; UNUSED_VAR(arg);
uint32_t i; uint32_t i;
while (1) { while (1) {
@ -652,12 +675,13 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
unlock; unlock;
if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */ if (video_thread_paused) {
else { usleep(10000); /* Wait for unpause. */
} else {
for (i = 0; i < size[vdt_input]; ++i) { for (i = 0; i < size[vdt_input]; ++i) {
lock; lock;
if ( video_devices_running[vdt_input][i] != NULL ) { if (video_devices_running[vdt_input][i] != NULL) {
/* Obtain frame image data from device buffers */ /* Obtain frame image data from device buffers */
VideoDevice *device = video_devices_running[vdt_input][i]; VideoDevice *device = video_devices_running[vdt_input][i];
uint16_t video_width = device->video_width; uint16_t video_width = device->video_width;
@ -666,19 +690,21 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
uint8_t *u = device->input.planes[1]; uint8_t *u = device->input.planes[1];
uint8_t *v = device->input.planes[2]; uint8_t *v = device->input.planes[2];
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
if (osx_video_read_device(y, u, v, &video_width, &video_height) != 0) {
unlock; unlock;
continue; continue;
} }
#else /* not __OSX__*/
#else /* not __OSX__ || __APPLE__ */
struct v4l2_buffer buf; struct v4l2_buffer buf;
memset(&(buf), 0, sizeof(buf)); memset(&(buf), 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP; buf.memory = V4L2_MEMORY_MMAP;
if ( -1 == ioctl(device->fd, VIDIOC_DQBUF, &buf) ) { if (-1 == ioctl(device->fd, VIDIOC_DQBUF, &buf)) {
unlock; unlock;
continue; continue;
} }
@ -691,8 +717,9 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
#endif #endif
/* Send frame data to friend through ToxAV */ /* Send frame data to friend through ToxAV */
if ( device->cb ) if (device->cb) {
device->cb(video_width, video_height, y, u, v, device->cb_data); device->cb(video_width, video_height, y, u, v, device->cb_data);
}
/* Convert YUV420 data to BGR */ /* Convert YUV420 data to BGR */
uint8_t *img_data = malloc(video_width * video_height * 4); uint8_t *img_data = malloc(video_width * video_height * 4);
@ -724,14 +751,14 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
XFlush(device->x_display); XFlush(device->x_display);
free(img_data); free(img_data);
#if defined(__linux__) || SYSTEM == BSD #if !(defined(__OSX__) || defined(__APPLE__))
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
unlock; unlock;
continue; continue;
} }
#endif /* __linux__ / BSD */ #endif
} }
@ -747,34 +774,36 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx) VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
{ {
if ( device_idx >= MAX_DEVICES ) return vde_InvalidSelection; if (device_idx >= MAX_DEVICES) {
return vde_InvalidSelection;
}
lock; lock;
VideoDevice *device = video_devices_running[type][device_idx]; VideoDevice *device = video_devices_running[type][device_idx];
VideoDeviceError rc = vde_None; VideoDeviceError rc = vde_None;
if ( !device ) { if (!device) {
unlock; unlock;
return vde_DeviceNotActive; return vde_DeviceNotActive;
} }
video_devices_running[type][device_idx] = NULL; video_devices_running[type][device_idx] = NULL;
if ( !device->ref_count ) { if (!device->ref_count) {
if ( type == vdt_input ) { if (type == vdt_input) {
#if defined(__OSX__) #if defined(__OSX__) || defined(__APPLE__)
osx_video_close_device(device_idx); osx_video_close_device(device_idx);
#else /* not __OSX__ */ #else /* not __OSX__ || __APPLE__ */
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {} if (-1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type)) {}
int i; int i;
for (i = 0; i < device->n_buffers; ++i) { for (i = 0; i < device->n_buffers; ++i) {
if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) { if (-1 == munmap(device->buffers[i].start, device->buffers[i].length)) {
} }
} }
@ -787,9 +816,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
XCloseDisplay(device->x_display); XCloseDisplay(device->x_display);
pthread_mutex_destroy(device->mutex); pthread_mutex_destroy(device->mutex);
#if defined(__linux__) || SYSTEM == BSD #if !(defined(__OSX__) || defined(__APPLE__))
free(device->buffers); free(device->buffers);
#endif /* __linux__ / BSD */ #endif /* not __OSX__ || __APPLE__ */
free(device); free(device);
} else { } else {
@ -801,7 +830,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
free(device); free(device);
} }
} else device->ref_count--; } else {
device->ref_count--;
}
unlock; unlock;
return rc; return rc;
@ -811,8 +842,9 @@ 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, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, video_devices_names[type][i]);
}
return; return;
} }
@ -821,3 +853,5 @@ VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection)
{ {
return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None; return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None;
} }
#endif /* VIDEO */

View File

@ -45,16 +45,16 @@ typedef enum VideoDeviceError {
vde_CaptureError = -9, vde_CaptureError = -9,
} VideoDeviceError; } VideoDeviceError;
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t *y, const uint8_t *u, typedef void (*VideoDataHandleCallback)(int16_t width, int16_t height, const uint8_t *y, const uint8_t *u,
const uint8_t *v, void *data); const uint8_t *v, void *data);
#ifdef VIDEO #ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV *av); VideoDeviceError init_video_devices(ToxAV *av);
#else #else
VideoDeviceError init_video_devices(); VideoDeviceError init_video_devices(void);
#endif /* VIDEO */ #endif /* VIDEO */
VideoDeviceError terminate_video_devices(); VideoDeviceError terminate_video_devices(void);
/* Callback handles ready data from INPUT device */ /* Callback handles ready data from INPUT device */
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback,
@ -76,4 +76,4 @@ void print_video_devices(ToxWindow *self, VideoDeviceType type);
void get_primary_video_device_name(VideoDeviceType type, char *buf, int size); void get_primary_video_device_name(VideoDeviceType type, char *buf, int size);
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection); VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);
#endif /* VIDEO_DEVICE_H */ #endif /* VIDEO_DEVICE_H */

View File

@ -39,279 +39,316 @@
extern char *DATA_FILE; extern char *DATA_FILE;
extern struct Winthread Winthread; extern struct Winthread Winthread;
static ToxWindow windows[MAX_WINDOWS_NUM];
static ToxWindow *active_window; ToxWindow *windows[MAX_WINDOWS_NUM];
static uint8_t active_window_index;
static int num_active_windows;
extern ToxWindow *prompt; extern ToxWindow *prompt;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
static int num_active_windows;
/* CALLBACKS START */ /* CALLBACKS START */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata) void on_friend_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
char msg[MAX_STR_SIZE + 1]; char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) data, length); length = copy_tox_str(msg, sizeof(msg), (const char *) data, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFriendRequest != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFriendRequest(windows[i], m, (const char *) public_key, msg, length);
if (windows[i].onFriendRequest != NULL) {
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, msg, length);
} }
} }
} }
void on_connectionchange(Tox *m, uint32_t friendnumber, TOX_CONNECTION connection_status, void *userdata) void on_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status, void *userdata)
{ {
size_t i; UNUSED_VAR(userdata);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onConnectionChange != NULL) if (windows[i] != NULL && windows[i]->onConnectionChange != NULL) {
windows[i].onConnectionChange(&windows[i], m, friendnumber, connection_status); windows[i]->onConnectionChange(windows[i], m, friendnumber, connection_status);
}
} }
} }
void on_typing_change(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata) void on_friend_typing(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata)
{ {
if (user_settings->show_typing_other == SHOW_TYPING_OFF) UNUSED_VAR(userdata);
if (user_settings->show_typing_other == SHOW_TYPING_OFF) {
return; return;
}
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onTypingChange != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onTypingChange(windows[i], m, friendnumber, is_typing);
if (windows[i].onTypingChange != NULL) }
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
} }
} }
void on_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length, void on_friend_message(Tox *m, uint32_t friendnumber, Tox_Message_Type type, const uint8_t *string, size_t length,
void *userdata) void *userdata)
{ {
UNUSED_VAR(userdata);
char msg[MAX_STR_SIZE + 1]; char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length); length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onMessage != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onMessage(windows[i], m, friendnumber, type, msg, length);
if (windows[i].onMessage != NULL) }
windows[i].onMessage(&windows[i], m, friendnumber, type, msg, length);
} }
} }
void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata) void on_friend_name(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
char nick[TOXIC_MAX_NAME_LENGTH + 1]; char nick[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(nick, sizeof(nick), (const char *) string, length); length = copy_tox_str(nick, sizeof(nick), (const char *) string, length);
filter_str(nick, length); filter_str(nick, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onNickChange != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onNickChange(windows[i], m, friendnumber, nick, length);
if (windows[i].onNickChange != NULL) }
windows[i].onNickChange(&windows[i], m, friendnumber, nick, length);
} }
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata) void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
UNUSED_VAR(m);
char msg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; char msg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length); length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
filter_str(msg, length); filter_str(msg, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onStatusMessageChange != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onStatusMessageChange(windows[i], friendnumber, msg, length);
if (windows[i].onStatusMessageChange != NULL) }
windows[i].onStatusMessageChange(&windows[i], friendnumber, msg, length);
} }
} }
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata) void on_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata)
{ {
size_t i; UNUSED_VAR(userdata);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusChange != NULL) if (windows[i] != NULL && windows[i]->onStatusChange != NULL) {
windows[i].onStatusChange(&windows[i], m, friendnumber, status); windows[i]->onStatusChange(windows[i], m, friendnumber, status);
}
} }
} }
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort) void on_friend_added(Tox *m, uint32_t friendnumber, bool sort)
{ {
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFriendAdded != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFriendAdded(windows[i], m, friendnumber, sort);
if (windows[i].onFriendAdded != NULL) }
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
} }
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type, void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
const uint8_t *message, size_t length, void *userdata) const uint8_t *message, size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
char msg[MAX_STR_SIZE + 1]; char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupMessage != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onGroupMessage(windows[i], m, groupnumber, peernumber, type, msg, length);
if (windows[i].onGroupMessage != NULL) }
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, type, msg, length);
} }
} }
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key, void on_conference_invite(Tox *m, uint32_t friendnumber, Tox_Conference_Type type, const uint8_t *group_pub_key,
size_t length, void *userdata) size_t length, void *userdata)
{ {
size_t i; UNUSED_VAR(userdata);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL) if (windows[i] != NULL && windows[i]->onGroupInvite != NULL) {
windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length); windows[i]->onGroupInvite(windows[i], m, friendnumber, type, (char *) group_pub_key, length);
}
} }
} }
void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change, void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata)
void *userdata)
{ {
size_t i; UNUSED_VAR(userdata);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNamelistChange != NULL) if (windows[i] != NULL && windows[i]->onGroupNameListChange != NULL) {
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change); windows[i]->onGroupNameListChange(windows[i], m, groupnumber);
}
} }
} }
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length, void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
void *userdata) size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
char nick[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(nick, sizeof(nick), (const char *) name, length);
filter_str(nick, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupPeerNameChange != NULL) {
windows[i]->onGroupPeerNameChange(windows[i], m, groupnumber, peernumber, nick, length);
}
}
}
void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata)
{
UNUSED_VAR(userdata);
char data[MAX_STR_SIZE + 1]; char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) title, length); length = copy_tox_str(data, sizeof(data), (const char *) title, length);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupTitleChange != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onGroupTitleChange(windows[i], m, groupnumber, peernumber, data, length);
if (windows[i].onGroupTitleChange != NULL) }
windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
} }
} }
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
size_t length, void *userdata) size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) if (!ft) {
return; return;
}
if (ft->file_type == TOX_FILE_KIND_AVATAR) { if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_chunk_request(m, ft, position, length); on_avatar_chunk_request(m, ft, position, length);
return; return;
} }
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileChunkRequest != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFileChunkRequest(windows[i], m, friendnumber, filenumber, position, length);
if (windows[i].onFileChunkRequest != NULL) }
windows[i].onFileChunkRequest(&windows[i], m, friendnumber, filenumber, position, length);
} }
} }
void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position,
const uint8_t *data, size_t length, void *user_data) const uint8_t *data, size_t length, void *userdata)
{ {
UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) if (!ft) {
return; return;
}
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileRecvChunk != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFileRecvChunk(windows[i], m, friendnumber, filenumber, position, (char *) data, length);
if (windows[i].onFileRecvChunk != NULL) }
windows[i].onFileRecvChunk(&windows[i], m, friendnumber, filenumber, position, (char *) data, length);
} }
} }
void on_file_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void on_file_recv_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, Tox_File_Control control,
void *userdata) void *userdata)
{ {
UNUSED_VAR(userdata);
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber); struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft) if (!ft) {
return; return;
}
if (ft->file_type == TOX_FILE_KIND_AVATAR) { if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_file_control(m, ft, control); on_avatar_file_control(m, ft, control);
return; return;
} }
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileControl != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFileControl(windows[i], m, friendnumber, filenumber, control);
if (windows[i].onFileControl != NULL) }
windows[i].onFileControl(&windows[i], m, friendnumber, filenumber, control);
} }
} }
void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size, void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t kind, uint64_t file_size,
const uint8_t *filename, size_t filename_length, void *userdata) const uint8_t *filename, size_t filename_length, void *userdata)
{ {
UNUSED_VAR(userdata);
/* We don't care about receiving avatars */ /* We don't care about receiving avatars */
if (kind != TOX_FILE_KIND_DATA) { if (kind != TOX_FILE_KIND_DATA) {
tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL); tox_file_control(m, friendnumber, filenumber, TOX_FILE_CONTROL_CANCEL, NULL);
return; return;
} }
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileRecv != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { windows[i]->onFileRecv(windows[i], m, friendnumber, filenumber, file_size, (char *) filename,
if (windows[i].onFileRecv != NULL) filename_length);
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, file_size, (char *) filename, }
filename_length);
} }
} }
void on_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)
{ {
size_t i; UNUSED_VAR(userdata);
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onReadReceipt != NULL) if (windows[i] != NULL && windows[i]->onReadReceipt != NULL) {
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt); windows[i]->onReadReceipt(windows[i], m, friendnumber, receipt);
}
} }
} }
/* CALLBACKS END */ /* CALLBACKS END */
int add_window(Tox *m, ToxWindow w) int add_window(Tox *m, ToxWindow *w)
{ {
if (LINES < 2) if (LINES < 2) {
return -1; return -1;
}
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; i++) {
if (windows[i] != NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; i++) {
if (windows[i].active)
continue; continue;
}
w.window = newwin(LINES - 2, COLS, 0, 0); w->index = i;
w->window = newwin(LINES - 2, COLS, 0, 0);
if (w.window == NULL) if (w->window == NULL) {
return -1; return -1;
}
#ifdef URXVT_FIX #ifdef URXVT_FIX
/* Fixes text color problem on some terminals. */ /* Fixes text color problem on some terminals. */
wbkgd(w.window, COLOR_PAIR(6)); wbkgd(w->window, COLOR_PAIR(6));
#endif #endif
windows[i] = w; windows[i] = w;
if (w.onInit) if (w->onInit) {
w.onInit(&w, m); w->onInit(w, m);
}
++num_active_windows; ++num_active_windows;
@ -321,42 +358,46 @@ int add_window(Tox *m, ToxWindow w)
return -1; return -1;
} }
void set_active_window(int index) void set_active_window_index(uint8_t index)
{ {
if (index < 0 || index >= MAX_WINDOWS_NUM) if (index < MAX_WINDOWS_NUM) {
return; active_window_index = index;
}
active_window = windows + index;
} }
/* Shows next window when tab or back-tab is pressed */ /* Shows next window when tab or back-tab is pressed */
void set_next_window(int ch) void set_next_window(int ch)
{ {
ToxWindow *end = windows + MAX_WINDOWS_NUM - 1; if (ch == user_settings->key_next_tab) {
ToxWindow *inf = active_window; for (uint8_t i = active_window_index + 1; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL) {
set_active_window_index(i);
return;
}
}
} else {
uint8_t start = active_window_index == 0 ? MAX_WINDOWS_NUM - 1 : active_window_index - 1;
while (true) { for (uint8_t i = start; i > 0; --i) {
if (ch == user_settings->key_next_tab) { if (windows[i] != NULL) {
if (++active_window > end) set_active_window_index(i);
active_window = windows; return;
} else if (--active_window < windows) }
active_window = end; }
if (active_window->window)
return;
if (active_window == inf) /* infinite loop check */
exit_toxic_err("failed in set_next_window", FATALERR_INFLOOP);
} }
set_active_window_index(0);
} }
/* 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(0); /* Go to prompt screen */ set_active_window_index(0);
uint8_t idx = w->index;
delwin(w->window); delwin(w->window);
memset(w, 0, sizeof(ToxWindow)); free(windows[idx]);
windows[idx] = NULL;
clear(); clear();
refresh(); refresh();
@ -365,13 +406,18 @@ void del_window(ToxWindow *w)
ToxWindow *init_windows(Tox *m) ToxWindow *init_windows(Tox *m)
{ {
int n_prompt = add_window(m, new_prompt()); prompt = new_prompt();
int n_prompt = add_window(m, prompt);
if (n_prompt == -1 || add_window(m, new_friendlist()) == -1) if (n_prompt < 0) {
exit_toxic_err("failed in init_windows", FATALERR_WININIT); exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT);
}
prompt = &windows[n_prompt]; if (add_window(m, new_friendlist()) == -1) {
active_window = prompt; exit_toxic_err("add_window() for friendlist failed in init_windows", FATALERR_WININIT);
}
set_active_window_index(n_prompt);
return prompt; return prompt;
} }
@ -387,25 +433,26 @@ void on_window_resize(void)
getmaxyx(stdscr, y2, x2); getmaxyx(stdscr, y2, x2);
y2 -= 2; y2 -= 2;
if (y2 <= 0 || x2 <= 0) if (y2 <= 0 || x2 <= 0) {
return; return;
}
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
continue; continue;
}
ToxWindow *w = &windows[i]; ToxWindow *w = windows[i];
if (windows[i].is_friendlist) { if (windows[i]->is_friendlist) {
delwin(w->window); delwin(w->window);
w->window = newwin(y2, x2, 0, 0); w->window = newwin(y2, x2, 0, 0);
continue; continue;
} }
if (w->help->active) if (w->help->active) {
wclear(w->help->win); wclear(w->help->win);
}
if (w->is_groupchat) { if (w->is_groupchat) {
delwin(w->chatwin->sidebar); delwin(w->chatwin->sidebar);
@ -427,8 +474,9 @@ void on_window_resize(void)
} else { } else {
w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0); w->chatwin->history = subwin(w->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
if (!w->is_groupchat) if (!w->is_groupchat) {
w->stb->topline = subwin(w->window, 2, x2, 0, 0); w->stb->topline = subwin(w->window, 2, x2, 0, 0);
}
} }
#ifdef AUDIO #ifdef AUDIO
@ -438,7 +486,7 @@ void on_window_resize(void)
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
} }
#endif /* AUDIO */ #endif /* AUDIO */
scrollok(w->chatwin->history, 0); scrollok(w->chatwin->history, 0);
} }
@ -448,7 +496,9 @@ static void draw_window_tab(ToxWindow *toxwin)
{ {
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert)); if (toxwin->alert != WINDOW_ALERT_NONE) {
attron(COLOR_PAIR(toxwin->alert));
}
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -457,7 +507,9 @@ static void draw_window_tab(ToxWindow *toxwin)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert)); if (toxwin->alert != WINDOW_ALERT_NONE) {
attroff(COLOR_PAIR(toxwin->alert));
}
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
@ -466,8 +518,14 @@ static void draw_bar(void)
{ {
int y, x; int y, x;
ToxWindow *w = windows[active_window_index];
if (w == NULL) {
return;
}
// save current cursor position // save current cursor position
getyx(active_window->window, y, x); getyx(w->window, y, x);
attron(COLOR_PAIR(BLUE)); attron(COLOR_PAIR(BLUE));
mvhline(LINES - 2, 0, '_', COLS); mvhline(LINES - 2, 0, '_', COLS);
@ -475,31 +533,32 @@ static void draw_bar(void)
move(LINES - 1, 0); move(LINES - 1, 0);
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) {
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
continue; continue;
}
if (windows + i == active_window) if (i == active_window_index) {
#ifdef URXVT_FIX #ifdef URXVT_FIX
attron(A_BOLD | COLOR_PAIR(GREEN)); attron(A_BOLD | COLOR_PAIR(GREEN));
else } else {
#endif #endif
attron(A_BOLD); attron(A_BOLD);
}
draw_window_tab(&windows[i]); draw_window_tab(windows[i]);
if (windows + i == active_window) if (i == active_window_index) {
#ifdef URXVT_FIX #ifdef URXVT_FIX
attroff(A_BOLD | COLOR_PAIR(GREEN)); attroff(A_BOLD | COLOR_PAIR(GREEN));
else } else {
#endif #endif
attroff(A_BOLD); attroff(A_BOLD);
}
} }
// restore cursor position after drawing // restore cursor position after drawing
@ -508,86 +567,179 @@ static void draw_bar(void)
refresh(); refresh();
} }
/*
* Gets current char from stdscr and puts it in ch.
*
* Return 1 if char is printable.
* Return 0 if char is not printable.
* Return -1 on error.
*/
static int get_current_char(wint_t *ch)
{
wint_t tmpchar = 0;
bool is_printable = false;
#ifdef HAVE_WIDECHAR
int status = wget_wch(stdscr, &tmpchar);
if (status == ERR) {
return -1;
}
if (status == OK) {
is_printable = iswprint(tmpchar);
}
#else
tmpchar = getch();
if (tmpchar == ERR) {
return -1;
}
is_printable = isprint(tmpchar);
#endif /* HAVE_WIDECHAR */
*ch = tmpchar;
return (int) is_printable;
}
static struct key_sequence_codes {
wchar_t *code;
wint_t key;
} Keys[] = {
{ L"[1;5A", T_KEY_C_UP },
{ L"[1;5B", T_KEY_C_DOWN },
{ L"[1;5C", T_KEY_C_RIGHT },
{ L"[1;5D", T_KEY_C_LEFT },
{ NULL, 0 }
};
/*
* Return key code corresponding to character sequence queued in stdscr.
* Return -1 if sequence is unknown.
*/
#define MAX_SEQUENCE_SIZE 5
static wint_t get_input_sequence_code(void)
{
wchar_t code[MAX_SEQUENCE_SIZE + 1];
size_t length = 0;
wint_t ch = 0;
for (size_t i = 0; i < MAX_SEQUENCE_SIZE; ++i) {
int res = get_current_char(&ch);
if (res < 0) {
break;
}
++length;
code[i] = (wchar_t) ch;
}
if (length == 0) {
return -1;
}
code[length] = L'\0';
for (size_t i = 0; Keys[i].key != 0; ++i) {
if (wcscmp(code, Keys[i].code) == 0) {
return Keys[i].key;
}
}
return -1;
}
void draw_active_window(Tox *m) void draw_active_window(Tox *m)
{ {
ToxWindow *a = active_window; ToxWindow *a = windows[active_window_index];
if (a == NULL) {
return;
}
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
a->alert = WINDOW_ALERT_NONE; a->alert = WINDOW_ALERT_NONE;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wint_t ch = 0;
draw_bar(); draw_bar();
touchwin(a->window); touchwin(a->window);
a->onDraw(a, m); a->onDraw(a, m);
wrefresh(a->window); wrefresh(a->window);
/* Handle input */ wint_t ch = 0;
bool ltr; int printable = get_current_char(&ch);
#ifdef HAVE_WIDECHAR
int status = wget_wch(stdscr, &ch);
if (status == ERR) if (printable < 0) {
return; return;
if (status == OK)
ltr = iswprint(ch);
else /* if (status == KEY_CODE_YES) */
ltr = false;
#else
ch = getch();
if (ch == ERR)
return;
/* TODO verify if this works */
ltr = isprint(ch);
#endif /* HAVE_WIDECHAR */
if (!ltr && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) {
set_next_window((int) ch);
} else {
pthread_mutex_lock(&Winthread.lock);
a->onKey(a, m, ch, ltr);
pthread_mutex_unlock(&Winthread.lock);
} }
if (printable == 0 && (ch == user_settings->key_next_tab || ch == user_settings->key_prev_tab)) {
set_next_window((int) ch);
return;
} else if (printable == 0 && !a->is_friendlist) {
pthread_mutex_lock(&Winthread.lock);
bool input_ret = a->onKey(a, m, ch, (bool) printable);
pthread_mutex_unlock(&Winthread.lock);
if (input_ret) {
return;
}
// if an unprintable key code is unrecognized by input handler we attempt to manually decode char sequence
wint_t tmp = get_input_sequence_code();
if (tmp != (wint_t) -1) {
ch = tmp;
}
}
pthread_mutex_lock(&Winthread.lock);
a->onKey(a, m, ch, (bool) printable);
pthread_mutex_unlock(&Winthread.lock);
} }
/* refresh inactive windows to prevent scrolling bugs. /* Refresh inactive windows to prevent scrolling bugs.
call at least once per second */ * Call at least once per second.
*/
void refresh_inactive_windows(void) void refresh_inactive_windows(void)
{ {
size_t i; for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
ToxWindow *toxwin = windows[i];
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (toxwin == NULL) {
ToxWindow *a = &windows[i]; continue;
}
if (a->active && a != active_window && !a->is_friendlist) { if (i != active_window_index && !toxwin->is_friendlist) {
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
line_info_print(a); line_info_print(toxwin);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
} }
} }
/* returns a pointer to the ToxWindow in the ith index. Returns NULL if no ToxWindow exists */ /* Returns a pointer to the ToxWindow in the ith index.
ToxWindow *get_window_ptr(int i) * Returns NULL if no ToxWindow exists.
*/
ToxWindow *get_window_ptr(size_t index)
{ {
ToxWindow *toxwin = NULL; if (index >= MAX_WINDOWS_NUM) {
return NULL;
}
if (i >= 0 && i < MAX_WINDOWS_NUM && windows[i].active) return windows[index];
toxwin = &windows[i];
return toxwin;
} }
/* returns a pointer to the currently open ToxWindow. */ /* Returns a pointer to the currently active ToxWindow. */
ToxWindow *get_active_window(void) ToxWindow *get_active_window(void)
{ {
return active_window; return windows[active_window_index];
} }
void force_refresh(WINDOW *w) void force_refresh(WINDOW *w)
@ -605,15 +757,19 @@ int get_num_active_windows(void)
/* destroys all chat and groupchat windows (should only be called on shutdown) */ /* destroys all chat and groupchat windows (should only be called on shutdown) */
void kill_all_windows(Tox *m) void kill_all_windows(Tox *m)
{ {
size_t i; for (uint8_t i = 2; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) {
continue;
}
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { if (windows[i]->is_chat) {
if (windows[i].is_chat) kill_chat_window(windows[i], m);
kill_chat_window(&windows[i], m); } else if (windows[i]->is_groupchat) {
else if (windows[i].is_groupchat) free_groupchat(windows[i], windows[i]->num);
close_groupchat(&windows[i], m, windows[i].num); }
} }
kill_prompt_window(prompt); /* TODO: use enum instead of magic indices */
kill_friendlist(); kill_friendlist(windows[1]);
kill_prompt_window(windows[0]);
} }

View File

@ -36,13 +36,13 @@
#include "toxic.h" #include "toxic.h"
#define MAX_WINDOWS_NUM 32 #define MAX_WINDOWS_NUM 16
#define MAX_WINDOW_NAME_LENGTH 22 #define MAX_WINDOW_NAME_LENGTH 22
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */ #define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 2 #define CHATBOX_HEIGHT 2
/* Curses foreground colours (background is black) */ /* Curses foreground colours (background is black) */
enum { typedef enum {
WHITE, WHITE,
GREEN, GREEN,
CYAN, CYAN,
@ -83,6 +83,7 @@ struct av_thread {
struct arg_opts { struct arg_opts {
bool use_ipv4; bool use_ipv4;
bool force_tcp; bool force_tcp;
bool disable_local_discovery;
bool debug; bool debug;
bool default_locale; bool default_locale;
bool use_custom_data; bool use_custom_data;
@ -94,6 +95,9 @@ struct arg_opts {
char config_path[MAX_STR_SIZE]; char config_path[MAX_STR_SIZE];
char nodes_path[MAX_STR_SIZE]; char nodes_path[MAX_STR_SIZE];
bool logging;
FILE *log_fp;
char proxy_address[256]; char proxy_address[256];
uint8_t proxy_type; uint8_t proxy_type;
uint16_t proxy_port; uint16_t proxy_port;
@ -109,25 +113,26 @@ typedef struct Help Help;
struct ToxWindow { struct ToxWindow {
/* ncurses */ /* ncurses */
void(*onKey)(ToxWindow *, Tox *, wint_t, bool); bool(*onKey)(ToxWindow *, Tox *, wint_t, bool);
void(*onDraw)(ToxWindow *, Tox *); void(*onDraw)(ToxWindow *, Tox *);
void(*onInit)(ToxWindow *, Tox *); void(*onInit)(ToxWindow *, Tox *);
/* toxcore */ /* toxcore */
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, size_t); void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, size_t);
void(*onFriendAdded)(ToxWindow *, Tox *, uint32_t, bool); void(*onFriendAdded)(ToxWindow *, Tox *, uint32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, uint32_t, TOX_CONNECTION); void(*onConnectionChange)(ToxWindow *, Tox *, uint32_t, Tox_Connection);
void(*onMessage)(ToxWindow *, Tox *, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t); void(*onMessage)(ToxWindow *, Tox *, uint32_t, Tox_Message_Type, const char *, size_t);
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS); void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, Tox_User_Status);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t); void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_Message_Type, const char *, size_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_CONFERENCE_STATE_CHANGE); void(*onGroupNameListChange)(ToxWindow *, Tox *, uint32_t);
void(*onGroupPeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t); void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, Tox_File_Control);
void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecv)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
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);
@ -161,7 +166,7 @@ struct ToxWindow {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
uint32_t num; /* corresponds to friendnumber in chat windows */ uint32_t num; /* corresponds to friendnumber in chat windows */
bool active; uint8_t index; /* This window's index in the windows array */
int x; int x;
bool is_chat; bool is_chat;
@ -186,8 +191,8 @@ struct StatusBar {
size_t statusmsg_len; size_t statusmsg_len;
char nick[TOXIC_MAX_NAME_LENGTH + 1]; char nick[TOXIC_MAX_NAME_LENGTH + 1];
size_t nick_len; size_t nick_len;
TOX_USER_STATUS status; Tox_User_Status status;
TOX_CONNECTION connection; Tox_Connection connection;
}; };
#ifdef AUDIO #ifdef AUDIO
@ -251,18 +256,18 @@ struct Help {
ToxWindow *init_windows(Tox *m); ToxWindow *init_windows(Tox *m);
void draw_active_window(Tox *m); void draw_active_window(Tox *m);
int add_window(Tox *m, ToxWindow w); int add_window(Tox *m, ToxWindow *w);
void del_window(ToxWindow *w); void del_window(ToxWindow *w);
void set_active_window(int ch); void set_active_window_index(uint8_t index);
int get_num_active_windows(void); int get_num_active_windows(void);
void kill_all_windows(Tox *m); /* should only be called on shutdown */ void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void); void on_window_resize(void);
void force_refresh(WINDOW *w); void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(int i); ToxWindow *get_window_ptr(size_t i);
ToxWindow *get_active_window(void); ToxWindow *get_active_window(void);
/* refresh inactive windows to prevent scrolling bugs. /* refresh inactive windows to prevent scrolling bugs.
call at least once per second */ call at least once per second */
void refresh_inactive_windows(void); void refresh_inactive_windows(void);
#endif /* #define WINDOWS_H */ #endif /* WINDOWS_H */

View File

@ -21,6 +21,9 @@
*/ */
#include "xtra.h" #include "xtra.h"
#include "misc_tools.h"
#ifndef __APPLE__
#include <X11/Xlib.h> #include <X11/Xlib.h>
#include <X11/Xatom.h> #include <X11/Xatom.h>
@ -47,7 +50,7 @@ static Atom XdndTypeList;
static Atom XdndActionCopy; static Atom XdndActionCopy;
static Atom XdndFinished; static Atom XdndFinished;
struct _Xtra { struct Xtra {
drop_callback on_drop; drop_callback on_drop;
Display *display; Display *display;
Window terminal_window; Window terminal_window;
@ -57,14 +60,14 @@ struct _Xtra {
Atom expecting_type; Atom expecting_type;
} Xtra; } Xtra;
typedef struct _Property { typedef struct Property {
unsigned char *data; unsigned char *data;
int read_format; int read_format;
unsigned long read_num; unsigned long read_num;
Atom read_type; Atom read_type;
} Property; } Property;
Property read_property(Window s, Atom p) static Property read_property(Window s, Atom p)
{ {
Atom read_type; Atom read_type;
int read_format; int read_format;
@ -76,7 +79,9 @@ Property read_property(Window s, Atom p)
/* Keep trying to read the property until there are no bytes unread */ /* Keep trying to read the property until there are no bytes unread */
do { do {
if (data) XFree(data); if (data) {
XFree(data);
}
XGetWindowProperty(Xtra.display, s, XGetWindowProperty(Xtra.display, s,
p, 0, p, 0,
@ -93,12 +98,14 @@ Property read_property(Window s, Atom p)
return property; return property;
} }
Atom get_dnd_type(long *a, int l) static Atom get_dnd_type(long *a, int l)
{ {
int i = 0; int i = 0;
for (; i < l; i ++) { for (; i < l; i ++) {
if (a[i] != XtraNil) return a[i]; /* Get first valid */ if (a[i] != XtraNil) {
return a[i]; /* Get first valid */
}
} }
return XtraNil; return XtraNil;
@ -175,6 +182,8 @@ static void handle_xdnd_drop(XClientMessageEvent *e)
static void handle_xdnd_selection(XSelectionEvent *e) static void handle_xdnd_selection(XSelectionEvent *e)
{ {
UNUSED_VAR(e);
/* DnD succesfully finished, send finished and call callback */ /* DnD succesfully finished, send finished and call callback */
XEvent ev = { XEvent ev = {
.xclient = { .xclient = {
@ -193,10 +202,11 @@ static void handle_xdnd_selection(XSelectionEvent *e)
Property p = read_property(Xtra.proxy_window, XdndSelection); Property p = read_property(Xtra.proxy_window, XdndSelection);
DropType dt; DropType dt;
if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) if (strcmp(XGetAtomName(Xtra.display, p.read_type), "text/uri-list") == 0) {
dt = DT_file_list; dt = DT_file_list;
else /* text/uri-list */ } else { /* text/uri-list */
dt = DT_plain; dt = DT_plain;
}
/* Call callback for every entry */ /* Call callback for every entry */
@ -204,20 +214,25 @@ static void handle_xdnd_selection(XSelectionEvent *e)
char *sptr; char *sptr;
char *str = strtok_r((char *) p.data, "\n\r", &sptr); char *str = strtok_r((char *) p.data, "\n\r", &sptr);
if (str) Xtra.on_drop(str, dt); if (str) {
while ((str = strtok_r(NULL, "\n\r", &sptr)))
Xtra.on_drop(str, dt); Xtra.on_drop(str, dt);
}
while ((str = strtok_r(NULL, "\n\r", &sptr))) {
Xtra.on_drop(str, dt);
}
} }
if (p.data) XFree(p.data); if (p.data) {
XFree(p.data);
}
} }
void *event_loop(void *p) void *event_loop(void *p)
{ {
/* Handle events like a real nigga */ /* Handle events like a real nigga */
(void) p; /* DINDUNOTHIN */ UNUSED_VAR(p); /* DINDUNOTHIN */
XEvent event; XEvent event;
int pending; int pending;
@ -227,7 +242,9 @@ void *event_loop(void *p)
XLockDisplay(Xtra.display); XLockDisplay(Xtra.display);
if ((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event); if ((pending = XPending(Xtra.display))) {
XNextEvent(Xtra.display, &event);
}
if (!pending) { if (!pending) {
XUnlockDisplay(Xtra.display); XUnlockDisplay(Xtra.display);
@ -238,13 +255,22 @@ void *event_loop(void *p)
if (event.type == ClientMessage) { if (event.type == ClientMessage) {
Atom type = event.xclient.message_type; Atom type = event.xclient.message_type;
if (type == XdndEnter) handle_xdnd_enter(&event.xclient); if (type == XdndEnter) {
else if (type == XdndPosition) handle_xdnd_position(&event.xclient); handle_xdnd_enter(&event.xclient);
else if (type == XdndDrop) handle_xdnd_drop(&event.xclient); } else if (type == XdndPosition) {
else if (type == XtraTerminate) break; handle_xdnd_position(&event.xclient);
} else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection); } else if (type == XdndDrop) {
handle_xdnd_drop(&event.xclient);
} else if (type == XtraTerminate) {
break;
}
} else if (event.type == SelectionNotify) {
handle_xdnd_selection(&event.xselection);
}
/* AINNOBODYCANHANDLEDEMEVENTS*/ /* AINNOBODYCANHANDLEDEMEVENTS*/
else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event); else {
XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
}
XUnlockDisplay(Xtra.display); XUnlockDisplay(Xtra.display);
} }
@ -253,28 +279,53 @@ void *event_loop(void *p)
* Please call xtra_terminate() at exit * Please call xtra_terminate() at exit
* otherwise HEWUSAGUDBOI happens * otherwise HEWUSAGUDBOI happens
*/ */
if (Xtra.display) XCloseDisplay(Xtra.display); if (Xtra.display) {
XCloseDisplay(Xtra.display);
}
return (Xtra.display = NULL); return (Xtra.display = NULL);
} }
static long unsigned int focused_window_id(void)
{
if (!Xtra.display) {
return 0;
}
Window focus;
int revert;
XLockDisplay(Xtra.display);
XGetInputFocus(Xtra.display, &focus, &revert);
XUnlockDisplay(Xtra.display);
return focus;
}
int is_focused(void)
{
return Xtra.display && (Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id());
}
int init_xtra(drop_callback d) int init_xtra(drop_callback d)
{ {
memset(&Xtra, 0, sizeof(Xtra)); if (!d) {
return -1;
if (!d) return -1; } else {
else Xtra.on_drop = d; Xtra.on_drop = d;
}
XInitThreads(); XInitThreads();
if ( !(Xtra.display = XOpenDisplay(NULL))) return -1; if (!(Xtra.display = XOpenDisplay(NULL))) {
return -1;
}
Xtra.terminal_window = focused_window_id(); Xtra.terminal_window = focused_window_id();
/* OSX: if focused window is 0, it means toxic is ran from /* OSX: if focused window is 0, it means toxic is ran from
* native terminal and not X11 terminal window, silently exit */ * native terminal and not X11 terminal window, silently exit */
if (!Xtra.terminal_window) if (!Xtra.terminal_window) {
return 0; return 0;
}
{ {
/* Create an invisible window which will act as proxy for the DnD operation. */ /* Create an invisible window which will act as proxy for the DnD operation. */
@ -299,17 +350,18 @@ int init_xtra(drop_callback d)
XDefaultRootWindow(Xtra.display), XDefaultRootWindow(Xtra.display),
&root, &x, &y, &wht, &hht, &b, &d); &root, &x, &y, &wht, &hht, &b, &d);
if (! (Xtra.proxy_window = XCreateWindow if (!(Xtra.proxy_window = XCreateWindow
(Xtra.display, Xtra.terminal_window, /* Parent */ (Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */ 0, 0, /* Position */
wht, hht, /* Width + height */ wht, hht, /* Width + height */
0, /* Border width */ 0, /* Border width */
CopyFromParent, /* Depth */ CopyFromParent, /* Depth */
InputOnly, /* Class */ InputOnly, /* Class */
CopyFromParent, /* Visual */ CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */ CWEventMask | CWCursor, /* Value mask */
&attr)) ) /* Attributes for value mask */ &attr))) { /* Attributes for value mask */
return -1; return -1;
}
} }
XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */ XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */
@ -339,17 +391,20 @@ int init_xtra(drop_callback d)
pthread_t id; pthread_t id;
if (pthread_create(&id, NULL, event_loop, NULL) != 0) if (pthread_create(&id, NULL, event_loop, NULL) != 0) {
return -1; return -1;
}
pthread_detach(id); pthread_detach(id);
return 0; return 0;
} }
void terminate_xtra() void terminate_xtra(void)
{ {
if (!Xtra.display || !Xtra.terminal_window) return; if (!Xtra.display || !Xtra.terminal_window) {
return;
}
XEvent terminate = { XEvent terminate = {
.xclient = { .xclient = {
@ -367,19 +422,4 @@ void terminate_xtra()
while (Xtra.display); /* Wait for termination */ while (Xtra.display); /* Wait for termination */
} }
long unsigned int focused_window_id() #endif /* !__APPLE__ */
{
if (!Xtra.display) return 0;
Window focus;
int revert;
XLockDisplay(Xtra.display);
XGetInputFocus(Xtra.display, &focus, &revert);
XUnlockDisplay(Xtra.display);
return focus;
}
int is_focused()
{
return Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id();
}

Some files were not shown because too many files have changed in this diff Show More