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

Compare commits

...

117 Commits

Author SHA1 Message Date
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
b1d8ab102f Bump to version 0.8.1 2017-11-17 17:44:56 -05:00
0bd5b4ddee Repair operating system checks (#474) 2017-11-16 16:59:51 -05:00
c387df35f8 Merge branch 'f8l-pkgsrc' 2017-11-16 16:03:20 -05:00
351a50c214 Do not assume what is not Linux or a BSD descendant is OSX 2017-11-15 21:02:10 +01:00
93175314b5 Merge branch 'aSourceFish-master' 2017-11-15 02:25:55 -05:00
b905a1a3c5 One more bit of standard... 2017-11-15 05:56:13 +02:00
c4386b195f A bit of standard... 2017-11-15 05:54:46 +02:00
ed1e617380 Make Toxic compile at NetBSD, STAGE 2 2017-11-15 05:50:00 +02:00
1382adb1f6 Make Toxic compile at NetBSD, STAGE 1 2017-11-15 05:46:47 +02:00
ecf1c317b7 Make Toxic compile at NetBSD, STAGE 0 2017-11-15 05:24:19 +02:00
cf0b99f1e5 Merge branch 'aramchandran-master' 2017-11-13 15:37:40 -05:00
3605a296a9 Update install.mk 2017-11-13 13:30:22 -05:00
9375d220f9 Merge branch 'aSourceFish-master' 2017-11-11 16:51:34 -05:00
8f94b0a218 Shut down the warning at *BSD 2017-11-11 21:17:34 +02:00
85a0becbf9 Make Toxic compile at OpenBSD 2017-11-11 21:04:45 +02:00
fec36ad9e6 Make Toxic compile at OpenBSD 2017-11-11 21:03:28 +02:00
ecdf6f01d2 Merge branch 'EnniRosario-refresh_devices' 2017-10-31 16:24:53 -04:00
e1bfa30769 Refresh device list on /lsdev. 2017-10-31 18:08:06 +02:00
ebcbc7497b Indicate selected device when printing. 2017-10-31 17:45:39 +02:00
e844ece28b Merge branch 'zugz-reduceRedrawing' 2017-10-29 16:00:46 -04:00
8508451ba6 avoid unnecessary redrawing 2017-10-29 16:50:42 +01:00
5cc83a7cb5 Merge branch 'patheticpat-fix-tmux' 2017-09-05 17:43:13 -04:00
febc725763 Fix tmux detached detection
The old code failed if the session had a custom name instead of the
default numeric id. To be safe in both cases, look for the session_id in
the tmux list-sessions output.
2017-09-05 12:49:48 +02:00
f2c116feb3 Fix potential string truncations with snprintf 2017-09-01 17:36:17 -04:00
52dd60dc86 Fix potential int truncation and double-check lengths before copy 2017-08-28 19:37:19 -04:00
80c0500299 Fix formatting bug caused by strings containing \r 2017-08-28 18:26:52 -04:00
ab490d28b4 Merge branch 'avoidr-status_cmd_feedback' 2017-08-28 17:13:53 -04:00
a9f7f85617 /status: give feedback to user on status change 2017-08-27 12:27:04 +02:00
1bfc1ba371 Merge branch 'Dako300-master' 2017-07-07 20:03:07 -04:00
2ede39369a added /bitrate 2017-07-07 23:47:59 +00:00
922c184195 Only include python libs when necessary 2017-06-06 19:03:40 -04:00
56a9571509 Make sure message id fits inside a signed int 2017-06-02 01:15:44 -04:00
0136f22076 Fix UI bugs & format
This fixes a bug where lines would sometimes be incorrectly marked as unread, as well as
a bug where inbound messages would sometimes be coloured incorrectly
2017-06-01 16:46:12 -04:00
c4ace288af Bump to v0.8.0 2017-05-24 15:10:50 -04:00
6d3fbfee59 Merge branch 'TsarFox-master' 2017-05-24 14:57:32 -04:00
=
be5e7906da Fix fortune.py input issues 2017-05-24 10:10:18 -04:00
369f26932e Fix error messages being printed when autorun_path is unset. 2017-05-23 19:12:53 -04:00
22ea522baf Add "/run" to autocompleter and fix example Python script. 2017-05-22 20:47:56 -04:00
4f60d546e6 Disable Python support in the default build 2017-05-21 16:18:39 -04:00
76d1eafdc0 Change visibility on Python commands list 2017-05-20 08:18:28 -04:00
37912f2d88 Update INSTALL.md 2017-05-19 21:09:29 -04:00
09710327c5 Merge branch 'Dako300-master' 2017-05-19 20:05:52 -04:00
acee4615f8 double signal handler prevents accidental closes 2017-05-19 14:38:39 -05:00
5ed26eda9b Added constants to the Python interface for calls to execute. 2017-05-19 15:35:31 -04:00
6d2b90ac9f Error reporting on failure of invoke_autoruns and solved issue with call to Py_FinalizeEx 2017-05-18 21:37:28 -04:00
02ea0fac44 get_status returns a string rather than an integer. 2017-05-17 08:39:29 -04:00
7d3d129624 Updated code style 2017-05-16 23:19:39 -04:00
b3ed8bc35c Finalized and documented the Python scripting interface. 2017-05-16 20:31:23 -04:00
90210daca7 Python interface for getting the user's nickname/status and executing commands 2017-05-15 22:12:28 -04:00
0e13a1f1bc Implemented a basic Python scripting engine, optional compilation. 2017-05-15 20:29:00 -04:00
09e2690211 Fixed INSTALL formatting (#443) 2017-04-12 12:00:43 -04:00
e65e3af274 Merge branch 'peetcamron-fix/fflush-for-musl' 2017-01-28 00:45:28 -05:00
c6c60d018e Added some fflush after the printf with no \n so we don't have a blank
screen when compiled with musl.
2017-01-28 00:06:03 -05:00
451d4ced80 Add option that prints toxic and toxcore version 2017-01-25 22:13:59 -05:00
7a7402ff86 Update text files with new toktok toxcore link 2016-12-21 17:08:32 -05:00
101 changed files with 4686 additions and 2342 deletions

1
.gitignore vendored
View File

@ -16,3 +16,4 @@ stamp-h1
build/toxic
build/*.o
build/*.d
apidoc/python/build

37
.travis.yml Normal file
View File

@ -0,0 +1,37 @@
---
language: python
python: nightly
addons:
apt:
packages:
- astyle
- 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))
# astyle
- wget -O ../astyle.tar.gz https://deb.debian.org/debian/pool/main/a/astyle/astyle_2.06.orig.tar.gz
- test -f $HOME/cache/astyle/build/gcc/bin/astyle || (tar -xf ../astyle.tar.gz -C "$HOME/cache" && make -C "$HOME/cache/astyle/build/gcc" -j2)
script:
- $HOME/cache/astyle/build/gcc/bin/astyle --options=astylerc $(find . -name "*.[ch]")
- make ENABLE_PYTHON=1 -j2

30
BUILD.bazel Normal file
View File

@ -0,0 +1,30 @@
load("//tools/project:build_defs.bzl", "project")
project()
cc_binary(
name = "toxic",
srcs = glob([
"src/*.c",
"src/*.h",
]),
copts = [
"-DAUDIO",
"-DPACKAGE_DATADIR='\"data\"'",
"-DPYTHON",
"-DQRCODE",
"-DVIDEO",
"-Wno-error=unused-result",
],
deps = [
"//c-toxcore",
"@curl",
"@libconfig",
"@libqrencode",
"@libvpx",
"@ncurses",
"@openal",
"@python3//:python",
"@x11",
],
)

View File

@ -1,70 +1,66 @@
# Installation
* [Dependencies](#deps)
* [OS X Notes](#deps_osx)
* [Dependencies](#dependencies)
* [OS X Notes](#os-x-notes)
* [Compiling](#compiling)
* [Documentation](#docs)
* [Documentation](#documentation)
* [Notes](#notes)
* [Compilation variables](#comp_vars)
* [Packaging](#packaging)
* [Compilation variables](#compilation-variables)
* [Environment variables](#environment-variables)
<a name="deps" />
## Dependencies
| Name | Needed by | Debian package |
|------------------------------------------------------|----------------------------|---------------------|
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
| [Tox Core](https://github.com/toktok/c-toxcore) | BASE | *None* |
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
| [libqrencode](https://fukuchi.org/works/qrencode/) | QRCODE | libqrencode-dev |
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
<sup>1</sup>: see [Documentation](#docs)
<a name="deps_osx" />
<sup>1</sup>: see [Documentation](#documentation)
#### OS X Notes
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 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.
<a name="Compiling">
## Compiling
```
make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install"
make
sudo make install
```
<a name="docs" />
#### Documentation
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 EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
**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.
<a name="notes" />
## Notes
<a name="comp_vars" />
#### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
* `DISABLE_X11=1`build toxic without X11 support (needed for focus tracking)
* `DISABLE_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1`build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1`build toxic without desktop notifications support
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
* Default compile options can be overridden by using special variables:
* `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
* `DISABLE_AV=1`Disable audio call support
* `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1`Disable desktop notifications support
* `ENABLE_PYTHON=1`Build toxic with Python scripting support
<a name="packaging" />
#### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
#### Environment variables
* You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
* 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
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 -fstack-protector-all
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
CFLAGS += $(USER_CFLAGS)
LDFLAGS = $(USER_LDFLAGS)
CFLAGS += ${USER_CFLAGS}
LDFLAGS ?=
LDFLAGS += ${USER_LDFLAGS}
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
@ -19,23 +21,19 @@ OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
# Check on wich system we are running
UNAME_S = $(shell uname -s)
ifeq ($(UNAME_S), Linux)
-include $(CFG_DIR)/systems/Linux.mk
endif
ifeq ($(UNAME_S), FreeBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), DragonFly)
-include $(CFG_DIR)/systems/FreeBSD.mk
LDFLAGS += -ldl -lrt
endif
ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif
ifeq ($(UNAME_S), NetBSD)
LIBS := $(filter-out ncursesw, $(LIBS))
LDFLAGS += -lncursesw
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif
ifeq ($(UNAME_S), Solaris)
-include $(CFG_DIR)/systems/Solaris.mk
endif
# Check on which platform we are running
UNAME_M = $(shell uname -m)

View File

@ -3,13 +3,12 @@
src="https://scan.coverity.com/projects/4975/badge.svg"/>
</a>
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
Toxic is a [Tox](https://tox.chat)-based instant messenging and video chat client.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)
## Installation
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
[Compile it yourself](/INSTALL.md)
[See the install instructions](/INSTALL.md)
## 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.

20
apidoc/python/Makefile Normal file
View File

@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXPROJ = toxic_api
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,157 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# toxic_api documentation build configuration file, created by
# sphinx-quickstart on Tue May 16 08:58:24 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'toxic_api'
copyright = '2017, Jakob Kreuze'
author = 'Jakob Kreuze'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '0.8.3'
# The full version, including alpha/beta/rc tags.
release = '0.8.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'toxic_apidoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'toxic_api.tex', 'toxic\\_api Documentation',
'Jakob Kreuze', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'toxic_api', 'toxic_api Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'toxic_api', 'toxic_api Documentation',
author, 'toxic_api', 'One line description of project.',
'Miscellaneous'),
]

View File

@ -0,0 +1,8 @@
============
API Examples
============
Fortune
=======
.. literalinclude:: fortune.py
:language: python

View File

@ -0,0 +1,37 @@
import toxic_api
import random
FORTUNES = [
"A bug in the code is worth two in the documentation.",
"A bug in the hand is better than one as yet undetected.",
"\"A debugged program is one for which you have not yet found the "
"conditions that make it fail.\" -- Jerry Ogdin"
]
def send_fortune(args):
"""Callback function that sends the contact of the current window a
given number of random fortunes.
"""
if len(args) != 1:
toxic_api.display("Only one argument allowed!")
return
try:
count = int(args[0])
except ValueError:
toxic_api.display("Argument must be a number!")
return
if count < 0 or count > 20:
toxic_api.display("Argument is too large!")
return
name = toxic_api.get_nick()
toxic_api.send("%s has decided to send you %d fortunes:" % (name, count))
for _ in range(count):
toxic_api.send(random.choice(FORTUNES))
toxic_api.register("/fortune", "Send a fortune to the contact of the current "
"window", send_fortune)

View File

@ -0,0 +1,9 @@
Toxic Scripting Interface Documentation
=======================================
.. toctree::
:maxdepth: 2
intro
reference
examples

View File

@ -0,0 +1,12 @@
=========================
Toxic Scripting Interface
=========================
A Python scripting interface to `Toxic <https://github.com/JFreegman/toxic>`_.
Getting Started
===============
Toxic is compiled with Python support by default. To access the scripting interface, simply import "toxic_api" in your script.
Scripts can be run by issuing "/run <path>" from toxic, or placing them in the "autorun_path" from your toxic configuration file.

View File

@ -0,0 +1,73 @@
=============
API Reference
=============
Messages
========
.. function:: display(msg)
Display a message to the user through the current window.
:param msg: The message to display.
:type msg: string
:rtype: none
.. function:: send(msg)
Send a message to the user specified by the currently open conversation.
:param msg: The message to display.
:type msg: string
:rtype: none
State
=====
.. function:: get_nick()
Return the user's current nickname.
:rtype: string
.. function:: get_status()
Return a string representing the user's current status. Can be either "online", "away", or "busy".
:rtype: string
.. function:: get_status_message()
Return the user's current status message.
:rtype: string
.. function:: get_all_friends()
Return a list of all the user's friends.
:rtype: list of (string, string) tuples containing the nickname followed by their public key
Commands
========
.. function:: execute(command, class)
Executes the given command. The API exports three constants for the class parameter; GLOBAL_COMMAND, CHAT_COMMAND, and GROUPCHAT_COMMAND.
:param command: The command to execute.
:type command: string
:param class: The class of the command.
:type class: int
:rtype: none
.. function:: register(command, help, callback)
Register a callback to be executed whenever command is run. The callback function will be called with one argument, a list of arguments from when the user calls the command.
:param command: The command to listen for.
:type command: string
:param help: A description of the command to be shown in the help menu.
:type help: string
:param callback: The function to be called.
:type callback: callable
:rtype: none

View File

@ -1,11 +1,26 @@
# Bracket Style Options
--style=kr
--pad-header
--max-code-length=120
--convert-tabs
--indent-switches
--pad-oper
--align-pointer=name
--align-reference=name
--preserve-date
--lineend=linux
--break-blocks
# Tab Options
--indent=spaces=4
# Indentation Options
--indent-switches
# Padding Options
--pad-header
--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
AUDIO_LIBS = libtoxav openal
AUDIO_LIBS = openal
AUDIO_CFLAGS = -DAUDIO
ifneq (, $(findstring audio_device.o, $(OBJ)))
AUDIO_OBJ = audio_call.o
@ -18,4 +18,4 @@ else ifneq ($(MAKECMDGOALS), clean)
$(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
endif
endif

View File

@ -13,7 +13,7 @@ ifneq ($(AUDIO), disabled)
endif
# 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 ($(AUDIO), disabled)
ifneq ($(VIDEO), disabled)
@ -34,12 +34,24 @@ ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk
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
QR_PNG = $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
ifneq ($(QR_PNG), disabled)
-include $(CHECKS_DIR)/qr_png.mk
endif
# Check if we want build Python scripting support
PYTHON = $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
ifneq ($(PYTHON), disabled)
-include $(CHECKS_DIR)/python.mk
endif
# Check if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error)

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

@ -0,0 +1,15 @@
# Variables for Python scripting support
PYTHON3_LIBS = python3
PYTHON_CFLAGS = -DPYTHON
PYTHON_OBJ = api.o python_api.o
# Check if we can build Python scripting support
CHECK_PYTHON3_LIBS = $(shell $(PKG_CONFIG) --exists $(PYTHON3_LIBS) || echo -n "error")
ifneq ($(CHECK_PYTHON3_LIBS), error)
LDFLAGS += $(shell python3-config --ldflags)
CFLAGS += $(PYTHON_CFLAGS) $(shell python3-config --includes)
OBJ += $(PYTHON_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
$(warning WARNING -- Toxic will be compiled without Python scripting support)
$(warning WARNING -- You need python3 installed for Python scripting support)
endif

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
VIDEO_LIBS = libtoxav vpx x11
VIDEO_LIBS = vpx x11
VIDEO_CFLAGS = -DVIDEO
ifneq (, $(findstring video_device.o, $(OBJ)))
VIDEO_OBJ = video_call.o

View File

@ -1,5 +1,5 @@
# Version
TOXIC_VERSION = 0.7.2
TOXIC_VERSION = 0.8.3
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
ifneq (, $(findstring error, $(REV)))
VERSION = $(TOXIC_VERSION)
@ -23,10 +23,10 @@ SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
# Install directories
PREFIX = /usr/local
PREFIX ?= /usr/local
BINDIR = $(PREFIX)/bin
DATADIR = $(PREFIX)/share/toxic
MANDIR = $(PREFIX)/share/man
MANDIR ?= $(PREFIX)/share/man
APPDIR = $(PREFIX)/share/applications
# 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))
# 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
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

@ -14,10 +14,22 @@ help:
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support"
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
@echo " 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)\")"
@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

View File

@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
continue ;\
fi ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
file=$$section/$$f ;\
mkdir -p $$section ;\
install -m 0644 $(DOC_DIR)/$$f $$file ;\
@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\
gzip -f -9 $$file ;\
gzip -f -n -9 $$file ;\
done
.PHONY: install

View File

@ -16,7 +16,7 @@ uninstall:
@echo "Removing man pages"
@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 ;\
rm -f $$file $$file.gz ;\
done

View File

@ -1,13 +1,13 @@
'\" t
.\" Title: toxic.conf
.\" Author: [see the "AUTHORS" section]
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
.\" Date: 2016-07-21
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
.\" Date: 2016-09-20
.\" Manual: Toxic Manual
.\" Source: toxic __VERSION__
.\" Language: English
.\"
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual"
.TH "TOXIC\&.CONF" "5" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
@ -227,6 +227,11 @@ Default path for downloads\&. String value\&. Absolute path for downloaded files
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
.RE
.PP
\fBautorun_path\fR
.RS 4
Path for any scripts that should be run on startup
.RE
.PP
\fBchatlogs_path\fR
.RS 4
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.

View File

@ -143,6 +143,9 @@ OPTIONS
*avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
*autorun_path*;;
Path for any scripts that should be run on startup
*chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files.

View File

@ -87,6 +87,9 @@ tox = {
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
// Path for scripts that should be run on startup
// autorun_path="/home/USERNAME/toxic_scripts/";
// Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
};
@ -118,4 +121,3 @@ keys = {
toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T";
};

219
src/api.c Normal file
View File

@ -0,0 +1,219 @@
/* api.c
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <dirent.h>
#include <stdint.h>
#include <tox/tox.h>
#include "execute.h"
#include "friendlist.h"
#include "line_info.h"
#include "message_queue.h"
#include "misc_tools.h"
#include "settings.h"
#include "toxic_strings.h"
#include "windows.h"
#ifdef PYTHON
#include "python_api.h"
Tox *user_tox;
static WINDOW *cur_window;
static ToxWindow *self_window;
extern FriendsList Friends;
extern struct user_settings *user_settings;
void api_display(const char *const msg)
{
if (msg == NULL) {
return;
}
self_window = get_active_window();
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
}
FriendsList api_get_friendslist(void)
{
return Friends;
}
char *api_get_nick(void)
{
size_t len = tox_self_get_name_size(user_tox);
uint8_t *name = malloc(len + 1);
if (name == NULL) {
return NULL;
}
tox_self_get_name(user_tox, name);
name[len] = '\0';
return (char *) name;
}
Tox_User_Status api_get_status(void)
{
return tox_self_get_status(user_tox);
}
char *api_get_status_message(void)
{
size_t len = tox_self_get_status_message_size(user_tox);
uint8_t *status = malloc(len + 1);
if (status == NULL) {
return NULL;
}
tox_self_get_status_message(user_tox, status);
status[len] = '\0';
return (char *) status;
}
void api_send(const char *msg)
{
if (msg == NULL || self_window->chatwin->cqueue == NULL) {
return;
}
char *name = api_get_nick();
char timefrmt[TIME_STR_SIZE];
if (name == NULL) {
return;
}
self_window = get_active_window();
get_time_str(timefrmt, sizeof(timefrmt));
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
add_line_to_hist(self_window->chatwin);
int id = line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg);
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
free(name);
}
void api_execute(const char *input, int mode)
{
self_window = get_active_window();
execute(cur_window, self_window, user_tox, input, mode);
}
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
{
return do_python_command(num_args, args);
}
int num_registered_handlers(void)
{
return python_num_registered_handlers();
}
int help_max_width(void)
{
return python_help_max_width();
}
void draw_handler_help(WINDOW *win)
{
python_draw_handler_help(win);
}
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
FILE *fp;
const char *error_str;
cur_window = window;
self_window = self;
if (argc != 1) {
if (argc < 1) {
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);
return;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
error_str = "Path does not exist.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
return;
}
run_python(fp, argv[1]);
fclose(fp);
}
void invoke_autoruns(WINDOW *window, ToxWindow *self)
{
struct dirent *dir;
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
size_t path_len;
DIR *d;
FILE *fp;
if (user_settings->autorun_path[0] == '\0') {
return;
}
d = opendir(user_settings->autorun_path);
if (d == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
api_display(err_buf);
return;
}
cur_window = window;
self_window = self;
while ((dir = readdir(d)) != NULL) {
path_len = strlen(dir->d_name);
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
fp = fopen(abspath_buf, "r");
if (fp == NULL) {
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
api_display(err_buf);
continue;
}
run_python(fp, abspath_buf);
fclose(fp);
}
}
closedir(d);
}
#endif /* PYTHON */

43
src/api.h Normal file
View File

@ -0,0 +1,43 @@
/* api.h
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef API_H
#define API_H
#include "friendlist.h"
#include "windows.h"
void api_display(const char *const msg);
FriendsList api_get_friendslist(void);
char *api_get_nick(void);
Tox_User_Status api_get_status(void);
char *api_get_status_message(void);
void api_send(const char *msg);
void api_execute(const char *input, int mode);
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
int num_registered_handlers(void);
int help_max_width(void);
void draw_handler_help(WINDOW *win);
void invoke_autoruns(WINDOW *w, ToxWindow *self);
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* API_H */

File diff suppressed because it is too large Load Diff

View File

@ -27,8 +27,6 @@
#include "audio_device.h"
#define MAX_CALLS 10
typedef enum _AudioError {
ae_None = 0,
ae_StartingCaptureDevice = 1 << 0,
@ -65,7 +63,9 @@ struct CallControl {
ToxAV *av;
ToxWindow *prompt;
Call calls[MAX_CALLS];
Call *calls;
uint32_t max_calls;
uint32_t call_state;
bool pending_call;
bool audio_enabled;
@ -79,15 +79,17 @@ struct CallControl {
uint32_t video_bit_rate;
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 */
ToxAV *init_audio(ToxWindow *self, Tox *tox);
void terminate_audio();
void terminate_audio(void);
int start_transmission(ToxWindow *self, Call *call);
int stop_transmission(Call *call, uint32_t friend_number);
void stop_current_call(ToxWindow *self);
void init_friend_AV(uint32_t index);
void del_friend_AV(uint32_t index);
#endif /* AUDIO_CALL_H */

View File

@ -38,8 +38,8 @@
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#include <stdbool.h>
#include <string.h>
@ -97,46 +97,21 @@ void *thread_poll(void *);
#ifdef AUDIO
DeviceError init_devices(ToxAV *av_)
#else
DeviceError init_devices()
DeviceError init_devices(void)
#endif /* AUDIO */
{
const char *stringed_device_list;
size[input] = 0;
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
else
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen( stringed_device_list ) + 1;
}
}
get_devices_names();
// Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0)
if (pthread_mutex_init(&mutex, NULL) != 0) {
return de_InternalError;
}
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;
}
#ifdef AUDIO
av = av_;
@ -145,7 +120,7 @@ DeviceError init_devices()
return (DeviceError) de_None;
}
DeviceError terminate_devices()
DeviceError terminate_devices(void)
{
/* Cleanup if needed */
lock;
@ -154,15 +129,52 @@ DeviceError terminate_devices()
usleep(20000);
if (pthread_mutex_destroy(&mutex) != 0)
if (pthread_mutex_destroy(&mutex) != 0) {
return (DeviceError) de_InternalError;
}
return (DeviceError) de_None;
}
void get_devices_names(void)
{
const char *stringed_device_list;
size[input] = 0;
if ((stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER))) {
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
for (; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input]) {
devices_names[input][size[input]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
}
}
size[output] = 0;
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE) {
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
} else {
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
}
if (stringed_device_list) {
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
for (; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output]) {
devices_names[output][size[output]] = stringed_device_list;
stringed_device_list += strlen(stringed_device_list) + 1;
}
}
}
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;
@ -182,7 +194,9 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
#ifdef AUDIO
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;
@ -203,7 +217,9 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
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;
@ -225,9 +241,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,
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;
@ -240,10 +260,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
if (i == MAX_DEVICES) {
unlock;
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 */
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);
running[type][*device_idx] = running[type][i];
@ -276,7 +298,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
} else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) {
if (!device->dhndl) {
free(device);
running[type][*device_idx] = NULL;
unlock;
@ -293,7 +315,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx
uint16_t zeros[frame_size];
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);
}
@ -319,7 +341,9 @@ DeviceError open_device(DeviceType type, int32_t selection, 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;
Device *device = running[type][device_idx];
@ -332,34 +356,45 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
running[type][device_idx] = NULL;
if ( !device->ref_count ) {
if (!device->ref_count) {
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
if (!alcCaptureCloseDevice(device->dhndl)) {
rc = de_AlError;
}
} else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
if (alcGetCurrentContext() != device->ctx) {
alcMakeContextCurrent(device->ctx);
}
alDeleteSources(1, &device->source);
alDeleteBuffers(OPENAL_BUFS, device->buffers);
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);
} else device->ref_count--;
} else {
device->ref_count--;
}
unlock;
return rc;
}
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD)
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
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;
}
lock;
running[input][device_idx]->cb = callback;
@ -374,11 +409,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,
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];
if (!device || device->muted) return de_DeviceNotActive;
if (!device || device->muted) {
return de_DeviceNotActive;
}
pthread_mutex_lock(device->mutex);
@ -393,8 +432,9 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
} else if (queued < 16) alGenBuffers(1, &bufid);
else {
} else if (queued < 16) {
alGenBuffers(1, &bufid);
} else {
pthread_mutex_unlock(device->mutex);
return de_Busy;
}
@ -407,14 +447,16 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_
ALint 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);
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.
@ -464,7 +506,9 @@ void *thread_poll (void *arg) // TODO: maybe use thread for every input source
continue;
}
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
if (device->cb) {
device->cb(frame, f_size, device->cb_data);
}
}
unlock;
@ -481,8 +525,10 @@ void print_devices(ToxWindow *self, DeviceType type)
{
int i;
for (i = 0; i < size[type]; ++i)
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[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]);
}
return;
}
@ -494,8 +540,9 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
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 running[input][device_idx]->cb_data;
}

View File

@ -52,16 +52,17 @@ typedef enum DeviceError {
de_AlError = -9,
} 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
DeviceError init_devices(ToxAV *av);
#else
DeviceError init_devices();
DeviceError init_devices(void);
#endif /* AUDIO */
DeviceError terminate_devices();
void get_devices_names(void);
DeviceError terminate_devices(void);
/* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,

View File

@ -29,7 +29,7 @@
#include <sys/dir.h>
#else
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#endif /* __APPLE__ */
#include "windows.h"
#include "toxic.h"
@ -38,16 +38,18 @@
#include "execute.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);
}
const char *L = (char *) list;
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, ""); /* formatting */
}
@ -83,49 +85,60 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
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
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, -1 if error */
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/*
* Looks for all instances in list that begin with the last entered word in line according to pos,
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
*
* 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;
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;
}
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 = " ";
char ubuf[MAX_STR_SIZE];
char ubuf[MAX_STR_SIZE] = {0};
/* 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;
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
}
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
snprintf(tmp, sizeof(tmp), "%s", ubuf);
tmp[ctx->pos] = '\0';
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
const char *s = strrchr(tmp, ' ');
char *sub = calloc(1, strlen(ubuf) + 1);
if (sub == NULL)
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
if (sub == NULL) {
exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY);
}
if (!s && !dir_search) {
strcpy(sub, tmp);
if (sub[0] != '/')
if (sub[0] != '/') {
endchrs = ": ";
}
} else if (s) {
strcpy(sub, &s[1]);
@ -133,18 +146,19 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int sub_len = strlen(sub);
int si = char_rfind(sub, '/', sub_len);
if (si || *sub == '/')
if (si || *sub == '/') {
memmove(sub, &sub[si + 1], sub_len - si);
}
}
}
if (!sub[0]) {
free(sub);
return -1;
return 0;
}
int s_len = strlen(sub);
int n_matches = 0;
size_t n_matches = 0;
char matches[n_items][MAX_STR_SIZE];
int i = 0;
@ -153,17 +167,20 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
char str[MAX_CMDNAME_SIZE + 1];
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);
}
}
free(sub);
if (!n_matches)
if (!n_matches) {
return -1;
}
if (!dir_search && n_matches > 1)
if (!dir_search && n_matches > 1) {
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
}
char match[MAX_STR_SIZE];
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
@ -173,10 +190,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
}
if (dir_search) {
if (n_matches == 1)
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
else
if (n_matches == 1) {
endchrs = char_rfind(match, '.', match_len) ? "" : "/";
} else {
endchrs = "";
}
} else if (n_matches > 1) {
endchrs = "";
}
@ -186,8 +204,9 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int strt = ctx->pos - s_len;
int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
if (ctx->len + diff >= MAX_STR_SIZE) {
return -1;
}
char tmpend[MAX_STR_SIZE];
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
@ -197,6 +216,21 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
}
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) { // should never happen
return -1;
}
if (file_type(path_start) == FILE_TYPE_REGULAR) {
endchrs = "";
diff -= n_endchrs;
}
}
strcpy(&ubuf[strt + match_len], endchrs);
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
@ -215,8 +249,18 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
return diff;
}
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
{
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;
@ -224,45 +268,51 @@ static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const cha
get_home_dir(homedir, sizeof(homedir));
char newline[MAX_STR_SIZE];
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen]);
snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
snprintf(path, pathsize, "%s", &newline[cmdlen-1]);
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;
}
int newlen = wcslen(wline);
if (ctx->len + newlen >= MAX_STR_SIZE)
if (ctx->len + newlen >= MAX_STR_SIZE) {
return;
}
wmemcpy(ctx->line, wline, newlen + 1);
ctx->pos = newlen;
ctx->len = ctx->pos;
}
/* 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 */
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
*
* Returns the diff between old len and new len of ctx->line on success.
* Returns -1 if no matches or more than one match.
*/
#define MAX_DIRS 512
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
{
char b_path[MAX_STR_SIZE];
char b_name[MAX_STR_SIZE];
char b_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;
}
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;
}
if (b_path[0] == '~')
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
if (b_path[0] == '~') {
complete_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
}
int si = char_rfind(b_path, '/', strlen(b_path));
@ -280,10 +330,11 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
int b_name_len = strlen(b_name);
DIR *dp = opendir(b_path);
if (dp == NULL)
if (dp == NULL) {
return -1;
}
char dirnames[MAX_DIRS][NAME_MAX];
char dirnames[MAX_DIRS][NAME_MAX + 1];
struct dirent *entry;
int dircount = 0;
@ -297,13 +348,14 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
closedir(dp);
if (dircount == 0)
if (dircount == 0) {
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
#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
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
/*
* Looks for all instances in list that begin with the last entered word in line according to pos,
* then fills line with the complete word. e.g. "Hello jo" would complete the line
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
*
* list is a pointer to the list of strings being compared, n_items is the number of items
* in the list, and size is the size of each item in the list.
*
* 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
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);
/* 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 */
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
* If there is only one match the line is auto-completed.
*
* Returns the diff between old len and new len of ctx->line on success.
* Returns -1 if no matches or more than one match.
*/
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)
{
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,
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
if (Avatar.size == 0)
if (Avatar.size == 0) {
return 0;
}
if (err != TOX_ERR_FILE_SEND_OK) {
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
@ -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);
if (!ft)
if (!ft) {
return -1;
}
ft->file = fopen(Avatar.path, "r");
if (ft->file == NULL)
if (ft->file == NULL) {
return -1;
}
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
ft->file_size = Avatar.size;
@ -86,8 +89,9 @@ static void avatar_send_all(Tox *m)
size_t 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);
}
}
}
@ -98,13 +102,15 @@ static void avatar_send_all(Tox *m)
*/
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;
}
FILE *fp = fopen(path, "rb");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
@ -117,8 +123,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len)
off_t size = file_size(path);
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
if (size == 0 || size > MAX_AVATAR_FILE_SIZE) {
return -1;
}
get_file_name(Avatar.name, sizeof(Avatar.name), path);
Avatar.name_len = strlen(Avatar.name);
@ -142,7 +149,7 @@ void avatar_unset(Tox *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) {
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)
{
if (ft->state != FILE_TRANSFER_STARTED)
if (ft->state != FILE_TRANSFER_STARTED) {
return;
}
if (length == 0) {
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;
}
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);
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);
}
ft->position += send_length;
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 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 */

View File

@ -34,6 +34,7 @@
#include "configdir.h"
#include "curl_util.h"
#include "settings.h"
#include "prompt.h"
extern struct arg_opts arg_opts;
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
* 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.
* 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);
if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) {
if (last_pinged <= 0 || node_is_offline(last_pinged)) {
return -3;
}
@ -556,7 +560,7 @@ static void DHT_bootstrap(Tox *m)
continue;
}
TOX_ERR_BOOTSTRAP err;
Tox_Err_Bootstrap err;
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
if (err != TOX_ERR_BOOTSTRAP_OK) {
@ -577,7 +581,7 @@ static void DHT_bootstrap(Tox *m)
void do_tox_connection(Tox *m)
{
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)) {
DHT_bootstrap(m);

View File

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

View File

@ -51,8 +51,8 @@
#include "audio_call.h"
#ifdef VIDEO
#include "video_call.h"
#endif /* VIDEO */
#endif /* AUDIO */
#endif /* VIDEO */
#endif /* AUDIO */
extern char *DATA_FILE;
extern FriendsList Friends;
@ -63,13 +63,24 @@ extern struct user_settings *user_settings;
#ifdef AUDIO
static void init_infobox(ToxWindow *self);
static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */
#endif /* AUDIO */
#ifdef AUDIO
#define AC_NUM_CHAT_COMMANDS 30
#define AC_NUM_CHAT_COMMANDS_AUDIO 9
#else
#define AC_NUM_CHAT_COMMANDS 22
#define AC_NUM_CHAT_COMMANDS_AUDIO 0
#endif /* AUDIO */
#ifdef PYTHON
#define AC_NUM_CHAT_COMMANDS_PYTHON 1
#else
#define AC_NUM_CHAT_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_CHAT_COMMANDS_QRCODE 1
#else
#define AC_NUM_CHAT_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_CHAT_COMMANDS (21 + AC_NUM_CHAT_COMMANDS_AUDIO + AC_NUM_CHAT_COMMANDS_PYTHON + AC_NUM_CHAT_COMMANDS_QRCODE)
/* Array of chat command names used for tab completion. */
static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -87,7 +98,9 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/join" },
{ "/log" },
{ "/myid" },
#ifdef QRCODE
{ "/myqr" },
#endif /* QRCODE */
{ "/nick" },
{ "/note" },
{ "/nospam" },
@ -106,14 +119,22 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/mute" },
{ "/sense" },
{ "/video" },
{ "/bitrate" },
#endif /* AUDIO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
};
static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
{
if (user_settings->show_typing_self == SHOW_TYPING_OFF)
if (user_settings->show_typing_self == SHOW_TYPING_OFF) {
return;
}
ChatContext *ctx = self->chatwin;
@ -129,9 +150,9 @@ void kill_chat_window(ToxWindow *self, Tox *m)
#ifdef AUDIO
#ifdef VIDEO
stop_video_stream(self);
#endif /* VIDEO */
#endif /* VIDEO */
stop_current_call(self);
#endif /* AUDIO */
#endif /* AUDIO */
kill_all_file_transfers_friend(m, self->num);
log_disable(ctx->log);
@ -159,12 +180,13 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, 0, "%s", msg);
write_to_log(msg, nick, ctx->log, false);
if (self->active_box != -1)
if (self->active_box != -1) {
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
self->active_box, "%s", msg);
else
} else {
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, nick, "%s", msg);
}
}
static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len,
@ -175,18 +197,20 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
if (self->active_box != -1)
if (self->active_box != -1) {
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
self->active_box, "* %s %s", nick, action );
else
self->active_box, "* %s %s", nick, action);
} else {
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, self->name, "* %s %s", nick, action );
&self->active_box, self->name, "* %s %s", nick, action);
}
}
static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len)
static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, Tox_Message_Type type, const char *msg, size_t len)
{
if (self->num != num)
if (self->num != num) {
return;
}
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
@ -194,20 +218,25 @@ static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TY
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
if (type == TOX_MESSAGE_TYPE_NORMAL)
return recv_message_helper(self, m, num, msg, len, nick, timefrmt);
if (type == TOX_MESSAGE_TYPE_NORMAL) {
recv_message_helper(self, m, num, msg, len, nick, timefrmt);
return;
}
if (type == TOX_MESSAGE_TYPE_ACTION)
return recv_action_helper(self, m, num, msg, len, nick, timefrmt);
if (type == TOX_MESSAGE_TYPE_ACTION) {
recv_action_helper(self, m, num, msg, len, nick, timefrmt);
return;
}
}
static void chat_pause_file_transfers(Tox *m, uint32_t friendnum);
static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t fnum);
static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_CONNECTION connection_status)
static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, Tox_Connection connection_status)
{
if (self->num != num)
if (self->num != num) {
return;
}
StatusBar *statusbar = self->stb;
ChatContext *ctx = self->chatwin;
@ -219,7 +248,7 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
char nick[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, nick, num);
TOX_CONNECTION prev_status = statusbar->connection;
Tox_Connection prev_status = statusbar->connection;
statusbar->connection = connection_status;
if (user_settings->show_connection_msg == SHOW_WELCOME_MSG_OFF) {
@ -227,8 +256,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
}
if (prev_status == TOX_CONNECTION_NONE) {
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
? tox_friend_get_typing(m, num, NULL) : false;
chat_resume_file_senders(self, m, num);
msg = "has come online";
@ -237,8 +264,9 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
} else if (connection_status == TOX_CONNECTION_NONE) {
Friends.list[num].is_typing = false;
if (self->chatwin->self_is_typing)
if (self->chatwin->self_is_typing) {
set_self_typingstatus(self, m, 0);
}
chat_pause_file_transfers(m, num);
@ -250,16 +278,18 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
static void chat_onTypingChange(ToxWindow *self, Tox *m, uint32_t num, bool is_typing)
{
if (self->num != num)
if (self->num != num) {
return;
}
Friends.list[num].is_typing = is_typing;
}
static void chat_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const char *nick, size_t length)
{
if (self->num != num)
if (self->num != num) {
return;
}
StatusBar *statusbar = self->stb;
@ -270,10 +300,11 @@ static void chat_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const char
set_window_title(self, statusbar->nick, length);
}
static void chat_onStatusChange(ToxWindow *self, Tox *m, uint32_t num, TOX_USER_STATUS status)
static void chat_onStatusChange(ToxWindow *self, Tox *m, uint32_t num, Tox_User_Status status)
{
if (self->num != num)
if (self->num != num) {
return;
}
StatusBar *statusbar = self->stb;
statusbar->status = status;
@ -281,8 +312,9 @@ static void chat_onStatusChange(ToxWindow *self, Tox *m, uint32_t num, TOX_USER_
static void chat_onStatusMessageChange(ToxWindow *self, uint32_t num, const char *status, size_t length)
{
if (self->num != num)
if (self->num != num) {
return;
}
StatusBar *statusbar = self->stb;
@ -303,11 +335,13 @@ static void chat_pause_file_transfers(Tox *m, uint32_t friendnum)
size_t i;
for (i = 0; i < MAX_FILES; ++i) {
if (friend->file_sender[i].state >= FILE_TRANSFER_STARTED)
if (friend->file_sender[i].state >= FILE_TRANSFER_STARTED) {
friend->file_sender[i].state = FILE_TRANSFER_PAUSED;
}
if (friend->file_receiver[i].state >= FILE_TRANSFER_STARTED)
if (friend->file_receiver[i].state >= FILE_TRANSFER_STARTED) {
friend->file_receiver[i].state = FILE_TRANSFER_PAUSED;
}
}
}
@ -319,10 +353,11 @@ static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t friendnum
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
if (ft->state != FILE_TRANSFER_PAUSED)
if (ft->state != FILE_TRANSFER_PAUSED) {
continue;
}
TOX_ERR_FILE_SEND err;
Tox_Err_File_Send err;
ft->filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_DATA, ft->file_size, ft->file_id,
(uint8_t *) ft->file_name, strlen(ft->file_name), &err);
@ -338,16 +373,19 @@ static void chat_resume_file_senders(ToxWindow *self, Tox *m, uint32_t friendnum
static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
size_t length)
{
if (friendnum != self->num)
if (friendnum != self->num) {
return;
}
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
if (!ft)
if (!ft) {
return;
}
if (ft->state != FILE_TRANSFER_STARTED)
if (ft->state != FILE_TRANSFER_STARTED) {
return;
}
char msg[MAX_STR_SIZE];
@ -383,11 +421,12 @@ static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum,
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);
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
fprintf(stderr, "tox_file_send_chunk failed in chat callback (error %d)\n", err);
}
ft->position += send_length;
ft->bps += send_length;
@ -397,16 +436,19 @@ static void chat_onFileChunkRequest(ToxWindow *self, Tox *m, uint32_t friendnum,
static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t position,
const char *data, size_t length)
{
if (friendnum != self->num)
if (friendnum != self->num) {
return;
}
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
if (!ft)
if (!ft) {
return;
}
if (ft->state != FILE_TRANSFER_STARTED)
if (ft->state != FILE_TRANSFER_STARTED) {
return;
}
char msg[MAX_STR_SIZE];
@ -434,15 +476,17 @@ static void chat_onFileRecvChunk(ToxWindow *self, Tox *m, uint32_t friendnum, ui
ft->last_keep_alive = get_unix_time();
}
static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, TOX_FILE_CONTROL control)
static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, Tox_File_Control control)
{
if (friendnum != self->num)
if (friendnum != self->num) {
return;
}
struct FileTransfer *ft = get_file_transfer_struct(friendnum, filenum);
if (!ft)
if (!ft) {
return;
}
char msg[MAX_STR_SIZE];
@ -489,8 +533,9 @@ static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, u
char msg[MAX_STR_SIZE];
uint8_t file_id[TOX_FILE_ID_LENGTH];
if (!tox_file_get_file_id(m, friendnum, filenum, file_id, NULL))
if (!tox_file_get_file_id(m, friendnum, filenum, file_id, NULL)) {
return false;
}
bool resuming = false;
struct FileTransfer *ft = NULL;
@ -499,8 +544,9 @@ static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, u
for (i = 0; i < MAX_FILES; ++i) {
ft = &Friends.list[friendnum].file_receiver[i];
if (ft->state == FILE_TRANSFER_INACTIVE)
if (ft->state == FILE_TRANSFER_INACTIVE) {
continue;
}
if (memcmp(ft->file_id, file_id, TOX_FILE_ID_LENGTH) == 0) {
ft->filenum = filenum;
@ -511,14 +557,17 @@ static bool chat_resume_broken_ft(ToxWindow *self, Tox *m, uint32_t friendnum, u
}
}
if (!resuming || !ft)
if (!resuming || !ft) {
return false;
}
if (!tox_file_seek(m, ft->friendnum, ft->filenum, ft->position, NULL))
if (!tox_file_seek(m, ft->friendnum, ft->filenum, ft->position, NULL)) {
goto on_error;
}
if (!tox_file_control(m, ft->friendnum, ft->filenum, TOX_FILE_CONTROL_RESUME, NULL))
if (!tox_file_control(m, ft->friendnum, ft->filenum, TOX_FILE_CONTROL_RESUME, NULL)) {
goto on_error;
}
return true;
@ -531,12 +580,14 @@ on_error:
static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_t filenum, uint64_t file_size,
const char *filename, size_t name_length)
{
if (self->num != friendnum)
if (self->num != friendnum) {
return;
}
/* first check if we need to resume a broken transfer */
if (chat_resume_broken_ft(self, m, friendnum, filenum))
if (chat_resume_broken_ft(self, m, friendnum, filenum)) {
return;
}
struct FileTransfer *ft = new_file_transfer(self, friendnum, filenum, FILE_TRANSFER_RECV, TOX_FILE_KIND_DATA);
@ -550,7 +601,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
bytes_convert_str(sizestr, sizeof(sizestr), file_size);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
char file_path[MAX_STR_SIZE];
char file_path[PATH_MAX + name_length + 1];
size_t path_len = name_length;
/* use specified download path in config if possible */
@ -601,27 +652,31 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
snprintf(ft->file_name, sizeof(ft->file_name), "%s", filename);
tox_file_get_file_id(m, friendnum, filenum, ft->file_id, NULL);
if (self->active_box != -1)
if (self->active_box != -1) {
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
self->active_box, "Incoming file: %s", filename );
else
self->active_box, "Incoming file: %s", filename);
} else {
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans,
&self->active_box, self->name, "Incoming file: %s", filename );
&self->active_box, self->name, "Incoming file: %s", filename);
}
}
static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key,
uint16_t length)
{
if (self->num != friendnumber)
if (self->num != friendnumber) {
return;
}
if (Friends.list[friendnumber].group_invite.key != NULL)
if (Friends.list[friendnumber].group_invite.key != NULL) {
free(Friends.list[friendnumber].group_invite.key);
}
char *k = malloc(length);
if (k == NULL)
if (k == NULL) {
exit_toxic_err("Failed in chat_onGroupInvite", FATALERR_MEMORY);
}
memcpy(k, group_pub_key, length);
Friends.list[friendnumber].group_invite.key = k;
@ -634,10 +689,11 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui
char name[TOX_MAX_NAME_LENGTH];
get_nick_truncate(m, name, friendnumber);
if (self->active_box != -1)
if (self->active_box != -1) {
box_silent_notify2(self, NT_WNDALERT_2 | NT_NOFOCUS, self->active_box, "invites you to join group chat");
else
} else {
box_silent_notify(self, NT_WNDALERT_2 | NT_NOFOCUS, &self->active_box, name, "invites you to join group chat");
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s has invited you to a group chat.", name);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type \"/join\" to join the chat.");
@ -646,44 +702,50 @@ static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, ui
/* AV Stuff */
#ifdef AUDIO
void chat_onInvite (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onInvite(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
/* call is flagged active here */
self->is_call = true;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Incoming audio call! Type: \"/answer\" or \"/reject\"");
if (self->ringing_sound == -1)
if (self->ringing_sound == -1) {
sound_notify(self, call_incoming, NT_LOOP | user_settings->bell_on_invite, &self->ringing_sound);
}
if (self->active_box != -1)
if (self->active_box != -1) {
box_silent_notify2(self, NT_NOFOCUS | NT_WNDALERT_0, self->active_box, "Incoming audio call!");
else
} else {
box_silent_notify(self, NT_NOFOCUS | NT_WNDALERT_0, &self->active_box, self->name, "Incoming audio call!");
}
}
void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onRinging(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
#ifdef SOUND_NOTIFY
if (self->ringing_sound == -1)
if (self->ringing_sound == -1) {
sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound);
}
#endif /* SOUND_NOTIFY */
}
void chat_onStarting (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onStarting(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
init_infobox(self);
@ -697,10 +759,11 @@ void chat_onStarting (ToxWindow *self, ToxAV *av, uint32_t friend_number, int st
#endif /* SOUND_NOTIFY */
}
void chat_onEnding (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onEnding(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
@ -712,10 +775,11 @@ void chat_onEnding (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat
#endif /* SOUND_NOTIFY */
}
void chat_onError (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onError(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
self->is_call = false;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Error!");
@ -725,10 +789,11 @@ void chat_onError (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
#endif /* SOUND_NOTIFY */
}
void chat_onStart (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onStart(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
/* call is flagged active here */
self->is_call = true;
@ -742,10 +807,11 @@ void chat_onStart (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state
#endif /* SOUND_NOTIFY */
}
void chat_onCancel (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onCancel(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
self->is_call = false;
kill_infobox(self);
@ -756,10 +822,11 @@ void chat_onCancel (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat
#endif /* SOUND_NOTIFY */
}
void chat_onReject (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onReject(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Rejected!");
self->is_call = false;
@ -769,10 +836,11 @@ void chat_onReject (ToxWindow *self, ToxAV *av, uint32_t friend_number, int stat
#endif /* SOUND_NOTIFY */
}
void chat_onEnd (ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
void chat_onEnd(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
if (!self || self->num != friend_number)
if (!self || self->num != friend_number) {
return;
}
kill_infobox(self);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Call ended!");
@ -790,8 +858,9 @@ static void init_infobox(ToxWindow *self)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
(void) y2;
@ -808,8 +877,9 @@ static void kill_infobox(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (!ctx->infobox.win)
if (!ctx->infobox.win) {
return;
}
delwin(ctx->infobox.win);
memset(&ctx->infobox, 0, sizeof(struct infobox));
@ -820,20 +890,23 @@ static void draw_infobox(ToxWindow *self)
{
struct infobox *infobox = &self->chatwin->infobox;
if (infobox->win == NULL)
if (infobox->win == NULL) {
return;
}
int x2, y2;
getmaxyx(self->window, y2, x2);
if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT)
if (x2 < INFOBOX_WIDTH || y2 < INFOBOX_HEIGHT) {
return;
}
time_t curtime = get_unix_time();
/* update elapsed time string once per second */
if (curtime > infobox->lastupdate)
if (curtime > infobox->lastupdate) {
get_elapsed_time_str(infobox->timestr, sizeof(infobox->timestr), curtime - infobox->starttime);
}
infobox->lastupdate = curtime;
@ -863,7 +936,7 @@ static void draw_infobox(ToxWindow *self)
wattron(infobox->win, A_BOLD);
wprintw(infobox->win, " VAD level: ");
wattroff(infobox->win, A_BOLD);
wprintw(infobox->win, "%.2f\n", infobox->vad_lvl);
wprintw(infobox->win, "%.2f\n", (double) infobox->vad_lvl);
wborder(infobox->win, ACS_VLINE, ' ', ACS_HLINE, ACS_HLINE, ACS_TTEE, ' ', ACS_LLCORNER, ' ');
wnoutrefresh(infobox->win);
@ -873,8 +946,9 @@ static void draw_infobox(ToxWindow *self)
static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
{
if (action == NULL)
if (action == NULL) {
return;
}
char selfname[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) selfname);
@ -885,8 +959,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1);
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id);
}
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
@ -898,11 +972,13 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
if (ctx->pastemode && key == '\r')
if (ctx->pastemode && key == '\r') {
key = '\n';
}
if (self->help->active) {
help_onKey(self, key);
@ -912,14 +988,16 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE)
if (ctx->line[0] != '/' && !ctx->self_is_typing && statusbar->connection != TOX_CONNECTION_NONE) {
set_self_typingstatus(self, m, 1);
}
return;
}
if (line_info_onKey(self, key))
if (line_info_onKey(self, key)) {
return;
}
input_handle(self, key, x, y, x2, y2);
@ -927,11 +1005,20 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
int diff = -1;
/* TODO: make this not suck */
if (wcsncmp(ctx->line, L"/sendfile \"", wcslen(L"/sendfile \"")) == 0) {
if (wcsncmp(ctx->line, L"/sendfile ", wcslen(L"/sendfile ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/sendfile");
} 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");
} else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
}
#ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run");
}
#endif
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = {
{"online"},
{"away"},
@ -961,8 +1048,9 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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));
}
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
@ -983,8 +1071,8 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
}
}
@ -993,8 +1081,9 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
reset_buf(ctx);
}
if (ctx->len <= 0 && ctx->self_is_typing)
if (ctx->len <= 0 && ctx->self_is_typing) {
set_self_typingstatus(self, m, 0);
}
}
static void chat_onDraw(ToxWindow *self, Tox *m)
@ -1002,8 +1091,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
ChatContext *ctx = self->chatwin;
@ -1015,8 +1105,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
curs_set(1);
if (ctx->len > 0)
if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
/* Draw status bar */
StatusBar *statusbar = self->stb;
@ -1026,7 +1117,7 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
/* Draw name, status and note in statusbar */
if (statusbar->connection != TOX_CONNECTION_NONE) {
int colour = MAGENTA;
TOX_USER_STATUS status = statusbar->status;
Tox_User_Status status = statusbar->status;
switch (status) {
case TOX_USER_STATUS_NONE:
@ -1046,15 +1137,17 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
wprintw(statusbar->topline, " %s", ONLINE_CHAR);
wattroff(statusbar->topline, COLOR_PAIR(colour) | A_BOLD);
if (Friends.list[self->num].is_typing)
if (Friends.list[self->num].is_typing) {
wattron(statusbar->topline, COLOR_PAIR(YELLOW));
}
wattron(statusbar->topline, A_BOLD);
wprintw(statusbar->topline, " %s ", statusbar->nick);
wattroff(statusbar->topline, A_BOLD);
if (Friends.list[self->num].is_typing)
if (Friends.list[self->num].is_typing) {
wattroff(statusbar->topline, COLOR_PAIR(YELLOW));
}
} else {
wprintw(statusbar->topline, " %s", OFFLINE_CHAR);
wattron(statusbar->topline, A_BOLD);
@ -1087,8 +1180,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
if (statusbar->statusmsg[0]) {
wprintw(statusbar->topline, ": %s ", statusbar->statusmsg);
}
wclrtoeol(statusbar->topline);
wmove(statusbar->topline, 0, x2 - (KEY_IDENT_DIGITS * 2) - 3);
@ -1096,8 +1190,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
size_t i;
for (i = 0; i < KEY_IDENT_DIGITS; ++i)
for (i = 0; i < KEY_IDENT_DIGITS; ++i) {
wprintw(statusbar->topline, "%02X", Friends.list[self->num].pub_key[i] & 0xff);
}
wprintw(statusbar->topline, "}\n");
@ -1119,8 +1214,9 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
#endif
if (self->help->active)
if (self->help->active) {
help_onDraw(self);
}
pthread_mutex_lock(&Winthread.lock);
refresh_file_transfer_progress(self, m, self->num);
@ -1133,16 +1229,16 @@ static void chat_onInit(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
exit_toxic_err("failed in chat_onInit", FATALERR_CURSES);
}
self->x = x2;
/* Init statusbar info */
StatusBar *statusbar = self->stb;
statusbar->status = tox_friend_get_status(m, self->num, NULL);
statusbar->connection = tox_friend_get_connection_status(m, self->num, NULL);
statusbar->status = get_friend_status(self->num);
statusbar->connection = get_friend_connection_status(self->num);
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
tox_friend_get_status_message(m, self->num, (uint8_t *) statusmsg, NULL);
@ -1170,8 +1266,9 @@ static void chat_onInit(ToxWindow *self, Tox *m)
ctx->log = calloc(1, sizeof(struct chatlog));
ctx->cqueue = calloc(1, sizeof(struct chat_queue));
if (ctx->log == NULL || ctx->hst == NULL || ctx->cqueue == NULL)
if (ctx->log == NULL || ctx->hst == NULL || ctx->cqueue == NULL) {
exit_toxic_err("failed in chat_onInit", FATALERR_MEMORY);
}
line_info_init(ctx->hst);
@ -1181,10 +1278,11 @@ static void chat_onInit(ToxWindow *self, Tox *m)
int log_ret = log_enable(nick, myid, Friends.list[self->num].pub_key, ctx->log, LOG_CHAT);
load_chat_history(self, ctx->log);
if (!Friends.list[self->num].logging_on)
if (!Friends.list[self->num].logging_on) {
log_disable(ctx->log);
else if (log_ret == -1)
} else if (log_ret == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
@ -1192,64 +1290,67 @@ static void chat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
ToxWindow new_chat(Tox *m, uint32_t friendnum)
ToxWindow *new_chat(Tox *m, uint32_t friendnum)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
ret.active = true;
ret.is_chat = true;
if (ret == NULL) {
exit_toxic_err("failed in new_chat", FATALERR_MEMORY);
}
ret.onKey = &chat_onKey;
ret.onDraw = &chat_onDraw;
ret.onInit = &chat_onInit;
ret.onMessage = &chat_onMessage;
ret.onConnectionChange = &chat_onConnectionChange;
ret.onTypingChange = & chat_onTypingChange;
ret.onGroupInvite = &chat_onGroupInvite;
ret.onNickChange = &chat_onNickChange;
ret.onStatusChange = &chat_onStatusChange;
ret.onStatusMessageChange = &chat_onStatusMessageChange;
ret.onFileChunkRequest = &chat_onFileChunkRequest;
ret.onFileRecvChunk = &chat_onFileRecvChunk;
ret.onFileControl = &chat_onFileControl;
ret.onFileRecv = &chat_onFileRecv;
ret.onReadReceipt = &chat_onReadReceipt;
ret->is_chat = true;
ret->onKey = &chat_onKey;
ret->onDraw = &chat_onDraw;
ret->onInit = &chat_onInit;
ret->onMessage = &chat_onMessage;
ret->onConnectionChange = &chat_onConnectionChange;
ret->onTypingChange = & chat_onTypingChange;
ret->onGroupInvite = &chat_onGroupInvite;
ret->onNickChange = &chat_onNickChange;
ret->onStatusChange = &chat_onStatusChange;
ret->onStatusMessageChange = &chat_onStatusMessageChange;
ret->onFileChunkRequest = &chat_onFileChunkRequest;
ret->onFileRecvChunk = &chat_onFileRecvChunk;
ret->onFileControl = &chat_onFileControl;
ret->onFileRecv = &chat_onFileRecv;
ret->onReadReceipt = &chat_onReadReceipt;
#ifdef AUDIO
ret.onInvite = &chat_onInvite;
ret.onRinging = &chat_onRinging;
ret.onStarting = &chat_onStarting;
ret.onEnding = &chat_onEnding;
ret.onError = &chat_onError;
ret.onStart = &chat_onStart;
ret.onCancel = &chat_onCancel;
ret.onReject = &chat_onReject;
ret.onEnd = &chat_onEnd;
ret->onInvite = &chat_onInvite;
ret->onRinging = &chat_onRinging;
ret->onStarting = &chat_onStarting;
ret->onEnding = &chat_onEnding;
ret->onError = &chat_onError;
ret->onStart = &chat_onStart;
ret->onCancel = &chat_onCancel;
ret->onReject = &chat_onReject;
ret->onEnd = &chat_onEnd;
ret.is_call = false;
ret.device_selection[0] = ret.device_selection[1] = -1;
ret.ringing_sound = -1;
ret->is_call = false;
ret->device_selection[0] = ret->device_selection[1] = -1;
ret->ringing_sound = -1;
#endif /* AUDIO */
ret.active_box = -1;
ret->active_box = -1;
char nick[TOX_MAX_NAME_LENGTH];
size_t n_len = get_nick_truncate(m, nick, friendnum);
set_window_title(&ret, nick, n_len);
set_window_title(ret, nick, n_len);
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
StatusBar *stb = calloc(1, sizeof(StatusBar));
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_chat", FATALERR_MEMORY);
}
ret.chatwin = chatwin;
ret.stb = stb;
ret.help = help;
ret->chatwin = chatwin;
ret->stb = stb;
ret->help = help;
ret.num = friendnum;
ret->num = friendnum;
return ret;
}

View File

@ -30,6 +30,6 @@
set msg to NULL if we don't want to display a message */
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
void kill_chat_window(ToxWindow *self, 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 */

View File

@ -92,7 +92,7 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
return;
}
TOX_ERR_CONFERENCE_INVITE err;
Tox_Err_Conference_Invite err;
if (!tox_conference_invite(m, self->num, groupnum, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
@ -123,9 +123,9 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
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) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
@ -167,16 +167,17 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
}
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);
return;
}
TOX_ERR_FILE_CONTROL err;
Tox_Err_File_Control 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;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
@ -224,16 +225,9 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
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];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int path_len = strlen(path) - 1;
path[path_len] = '\0';
snprintf(path, sizeof(path), "%s", argv[1]);
int path_len = strlen(path);
if (path_len >= MAX_STR_SIZE) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
@ -258,12 +252,13 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
char file_name[TOX_MAX_FILENAME_LENGTH];
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,
(uint8_t *) file_name, namelen, &err);
if (err != TOX_ERR_FILE_SEND_OK)
if (err != TOX_ERR_FILE_SEND_OK) {
goto on_send_error;
}
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);

View File

@ -41,6 +41,7 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* AUDIO */
#ifdef VIDEO
@ -48,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]);
#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 {
hmstr = getenv("HOME");
if (hmstr == NULL)
if (hmstr == NULL) {
return;
}
snprintf(buf, sizeof(buf), "%s", hmstr);
hmstr = buf;
@ -77,8 +78,9 @@ char *get_user_config_dir(void)
len = strlen(home) + strlen("/Library/Application Support") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL)
if (user_config_dir == NULL) {
return NULL;
}
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
# else /* __APPLE__ */
@ -89,8 +91,9 @@ char *get_user_config_dir(void)
len = strlen(home) + strlen("/.config") + 1;
user_config_dir = malloc(len);
if (user_config_dir == NULL)
if (user_config_dir == NULL) {
return NULL;
}
snprintf(user_config_dir, len, "%s/.config", home);
} else {
@ -112,14 +115,16 @@ int create_user_config_dirs(char *path)
struct stat buf;
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;
}
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 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);
}
strcpy(fullpath, path);
strcat(fullpath, CONFIGDIR);

View File

@ -53,4 +53,4 @@ void get_home_dir(char *home, int size);
*/
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)
{
if (proxy_type == TOX_PROXY_TYPE_NONE)
if (proxy_type == TOX_PROXY_TYPE_NONE) {
return 0;
}
if (proxy_address == NULL || port == 0) {
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);
#endif /* CURL_UTIL_H */
#endif /* CURL_UTIL_H */

View File

@ -33,6 +33,7 @@
#include "line_info.h"
#include "misc_tools.h"
#include "notify.h"
#include "api.h"
struct cmd_func {
const char *name;
@ -51,7 +52,9 @@ static struct cmd_func global_commands[] = {
{ "/help", cmd_prompt_help },
{ "/log", cmd_log },
{ "/myid", cmd_myid },
#ifdef QRCODE
{ "/myqr", cmd_myqr },
#endif /* QRCODE */
{ "/nick", cmd_nick },
{ "/note", cmd_note },
{ "/nospam", cmd_nospam },
@ -65,8 +68,11 @@ static struct cmd_func global_commands[] = {
#endif /* AUDIO */
#ifdef VIDEO
{ "/lsvdev", cmd_list_video_devices },
{ "/svdev" , cmd_change_video_device },
{ "/svdev", cmd_change_video_device },
#endif /* VIDEO */
#ifdef PYTHON
{ "/run", cmd_run },
#endif /* PYTHON */
{ NULL, NULL },
};
@ -83,6 +89,7 @@ static struct cmd_func chat_commands[] = {
{ "/hangup", cmd_hangup },
{ "/mute", cmd_mute },
{ "/sense", cmd_sense },
{ "/bitrate", cmd_bitrate },
#endif /* AUDIO */
#ifdef VIDEO
{ "/video", cmd_video },
@ -100,52 +107,104 @@ static struct cmd_func group_commands[] = {
{ NULL, NULL },
};
#ifdef PYTHON
#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 */
"/title",
"/sendfile",
};
/* 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(WINDOW *w, ToxWindow *self, 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 number of arguments on success, -1 on failure. */
*
* Returns the number of arguments.
*/
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
{
if (is_special_command(input)) {
return parse_special_command(w, self, input, args);
}
char *cmd = strdup(input);
if (cmd == NULL)
if (cmd == NULL) {
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
}
int num_args = 0;
int i = 0; /* index of last char in an argument */
/* characters wrapped in double quotes count as one arg */
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 == '\"') {
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 */
if (cmd[i] == '\0') { // no more args
break;
}
char tmp[MAX_STR_SIZE];
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);
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,
char (*args)[MAX_STR_SIZE])
{
@ -163,35 +222,49 @@ 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)
{
if (string_is_empty(input))
if (string_is_empty(input)) {
return;
}
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
int num_args = parse_command(w, self, input, args);
if (num_args == -1)
if (num_args <= 0) {
return;
}
/* 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.
Note: Global commands must come last in case of duplicate command names */
* try specified mode's commands first, then upon failure try global commands.
*
* Note: Global commands must come last in case of duplicate command names
*/
switch (mode) {
case CHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
return;
}
break;
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;
}
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;
}
#ifdef PYTHON
if (do_plugin_command(num_args, args) == 0) {
return;
}
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
}

View File

@ -36,4 +36,4 @@ enum {
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% [");
int i;
for (i = 0; i < NUM_PROG_MARKS; ++i)
for (i = 0; i < NUM_PROG_MARKS; ++i) {
strcat(progline, "-");
}
strcat(progline, "] 0.0 B/s");
}
@ -54,8 +55,9 @@ void init_progress_bar(char *progline)
/* prints a progress bar for file transfers. */
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;
}
char pct_str[24];
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 i, j;
for (i = 0; i < n; ++i)
for (i = 0; i < n; ++i) {
strcat(prog_line, "=");
}
if (pct_done < 100)
if (pct_done < 100) {
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, "-");
}
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);
@ -84,12 +89,14 @@ void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t l
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
{
if (ft->state == FILE_TRANSFER_INACTIVE)
if (ft->state == FILE_TRANSFER_INACTIVE) {
return;
}
/* 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;
}
double remain = ft->file_size - ft->position;
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
@ -120,13 +127,15 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
for (i = 0; i < MAX_FILES; ++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;
}
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 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,
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
return NULL;
}
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_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
return ft;
}
}
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,
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);
}
if (direction == FILE_TRANSFER_SEND)
if (direction == FILE_TRANSFER_SEND) {
return new_file_sender(window, friendnum, filenum, type);
}
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,
Notification sound_type)
{
if (!ft)
if (!ft) {
return;
}
if (ft->state == FILE_TRANSFER_INACTIVE)
if (ft->state == FILE_TRANSFER_INACTIVE) {
return;
}
if (ft->file)
if (ft->file) {
fclose(ft->file);
}
if (CTRL >= 0)
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
if (CTRL >= 0) {
tox_file_control(m, ft->friendnum, ft->filenum, (Tox_File_Control) CTRL, NULL);
}
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);
else
} else {
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);
}
@ -272,6 +290,7 @@ void kill_all_file_transfers(Tox *m)
{
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);
}
}

View File

@ -108,4 +108,4 @@ void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
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));
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);
}
Friends.list = f;
Friends.index = f_idx;
@ -104,24 +105,26 @@ static void realloc_blocklist(int n)
BlockedFriend *b = realloc(Blocked.list, n * sizeof(BlockedFriend));
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);
}
Blocked.list = b;
Blocked.index = b_idx;
}
void kill_friendlist(void)
void kill_friendlist(ToxWindow *self)
{
int i;
for (i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL)
for (size_t i = 0; i < Friends.max_idx; ++i) {
if (Friends.list[i].active && Friends.list[i].group_invite.key != NULL) {
free(Friends.list[i].group_invite.key);
}
}
realloc_blocklist(0);
realloc_friends(0);
free(self->help);
del_window(self);
}
/* Saves the blocklist to path. If there are no items in the blocklist the
@ -153,10 +156,14 @@ static int save_blocklist(char *path)
}
if (Blocked.list[i].active) {
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
continue;
}
BlockedFriend tmp;
memset(&tmp, 0, sizeof(BlockedFriend));
tmp.namelength = htons(Blocked.list[i].namelength);
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1); // Include null byte
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)];
@ -211,13 +218,15 @@ static void sort_blocklist_index(void);
int load_blocklist(char *path)
{
if (path == NULL)
if (path == NULL) {
return -1;
}
FILE *fp = fopen(path, "rb");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
off_t len = file_size(path);
@ -250,10 +259,15 @@ int load_blocklist(char *path)
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
Blocked.list[i].namelength = ntohs(tmp.namelength);
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
continue;
}
Blocked.list[i].active = true;
Blocked.list[i].num = i;
Blocked.list[i].namelength = MIN(TOXIC_MAX_NAME_LENGTH, ntohs(tmp.namelength));
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1); // copy null byte
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)];
@ -289,8 +303,9 @@ void sort_friendlist_index(void)
uint32_t n = 0;
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;
}
}
qsort(Friends.index, Friends.num_friends, sizeof(uint32_t), index_name_cmp);
@ -307,8 +322,9 @@ static void sort_blocklist_index(void)
uint32_t n = 0;
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;
}
}
qsort(Blocked.index, Blocked.num_blocked, sizeof(uint32_t), index_name_cmp_block);
@ -325,14 +341,16 @@ static void update_friend_last_online(uint32_t num, time_t timestamp)
&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)
{
if (num >= Friends.max_idx)
if (num >= Friends.max_idx) {
return;
}
if (Friends.list[num].chatwin != -1)
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -350,18 +368,20 @@ static void friendlist_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESS
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)
if (num >= Friends.max_idx) {
return;
}
if (connection_status == TOX_CONNECTION_NONE) {
--Friends.num_online;
} else if (Friends.list[num].connection_status == TOX_CONNECTION_NONE) {
++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);
}
}
Friends.list[num].connection_status = connection_status;
@ -372,8 +392,9 @@ 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)
{
if (num >= Friends.max_idx)
if (num >= Friends.max_idx) {
return;
}
/* save old name for log renaming */
char oldname[TOXIC_MAX_NAME_LENGTH + 1];
@ -389,24 +410,27 @@ static void friendlist_onNickChange(ToxWindow *self, Tox *m, uint32_t num, const
strcpy(newnamecpy, Friends.list[num].name);
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);
}
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)
if (num >= Friends.max_idx) {
return;
}
Friends.list[num].status = status;
}
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)
if (length > TOX_MAX_STATUS_MESSAGE_LENGTH || num >= Friends.max_idx) {
return;
}
snprintf(Friends.list[num].statusmsg, sizeof(Friends.list[num].statusmsg), "%s", note);
Friends.list[num].statusmsg_len = strlen(Friends.list[num].statusmsg);
@ -420,8 +444,9 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
uint32_t i;
for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active)
if (Friends.list[i].active) {
continue;
}
++Friends.num_friends;
@ -432,17 +457,19 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
Friends.list[i].status = TOX_USER_STATUS_NONE;
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);
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);
}
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);
if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK)
if (loerr != TOX_ERR_FRIEND_GET_LAST_ONLINE_OK) {
t = 0;
}
update_friend_last_online(i, t);
@ -451,17 +478,23 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
snprintf(Friends.list[i].name, sizeof(Friends.list[i].name), "%s", tempname);
Friends.list[i].namelength = strlen(Friends.list[i].name);
if (i == Friends.max_idx)
if (i == Friends.max_idx) {
++Friends.max_idx;
}
if (sort)
if (sort) {
sort_friendlist_index();
}
#ifdef AUDIO
init_friend_AV(i);
#endif
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)
{
realloc_friends(Friends.max_idx + 1);
@ -470,8 +503,9 @@ static void friendlist_add_blocked(Tox *m, uint32_t fnum, uint32_t bnum)
int i;
for (i = 0; i <= Friends.max_idx; ++i) {
if (Friends.list[i].active)
if (Friends.list[i].active) {
continue;
}
++Friends.num_friends;
@ -485,12 +519,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].pub_key, Blocked.list[bnum].pub_key, TOX_PUBLIC_KEY_SIZE);
if (i == Friends.max_idx)
if (i == Friends.max_idx) {
++Friends.max_idx;
}
sort_blocklist_index();
sort_friendlist_index();
#ifdef AUDIO
init_friend_AV(i);
#endif
return;
}
}
@ -498,11 +536,13 @@ 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,
uint64_t file_size, const char *filename, size_t name_length)
{
if (num >= Friends.max_idx)
if (num >= Friends.max_idx) {
return;
}
if (Friends.list[num].chatwin != -1)
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -523,11 +563,13 @@ 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,
uint16_t length)
{
if (num >= Friends.max_idx)
if (num >= Friends.max_idx) {
return;
}
if (Friends.list[num].chatwin != -1)
if (Friends.list[num].chatwin != -1) {
return;
}
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
Friends.list[num].chatwin = add_window(m, new_chat(m, Friends.list[num].num));
@ -546,12 +588,14 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
/* move friendlist/blocklist cursor up and down */
static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
{
if (num <= 0)
if (num <= 0) {
return;
}
if (key == KEY_UP) {
if (--(*selected) < 0)
if (--(*selected) < 0) {
*selected = num - 1;
}
} else if (key == KEY_DOWN) {
*selected = (*selected + 1) % num;
}
@ -559,7 +603,7 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int 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) {
fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
@ -568,8 +612,9 @@ static void delete_friend(Tox *m, uint32_t f_num)
--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;
}
/* close friend's chatwindow if it's currently open */
if (Friends.list[f_num].chatwin >= 0) {
@ -577,28 +622,35 @@ static void delete_friend(Tox *m, uint32_t f_num)
if (toxwin != NULL) {
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);
}
memset(&Friends.list[f_num], 0, sizeof(ToxicFriend));
int i;
for (i = Friends.max_idx; i > 0; --i) {
if (Friends.list[i - 1].active)
if (Friends.list[i - 1].active) {
break;
}
}
Friends.max_idx = i;
realloc_friends(i);
#ifdef AUDIO
del_friend_AV(i);
#endif
/* 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;
}
store_data(m, DATA_FILE);
}
@ -634,8 +686,9 @@ static void del_friend_deactivate(ToxWindow *self, Tox *m, wint_t key)
static void draw_del_popup(void)
{
if (!PendingDelete.active)
if (!PendingDelete.active) {
return;
}
wattron(PendingDelete.popup, A_BOLD);
box(PendingDelete.popup, ACS_VLINE, ACS_HLINE);
@ -647,17 +700,18 @@ static void draw_del_popup(void)
pthread_mutex_lock(&Winthread.lock);
if (blocklist_view == 0)
if (blocklist_view == 0) {
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
else
} else {
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
}
pthread_mutex_unlock(&Winthread.lock);
wattroff(PendingDelete.popup, A_BOLD);
wprintw(PendingDelete.popup, "? y/n");
wrefresh(PendingDelete.popup);
wnoutrefresh(PendingDelete.popup);
}
/* deletes contact from blocked list */
@ -668,8 +722,9 @@ static void delete_blocked_friend(uint32_t bnum)
int i;
for (i = Blocked.max_idx; i > 0; --i) {
if (Blocked.list[i - 1].active)
if (Blocked.list[i - 1].active) {
break;
}
}
--Blocked.num_blocked;
@ -677,15 +732,17 @@ static void delete_blocked_friend(uint32_t bnum)
realloc_blocklist(i);
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;
}
}
/* deletes contact from friendlist and puts in blocklist */
void block_friend(Tox *m, uint32_t fnum)
{
if (Friends.num_friends <= 0)
if (Friends.num_friends <= 0) {
return;
}
realloc_blocklist(Blocked.max_idx + 1);
memset(&Blocked.list[Blocked.max_idx], 0, sizeof(BlockedFriend));
@ -693,8 +750,9 @@ void block_friend(Tox *m, uint32_t fnum)
int i;
for (i = 0; i <= Blocked.max_idx; ++i) {
if (Blocked.list[i].active)
if (Blocked.list[i].active) {
continue;
}
Blocked.list[i].active = true;
Blocked.list[i].num = i;
@ -705,8 +763,9 @@ void block_friend(Tox *m, uint32_t fnum)
++Blocked.num_blocked;
if (i == Blocked.max_idx)
if (i == Blocked.max_idx) {
++Blocked.max_idx;
}
delete_friend(m, fnum);
save_blocklist(BLOCK_FILE);
@ -720,10 +779,11 @@ void block_friend(Tox *m, uint32_t fnum)
/* removes friend from blocklist, puts back in friendlist */
static void unblock_friend(Tox *m, uint32_t bnum)
{
if (Blocked.num_blocked <= 0)
if (Blocked.num_blocked <= 0) {
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);
if (err != TOX_ERR_FRIEND_ADD_OK) {
@ -750,41 +810,47 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return;
}
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;
}
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;
}
int f = 0;
if (blocklist_view == 1 && Blocked.num_blocked)
if (blocklist_view == 1 && Blocked.num_blocked) {
f = Blocked.index[Blocked.num_selected];
else if (Friends.num_friends)
} else if (Friends.num_friends) {
f = Friends.index[Friends.num_selected];
}
/* lock screen and force decision on deletion popup */
if (PendingDelete.active) {
if (key == 'y' || key == 'n')
if (key == 'y' || key == 'n') {
del_friend_deactivate(self, m, key);
}
return;
}
if (key == ltr)
if (key == ltr) {
return;
}
switch (key) {
case '\r':
if (blocklist_view)
if (blocklist_view) {
break;
}
/* Jump to chat window if already open */
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) {
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 {
const char *msg = "* Warning: Too many windows are open.";
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, msg);
@ -798,10 +864,11 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
break;
case 'b':
if (!blocklist_view)
if (!blocklist_view) {
block_friend(m, f);
else
} else {
unblock_friend(m, f);
}
break;
@ -811,10 +878,11 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
break;
default:
if (blocklist_view == 0)
if (blocklist_view == 0) {
select_friend(self, key, &Friends.num_selected, Friends.num_friends);
else
} else {
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
}
break;
}
@ -829,8 +897,9 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wattroff(self->window, A_BOLD);
wprintw(self->window, "%d\n\n", Blocked.num_blocked);
if ((y2 - FLIST_OFST) <= 0)
if ((y2 - FLIST_OFST) <= 0) {
return;
}
uint32_t selected_num = 0;
@ -859,15 +928,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
wprintw(self->window, "x");
wattroff(self->window, COLOR_PAIR(RED));
if (f_selected)
if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD);
wprintw(self->window, " %s\n", Blocked.list[f].name);
wattroff(self->window, A_BOLD);
if (f_selected)
if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE));
}
}
wprintw(self->window, "\n");
@ -882,15 +953,17 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
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);
}
}
wrefresh(self->window);
wnoutrefresh(self->window);
draw_del_popup();
if (self->help->active)
if (self->help->active) {
help_onDraw(self);
}
}
static void friendlist_onDraw(ToxWindow *self, Tox *m)
@ -924,8 +997,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%d/%d \n\n", Friends.num_online, Friends.num_friends);
if ((y2 - FLIST_OFST) <= 0)
if ((y2 - FLIST_OFST) <= 0) {
return;
}
uint32_t selected_num = 0;
@ -964,8 +1038,8 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
}
pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection_status = Friends.list[f].connection_status;
TOX_USER_STATUS status = Friends.list[f].status;
Tox_Connection connection_status = Friends.list[f].connection_status;
Tox_User_Status status = Friends.list[f].status;
pthread_mutex_unlock(&Winthread.lock);
if (connection_status != TOX_CONNECTION_NONE) {
@ -989,8 +1063,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%s ", ONLINE_CHAR);
wattroff(self->window, COLOR_PAIR(colour) | A_BOLD);
if (f_selected)
if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD);
pthread_mutex_lock(&Winthread.lock);
@ -998,8 +1073,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_unlock(&Winthread.lock);
wattroff(self->window, A_BOLD);
if (f_selected)
if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE));
}
/* Reset Friends.list[f].statusmsg on window resize */
if (fix_statuses) {
@ -1032,8 +1108,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
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);
}
pthread_mutex_unlock(&Winthread.lock);
@ -1041,8 +1118,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
} else {
wprintw(self->window, "%s ", OFFLINE_CHAR);
if (f_selected)
if (f_selected) {
wattron(self->window, COLOR_PAIR(BLUE));
}
wattron(self->window, A_BOLD);
pthread_mutex_lock(&Winthread.lock);
@ -1050,8 +1128,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_unlock(&Winthread.lock);
wattroff(self->window, A_BOLD);
if (f_selected)
if (f_selected) {
wattroff(self->window, COLOR_PAIR(BLUE));
}
pthread_mutex_lock(&Winthread.lock);
time_t last_seen = Friends.list[f].last_online.last_on;
@ -1099,15 +1178,17 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
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);
}
}
wrefresh(self->window);
wnoutrefresh(self->window);
draw_del_popup();
if (self->help->active)
if (self->help->active) {
help_onDraw(self);
}
}
void disable_chatwin(uint32_t f_num)
@ -1120,8 +1201,9 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
{
assert(0);
if ( friend_number >= Friends.max_idx)
if (friend_number >= Friends.max_idx) {
return;
}
assert(0);
Tox *m = toxav_get_tox(av);
@ -1130,7 +1212,7 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
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 {
char nick[TOX_MAX_NAME_LENGTH];
@ -1146,49 +1228,64 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
}
#endif /* AUDIO */
ToxWindow new_friendlist(void)
/* Returns a friend's status */
Tox_User_Status get_friend_status(uint32_t friendnumber)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
return Friends.list[friendnumber].status;
}
ret.active = true;
ret.is_friendlist = true;
/* Returns a friend's connection status */
Tox_Connection get_friend_connection_status(uint32_t friendnumber)
{
return Friends.list[friendnumber].connection_status;
}
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;
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
ret.onInvite = &friendlist_onAV;
ret.onRinging = &friendlist_onAV;
ret.onStarting = &friendlist_onAV;
ret.onEnding = &friendlist_onAV;
ret.onError = &friendlist_onAV;
ret.onStart = &friendlist_onAV;
ret.onCancel = &friendlist_onAV;
ret.onReject = &friendlist_onAV;
ret.onEnd = &friendlist_onAV;
ret->onInvite = &friendlist_onAV;
ret->onRinging = &friendlist_onAV;
ret->onStarting = &friendlist_onAV;
ret->onEnding = &friendlist_onAV;
ret->onError = &friendlist_onAV;
ret->onStart = &friendlist_onAV;
ret->onCancel = &friendlist_onAV;
ret->onReject = &friendlist_onAV;
ret->onEnd = &friendlist_onAV;
ret.is_call = false;
ret.device_selection[0] = ret.device_selection[1] = -1;
ret->is_call = false;
ret->device_selection[0] = ret->device_selection[1] = -1;
#endif /* AUDIO */
ret.num = -1;
ret.active_box = -1;
ret->num = -1;
ret->active_box = -1;
Help *help = calloc(1, sizeof(Help));
if (help == NULL)
if (help == NULL) {
exit_toxic_err("failed in new_friendlist", FATALERR_MEMORY);
}
ret.help = help;
strcpy(ret.name, "contacts");
ret->help = help;
strcpy(ret->name, "contacts");
return ret;
}

View File

@ -44,17 +44,17 @@ struct GroupChatInvite {
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
uint16_t namelength;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
int chatwin;
bool active;
TOX_CONNECTION connection_status;
Tox_Connection connection_status;
bool is_typing;
bool logging_on; /* saves preference for friend irrespective of global settings */
uint8_t status;
Tox_User_Status status;
struct LastOnline last_online;
struct GroupChatInvite group_invite;
@ -65,7 +65,7 @@ typedef struct {
typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength;
uint16_t namelength;
char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num;
bool active;
@ -81,12 +81,14 @@ typedef struct {
ToxicFriend *list;
} FriendsList;
ToxWindow new_friendlist(void);
ToxWindow *new_friendlist(void);
void disable_chatwin(uint32_t f_num);
int get_friendnum(uint8_t *name);
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);
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 */
void sort_friendlist_index(void);

View File

@ -41,7 +41,6 @@
extern char *DATA_FILE;
extern ToxWindow *prompt;
extern FriendsList Friends;
extern FriendRequests FrndRequests;
/* command functions */
void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -63,7 +62,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return;
}
TOX_ERR_FRIEND_ADD err;
Tox_Err_Friend_Add err;
uint32_t friendnum = tox_friend_add_norequest(m, FrndRequests.request[req].key, &err);
if (err != TOX_ERR_FRIEND_ADD_OK) {
@ -71,7 +70,7 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
return;
} else {
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));
@ -79,8 +78,9 @@ void cmd_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
int i;
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
if (FrndRequests.request[i - 1].active) {
break;
}
}
FrndRequests.max_idx = i;
@ -92,8 +92,8 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
{
const char *errmsg;
TOX_ERR_FRIEND_ADD err;
uint32_t f_num = tox_friend_add(m, (uint8_t *) id_bin, (uint8_t *) msg, strlen(msg), &err);
Tox_Err_Friend_Add err;
uint32_t f_num = tox_friend_add(m, (const uint8_t *) id_bin, (const uint8_t *) msg, strlen(msg), &err);
switch (err) {
case TOX_ERR_FRIEND_ADD_TOO_LONG:
@ -126,7 +126,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
case TOX_ERR_FRIEND_ADD_OK:
errmsg = "Friend request sent.";
on_friendadded(m, f_num, true);
on_friend_added(m, f_num, true);
break;
case TOX_ERR_FRIEND_ADD_NULL:
@ -201,21 +201,15 @@ 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])
{
if (argc < 2 || strlen(argv[1]) < 3) {
if (argc != 1 || strlen(argv[1]) < 3) {
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;
}
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];
snprintf(path, sizeof(path), "%s", &argv[1][1]);
int len = strlen(path) - 1;
snprintf(path, sizeof(path), "%s", argv[1]);
int len = strlen(path);
if (len <= 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid path.");
@ -267,7 +261,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
return;
}
TOX_ERR_BOOTSTRAP err;
Tox_Err_Bootstrap err;
tox_bootstrap(m, ip, port, (uint8_t *) key_binary, &err);
tox_add_tcp_relay(m, ip, port, (uint8_t *) key_binary, &err);
@ -313,8 +307,9 @@ void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
int i;
for (i = FrndRequests.max_idx; i > 0; --i) {
if (FrndRequests.request[i - 1].active)
if (FrndRequests.request[i - 1].active) {
break;
}
}
FrndRequests.max_idx = i;
@ -335,11 +330,11 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
uint8_t type;
if (!strcasecmp(argv[1], "audio"))
if (!strcasecmp(argv[1], "audio")) {
type = TOX_CONFERENCE_TYPE_AV;
else if (!strcasecmp(argv[1], "text"))
} else if (!strcasecmp(argv[1], "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");
return;
}
@ -349,7 +344,7 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
return;
}
TOX_ERR_CONFERENCE_NEW err;
Tox_Err_Conference_New err;
uint32_t groupnum = tox_conference_new(m, &err);
@ -373,10 +368,11 @@ void cmd_log(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX
struct chatlog *log = self->chatwin->log;
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)";
else
} else {
msg = "Logging for this window is OFF; type \"/log on\" to enable.";
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
return;
@ -403,8 +399,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);
return;
} else if (!strcmp(swch, "0") || !strcmp(swch, "off")) {
if (self->is_chat)
if (self->is_chat) {
Friends.list[self->num].logging_on = false;
}
log_disable(log);
@ -431,6 +428,7 @@ 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);
}
#ifdef QRCODE
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char id_string[TOX_ADDRESS_SIZE * 2 + 1];
@ -488,6 +486,7 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
#endif /* QRPNG */
}
#endif /* QRCODE */
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
@ -497,16 +496,8 @@ void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
}
char nick[MAX_STR_SIZE];
size_t len = 0;
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);
}
snprintf(nick, sizeof(nick), "%s", argv[1]);
size_t len = strlen(nick);
if (!valid_nick(nick)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
@ -529,19 +520,7 @@ void cmd_note(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
return;
}
if (argv[1][0] != '\"') {
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);
prompt_update_statusmessage(prompt, m, argv[1]);
}
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -590,8 +569,9 @@ void cmd_requests(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
int count = 0;
for (i = 0; i < FrndRequests.max_idx; ++i) {
if (!FrndRequests.request[i].active)
if (!FrndRequests.request[i].active) {
continue;
}
char id[TOX_PUBLIC_KEY_SIZE * 2 + 1] = {0};
@ -604,36 +584,34 @@ 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, "%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, "");
}
}
}
void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
bool have_note = false;
const char *errmsg;
lock_status ();
lock_status();
if (argc >= 2) {
have_note = true;
} else if (argc < 1) {
if (argc < 1) {
errmsg = "Require a status. Statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
}
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;
else if (!strcasecmp(status_str, "away"))
} else if (!strcasecmp(status_str, "away")) {
status = TOX_USER_STATUS_AWAY;
else if (!strcasecmp(status_str, "busy"))
} else if (!strcasecmp(status_str, "busy")) {
status = TOX_USER_STATUS_BUSY;
else {
} else {
errmsg = "Invalid status. Valid statuses are: online, busy and away.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
goto finish;
@ -641,22 +619,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
tox_self_set_status(m, status);
prompt_update_status(prompt, status);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
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);
}
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_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#ifdef QRCODE
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_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]);
@ -56,4 +58,8 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */
#endif /* #define GLOBAL_COMMANDS_H */
#ifdef PYTHON
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* PYTHON */
#endif /* GLOBAL_COMMANDS_H */

View File

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

View File

@ -28,4 +28,4 @@
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

@ -41,9 +41,9 @@
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#include "windows.h"
#include "toxic.h"
@ -69,11 +69,17 @@ static int max_groupchat_index = 0;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
#ifdef AUDIO
#define AC_NUM_GROUP_COMMANDS 24
#ifdef PYTHON
#define AC_NUM_GROUP_COMMANDS_PYTHON 1
#else
#define AC_NUM_GROUP_COMMANDS 20
#endif /* AUDIO */
#define AC_NUM_GROUP_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_GROUP_COMMANDS_QRCODE 1
#else
#define AC_NUM_GROUP_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_GROUP_COMMANDS (19 + AC_NUM_GROUP_COMMANDS_PYTHON + AC_NUM_GROUP_COMMANDS_QRCODE)
/* Array of groupchat command names used for tab completion. */
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -89,7 +95,9 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" },
{ "/log" },
{ "/myid" },
#ifdef QRCODE
{ "/myqr" },
#endif /* QRCODE */
{ "/nick" },
{ "/note" },
{ "/nospam" },
@ -97,49 +105,14 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/requests" },
{ "/status" },
{ "/title" },
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
};
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
{
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)
{
ChatContext *ctx = self->chatwin;
@ -155,27 +128,61 @@ static void kill_groupchat_window(ToxWindow *self)
del_window(self);
}
void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
{
tox_conference_delete(m, groupnum, NULL);
if (groupnum > MAX_GROUPCHAT_NUM) {
return -1;
}
free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths);
free(groupchats[groupnum].oldpeer_name_lengths);
ToxWindow *self = new_group_chat(m, groupnum);
for (int i = 0; i <= max_groupchat_index; ++i) {
if (!groupchats[i].active) {
groupchats[i].chatwin = add_window(m, self);
groupchats[i].active = true;
groupchats[i].num_peers = 0;
groupchats[i].type = type;
groupchats[i].start_time = get_unix_time();
set_active_window_index(groupchats[i].chatwin);
if (i == max_groupchat_index) {
++max_groupchat_index;
}
return 0;
}
}
kill_groupchat_window(self);
return -1;
}
void free_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
{
free(groupchats[groupnum].name_list);
free(groupchats[groupnum].peer_list);
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i;
for (i = max_groupchat_index; i > 0; --i) {
if (groupchats[i - 1].active)
if (groupchats[i - 1].active) {
break;
}
}
max_groupchat_index = i;
kill_groupchat_window(self);
}
static void delete_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
{
tox_conference_delete(m, groupnum, NULL);
free_groupchat(self, m, groupnum);
}
/* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self)
{
@ -189,8 +196,9 @@ void redraw_groupchat_win(ToxWindow *self)
getmaxyx(stdscr, y2, x2);
y2 -= 2;
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
if (ctx->sidebar) {
delwin(ctx->sidebar);
@ -216,10 +224,11 @@ void redraw_groupchat_win(ToxWindow *self)
}
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)
if (self->num != groupnum) {
return;
}
ChatContext *ctx = self->chatwin;
@ -238,10 +247,11 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum,
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
if (self->active_box != -1)
if (self->active_box != -1) {
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);
}
nick_clr = RED;
} else {
@ -255,13 +265,15 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum,
write_to_log(msg, nick, ctx->log, false);
}
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum, const char *title,
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
const char *title,
size_t length)
{
ChatContext *ctx = self->chatwin;
if (self->num != groupnum)
if (self->num != groupnum) {
return;
}
set_window_title(self, title, length);
@ -269,8 +281,9 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t group
get_time_str(timefrmt, sizeof(timefrmt));
/* don't announce title when we join the room */
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) {
return;
}
char nick[TOX_MAX_NAME_LENGTH];
get_group_nick_truncate(m, nick, peernum, groupnum);
@ -281,212 +294,162 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t group
write_to_log(tmp_event, nick, ctx->log, true);
}
/* Puts two copies of peerlist/lengths in chat instance */
static void copy_peernames(Tox *m, uint32_t gnum, size_t npeers)
static void group_update_name_list(uint32_t groupnum)
{
/* Assumes these are initiated in init_groupchat_win */
free(groupchats[gnum].peer_names);
free(groupchats[gnum].oldpeer_names);
free(groupchats[gnum].peer_name_lengths);
free(groupchats[gnum].oldpeer_name_lengths);
GroupChat *chat = &groupchats[groupnum];
int N = TOX_MAX_NAME_LENGTH;
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);
if (!chat) {
return;
}
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) {
uint8_t name[TOX_MAX_NAME_LENGTH];
TOX_ERR_CONFERENCE_PEER_QUERY err;
if (chat->name_list == NULL) {
exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY);
}
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) {
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) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
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;
continue;
}
pthread_mutex_unlock(&Winthread.lock);
peer->active = true;
peer->name_length = length;
peer->peernumber = i;
}
const char *event = "has joined the room";
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);
group_update_name_list(groupnum);
}
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
TOX_CONFERENCE_STATE_CHANGE change)
static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum)
{
if (self->num != groupnum)
if (self->num != groupnum) {
return;
}
if (groupnum > max_groupchat_index)
if (groupnum > max_groupchat_index) {
return;
}
TOX_ERR_CONFERENCE_PEER_QUERY err;
GroupChat *chat = &groupchats[groupnum];
Tox_Err_Conference_Peer_Query err;
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
uint32_t old_num = chat->num_peers;
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
groupchats[groupnum].num_peers = num_peers;
chat->num_peers = num_peers;
} 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)
{
if (self->num != groupnum) {
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 */
copy_peernames(m, groupnum, num_peers);
GroupChat *chat = &groupchats[groupnum];
/* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH];
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';
if (!chat) {
return;
}
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
size_t i;
ChatContext *ctx = self->chatwin;
for (i = 0; i < chat->max_idx; ++i) {
GroupPeer *peer = &chat->peer_list[i];
const char *event;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
switch (change) {
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);
// Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting)
if (peer->active && peer->peernumber == peernum && peer->name_length > 0) {
ChatContext *ctx = self->chatwin;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (const char *) name);
write_to_log(tmp_event, peer->name, ctx->log, true);
line_info_add(self, timefrmt, peer->name, (const char *) name, NAME_CHANGE, 0, 0, " is now known as ");
break;
}
}
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)
@ -496,7 +459,7 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
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)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
@ -511,27 +474,31 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0)
if (x2 <= 0 || y2 <= 0) {
return;
}
if (self->help->active) {
help_onKey(self, key);
return;
}
if (ctx->pastemode && key == '\r')
if (ctx->pastemode && key == '\r') {
key = '\n';
}
if (ltr || key == '\n') { /* char is printable */
input_new_char(self, key, x, y, x2, y2);
return;
}
if (line_info_onKey(self, key))
if (line_info_onKey(self, key)) {
return;
}
if (input_handle(self, key, x, y, x2, y2))
if (input_handle(self, key, x, y, x2, y2)) {
return;
}
if (key == '\t') { /* TAB key: auto-completes peer name or command */
if (ctx->len > 0) {
@ -539,11 +506,20 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
/* TODO: make this not suck */
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);
} 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");
} else {
}
#ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run");
}
#endif
else {
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
}
@ -561,11 +537,13 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */
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 < groupchats[self->num].num_peers - L) {
++groupchats[self->num].side_pos;
}
} else if (key == user_settings->key_peer_list_up) {
if (groupchats[self->num].side_pos > 0)
if (groupchats[self->num].side_pos > 0) {
--groupchats[self->num].side_pos;
}
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
@ -576,12 +554,13 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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));
}
if (line[0] == '/') {
if (strcmp(line, "/close") == 0) {
close_groupchat(self, m, self->num);
delete_groupchat(self, m, self->num);
return;
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
send_group_action(self, ctx, m, line + strlen("/me "));
@ -589,7 +568,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} 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)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
@ -608,8 +587,9 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0)
if (x2 <= 0 || y2 <= 0) {
return;
}
ChatContext *ctx = self->chatwin;
@ -621,8 +601,9 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
curs_set(1);
if (ctx->len > 0)
if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
wclear(ctx->sidebar);
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
@ -658,7 +639,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int maxlen = SIDEBAR_WIDTH - 2;
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);
tmpnck[maxlen] = '\0';
@ -673,10 +654,11 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x);
wrefresh(self->window);
wnoutrefresh(self->window);
if (self->help->active)
if (self->help->active) {
help_onDraw(self);
}
}
static void groupchat_onInit(ToxWindow *self, Tox *m)
@ -684,8 +666,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0)
if (x2 <= 0 || y2 <= 0) {
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
}
ChatContext *ctx = self->chatwin;
@ -696,8 +679,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
ctx->hst = calloc(1, sizeof(struct history));
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);
}
line_info_init(ctx->hst);
@ -705,8 +689,9 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
}
}
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
@ -715,35 +700,39 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
ToxWindow new_group_chat(Tox *m, uint32_t groupnum)
ToxWindow *new_group_chat(Tox *m, uint32_t groupnum)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
ret.active = true;
ret.is_groupchat = true;
if (ret == NULL) {
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
}
ret.onKey = &groupchat_onKey;
ret.onDraw = &groupchat_onDraw;
ret.onInit = &groupchat_onInit;
ret.onGroupMessage = &groupchat_onGroupMessage;
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
ret->is_groupchat = true;
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));
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);
}
ret.chatwin = chatwin;
ret.help = help;
ret->chatwin = chatwin;
ret->help = help;
ret.num = groupnum;
ret.show_peerlist = true;
ret.active_box = -1;
ret->num = groupnum;
ret->show_peerlist = true;
ret->active_box = -1;
return ret;
}

View File

@ -31,25 +31,36 @@
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3
typedef struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peernumber;
} GroupPeer;
typedef struct {
int chatwin;
bool active;
uint8_t type;
uint32_t num_peers;
int side_pos; /* current position of the sidebar - used for scrolling up and down */
time_t start_time;
uint8_t *peer_names;
uint8_t *oldpeer_names;
uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths;
GroupPeer *peer_list;
size_t max_idx;
char *name_list;
size_t num_peers;
} 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() ) */
void free_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type);
/* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
ToxWindow *new_group_chat(Tox *m, uint32_t groupnum);
#endif /* #define GROUPCHAT_H */
#endif /* GROUPCHAT_H */

View File

@ -27,19 +27,29 @@
#include "help.h"
#include "misc_tools.h"
#ifdef PYTHON
#include "api.h"
#endif /* PYTHON */
#ifdef PYTHON
#define HELP_MENU_HEIGHT 10
#else
#define HELP_MENU_HEIGHT 9
#endif /* PYTHON */
#define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self)
{
if (self->help->win)
if (self->help->win) {
delwin(self->help->win);
}
int 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;
}
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
self->help->active = true;
@ -54,14 +64,16 @@ static void help_exit(ToxWindow *self)
static void help_init_window(ToxWindow *self, int height, int width)
{
if (self->help->win)
if (self->help->win) {
delwin(self->help->win);
}
int y2, x2;
getmaxyx(stdscr, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
height = MIN(height, y2);
width = MIN(width, x2);
@ -95,6 +107,13 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n");
#ifdef PYTHON
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " p");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "lugin commands\n");
#endif /* PYTHON */
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -112,7 +131,7 @@ static void help_draw_menu(ToxWindow *self)
wprintw(win, "it menu\n");
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_bottom_menu(WINDOW *win)
@ -158,11 +177,13 @@ static void help_draw_global(ToxWindow *self)
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, " /myid : Print your Tox ID\n");
#ifdef QRCODE
#ifdef QRPNG
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
#else
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
#endif /* QRPNG */
#endif /* QRCODE */
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -185,10 +206,18 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /svdev <type> <id> : Set active video device\n");
#endif /* VIDEO */
#ifdef PYTHON
wattron(win, A_BOLD);
wprintw(win, "\n Scripting:\n");
wattroff(win, A_BOLD);
wprintw(win, " /run <path> : Load and run the script at path\n");
#endif /* PYTHON */
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_chat(ToxWindow *self)
@ -219,6 +248,7 @@ static void help_draw_chat(ToxWindow *self)
wprintw(win, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
#endif /* AUDIO */
#ifdef VIDEO
@ -231,7 +261,7 @@ static void help_draw_chat(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_keys(ToxWindow *self)
@ -257,7 +287,7 @@ static void help_draw_keys(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
static void help_draw_group(ToxWindow *self)
@ -275,9 +305,29 @@ static void help_draw_group(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
#ifdef PYTHON
static void help_draw_plugin(ToxWindow *self)
{
WINDOW *win = self->help->win;
wmove(win, 1, 1);
wattron(win, A_BOLD | COLOR_PAIR(RED));
wprintw(win, "Plugin commands:\n");
wattroff(win, A_BOLD | COLOR_PAIR(RED));
draw_handler_help(win);
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wnoutrefresh(win);
}
#endif /* PYTHON */
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
@ -297,11 +347,13 @@ static void help_draw_contacts(ToxWindow *self)
help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win);
wnoutrefresh(win);
}
void help_onKey(ToxWindow *self, wint_t key)
{
int height;
switch (key) {
case 'x':
case T_KEY_ESC:
@ -310,9 +362,9 @@ void help_onKey(ToxWindow *self, wint_t key)
case 'c':
#ifdef VIDEO
help_init_window(self, 22, 80);
help_init_window(self, 23, 80);
#elif AUDIO
help_init_window(self, 19, 80);
help_init_window(self, 20, 80);
#else
help_init_window(self, 10, 80);
#endif
@ -320,13 +372,16 @@ void help_onKey(ToxWindow *self, wint_t key)
break;
case 'g':
height = 22;
#ifdef VIDEO
help_init_window(self, 30, 80);
height += 8;
#elif AUDIO
help_init_window(self, 26, 80);
#else
help_init_window(self, 22, 80);
height += 4;
#endif
#ifdef PYTHON
height += 2;
#endif
help_init_window(self, height, 80);
self->help->type = HELP_GLOBAL;
break;
@ -335,6 +390,14 @@ void help_onKey(ToxWindow *self, wint_t key)
self->help->type = HELP_GROUP;
break;
#ifdef PYTHON
case 'p':
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
self->help->type = HELP_PLUGIN;
break;
#endif /* PYTHON */
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
@ -354,8 +417,6 @@ void help_onKey(ToxWindow *self, wint_t key)
void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch (self->help->type) {
case HELP_MENU:
help_draw_menu(self);
@ -380,5 +441,12 @@ void help_onDraw(ToxWindow *self)
case HELP_GROUP:
help_draw_group(self);
break;
#ifdef PYTHON
case HELP_PLUGIN:
help_draw_plugin(self);
break;
#endif /* PYTHON */
}
}

View File

@ -33,10 +33,13 @@ typedef enum {
HELP_GROUP,
HELP_KEYS,
HELP_CONTACTS,
#ifdef PYTHON
HELP_PLUGIN,
#endif
} HELP_TYPES;
void help_onDraw(ToxWindow *self);
void help_init_menu(ToxWindow *self);
void help_onKey(ToxWindow *self, wint_t key);
#endif /* #define HELP_H */
#endif /* HELP_H */

View File

@ -43,8 +43,9 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
ChatContext *ctx = self->chatwin;
/* this is the only place we need to do this check */
if (key == '\n')
if (key == '\n') {
key = L'';
}
int cur_len = wcwidth(key);
@ -77,17 +78,19 @@ 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 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));
else if (ctx->start)
} else if (ctx->start) {
ctx->start = MAX(0, ctx->start - cur_len);
}
}
/* delete a char via delete key from input field and buffer */
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);
}
}
/* delete last typed word */
@ -104,15 +107,17 @@ static void input_del_word(ToxWindow *self, int x, int mx_x)
/* deletes entire line before cursor from input field and buffer */
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);
}
}
/* deletes entire line after cursor from input field and buffer */
static void input_kill(ChatContext *ctx)
{
if (kill_buf(ctx) == -1)
if (kill_buf(ctx) == -1) {
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
}
}
static void input_yank(ToxWindow *self, int x, int mx_x)
@ -149,8 +154,9 @@ static void input_mv_home(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return;
}
ctx->pos = 0;
ctx->start = 0;
@ -161,18 +167,20 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return;
}
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;
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));
else if (ctx->start)
} else if (ctx->start) {
ctx->start = MAX(0, ctx->start - cur_len);
}
}
/* moves cursor/line position right in input field and buffer */
@ -180,8 +188,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
{
ChatContext *ctx = self->chatwin;
if (ctx->pos >= ctx->len)
if (ctx->pos >= ctx->len) {
return;
}
++ctx->pos;

View File

@ -30,4 +30,4 @@ void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_
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);
#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));
if (hst->line_root == NULL)
if (hst->line_root == NULL) {
exit_toxic_err("failed in line_info_init", FATALERR_MEMORY);
}
hst->line_start = hst->line_root;
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;
if (line->prev == NULL)
if (line->prev == NULL) {
return;
}
int y2, x2;
getmaxyx(self->window, y2, x2);
@ -88,8 +90,9 @@ void line_info_cleanup(struct history *hst)
int i;
for (i = 0; i < hst->queue_sz; ++i) {
if (hst->queue[i])
if (hst->queue[i]) {
free(hst->queue[i]);
}
}
free(hst);
@ -114,37 +117,46 @@ static void line_info_root_fwd(struct history *hst)
/* returns ptr to queue item 0 and removes it from queue. Returns NULL if queue is empty. */
static struct line_info *line_info_ret_queue(struct history *hst)
{
if (hst->queue_sz <= 0)
if (hst->queue_sz <= 0) {
return NULL;
}
struct line_info *line = hst->queue[0];
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_sz;
return line;
}
/* creates new line_info line and puts it in the queue. */
void 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, ...)
/* creates new line_info line and puts it in the queue.
*
* Returns the id of the new line.
* Returns -1 on failure.
*/
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...)
{
if (!self)
return;
if (!self) {
return -1;
}
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) {
return -1;
}
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);
}
char frmt_msg[MAX_LINE_INFO_MSG_SIZE] = {0};
@ -202,8 +214,9 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
int i;
for (i = 0; frmt_msg[i]; ++i) {
if (frmt_msg[i] == '\n')
if (frmt_msg[i] == '\n') {
++new_line->newlines;
}
}
}
@ -222,6 +235,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
len += strlen(new_line->name2);
}
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
new_line->len = len;
new_line->type = type;
new_line->bold = bold;
@ -230,6 +244,8 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
new_line->timestamp = get_unix_time();
hst->queue[hst->queue_sz++] = new_line;
return new_line->id;
}
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
@ -238,24 +254,27 @@ static void line_info_check_queue(ToxWindow *self)
struct history *hst = self->chatwin->hst;
struct line_info *line = line_info_ret_queue(hst);
if (line == NULL)
if (line == NULL) {
return;
}
if (hst->start_id > user_settings->history_size)
if (hst->start_id > user_settings->history_size) {
line_info_root_fwd(hst);
}
line->id = hst->line_end->id + 1;
line->prev = hst->line_end;
hst->line_end->next = line;
hst->line_end = line;
hst->line_end->id = line->id;
int y, y2, x, x2;
getmaxyx(self->window, y2, x2);
getyx(self->chatwin->history, y, x);
(void) x;
if (x2 <= SIDEBAR_WIDTH)
if (x2 <= SIDEBAR_WIDTH) {
return;
}
int offst = self->show_peerlist ? SIDEBAR_WIDTH : 0; /* offset width of groupchat sidebar */
int lines = 1 + line->newlines + (line->len / (x2 - offst));
@ -277,8 +296,9 @@ void line_info_print(ToxWindow *self)
{
ChatContext *ctx = self->chatwin;
if (ctx == NULL)
if (ctx == NULL) {
return;
}
struct history *hst = ctx->hst;
@ -286,19 +306,25 @@ void line_info_print(ToxWindow *self)
line_info_check_queue(self);
WINDOW *win = ctx->history;
wclear(win);
int y2, x2;
getmaxyx(self->window, y2, x2);
if (x2 <= SIDEBAR_WIDTH)
if (x2 <= SIDEBAR_WIDTH) {
return;
}
if (self->is_groupchat)
if (self->is_groupchat) {
wmove(win, 0, 0);
else
} else {
wmove(win, 2, 0);
}
struct line_info *line = hst->line_start->next;
int numlines = 0;
while (line && numlines++ <= y2) {
@ -318,10 +344,11 @@ void line_info_print(ToxWindow *self)
int nameclr = GREEN;
if (line->colour)
if (line->colour) {
nameclr = line->colour;
else if (type == IN_MSG)
} else if (type == IN_MSG) {
nameclr = CYAN;
}
wattron(win, COLOR_PAIR(nameclr));
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
@ -332,21 +359,24 @@ void line_info_print(ToxWindow *self)
while (msg) {
char *line = strsep(&msg, "\n");
if (line[0] == '>')
if (line[0] == '>') {
wattron(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
} else if (line[0] == '<') {
wattron(win, COLOR_PAIR(RED));
}
wprintw(win, "%s%c", line, msg ? '\n' : '\0');
if (line[0] == '>')
if (line[0] == '>') {
wattroff(win, COLOR_PAIR(GREEN));
else if (line[0] == '<')
} else if (line[0] == '<') {
wattroff(win, COLOR_PAIR(RED));
}
// change the \0 set by strsep back to \n
if (msg)
if (msg) {
msg[-1] = '\n';
}
}
if (type == OUT_MSG && timed_out(line->timestamp, NOREAD_FLAG_TIMEOUT)) {
@ -399,19 +429,23 @@ void line_info_print(ToxWindow *self)
wattroff(win, COLOR_PAIR(BLUE));
}
if (line->bold)
if (line->bold) {
wattron(win, A_BOLD);
}
if (line->colour)
if (line->colour) {
wattron(win, COLOR_PAIR(line->colour));
}
wprintw(win, "%s\n", line->msg);
if (line->bold)
if (line->bold) {
wattroff(win, A_BOLD);
}
if (line->colour)
if (line->colour) {
wattroff(win, COLOR_PAIR(line->colour));
}
break;
@ -420,8 +454,9 @@ void line_info_print(ToxWindow *self)
wprintw(win, "$ ");
wattroff(win, COLOR_PAIR(GREEN));
if (line->msg[0])
if (line->msg[0]) {
wprintw(win, "%s", line->msg);
}
wprintw(win, "\n");
break;
@ -485,8 +520,9 @@ void line_info_print(ToxWindow *self)
}
/* keep calling until queue is empty */
if (hst->queue_sz > 0)
if (hst->queue_sz > 0) {
line_info_print(self);
}
}
/* puts msg in specified line_info msg buffer */
@ -511,16 +547,20 @@ void line_info_set(ToxWindow *self, uint32_t id, char *msg)
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;
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)
{
if (hst->line_start->next)
if (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)
@ -531,8 +571,9 @@ static void line_info_page_up(ToxWindow *self, struct history *hst)
int jump_dist = y2 / 2;
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;
}
}
static void line_info_page_down(ToxWindow *self, struct history *hst)
@ -543,8 +584,9 @@ static void line_info_page_down(ToxWindow *self, struct history *hst)
int jump_dist = y2 / 2;
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;
}
}
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_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
enum {
typedef enum {
SYS_MSG,
IN_MSG,
OUT_MSG,
@ -74,9 +74,13 @@ struct history {
int queue_sz;
};
/* creates new line_info line and puts it in the queue. */
void 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, ...);
/* creates new line_info line and puts it in the queue.
*
* Returns the id of the new line.
* Returns -1 on failure.
*/
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
uint8_t bold, uint8_t colour, const char *msg, ...);
/* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self);
@ -96,4 +100,4 @@ void line_info_reset_start(ToxWindow *self, 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 */
#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 */
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;
}
const char *namedash = logtype == LOG_PROMPT ? "" : "-";
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;
}
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);
else
} else {
snprintf(dest, destsize, "%s%s%s-%s%s%s.log", user_config_dir, LOGDIR, self_id, name, namedash, other_id);
}
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 */
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;
}
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;
}
log->file = fopen(log_path, "a+");
snprintf(log->path, sizeof(log->path), "%s", log_path);
if (log->file == NULL)
if (log->file == NULL) {
return -1;
}
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)
{
if (!log->log_on)
if (!log->log_on) {
return;
}
if (log->file == NULL) {
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];
if (event)
if (event) {
snprintf(name_frmt, sizeof(name_frmt), "* %s", name);
else
} else {
snprintf(name_frmt, sizeof(name_frmt), "%s:", name);
}
const char *t = user_settings->log_timestamp_format;
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)
{
if (log->file != NULL)
if (log->file != NULL) {
fclose(log->file);
}
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;
if (log->file != NULL)
if (log->file != NULL) {
return 0;
}
if (init_logging_session(name, selfkey, otherkey, log, logtype) == -1) {
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 */
void load_chat_history(ToxWindow *self, struct chatlog *log)
{
if (log->file == NULL)
if (log->file == NULL) {
return;
}
off_t sz = file_size(log->path);
if (sz <= 0)
if (sz <= 0) {
return;
}
char *hstbuf = malloc(sz + 1);
if (hstbuf == NULL)
if (hstbuf == NULL) {
exit_toxic_err("failed in load_chat_history", FATALERR_MEMORY);
}
if (fseek(log->file, 0L, SEEK_SET) == -1) {
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 */
for (start = sz - 1; start >= 0 && count < L; --start) {
if (hstbuf[start] == '\n')
if (hstbuf[start] == '\n') {
++count;
}
}
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;
}
if (log_on)
if (log_on) {
log_disable(log);
}
char newpath[MAX_STR_SIZE];
char oldpath[MAX_STR_SIZE];
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1)
if (get_log_path(oldpath, sizeof(oldpath), src, selfkey, otherkey, LOG_CHAT) == -1) {
goto on_error;
}
if (!file_exists(oldpath))
if (!file_exists(oldpath)) {
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;
}
if (rename(oldpath, newpath) != 0)
if (rename(oldpath, newpath) != 0) {
goto on_error;
}
if (log_on)
if (log_on) {
log_enable(dest, selfkey, otherkey, log, LOG_CHAT);
}
return 0;
on_error:
if (log_on)
if (log_on) {
log_enable(src, selfkey, otherkey, log, LOG_CHAT);
}
return -1;
}

View File

@ -30,7 +30,7 @@ struct chatlog {
bool log_on; /* specific to current chat window */
};
enum {
typedef enum {
LOG_GROUP,
LOG_PROMPT,
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. */
int rename_logfile(char *src, char *dest, const char *selfkey, const char *otherkey, int winnum);
#endif /* #define LOG_H */
#endif /* LOG_H */

View File

@ -42,12 +42,17 @@ void cqueue_cleanup(struct chat_queue *q)
free(q);
}
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id)
{
if (line_id < 0) {
return;
}
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);
}
snprintf(new_m->message, sizeof(new_m->message), "%s", msg);
new_m->len = len;
@ -114,8 +119,9 @@ void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt)
struct cqueue_msg *next = msg->next;
if (msg->prev == NULL) { /* root */
if (next)
if (next) {
next->prev = NULL;
}
free(msg);
q->root = next;
@ -129,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. */
void cqueue_try_send(ToxWindow *self, Tox *m)
@ -137,18 +143,15 @@ void cqueue_try_send(ToxWindow *self, Tox *m)
struct chat_queue *q = self->chatwin->cqueue;
struct cqueue_msg *msg = q->root;
if (!msg)
if (!msg) {
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;
}
uint32_t receipt = 0;
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);
Tox_Message_Type type = msg->type == OUT_MSG ? TOX_MESSAGE_TYPE_NORMAL : TOX_MESSAGE_TYPE_ACTION;
msg->receipt = tox_friend_send_message(m, self->num, type, (uint8_t *) msg->message, msg->len, NULL);
msg->last_send_try = get_unix_time();
msg->receipt = receipt;
return;
}

View File

@ -40,7 +40,7 @@ struct chat_queue {
};
void cqueue_cleanup(struct chat_queue *q);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
/* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m);
@ -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. */
void cqueue_remove(ToxWindow *self, Tox *m, uint32_t receipt);
#endif /* #define MESSAGE_QUEUE_H */
#endif /* MESSAGE_QUEUE_H */

View File

@ -26,12 +26,9 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#if defined(__FreeBSD__)
#include <netinet/in.h>
#include <sys/socket.h>
#else
#include <arpa/inet.h>
#endif
#include <sys/stat.h>
#include "toxic.h"
@ -87,25 +84,30 @@ void get_time_str(char *buf, int bufsize)
}
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 */
void get_elapsed_time_str(char *buf, int bufsize, time_t secs)
{
if (!secs)
if (!secs) {
return;
}
long int seconds = secs % 60;
long int minutes = (secs % 3600) / 60;
long int hours = secs / 3600;
if (!minutes && !hours)
if (!minutes && !hours) {
snprintf(buf, bufsize, "%.2ld", seconds);
else if (!hours)
} else if (!hours) {
snprintf(buf, bufsize, "%ld:%.2ld", minutes, seconds);
else
} else {
snprintf(buf, bufsize, "%ld:%.2ld:%.2ld", hours, minutes, seconds);
}
}
/*
@ -117,11 +119,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)
{
if (output_size == 0 || hex_len != output_size * 2)
if (output_size == 0 || hex_len != output_size * 2) {
return -1;
}
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;
}
@ -130,18 +133,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)
{
if (size % 2 != 0)
if (size % 2 != 0) {
return -1;
}
int i, res;
const char *pos = keystr;
for (i = 0; i < size; ++i) {
res = sscanf(pos, "%2hhx", &buf[i]);
res = sscanf(pos, "%2hhx", (unsigned char *)&buf[i]);
pos += 2;
if (res == EOF || res < 1)
if (res == EOF || res < 1) {
return -1;
}
}
return 0;
@ -154,13 +159,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)
{
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;
}
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);
}
return 0;
}
@ -168,8 +175,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 */
int string_is_empty(const char *string)
{
if (!string)
if (!string) {
return true;
}
return string[0] == '\0';
}
@ -177,8 +185,9 @@ int string_is_empty(const char *string)
/* Returns 1 if the string is empty, 0 otherwise */
int wstring_is_empty(const wchar_t *string)
{
if (!string)
if (!string) {
return true;
}
return string[0] == L'\0';
}
@ -188,11 +197,13 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
{
size_t len = mbstowcs(NULL, string, 0) + 1;
if (n < len)
if (n < len) {
return -1;
}
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
if ((len = mbstowcs(buf, string, n)) == (size_t) - 1) {
return -1;
}
return len;
}
@ -202,11 +213,13 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
{
size_t len = wcstombs(NULL, string, 0) + 1;
if (n < len)
if (n < len) {
return -1;
}
if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
if ((len = wcstombs(buf, string, n)) == (size_t) - 1) {
return -1;
}
return len;
}
@ -225,8 +238,9 @@ int qsort_strcasecmp_hlpr(const void *str1, const void *str2)
- must not contain a newline or tab seqeunce */
int valid_nick(const char *nick)
{
if (!nick[0] || nick[0] == ' ')
if (!nick[0] || nick[0] == ' ') {
return 0;
}
int i;
@ -238,7 +252,9 @@ int valid_nick(const char *nick)
|| nick[i] == '\v'
|| nick[i] == '\r')
{
return 0;
}
}
return 1;
@ -250,8 +266,9 @@ void filter_str(char *str, size_t len)
size_t 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] = ' ';
}
}
}
@ -263,22 +280,26 @@ size_t get_file_name(char *namebuf, size_t bufsize, const char *pathname)
int len = strlen(pathname) - 1;
char *path = strdup(pathname);
if (path == NULL)
if (path == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
while (len >= 0 && pathname[len] == '/')
while (len >= 0 && pathname[len] == '/') {
path[len--] = '\0';
}
char *finalname = strdup(path);
if (finalname == NULL)
if (finalname == NULL) {
exit_toxic_err("failed in get_file_name", FATALERR_MEMORY);
}
const char *basenm = strrchr(path, '/');
if (basenm != NULL) {
if (basenm[1])
if (basenm[1]) {
strcpy(finalname, &basenm[1]);
}
}
snprintf(namebuf, bufsize, "%s", finalname);
@ -295,13 +316,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)
{
if (path_len == 0 || path == NULL)
if (path_len == 0 || path == NULL) {
return 0;
}
size_t dir_len = char_rfind(path, '/', path_len);
if (dir_len != 0 && dir_len < path_len)
++dir_len; /* Leave trailing slash */
if (dir_len != 0 && dir_len < path_len) {
++dir_len; /* Leave trailing slash */
}
memcpy(dir, path, dir_len);
dir[dir_len] = '\0';
@ -314,8 +337,9 @@ void str_to_lower(char *str)
{
int i;
for (i = 0; str[i]; ++i)
for (i = 0; str[i]; ++i) {
str[i] = tolower(str[i]);
}
}
/* puts friendnum's nick in buf, truncating at TOXIC_MAX_NAME_LENGTH if necessary.
@ -323,7 +347,7 @@ void str_to_lower(char *str)
Returns nick len */
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);
if (err != TOX_ERR_FRIEND_QUERY_OK) {
@ -349,7 +373,7 @@ on_error:
/* same as get_nick_truncate but for groupchats */
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);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
@ -372,14 +396,22 @@ on_error:
return len;
}
/* copies data to msg buffer.
/* copies data to msg buffer, removing return characters.
returns length of msg, which will be no larger than size-1 */
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{
size_t len = MIN(length, size - 1);
memcpy(msg, data, len);
msg[len] = '\0';
return len;
size_t i;
size_t j = 0;
for (i = 0; (i < length) && (j < size - 1); ++i) {
if (data[i] != '\r') {
msg[j++] = data[i];
}
}
msg[j] = '\0';
return j;
}
/* returns index of the first instance of ch in s starting at idx.
@ -393,8 +425,9 @@ int char_find(int idx, const char *s, char ch)
int i = idx;
for (i = idx; s[i]; ++i) {
if (s[i] == ch)
if (s[i] == ch) {
break;
}
}
return i;
@ -411,8 +444,9 @@ int char_rfind(const char *s, char ch, int len)
int i = 0;
for (i = len; i > 0; --i) {
if (s[i] == ch)
if (s[i] == ch) {
break;
}
}
return i;
@ -447,13 +481,36 @@ bool file_exists(const char *path)
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;
stat(path, &s);
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. */
off_t file_size(const char *path)
{
struct stat st;
if (stat(path, &st) == -1)
if (stat(path, &st) == -1) {
return 0;
}
return st.st_size;
}
@ -466,13 +523,15 @@ int check_file_signature(const char *signature, size_t size, FILE *fp)
{
char buf[size];
if (fread(buf, size, 1, fp) != 1)
if (fread(buf, size, 1, fp) != 1) {
return -1;
}
int ret = memcmp(signature, buf, size);
if (fseek(fp, 0L, SEEK_SET) == -1)
if (fseek(fp, 0L, SEEK_SET) == -1) {
return -1;
}
return ret == 0 ? 0 : 1;
}
@ -482,10 +541,11 @@ void set_window_title(ToxWindow *self, const char *title, int len)
{
char cpy[TOXIC_MAX_NAME_LENGTH + 1];
if (self->is_groupchat) /* keep groupnumber in title */
if (self->is_groupchat) { /* keep groupnumber in title */
snprintf(cpy, sizeof(cpy), "%d %s", self->num, title);
else
} else {
snprintf(cpy, sizeof(cpy), "%s", title);
}
if (len > MAX_WINDOW_NAME_LENGTH) {
strcpy(&cpy[MAX_WINDOW_NAME_LENGTH - 3], "...");

View File

@ -39,6 +39,14 @@
#define net_to_host(x, y) hst_to_net(x, y)
#endif
typedef enum File_Type
{
FILE_TYPE_REGULAR,
FILE_TYPE_DIRECTORY,
FILE_TYPE_OTHER,
} File_Type;
void hst_to_net(uint8_t *num, uint16_t numbytes);
/*
@ -78,13 +86,13 @@ int string_is_empty(const char *string);
/* Same as above but for wide character strings */
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);
/* 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);
/* 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);
/* Returns 1 if connection has timed out, 0 otherwise */
@ -146,6 +154,15 @@ void bytes_convert_str(char *buf, int size, uint64_t bytes);
/* checks if a file exists. Returns true or false */
bool file_exists(const char *path);
/*
* 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. */
off_t file_size(const char *path);
@ -170,4 +187,4 @@ bool is_ip4_address(const char *address);
*/
bool is_ip6_address(const char *address);
#endif /* #define MISC_TOOLS_H */
#endif /* MISC_TOOLS_H */

View File

@ -21,6 +21,7 @@
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <curl/curl.h>
@ -41,7 +42,7 @@ extern struct Winthread Winthread;
#define MAX_DOMAIN_SIZE 32
#define MAX_SERVER_LINE MAX_DOMAIN_SIZE + (SERVER_KEY_SIZE * 2) + 3
struct Nameservers {
static struct Nameservers {
int lines;
char names[MAX_SERVERS][MAX_DOMAIN_SIZE];
char keys[MAX_SERVERS][SERVER_KEY_SIZE];
@ -96,42 +97,49 @@ static int load_nameserver_list(const char *path)
{
FILE *fp = fopen(path, "r");
if (fp == NULL)
if (fp == NULL) {
return -2;
}
char line[MAX_SERVER_LINE];
while (fgets(line, sizeof(line), fp) && Nameservers.lines < MAX_SERVERS) {
int linelen = strlen(line);
if (linelen < SERVER_KEY_SIZE * 2 + 5)
if (linelen < SERVER_KEY_SIZE * 2 + 5) {
continue;
}
if (line[linelen - 1] == '\n')
if (line[linelen - 1] == '\n') {
line[--linelen] = '\0';
}
const char *name = strtok(line, " ");
const char *keystr = strtok(NULL, " ");
if (name == NULL || keystr == NULL)
if (name == NULL || keystr == NULL) {
continue;
}
if (strlen(keystr) != SERVER_KEY_SIZE * 2)
if (strlen(keystr) != SERVER_KEY_SIZE * 2) {
continue;
}
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);
if (res == -1)
if (res == -1) {
continue;
}
++Nameservers.lines;
}
fclose(fp);
if (Nameservers.lines < 1)
if (Nameservers.lines < 1) {
return -3;
}
return 0;
}
@ -144,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)
{
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH)))
if (strlen(addr) >= (MAX_STR_SIZE - strlen(NAMESERVER_API_PATH))) {
return -1;
}
char tmpaddr[MAX_STR_SIZE];
char *tmpname = NULL;
@ -155,8 +164,9 @@ static int parse_addr(const char *addr, char *namebuf, size_t namebuf_sz, char *
tmpname = strtok(tmpaddr, "@");
tmpdom = strtok(NULL, "");
if (tmpname == NULL || tmpdom == NULL)
if (tmpname == NULL || tmpdom == NULL) {
return -1;
}
str_to_lower(tmpdom);
snprintf(namebuf, namebuf_sz, "%s", tmpname);
@ -196,23 +206,27 @@ static int process_response(struct Recv_Curl_Data *recv_data)
{
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;
}
const char *IDstart = strstr(recv_data->data, ID_PREFIX);
if (IDstart == NULL)
if (IDstart == NULL) {
return -1;
}
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size)
if (strlen(IDstart) < TOX_ADDRESS_SIZE * 2 + prefix_size) {
return -1;
}
char ID_string[TOX_ADDRESS_SIZE * 2 + 1];
memcpy(ID_string, IDstart + prefix_size, TOX_ADDRESS_SIZE * 2);
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 0;
}
@ -233,10 +247,11 @@ void *lookup_thread_func(void *data)
char real_domain[MAX_DOMAIN_SIZE];
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.");
else
} else {
lookup_error(self, "Name server domain not found.");
}
kill_lookup_thread();
}

View File

@ -47,12 +47,12 @@
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#endif /* AUDIO */
#endif /* SOUND_NOTIFY */
#endif /* defined(AUDIO) || defined(SOUND_NOTIFY) */
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
@ -64,7 +64,7 @@
extern struct user_settings *user_settings;
struct Control {
static struct Control {
time_t cooldown;
time_t notif_timeout;
@ -79,12 +79,12 @@ struct Control {
#endif /* SOUND_NOTIFY */
} Control = {0};
struct _ActiveNotifications {
static struct _ActiveNotifications {
#ifdef SOUND_NOTIFY
uint32_t source;
uint32_t buffer;
bool looping;
#endif
#endif /* SOUND_NOTIFY */
bool active;
int *id_indicator;
#ifdef BOX_NOTIFY
@ -93,7 +93,7 @@ struct _ActiveNotifications {
char title[64];
size_t size;
time_t n_timeout;
#endif
#endif /* BOX_NOTIFY */
} actives[ACTIVE_NOTIFS_MAX];
/**********************************************************************************/
/**********************************************************************************/
@ -104,21 +104,24 @@ struct _ActiveNotifications {
/* coloured tab notifications: primary notification type */
static void tab_notify(ToxWindow *self, uint64_t flags)
{
if (self == NULL)
if (self == NULL) {
return;
}
if (flags & NT_WNDALERT_0)
if (flags & NT_WNDALERT_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;
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;
}
}
static bool notifications_are_disabled(uint64_t flags)
{
if (user_settings->alerts != ALERTS_ENABLED)
if (user_settings->alerts != ALERTS_ENABLED) {
return true;
}
bool res = (flags & NT_RESTOL) && (Control.cooldown > get_unix_time());
#ifdef X11
@ -128,14 +131,14 @@ static bool notifications_are_disabled(uint64_t flags)
#endif
}
static void control_lock()
static void control_lock(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_lock(Control.poll_mutex);
#endif
}
static void control_unlock()
static void control_unlock(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
pthread_mutex_unlock(Control.poll_mutex);
@ -157,11 +160,13 @@ static bool device_opened = false;
time_t last_opened_update = 0;
/* Opens primary device. Returns true on succe*/
void m_open_device()
void m_open_device(void)
{
last_opened_update = get_unix_time();
if (device_opened) return;
if (device_opened) {
return;
}
/* Blah error check */
open_primary_device(output, &Control.device_idx, 48000, 20, 1);
@ -169,9 +174,11 @@ void m_open_device()
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);
@ -179,7 +186,7 @@ void m_close_device()
}
/* Terminate all sounds but wait for them to finish first */
void graceful_clear()
void graceful_clear(void)
{
control_lock();
@ -196,17 +203,20 @@ void graceful_clear()
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 */
}
if ( actives[i].looping ) {
if (actives[i].looping) {
stop_sound(i);
} else {
if (!is_playing(actives[i].source))
if (!is_playing(actives[i].source)) {
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
else break;
} else {
break;
}
}
}
}
@ -241,7 +251,9 @@ void *do_playing(void *_p)
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;
#ifdef BOX_NOTIFY
@ -249,8 +261,9 @@ void *do_playing(void *_p)
#endif
if (test_active_notify) {
if (actives[i].id_indicator)
if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */
}
if (!is_playing(actives[i].source)) {
/* Close */
@ -267,8 +280,9 @@ void *do_playing(void *_p)
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if (actives[i].id_indicator)
if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */
}
if (!actives[i].looping && !is_playing(actives[i].source)) {
/* 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*/
@ -303,7 +317,7 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) {
if (i == ACTIVE_NOTIFS_MAX) {
return -1; /* Full */
}
@ -338,8 +352,9 @@ void *do_playing(void *_p)
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if (actives[i].id_indicator)
if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */
}
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
@ -352,7 +367,7 @@ void *do_playing(void *_p)
pthread_exit(NULL);
}
void graceful_clear()
void graceful_clear(void)
{
int i;
control_lock();
@ -364,15 +379,16 @@ void graceful_clear()
actives[i].box = NULL;
}
if (actives[i].id_indicator)
if (actives[i].id_indicator) {
*actives[i].id_indicator = -1; /* reset indicator value */
}
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
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 (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0) {
return -1;
}
Control.poll_active = 1;
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);
Control.poll_active = 0;
return -1;
}
#endif
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
Control.cooldown = time(NULL) + login_cooldown;
@ -414,12 +431,12 @@ int init_notify(int login_cooldown, int notification_timeout)
return 1;
}
void terminate_notify()
void terminate_notify(void)
{
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
control_lock();
if ( !Control.poll_active ) {
if (!Control.poll_active) {
control_unlock();
return;
}
@ -428,12 +445,14 @@ void terminate_notify()
control_unlock();
graceful_clear();
#endif
#endif /* defined(SOUND_NOTIFY) || defined(BOX_NOTIFY) */
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
for (; i < SOUNDS_SIZE; i ++) {
free(Control.sounds[i]);
}
alutExit();
#endif /* SOUND_NOTIFY */
@ -446,7 +465,9 @@ void terminate_notify()
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char *value)
{
if (sound == silent) return 0;
if (sound == silent) {
return 0;
}
free(Control.sounds[sound]);
@ -487,11 +508,14 @@ int play_notify_sound(Notification notif, uint64_t flags)
{
int rc = -1;
if (flags & NT_BEEP) beep();
if (flags & NT_BEEP) {
beep();
}
if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
if (!Control.poll_active || !Control.sounds[notif]) {
return -1;
}
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)
{
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
if (actives[id].box) {
@ -510,19 +534,20 @@ void stop_sound(int id)
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;
}
// alSourcei(actives[id].source, AL_LOOPING, false);
// alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1, &actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
#endif /* SOUND_NOTIFY */
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);
#else
if (notif != silent)
if (notif != silent) {
beep();
}
return -1;
#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)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
int id = -1;
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);
else if (flags & NT_ALWAYS)
} else if (flags & NT_ALWAYS) {
id = m_play_sound(notif, flags);
}
#if defined(BOX_NOTIFY) && !defined(SOUND_NOTIFY)
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
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;
*id_indicator = id;
}
@ -585,10 +607,13 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) {
return -1;
}
#ifdef SOUND_NOTIFY
control_lock();
@ -617,8 +642,9 @@ int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
return id;
#else
if (notif != silent)
if (notif != silent) {
beep();
}
return 0;
#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 ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
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].id_indicator = id_indicator;
if (id_indicator) *id_indicator = id;
if (id_indicator) {
*id_indicator = id;
}
}
#else
if (id == -1)
if (id == -1) {
return -1;
}
#endif /* SOUND_NOTIFY */
#endif /* SOUND_NOTIFY */
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_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __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, "...");
}
actives[id].box = notify_notification_new(actives[id].title, actives[id].messages[0], NULL);
actives[id].size++;
@ -687,7 +719,7 @@ int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indi
return id;
#else
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, ...)
@ -699,8 +731,9 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
#ifdef BOX_NOTIFY
if (sound_notify2(self, notif, flags, id) == -1)
if (sound_notify2(self, notif, flags, id) == -1) {
return -1;
}
control_lock();
@ -710,12 +743,13 @@ int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, con
}
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __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, "...");
}
actives[id].size++;
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;
#else
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, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
#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 ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
if (id == ACTIVE_NOTIFS_MAX) {
control_unlock();
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);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
if (strlen(title) > 23) {
strcpy(actives[id].title + 20, "...");
}
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[0], MAX_BOX_MSG_LEN, format, __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, "...");
}
actives[id].active = 1;
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;
#else
return -1;
#endif
#endif /* BOX_NOTIFY */
}
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
{
tab_notify(self, flags);
if (notifications_are_disabled(flags))
if (notifications_are_disabled(flags)) {
return -1;
}
#ifdef BOX_NOTIFY
control_lock();
if (id < 0 || id >= ACTIVE_NOTIFS_MAX || !actives[id].box || actives[id].size >= 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();
return -1;
}
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
va_start(__ARGS__, format);
vsnprintf(actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __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, "...");
}
actives[id].size ++;
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;
#else
return -1;
#endif
#endif /* BOX_NOTIFY */
}

View File

@ -60,7 +60,7 @@ typedef enum _Flags {
} Flags;
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_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__ */
int osx_video_init(char **device_names, int *size);
void osx_video_release();
void osx_video_release(void);
/* Start device */
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height);
/* 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);
#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;
}
if ( i <= 0 )
if (i <= 0) {
return nil;
}
*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;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
if ( error != NULL ) {
if (error != NULL) {
[input release];
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];
CMFormatDescriptionRef format_description = port.formatDescription;
if ( format_description ) {
if (format_description) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
*width = dimensions.width;
*height = dimensions.height;
@ -208,10 +209,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
// TODO possibly get a better pixel format
if (_shouldMangleDimensions) {
[_linkerVideo setVideoSettings: @ {
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480
}];
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480
}];
} else {
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
}
@ -303,8 +304,9 @@ int osx_video_init(char **device_names, int *size)
{
_OSXVideo = [[OSXVideo alloc] initWithDeviceNames: device_names AmtDevices: size];
if ( _OSXVideo == nil )
if (_OSXVideo == nil) {
return -1;
}
return 0;
}
@ -317,8 +319,9 @@ void osx_video_release()
int osx_video_open_device(uint32_t selection, uint16_t *width, uint16_t *height)
{
if ( _OSXVideo == nil )
if (_OSXVideo == nil) {
return -1;
}
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)
{
if ( _OSXVideo == nil )
if (_OSXVideo == nil) {
return -1;
}
return [_OSXVideo getVideoFrameY: y U: u V: v Width: width Height: height];
}

View File

@ -49,13 +49,27 @@ extern struct Winthread Winthread;
extern FriendsList Friends;
FriendRequests FrndRequests;
#ifdef VIDEO
#define AC_NUM_GLOB_COMMANDS 22
#elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20
#ifdef AUDIO
#define AC_NUM_GLOB_COMMANDS_AUDIO 2
#else
#define AC_NUM_GLOB_COMMANDS 18
#endif
#define AC_NUM_GLOB_COMMANDS_AUDIO 0
#endif /* AUDIO */
#ifdef VIDEO
#define AC_NUM_GLOB_COMMANDS_VIDEO 2
#else
#define AC_NUM_GLOB_COMMANDS_VIDEO 0
#endif /* VIDEO */
#ifdef PYTHON
#define AC_NUM_GLOB_COMMANDS_PYTHON 1
#else
#define AC_NUM_GLOB_COMMANDS_PYTHON 0
#endif /* PYTHON */
#ifdef QRCODE
#define AC_NUM_GLOB_COMMANDS_QRCODE 1
#else
#define AC_NUM_GLOB_COMMANDS_QRCODE 0
#endif /* QRCODE */
#define AC_NUM_GLOB_COMMANDS (17 + AC_NUM_GLOB_COMMANDS_AUDIO + AC_NUM_GLOB_COMMANDS_VIDEO + AC_NUM_GLOB_COMMANDS_PYTHON + AC_NUM_GLOB_COMMANDS_QRCODE)
/* Array of global command names used for tab completion. */
static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
@ -70,7 +84,9 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/help" },
{ "/log" },
{ "/myid" },
#ifdef QRCODE
{ "/myqr" },
#endif /* QRCODE */
{ "/nick" },
{ "/note" },
{ "/nospam" },
@ -92,6 +108,12 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
#endif /* VIDEO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
};
void kill_prompt_window(ToxWindow *self)
@ -115,7 +137,7 @@ void kill_prompt_window(ToxWindow *self)
}
/* 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)
{
StatusBar *statusbar = prompt->stb;
statusbar->connection = connection_status;
@ -137,26 +159,35 @@ void prompt_update_statusmessage(ToxWindow *prompt, Tox *m, const char *statusms
size_t len = strlen(statusbar->statusmsg);
statusbar->statusmsg_len = len;
TOX_ERR_SET_INFO err;
tox_self_set_status_message(m, (uint8_t *) statusmsg, len, &err);
Tox_Err_Set_Info 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);
}
}
/* 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->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.
Returns request number on success, -1 if queue is full. */
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;
}
int i;
@ -166,8 +197,9 @@ static int add_friend_request(const char *public_key, const char *data)
memcpy(FrndRequests.request[i].key, public_key, TOX_PUBLIC_KEY_SIZE);
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.num_requests;
@ -186,11 +218,13 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
getyx(self->window, y, x);
getmaxyx(self->window, y2, x2);
if (x2 <= 0 || y2 <= 0)
if (x2 <= 0 || y2 <= 0) {
return;
}
if (ctx->pastemode && key == '\r')
if (ctx->pastemode && key == '\r') {
key = '\n';
}
/* ignore non-menu related input if active */
if (self->help->active) {
@ -203,8 +237,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
return;
}
if (line_info_onKey(self, key))
if (line_info_onKey(self, key)) {
return;
}
input_handle(self, key, x, y, x2, y2);
@ -212,8 +247,17 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (ctx->len > 1 && ctx->line[0] == '/') {
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");
}
#ifdef PYTHON
else if (wcsncmp(ctx->line, L"/run ", wcslen(L"/run ")) == 0) {
diff = dir_match(self, m, ctx->line, L"/run");
}
#endif
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
const char status_cmd_list[3][8] = {
{"online"},
@ -221,8 +265,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
{"busy"},
};
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);
}
if (diff != -1) {
if (x + diff > x2 - 1) {
@ -244,8 +289,9 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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));
}
line_info_add(self, NULL, NULL, NULL, PROMPT, 0, 0, "%s", line);
execute(ctx->history, self, m, line, GLOBAL_COMMAND_MODE);
@ -262,8 +308,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
int x2, y2;
getmaxyx(self->window, y2, x2);
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
ChatContext *ctx = self->chatwin;
@ -275,8 +322,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
curs_set(1);
if (ctx->len > 0)
if (ctx->len > 0) {
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
}
StatusBar *statusbar = self->stb;
@ -284,7 +332,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wmove(statusbar->topline, 0, 0);
pthread_mutex_lock(&Winthread.lock);
TOX_CONNECTION connection = statusbar->connection;
Tox_Connection connection = statusbar->connection;
pthread_mutex_unlock(&Winthread.lock);
if (connection != TOX_CONNECTION_NONE) {
@ -292,7 +340,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
const char *status_text = "ERROR";
pthread_mutex_lock(&Winthread.lock);
TOX_USER_STATUS status = statusbar->status;
Tox_User_Status status = statusbar->status;
pthread_mutex_unlock(&Winthread.lock);
switch (status) {
@ -336,7 +384,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock);
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';
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg);
@ -356,8 +404,9 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
statusbar->statusmsg_len = maxlen;
}
if (statusbar->statusmsg[0])
if (statusbar->statusmsg[0]) {
wprintw(statusbar->topline, " : %s", statusbar->statusmsg);
}
pthread_mutex_unlock(&Winthread.lock);
@ -372,19 +421,21 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
wnoutrefresh(self->window);
if (self->help->active)
if (self->help->active) {
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;
char nick[TOX_MAX_NAME_LENGTH] = {0}; /* stop removing this initiation */
get_nick_truncate(m, nick, friendnum);
if (!nick[0])
if (!nick[0]) {
snprintf(nick, sizeof(nick), "%s", UNKNOWN_NAME);
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
@ -399,23 +450,25 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
line_info_add(self, timefrmt, nick, NULL, CONNECTION, 0, GREEN, msg);
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,
"%s has come online", nick );
else
"%s has come online", nick);
} else {
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) {
msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
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,
"%s has gone offline", nick );
else
"%s has gone offline", nick);
} else {
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);
}
}
}
@ -441,13 +494,14 @@ static void prompt_onFriendRequest(ToxWindow *self, Tox *m, const char *key, con
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;
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);
}
(void) y2;
@ -465,12 +519,12 @@ void prompt_init_statusbar(ToxWindow *self, Tox *m)
size_t s_len = tox_self_get_status_message_size(m);
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';
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");
s_len = strlen(statusmsg);
statusmsg[s_len] = '\0';
@ -506,8 +560,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
int 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);
}
ChatContext *ctx = self->chatwin;
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
@ -516,8 +571,9 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
ctx->log = calloc(1, sizeof(struct chatlog));
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);
}
line_info_init(ctx->hst);
@ -525,46 +581,51 @@ static void prompt_onInit(ToxWindow *self, Tox *m)
char myid[TOX_ADDRESS_SIZE];
tox_self_get_address(m, (uint8_t *) myid);
if (log_enable(self->name, myid, NULL, ctx->log, LOG_PROMPT) == -1)
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.");
}
}
scrollok(ctx->history, 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);
}
}
ToxWindow new_prompt(void)
ToxWindow *new_prompt(void)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
ToxWindow *ret = calloc(1, sizeof(ToxWindow));
ret.num = -1;
ret.active = true;
ret.is_prompt = true;
if (ret == NULL) {
exit_toxic_err("failed in new_prompt", FATALERR_MEMORY);
}
ret.onKey = &prompt_onKey;
ret.onDraw = &prompt_onDraw;
ret.onInit = &prompt_onInit;
ret.onConnectionChange = &prompt_onConnectionChange;
ret.onFriendRequest = &prompt_onFriendRequest;
ret->num = -1;
ret->is_prompt = true;
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));
StatusBar *stb = calloc(1, sizeof(StatusBar));
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);
}
ret.chatwin = chatwin;
ret.stb = stb;
ret.help = help;
ret->chatwin = chatwin;
ret->stb = stb;
ret->help = help;
ret.active_box = -1;
ret->active_box = -1;
return ret;
}

View File

@ -40,17 +40,22 @@ typedef struct {
struct friend_request request[MAX_FRIEND_REQUESTS];
} FriendRequests;
ToxWindow new_prompt(void);
extern FriendRequests FrndRequests;
ToxWindow *new_prompt(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_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 kill_prompt_window(ToxWindow *self);
/* 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 */

367
src/python_api.c Normal file
View File

@ -0,0 +1,367 @@
/* python_api.c
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifdef PYTHON
#include <Python.h>
#include "api.h"
#include "execute.h"
extern Tox *user_tox;
static struct python_registered_func {
char *name;
char *help;
PyObject *callback;
struct python_registered_func *next;
} python_commands = {0};
static PyObject *python_api_display(PyObject *self, PyObject *args)
{
const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL;
}
api_display(msg);
return Py_None;
}
static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
{
char *name;
PyObject *ret;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
name = api_get_nick();
if (name == NULL) {
return NULL;
}
ret = Py_BuildValue("s", name);
free(name);
return ret;
}
static PyObject *python_api_get_status(PyObject *self, PyObject *args)
{
PyObject *ret = NULL;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
switch (api_get_status()) {
case TOX_USER_STATUS_NONE:
ret = Py_BuildValue("s", "online");
break;
case TOX_USER_STATUS_AWAY:
ret = Py_BuildValue("s", "away");
break;
case TOX_USER_STATUS_BUSY:
ret = Py_BuildValue("s", "busy");
break;
}
return ret;
}
static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
{
char *status;
PyObject *ret;
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
status = api_get_status_message();
if (status == NULL) {
return NULL;
}
ret = Py_BuildValue("s", status);
free(status);
return ret;
}
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
{
size_t i, ii;
FriendsList friends;
PyObject *cur, *ret;
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (!PyArg_ParseTuple(args, "")) {
return NULL;
}
friends = api_get_friendslist();
ret = PyList_New(0);
for (i = 0; i < friends.num_friends; i++) {
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++) {
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
}
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
PyList_Append(ret, cur);
}
return ret;
}
static PyObject *python_api_send(PyObject *self, PyObject *args)
{
const char *msg;
if (!PyArg_ParseTuple(args, "s", &msg)) {
return NULL;
}
api_send(msg);
return Py_None;
}
static PyObject *python_api_execute(PyObject *self, PyObject *args)
{
int mode;
const char *command;
if (!PyArg_ParseTuple(args, "si", &command, &mode)) {
return NULL;
}
api_execute(command, mode);
return Py_None;
}
static PyObject *python_api_register(PyObject *self, PyObject *args)
{
struct python_registered_func *cur;
size_t command_len, help_len;
const char *command, *help;
PyObject *callback;
if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback)) {
return NULL;
}
if (!PyCallable_Check(callback)) {
PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable");
return NULL;
}
if (command[0] != '/') {
PyErr_SetString(PyExc_TypeError, "Command must be prefixed with a '/'");
return NULL;
}
for (cur = &python_commands; ; cur = cur->next) {
if (cur->name != NULL && !strcmp(command, cur->name)) {
Py_XDECREF(cur->callback);
Py_XINCREF(callback);
cur->callback = callback;
break;
}
if (cur->next == NULL) {
Py_XINCREF(callback);
cur->next = malloc(sizeof(struct python_registered_func));
if (cur->next == NULL) {
return PyErr_NoMemory();
}
command_len = strlen(command);
cur->next->name = malloc(command_len + 1);
if (cur->next->name == NULL) {
return PyErr_NoMemory();
}
strncpy(cur->next->name, command, command_len + 1);
help_len = strlen(help);
cur->next->help = malloc(help_len + 1);
if (cur->next->help == NULL) {
return PyErr_NoMemory();
}
strncpy(cur->next->help, help, help_len + 1);
cur->next->callback = callback;
cur->next->next = NULL;
break;
}
}
Py_INCREF(Py_None);
return Py_None;
}
static PyMethodDef ToxicApiMethods[] = {
{"display", python_api_display, METH_VARARGS, "Display a message to the current prompt"},
{"get_nick", python_api_get_nick, METH_VARARGS, "Return the user's current nickname"},
{"get_status", python_api_get_status, METH_VARARGS, "Returns the user's current status"},
{"get_status_message", python_api_get_status_message, METH_VARARGS, "Return the user's current status message"},
{"get_all_friends", python_api_get_all_friends, METH_VARARGS, "Return all of the user's friends"},
{"send", python_api_send, METH_VARARGS, "Send the message to the current user"},
{"execute", python_api_execute, METH_VARARGS, "Execute a command like `/nick`"},
{"register", python_api_register, METH_VARARGS, "Register a command like `/nick` to a Python function"},
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef toxic_api_module = {
PyModuleDef_HEAD_INIT,
"toxic_api",
NULL,
-1,
ToxicApiMethods
};
PyMODINIT_FUNC PyInit_toxic_api(void)
{
PyObject *m = PyModule_Create(&toxic_api_module);
PyObject *global_command_const = Py_BuildValue("i", GLOBAL_COMMAND_MODE);
PyObject *chat_command_const = Py_BuildValue("i", CHAT_COMMAND_MODE);
PyObject *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE);
PyObject_SetAttrString(m, "GLOBAL_COMMAND", global_command_const);
PyObject_SetAttrString(m, "CHAT_COMMAND", chat_command_const);
PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const);
Py_DECREF(global_command_const);
Py_DECREF(chat_command_const);
Py_DECREF(groupchat_command_const);
return m;
}
void terminate_python(void)
{
struct python_registered_func *cur, *old;
if (python_commands.name != NULL) {
free(python_commands.name);
}
for (cur = python_commands.next; cur != NULL;) {
old = cur;
cur = cur->next;
free(old->name);
free(old);
}
Py_Finalize();
}
void init_python(Tox *m)
{
user_tox = m;
PyImport_AppendInittab("toxic_api", PyInit_toxic_api);
Py_Initialize();
}
void run_python(FILE *fp, char *path)
{
PyRun_SimpleFile(fp, path);
}
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE])
{
int i;
PyObject *callback_args, *args_strings;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name == NULL) {
continue;
}
if (!strcmp(args[0], cur->name)) {
args_strings = PyList_New(0);
for (i = 1; i < num_args; i++) {
PyList_Append(args_strings, Py_BuildValue("s", args[i]));
}
callback_args = PyTuple_Pack(1, args_strings);
if (PyObject_CallObject(cur->callback, callback_args) == NULL) {
api_display("Exception raised in callback function");
}
return 0;
}
}
return 1;
}
int python_num_registered_handlers(void)
{
int n = 0;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
n++;
}
}
return n;
}
int python_help_max_width(void)
{
size_t tmp;
int max = 0;
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
tmp = strlen(cur->help);
max = tmp > max ? tmp : max;
}
}
max = max > 50 ? 50 : max;
return 37 + max;
}
void python_draw_handler_help(WINDOW *win)
{
struct python_registered_func *cur;
for (cur = &python_commands; cur != NULL; cur = cur->next) {
if (cur->name != NULL) {
wprintw(win, " %-29s: %.50s\n", cur->name, cur->help);
}
}
}
#endif /* PYTHON */

39
src/python_api.h Normal file
View File

@ -0,0 +1,39 @@
/* python_api.h
*
*
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
*
* This file is part of Toxic.
*
* Toxic is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Toxic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef PYTHON_API_H
#define PYTHON_API_H
#ifdef PYTHON
#include <Python.h>
#endif /* PYTHON */
PyMODINIT_FUNC PyInit_toxic_api(void);
void terminate_python(void);
void init_python(Tox *m);
void run_python(FILE *fp, char *path);
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE]);
int python_num_registered_handlers(void);
int python_help_max_width(void);
void python_draw_handler_help(WINDOW *win);
#endif /* PYTHON_API_H */

View File

@ -20,6 +20,8 @@
*
*/
#ifdef QRCODE
#include <stdlib.h>
#include <qrencode.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");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
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 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, "\n");
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);
}
const unsigned char *row_1 = qr_obj->data + width * i;
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 y = (i + 1) < width ? (row_2[j] & 1) : false;
if (x && y)
if (x && y) {
fprintf(fp, " ");
else if (x)
} else if (x) {
fprintf(fp, "%s", CHAR_2);
else if (y)
} else if (y) {
fprintf(fp, "%s", CHAR_3);
else
} else {
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, "\n");
}
@ -208,3 +215,5 @@ int ID_to_QRcode_png(const char *tox_id, const char *outfile)
return 0;
}
#endif /* QRPNG */
#endif /* QRCODE */

View File

@ -23,6 +23,8 @@
#ifndef QR_CODE
#define QR_CODE
#ifdef QRCODE
#define QRCODE_FILENAME_EXT ".QRcode"
/* 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);
#endif /* QRPNG */
#endif /* QRCODE */
#endif /* QR_CODE */

View File

@ -124,10 +124,10 @@ static void ui_defaults(struct user_settings *settings)
snprintf(settings->line_normal, LINE_HINT_MAX + 1, "%s", LINE_NORMAL);
settings->mplex_away = MPLEX_ON;
snprintf (settings->mplex_away_note,
sizeof (settings->mplex_away_note),
"%s",
MPLEX_AWAY_NOTE);
snprintf(settings->mplex_away_note,
sizeof(settings->mplex_away_note),
"%s",
MPLEX_AWAY_NOTE);
}
static const struct keys_strings {
@ -179,12 +179,14 @@ static const struct tox_strings {
const char *download_path;
const char *chatlogs_path;
const char *avatar_path;
const char *autorun_path;
const char *password_eval;
} tox_strings = {
"tox",
"download_path",
"chatlogs_path",
"avatar_path",
"autorun_path",
"password_eval",
};
@ -193,6 +195,7 @@ static void tox_defaults(struct user_settings *settings)
strcpy(settings->download_path, "");
strcpy(settings->chatlogs_path, "");
strcpy(settings->avatar_path, "");
strcpy(settings->autorun_path, "");
strcpy(settings->password_eval, "");
}
@ -250,15 +253,18 @@ static int key_parse(const char **bind)
int len = strlen(*bind);
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;
}
}
if (strncasecmp(*bind, "tab", 3) == 0)
if (strncasecmp(*bind, "tab", 3) == 0) {
return T_KEY_TAB;
}
if (strncasecmp(*bind, "page", 4) == 0)
if (strncasecmp(*bind, "page", 4) == 0) {
return len == 6 ? KEY_PPAGE : KEY_NPAGE;
}
return -1;
}
@ -301,8 +307,9 @@ int settings_load(struct user_settings *s, const char *patharg)
if (!file_exists(path)) {
FILE *fp = fopen(path, "w");
if (fp == NULL)
if (fp == NULL) {
return -1;
}
fclose(fp);
}
@ -321,18 +328,18 @@ int settings_load(struct user_settings *s, const char *patharg)
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) {
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]");
}
}
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);
}
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);
}
@ -364,66 +371,85 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_int(setting, ui_strings.history_size, &s->history_size);
config_setting_lookup_int(setting, ui_strings.nodeslist_update_freq, &s->nodeslist_update_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);
}
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);
}
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);
}
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);
}
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)) {
snprintf (s->mplex_away_note, sizeof (s->mplex_away_note), "%s", 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);
}
}
/* paths */
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);
int len = strlen(s->download_path);
/* 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';
else if (s->download_path[len - 1] != '/')
} else if (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);
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';
else if (s->chatlogs_path[len - 1] != '/')
} else if (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);
int len = strlen(str);
if (len >= sizeof(s->avatar_path))
if (len >= sizeof(s->avatar_path)) {
s->avatar_path[0] = '\0';
}
}
if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) {
#ifdef PYTHON
if (config_setting_lookup_string(setting, tox_strings.autorun_path, &str)) {
snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str);
int len = strlen(str);
if (len >= sizeof(s->autorun_path) - 2) {
s->autorun_path[0] = '\0';
} else if (s->autorun_path[len - 1] != '/') {
strcat(&s->autorun_path[len - 1], "/");
}
}
#endif
if (config_setting_lookup_string(setting, tox_strings.password_eval, &str)) {
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
int len = strlen(str);
if (len >= sizeof(s->password_eval))
if (len >= sizeof(s->password_eval)) {
s->password_eval[0] = '\0';
}
}
}
@ -431,38 +457,49 @@ int settings_load(struct user_settings *s, const char *patharg)
if ((setting = config_lookup(cfg, key_strings.self)) != 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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp))
if (config_setting_lookup_string(setting, key_strings.peer_list_up, &tmp)) {
set_key_binding(&s->key_peer_list_up, &tmp);
}
if (config_setting_lookup_string(setting, key_strings.peer_list_down, &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))
if (config_setting_lookup_string(setting, key_strings.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);
}
}
#ifdef AUDIO
@ -482,52 +519,60 @@ int settings_load(struct user_settings *s, const char *patharg)
#ifdef SOUND_NOTIFY
if ((setting = config_lookup(cfg, sound_strings.self)) != NULL) {
if ( (config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
!set_sound(notif_error, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if ((config_setting_lookup_string(setting, sound_strings.notif_error, &str) != CONFIG_TRUE) ||
!set_sound(notif_error, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.user_log_in, &str) ||
!set_sound(user_log_in, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.user_log_out, &str) ||
!set_sound(user_log_out, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.call_incoming, &str) ||
!set_sound(call_incoming, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(call_incoming, PACKAGE_DATADIR "/sounds/ToxicIncomingCall.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.call_outgoing, &str) ||
!set_sound(call_outgoing, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(call_outgoing, PACKAGE_DATADIR "/sounds/ToxicOutgoingCall.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.generic_message, &str) ||
!set_sound(generic_message, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(generic_message, PACKAGE_DATADIR "/sounds/ToxicRecvMessage.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.transfer_pending, &str) ||
!set_sound(transfer_pending, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
}
}
if ( !config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str) ) {
if (str && strcasecmp(str, NO_SOUND) != 0)
if (!config_setting_lookup_string(setting, sound_strings.transfer_completed, &str) ||
!set_sound(transfer_completed, str)) {
if (str && strcasecmp(str, NO_SOUND) != 0) {
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
}
} else {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");

View File

@ -63,6 +63,7 @@ struct user_settings {
char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX];
char autorun_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_MAX];
int key_next_tab;
@ -87,7 +88,7 @@ struct user_settings {
#endif
};
enum {
enum settings_values {
AUTOLOG_OFF = 0,
AUTOLOG_ON = 1,
@ -113,7 +114,7 @@ enum {
MPLEX_OFF = 0,
MPLEX_ON = 1,
} settings_values;
};
#define LINE_JOIN "-->"
#define LINE_QUIT "<--"
@ -124,4 +125,5 @@ enum {
#define MPLEX_AWAY_NOTE "Detached from screen"
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 "global_commands.h"
#include "execute.h"
#include "windows.h"
#include "term_mplex.h"
#include "toxic.h"
@ -69,7 +69,7 @@ static char buffer [BUFFER_SIZE];
static bool auto_away_active = false;
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] = "";
/* 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_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;
char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) {
int length = dyn_buffer_size + strlen (input_ptr);
while ((input_ptr = fgets(buffer, BUFFER_SIZE, stream)) != NULL) {
int length = dyn_buffer_size + strlen(input_ptr);
if (dyn_buffer)
dyn_buffer = (char *) realloc (dyn_buffer, length);
else
dyn_buffer = (char *) malloc (length);
if (dyn_buffer) {
dyn_buffer = (char *) realloc(dyn_buffer, length);
} else {
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;
}
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 *pos = strstr (info, search_str);
const char *pos = strstr(info, search_str);
char *end = NULL;
char *path = NULL;
if (!pos)
if (!pos) {
return NULL;
}
pos += strlen (search_str);
pos = strchr (pos, PATH_SEP_C);
pos += strlen(search_str);
pos = strchr(pos, PATH_SEP_C);
if (!pos)
if (!pos) {
return NULL;
}
end = strchr (pos, '\n');
end = strchr(pos, '\n');
if (!end)
if (!end) {
return NULL;
}
*end = '\0';
end = strrchr (pos, '.');
end = strrchr(pos, '.');
if (!end)
if (!end) {
return NULL;
}
path = (char *) malloc (end - pos + 1);
path = (char *) malloc(end - pos + 1);
*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;
char *socket_name = NULL, *socket_path = NULL;
char *dyn_buffer = NULL;
socket_name = getenv ("STY");
socket_name = getenv("STY");
if (!socket_name)
if (!socket_name) {
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;
}
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;
}
pclose (session_info_stream);
pclose(session_info_stream);
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;
}
free (dyn_buffer);
free(dyn_buffer);
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;
}
strcpy (mplex_data, socket_path);
strcat (mplex_data, PATH_SEP_S);
strcat (mplex_data, socket_name);
free (socket_path);
strcpy(mplex_data, socket_path);
strcat(mplex_data, PATH_SEP_S);
strcat(mplex_data, socket_name);
free(socket_path);
socket_path = NULL;
mplex = MPLEX_SCREEN;
@ -189,33 +199,38 @@ static int detect_gnu_screen ()
nomplex:
if (session_info_stream)
pclose (session_info_stream);
if (session_info_stream) {
pclose(session_info_stream);
}
if (dyn_buffer)
free (dyn_buffer);
if (dyn_buffer) {
free(dyn_buffer);
}
if (socket_path)
if (socket_path) {
free(socket_path);
}
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;
}
/* find second separator */
pos = strrchr (tmux_env, ',');
pos = strrchr(tmux_env, ',');
if (!pos)
if (!pos) {
return 0;
}
/* store the session number string for later use */
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1);
/* store the session id for later use */
snprintf(mplex_data, sizeof(mplex_data), "$%s", pos + 1);
mplex = MPLEX_TMUX;
return 1;
}
@ -228,98 +243,102 @@ static int detect_tmux ()
Returns 1 if present, 0 otherwise. This value can be used to determine
whether an auto-away detection timer is needed.
*/
static int detect_mplex ()
static int detect_mplex(void)
{
/* 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
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;
}
struct stat sb;
if (stat (mplex_data, &sb) != 0)
if (stat(mplex_data, &sb) != 0) {
return 0;
}
/* 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
current session's entry. An attached entry ends with "(attached)". Example:
$ tmux list-sessions
0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached)
1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23]
In this example, session 0 is attached and session 1 is detached.
*/
static int tmux_is_detached ()
current session's entry.
*/
static int tmux_is_detached(void)
{
if (mplex != MPLEX_TMUX)
if (mplex != MPLEX_TMUX) {
return 0;
}
FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos, *nl_pos, *attached_pos;
const int numstr_len = strlen (mplex_data);
char *entry_pos;
int detached;
const int numstr_len = strlen(mplex_data);
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
/* get the number of attached clients for each session */
session_info_stream = popen("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r");
if (!session_info_stream)
if (!session_info_stream) {
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;
}
pclose (session_info_stream);
pclose(session_info_stream);
session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */
search_str = (char *) malloc (numstr_len + 4);
search_str = (char *) malloc(numstr_len + 2);
search_str[0] = '\n';
strcpy (search_str + 1, mplex_data);
strcat (search_str, ": ");
strcpy(search_str + 1, mplex_data);
/* do the search */
if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0)
if (strncmp(dyn_buffer, search_str + 1, numstr_len) == 0) {
entry_pos = dyn_buffer;
else
entry_pos = strstr (dyn_buffer, search_str);
} else {
entry_pos = strstr(dyn_buffer, search_str);
}
if (! entry_pos)
if (! entry_pos) {
goto fail;
}
/* find the next \n and look for the "(attached)" before it */
nl_pos = strchr (entry_pos + 1, '\n');
attached_pos = strstr (entry_pos + 1, "(attached)\n");
entry_pos = strchr(entry_pos, ' ') + 1;
detached = strncmp(entry_pos, "0\n", 2) == 0;
free (search_str);
free(search_str);
search_str = NULL;
free (dyn_buffer);
free(dyn_buffer);
dyn_buffer = NULL;
return attached_pos == NULL || attached_pos > nl_pos;
return detached;
fail:
if (session_info_stream)
pclose (session_info_stream);
if (session_info_stream) {
pclose(session_info_stream);
}
if (dyn_buffer)
free (dyn_buffer);
if (dyn_buffer) {
free(dyn_buffer);
}
if (search_str)
free (search_str);
if (search_str) {
free(search_str);
}
return 0;
}
@ -333,24 +352,25 @@ fail:
sample its state and update away status according to attached/detached state
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;
if (mplex == MPLEX_NONE)
if (mplex == MPLEX_NONE) {
return;
}
int detached = mplex_is_detached ();
int detached = mplex_is_detached();
pthread_mutex_lock (&Winthread.lock);
current_status = tox_self_get_status (m);
pthread_mutex_unlock (&Winthread.lock);
pthread_mutex_lock(&Winthread.lock);
current_status = tox_self_get_status(m);
pthread_mutex_unlock(&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) {
auto_away_active = false;
@ -360,25 +380,27 @@ static void mplex_timer_handler (Tox *m)
auto_away_active = true;
prev_status = current_status;
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);
tox_self_get_status_message (m, (uint8_t *) prev_note);
tox_self_get_status_message(m, (uint8_t *) prev_note);
prev_note[slen] = '\0';
pthread_mutex_unlock (&Winthread.lock);
pthread_mutex_unlock(&Winthread.lock);
new_note = user_settings->mplex_away_note;
} else
} else {
return;
}
char argv[3][MAX_STR_SIZE];
strcpy (argv[0], "/status");
strcpy (argv[1], (new_status == TOX_USER_STATUS_AWAY ? "away" :
new_status == TOX_USER_STATUS_BUSY ? "busy" : "online"));
argv[2][0] = '\"';
strcpy (argv[2] + 1, new_note);
strcat (argv[2], "\"");
pthread_mutex_lock (&Winthread.lock);
cmd_status (prompt->chatwin->history, prompt, m, 2, argv);
pthread_mutex_unlock (&Winthread.lock);
char status_str[MAX_STR_SIZE];
char note_str[MAX_STR_SIZE];
const char *status = new_status == TOX_USER_STATUS_AWAY ? "away" :
new_status == TOX_USER_STATUS_BUSY ? "busy" : "online";
snprintf(status_str, sizeof(status_str), "/status %s", status);
snprintf(note_str, sizeof(status_str), "/note %s", new_note);
pthread_mutex_lock(&Winthread.lock);
execute(prompt->chatwin->history, prompt, m, status_str, GLOBAL_COMMAND_MODE);
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 */
@ -394,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;
}
if (! user_settings->mplex_away)
if (! user_settings->mplex_away) {
return 0;
}
/* status access mutex */
if (pthread_mutex_init (&status_lock, NULL) != 0)
if (pthread_mutex_init(&status_lock, NULL) != 0) {
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 0;
}

View File

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

View File

@ -26,6 +26,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdarg.h>
#include <signal.h>
#include <locale.h>
#include <string.h>
@ -51,6 +52,7 @@
#include "friendlist.h"
#include "prompt.h"
#include "misc_tools.h"
#include "groupchat.h"
#include "file_transfers.h"
#include "line_info.h"
#include "settings.h"
@ -75,6 +77,11 @@
ToxAV *av;
#endif /* AUDIO */
#ifdef PYTHON
#include "api.h"
#include "python_api.h"
#endif
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#endif
@ -104,9 +111,17 @@ static struct user_password {
int len;
} user_password;
static time_t last_signal_time;
static void catch_SIGINT(int sig)
{
Winthread.sig_exit_toxic = 1;
time_t cur_time = get_unix_time();
if (difftime(cur_time, last_signal_time) <= 1) {
Winthread.sig_exit_toxic = 1;
} else {
last_signal_time = cur_time;
}
}
static void catch_SIGSEGV(int sig)
@ -162,6 +177,10 @@ void exit_toxic_success(Tox *m)
terminate_audio();
#endif /* AUDIO */
#ifdef PYTHON
terminate_python();
#endif /* PYTHON */
free_global_data();
tox_kill(m);
endwin();
@ -210,8 +229,9 @@ static void init_term(void)
start_color();
if (user_settings->colour_theme == NATIVE_COLS) {
if (assume_default_colors(-1, -1) == OK)
if (assume_default_colors(-1, -1) == OK) {
bg_color = -1;
}
}
init_pair(0, COLOR_WHITE, COLOR_BLACK);
@ -248,13 +268,15 @@ static void queue_init_message(const char *msg, ...)
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);
}
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);
}
snprintf(new_msgs[i], MAX_STR_SIZE, "%s", frmt_msg);
init_messages.msgs = new_msgs;
@ -263,13 +285,15 @@ static void queue_init_message(const char *msg, ...)
/* called after messages have been printed to prompt and are no longer needed */
static void cleanup_init_messages(void)
{
if (init_messages.num <= 0)
if (init_messages.num <= 0) {
return;
}
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);
}
@ -278,8 +302,9 @@ static void print_init_messages(ToxWindow *toxwin)
{
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]);
}
}
static void load_friendlist(Tox *m)
@ -287,12 +312,46 @@ static void load_friendlist(Tox *m)
size_t i;
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);
}
sort_friendlist_index();
}
static void load_groups(ToxWindow *prompt, Tox *m)
{
size_t i;
size_t num_chats = tox_conference_get_chatlist_size(m);
uint32_t chatlist[num_chats];
if (num_chats) {
tox_conference_get_chatlist(m, chatlist);
}
for (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;
}
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
tox_conference_delete(m, groupnum, NULL);
continue;
}
}
}
/* return length of password on success, 0 on failure */
static int password_prompt(char *buf, int size)
{
@ -305,8 +364,9 @@ static int password_prompt(char *buf, int size)
nflags.c_lflag &= ~ECHO;
nflags.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0)
if (tcsetattr(fileno(stdin), TCSANOW, &nflags) != 0) {
return 0;
}
const char *p = fgets(buf, size, stdin);
int len = strlen(buf);
@ -314,15 +374,16 @@ static int password_prompt(char *buf, int size)
/* re-enable terminal echo */
tcsetattr(fileno(stdin), TCSANOW, &oflags);
if (p == NULL || len <= 1)
if (p == NULL || len <= 1) {
return 0;
}
/* eat overflowed stdin and return error */
if (buf[--len] != '\n') {
int ch;
while ((ch = getchar()) != '\n' && ch > 0)
;
while ((ch = getchar()) != '\n' && ch > 0) {
}
return 0;
}
@ -382,17 +443,20 @@ static void first_time_encrypt(const char *msg)
do {
system("clear");
printf("%s ", msg);
fflush(stdout);
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;
}
} while (fgets(ch, sizeof(ch), stdin));
printf("\n");
if (ch[0] == 'q' || ch[0] == 'Q')
if (ch[0] == 'q' || ch[0] == 'Q') {
exit(0);
}
if (ch[0] == 'y' || ch[0] == 'Y') {
int len = 0;
@ -402,11 +466,13 @@ static void first_time_encrypt(const char *msg)
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
while (valid_password == false) {
fflush(stdout); // Flush all before user input
len = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = len;
if (strcasecmp(user_password.pass, "q") == 0)
if (strcasecmp(user_password.pass, "q") == 0) {
exit(0);
}
if (string_is_empty(passconfirm) && (len < MIN_PASSWORD_LEN || len > MAX_PASSWORD_LEN)) {
printf("Password must be between %d and %d characters long. ", MIN_PASSWORD_LEN, MAX_PASSWORD_LEN);
@ -520,22 +586,23 @@ int store_data(Tox *m, const char *path)
static void init_tox_callbacks(Tox *m)
{
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange);
tox_callback_friend_connection_status(m, on_connectionchange);
tox_callback_friend_typing(m, on_typing_change);
tox_callback_friend_request(m, on_request);
tox_callback_friend_message(m, on_message);
tox_callback_friend_name(m, on_nickchange);
tox_callback_friend_status(m, on_statuschange);
tox_callback_friend_status_message(m, on_statusmessagechange);
tox_callback_friend_read_receipt(m, on_read_receipt);
tox_callback_conference_invite(m, on_groupinvite);
tox_callback_conference_message(m, on_groupmessage);
tox_callback_conference_namelist_change(m, on_group_namelistchange);
tox_callback_conference_title(m, on_group_titlechange);
tox_callback_self_connection_status(m, on_self_connection_status);
tox_callback_friend_connection_status(m, on_friend_connection_status);
tox_callback_friend_typing(m, on_friend_typing);
tox_callback_friend_request(m, on_friend_request);
tox_callback_friend_message(m, on_friend_message);
tox_callback_friend_name(m, on_friend_name);
tox_callback_friend_status(m, on_friend_status);
tox_callback_friend_status_message(m, on_friend_status_message);
tox_callback_friend_read_receipt(m, on_friend_read_receipt);
tox_callback_conference_invite(m, on_conference_invite);
tox_callback_conference_message(m, on_conference_message);
tox_callback_conference_peer_list_changed(m, on_conference_peer_list_changed);
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_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);
}
@ -543,30 +610,32 @@ static void init_tox_options(struct Tox_Options *tox_opts)
{
tox_options_default(tox_opts);
tox_opts->ipv6_enabled = !arg_opts.use_ipv4;
tox_opts->udp_enabled = !arg_opts.force_tcp;
tox_opts->proxy_type = arg_opts.proxy_type;
tox_opts->tcp_port = arg_opts.tcp_port;
tox_options_set_ipv6_enabled(tox_opts, !arg_opts.use_ipv4);
tox_options_set_udp_enabled(tox_opts, !arg_opts.force_tcp);
tox_options_set_proxy_type(tox_opts, arg_opts.proxy_type);
tox_options_set_tcp_port(tox_opts, arg_opts.tcp_port);
if (!tox_opts->ipv6_enabled)
if (!tox_options_get_ipv6_enabled(tox_opts)) {
queue_init_message("Forcing IPv4 connection");
}
if (tox_opts->tcp_port)
queue_init_message("TCP relaying enabled on port %d", tox_opts->tcp_port);
if (tox_options_get_tcp_port(tox_opts)) {
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) {
tox_opts->proxy_port = arg_opts.proxy_port;
tox_opts->proxy_host = arg_opts.proxy_address;
const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
if (tox_options_get_proxy_type(tox_opts) != TOX_PROXY_TYPE_NONE) {
tox_options_set_proxy_port(tox_opts, arg_opts.proxy_port);
tox_options_set_proxy_host(tox_opts, arg_opts.proxy_address);
const char *ps = tox_options_get_proxy_type(tox_opts) == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
char tmp[48];
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);
queue_init_message("%s", tmp);
}
if (!tox_opts->udp_enabled) {
if (!tox_options_get_udp_enabled(tox_opts)) {
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.";
queue_init_message("%s", msg);
msg = "Use the -t option to disable UDP.";
@ -577,7 +646,7 @@ static void init_tox_options(struct Tox_Options *tox_opts)
/* Returns a new Tox object on success.
* 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;
@ -606,14 +675,16 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
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);
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);
}
if (is_encrypted) {
if (!arg_opts.unencrypt_data)
if (!arg_opts.unencrypt_data) {
user_password.data_is_encrypted = true;
}
size_t pwlen = 0;
int pweval = user_settings->password_eval[0];
@ -627,6 +698,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
char plain[plain_len];
while (true) {
fflush(stdout); // Flush before prompts so the user sees the question/message
if (pweval) {
pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
} else {
@ -653,9 +726,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
(uint8_t *) plain, &pwerr);
if (pwerr == TOX_ERR_DECRYPTION_OK) {
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
tox_opts->savedata_data = (uint8_t *) plain;
tox_opts->savedata_length = plain_len;
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(tox_opts, (uint8_t *) plain, plain_len);
m = tox_new(tox_opts, new_err);
@ -676,9 +748,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
}
}
} else { /* data is not encrypted */
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
tox_opts->savedata_data = (uint8_t *) data;
tox_opts->savedata_length = len;
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len);
m = tox_new(tox_opts, new_err);
@ -690,18 +761,21 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
fclose(fp);
} 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);
}
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);
if (m == NULL)
if (m == 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);
}
}
return m;
@ -709,31 +783,41 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
static Tox *load_toxic(char *data_path)
{
struct Tox_Options tox_opts;
init_tox_options(&tox_opts);
Tox_Err_Options_New options_new_err;
struct Tox_Options *tox_opts = tox_options_new(&options_new_err);
TOX_ERR_NEW new_err;
Tox *m = load_tox(data_path, &tox_opts, &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 (!tox_opts) {
exit_toxic_err("tox_options_new returned fatal error", options_new_err);
}
if (!m)
exit_toxic_err("tox_new returned fatal error", new_err);
init_tox_options(tox_opts);
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);
}
init_tox_callbacks(m);
load_friendlist(m);
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_options_free(tox_opts);
return m;
}
@ -792,8 +876,9 @@ void *thread_cqueue(void *data)
ToxWindow *toxwin = get_window_ptr(i);
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);
}
}
pthread_mutex_unlock(&Winthread.lock);
@ -815,7 +900,7 @@ void *thread_av(void *data)
usleep(toxav_iteration_interval(av) * 1000);
}
}
#endif /* AUDIO */
#endif /* AUDIO */
static void print_usage(void)
{
@ -835,6 +920,13 @@ static void print_usage(void)
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
fprintf(stderr, " -v, --version Print the version\n");
}
static void print_version(void)
{
fprintf(stderr, "Toxic version %s\n", TOXICVER);
fprintf(stderr, "Toxcore version %d.%d.%d\n", tox_version_major(), tox_version_minor(), tox_version_patch());
}
static void set_default_opts(void)
@ -865,10 +957,11 @@ static void parse_args(int argc, char *argv[])
{"SOCKS5-proxy", required_argument, 0, 'p'},
{"HTTP-proxy", required_argument, 0, 'P'},
{"unencrypt-data", no_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{NULL, no_argument, NULL, 0},
};
const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:";
const char *opts_str = "4bdehotuxvc:f:n:r:p:P:T:";
int opt, indexptr;
long int port = 0;
@ -886,8 +979,9 @@ static void parse_args(int argc, char *argv[])
case 'c':
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");
}
break;
@ -903,22 +997,26 @@ static void parse_args(int argc, char *argv[])
case 'f':
arg_opts.use_custom_data = 1;
if (DATA_FILE)
if (DATA_FILE) {
free(DATA_FILE);
}
if (BLOCK_FILE)
if (BLOCK_FILE) {
free(BLOCK_FILE);
}
DATA_FILE = malloc(strlen(optarg) + 1);
strcpy(DATA_FILE, optarg);
if (DATA_FILE == NULL)
if (DATA_FILE == NULL) {
exit_toxic_err("failed in parse_args", FATALERR_MEMORY);
}
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);
}
strcpy(BLOCK_FILE, optarg);
strcat(BLOCK_FILE, "-blocklist");
@ -940,13 +1038,15 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
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);
}
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);
}
arg_opts.proxy_port = port;
break;
@ -955,13 +1055,15 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
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);
}
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);
}
arg_opts.proxy_port = port;
break;
@ -969,8 +1071,9 @@ static void parse_args(int argc, char *argv[])
case 'r':
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");
}
break;
@ -981,8 +1084,9 @@ static void parse_args(int argc, char *argv[])
case 'T':
port = strtol(optarg, NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
if (port <= 0 || port > MAX_PORT_RANGE) {
port = 14191;
}
arg_opts.tcp_port = port;
break;
@ -991,6 +1095,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.unencrypt_data = 1;
break;
case 'v':
print_version();
exit(EXIT_SUCCESS);
case 'h':
default:
print_usage();
@ -1011,28 +1119,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];
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;
}
if (file_exists(DATA_FILE))
if (file_exists(DATA_FILE)) {
return 0;
}
if (rename(old_data_file, DATA_FILE) != 0)
if (rename(old_data_file, DATA_FILE) != 0) {
return -1;
}
queue_init_message("Data file has been moved to %s", DATA_FILE);
char old_data_blocklist[strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(OLD_DATA_BLOCKLIST_NAME) + 1];
snprintf(old_data_blocklist, sizeof(old_data_blocklist), "%s%s%s", user_config_dir, CONFIGDIR, OLD_DATA_BLOCKLIST_NAME);
if (!file_exists(old_data_blocklist))
if (!file_exists(old_data_blocklist)) {
return 0;
}
if (file_exists(BLOCK_FILE))
if (file_exists(BLOCK_FILE)) {
return 0;
}
if (rename(old_data_blocklist, BLOCK_FILE) != 0)
if (rename(old_data_blocklist, BLOCK_FILE) != 0) {
return -1;
}
return 0;
}
@ -1043,13 +1157,15 @@ static int rename_old_profile(const char *user_config_dir)
*/
static void init_default_data_files(void)
{
if (arg_opts.use_custom_data)
if (arg_opts.use_custom_data) {
return;
}
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);
}
int config_err = create_user_config_dirs(user_config_dir);
@ -1057,14 +1173,16 @@ static void init_default_data_files(void)
DATA_FILE = strdup(DATANAME);
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);
}
} else {
DATA_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(DATANAME) + 1);
BLOCK_FILE = malloc(strlen(user_config_dir) + strlen(CONFIGDIR) + strlen(BLOCKNAME) + 1);
if (DATA_FILE == NULL || BLOCK_FILE == NULL)
if (DATA_FILE == NULL || BLOCK_FILE == NULL) {
exit_toxic_err("failed in init_default_data_files()", FATALERR_MEMORY);
}
strcpy(DATA_FILE, user_config_dir);
strcat(DATA_FILE, CONFIGDIR);
@ -1076,8 +1194,9 @@ static void init_default_data_files(void)
}
/* 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.");
}
free(user_config_dir);
}
@ -1100,8 +1219,9 @@ int main(int argc, char **argv)
parse_args(argc, argv);
/* Use the -b flag to enable stderr */
if (!arg_opts.debug)
if (!arg_opts.debug) {
freopen("/dev/null", "w", stderr);
}
if (arg_opts.encrypt_data && arg_opts.unencrypt_data) {
arg_opts.encrypt_data = 0;
@ -1116,17 +1236,19 @@ int main(int argc, char **argv)
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)");
else if (arg_opts.encrypt_data)
} else if (arg_opts.encrypt_data) {
first_time_encrypt("Encrypt existing data file? Y/n (q to quit)");
}
/* init user_settings struct and load settings from conf file */
user_settings = calloc(1, sizeof(struct user_settings));
if (user_settings == NULL)
if (user_settings == NULL) {
exit_toxic_err("failed in main", FATALERR_MEMORY);
}
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
@ -1147,32 +1269,37 @@ int main(int argc, char **argv)
#ifdef X11
if (init_xtra(DnD_callback) == -1)
if (init_xtra(DnD_callback) == -1) {
queue_init_message("X failed to initialize");
}
#endif
#endif /* X11 */
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;
}
init_term();
prompt = init_windows(m);
prompt_init_statusbar(prompt, m);
prompt_init_statusbar(prompt, m, !datafile_exists);
load_groups(prompt, m);
/* 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);
}
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);
}
/* 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);
}
#ifdef AUDIO
@ -1184,24 +1311,34 @@ int main(int argc, char **argv)
#endif /* VIDEO */
/* 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);
}
set_primary_device(input, user_settings->audio_in_dev);
set_primary_device(output, user_settings->audio_out_dev);
#elif SOUND_NOTIFY
if ( init_devices() == de_InternalError )
if (init_devices() == de_InternalError) {
queue_init_message("Failed to init audio devices");
}
#endif /* AUDIO */
#ifdef PYTHON
init_python(m);
invoke_autoruns(prompt->chatwin->history, prompt);
#endif /* PYTHON */
init_notify(60, 3000);
/* 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.");
}
int nodeslist_ret = load_DHT_nodeslist();
@ -1216,8 +1353,8 @@ int main(int argc, char **argv)
cleanup_init_messages();
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
char avatarstr[MAX_STR_SIZE];
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path);
char avatarstr[PATH_MAX + 11];
snprintf(avatarstr, sizeof(avatarstr), "/avatar %s", user_settings->avatar_path);
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
time_t last_save = get_unix_time();
@ -1230,8 +1367,9 @@ int main(int argc, char **argv)
if (timed_out(last_save, AUTOSAVE_FREQ)) {
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");
}
pthread_mutex_unlock(&Winthread.lock);

View File

@ -96,8 +96,8 @@ typedef enum _FATAL_ERRS {
Uncomment if necessary */
/* #define URXVT_FIX */
void lock_status ();
void unlock_status ();
void lock_status(void);
void unlock_status(void);
void exit_toxic_success(Tox *m);
void exit_toxic_err(const char *errmsg, int errcode);
@ -105,31 +105,32 @@ void exit_toxic_err(const char *errmsg, int errcode);
int store_data(Tox *m, const char *path);
/* callbacks */
void on_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_message(Tox *m, uint32_t friendnumber, TOX_MESSAGE_TYPE type, const uint8_t *string, size_t length,
void *userdata);
void on_action(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_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata);
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort);
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata);
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
size_t length, void *userdata);
void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void *userdata);
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, 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_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection status, void *userdata);
void on_friend_message(Tox *m, uint32_t friendnumber, Tox_Message_Type type, 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_friend_status(Tox *m, uint32_t friendnumber, Tox_User_Status status, void *userdata);
void on_friend_status_message(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
void on_friend_added(Tox *m, uint32_t friendnumber, bool sort);
void on_conference_message(Tox *m, uint32_t groupnumber, uint32_t peernumber, Tox_Message_Type type,
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,
size_t length, void *userdata);
void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata);
void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
size_t length, void *userdata);
void on_conference_title(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata);
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
void *userdata);
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);
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,
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_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
void on_friend_typing(Tox *m, uint32_t friendnumber, bool is_typing, void *userdata);
void on_friend_read_receipt(Tox *m, uint32_t friendnumber, uint32_t receipt, void *userdata);
#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 */
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;
}
wmemmove(&ctx->line[ctx->pos + 1], &ctx->line[ctx->pos], ctx->len - ctx->pos);
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 */
int del_char_buf_bck(ChatContext *ctx)
{
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return -1;
}
wmemmove(&ctx->line[ctx->pos - 1], &ctx->line[ctx->pos], ctx->len - 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. */
int del_char_buf_frnt(ChatContext *ctx)
{
if (ctx->pos >= ctx->len)
if (ctx->pos >= ctx->len) {
return -1;
}
wmemmove(&ctx->line[ctx->pos], &ctx->line[ctx->pos + 1], ctx->len - ctx->pos - 1);
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. */
int discard_buf(ChatContext *ctx)
{
if (ctx->pos <= 0)
if (ctx->pos <= 0) {
return -1;
}
ctx->yank_len = ctx->pos;
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. */
int kill_buf(ChatContext *ctx)
{
if (ctx->len <= ctx->pos)
if (ctx->len <= ctx->pos) {
return -1;
}
ctx->yank_len = ctx->len - ctx->pos;
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 */
int yank_buf(ChatContext *ctx)
{
if (!ctx->yank[0])
if (!ctx->yank[0]) {
return -1;
}
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE)
if (ctx->yank_len + ctx->len >= MAX_STR_SIZE) {
return -1;
}
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);
@ -130,8 +137,9 @@ int yank_buf(ChatContext *ctx)
Return 0 on success, -1 if nothing to delete */
int del_word_buf(ChatContext *ctx)
{
if (ctx->len == 0 || ctx->pos == 0)
if (ctx->len == 0 || ctx->pos == 0) {
return -1;
}
int i = ctx->pos, count = 0;
@ -169,17 +177,20 @@ void reset_buf(ChatContext *ctx)
/* Removes trailing spaces and newlines from line. */
void rm_trailing_spaces_buf(ChatContext *ctx)
{
if (ctx->len <= 0)
if (ctx->len <= 0) {
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;
}
int 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;
}
}
ctx->len = i + 1;
@ -195,8 +206,9 @@ static void shift_hist_back(ChatContext *ctx)
int i;
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);
}
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. */
void add_line_to_hist(ChatContext *ctx)
{
if (ctx->len >= MAX_STR_SIZE)
if (ctx->len >= MAX_STR_SIZE) {
return;
}
if (ctx->hst_tot >= MAX_LINE_HIST)
if (ctx->hst_tot >= MAX_LINE_HIST) {
shift_hist_back(ctx);
}
++ctx->hst_tot;
ctx->hst_pos = ctx->hst_tot;
@ -247,16 +261,20 @@ void strsubst(char *str, char old, char new)
{
int i;
for (i = 0; str[i] != '\0'; ++i)
if (str[i] == old)
for (i = 0; str[i] != '\0'; ++i) {
if (str[i] == old) {
str[i] = new;
}
}
}
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new)
{
int i;
for (i = 0; str[i] != L'\0'; ++i)
if (str[i] == old)
for (i = 0; str[i] != L'\0'; ++i) {
if (str[i] == old) {
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 wstrsubst(wchar_t *str, wchar_t old, wchar_t new);
#endif /* #define TOXIC_STRINGS_H */
#endif /* TOXIC_STRINGS_H */

View File

@ -37,18 +37,19 @@
#include <stdlib.h>
#include <assert.h>
#ifdef VIDEO
#define default_video_bit_rate 5000
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data );
void on_video_receive_frame(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data);
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data);
void on_video_bit_rate(ToxAV *av, uint32_t friend_number, 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);
}
@ -61,35 +62,36 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
CallControl.video_bit_rate = 0;
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");
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");
return NULL;
}
toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl);
toxav_callback_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl);
toxav_callback_video_receive_frame(CallControl.av, on_video_receive_frame, &CallControl);
toxav_callback_video_bit_rate(CallControl.av, on_video_bit_rate, &CallControl);
return CallControl.av;
}
void terminate_video()
void terminate_video(void)
{
int i;
for (i = 0; i < MAX_CALLS; ++i) {
for (i = 0; i < CallControl.max_calls; ++i) {
Call *this_call = &CallControl.calls[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);
}
}
terminate_video_devices();
@ -100,21 +102,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 */
Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_SEND_FRAME error;
Toxav_Err_Send_Frame error;
/* 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.");
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");
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");
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");
}
}
}
@ -128,24 +131,24 @@ void write_video_device_callback(uint32_t friend_number, uint16_t width, uint16_
int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
{
if ( !self || !av) {
if (!self || !av) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to prepare transmission");
return -1;
}
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");
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!");
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!");
return -1;
}
@ -156,9 +159,9 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
int stop_video_transmission(Call *call, int friend_number)
{
CallControl.video_bit_rate = 0;
toxav_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);
call->vin_idx = -1;
}
@ -176,7 +179,7 @@ int stop_video_transmission(Call *call, int friend_number)
/*
* 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,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
@ -185,19 +188,19 @@ void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
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,
uint32_t video_bit_rate, void *user_data)
void on_video_bit_rate(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data)
{
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)
{
Call *this_call = &CallControl.calls[friend_number];
if ( this_call->vout_idx != -1 )
if (this_call->vout_idx != -1) {
return;
}
open_primary_video_device(vdt_output, &this_call->vout_idx);
}
@ -213,15 +216,15 @@ void callback_video_starting(uint32_t friend_number)
ToxWindow *windows = CallControl.prompt;
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);
if (error == TOXAV_ERR_CALL_CONTROL_OK) {
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].is_call && windows[i].num == friend_number ) {
if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) {
if (windows[i].is_call && windows[i].num == friend_number) {
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!");
return;
}
@ -249,54 +252,60 @@ void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[M
const char *error_str;
Call *this_call = &CallControl.calls[self->num];
if ( argc != 0 ) {
if (argc != 0) {
error_str = "Unknown arguments.";
goto on_error;
}
if ( !CallControl.av ) {
if (!CallControl.av) {
error_str = "ToxAV not supported!";
goto on_error;
}
if ( !self->stb->connection ) {
if (!self->stb->connection) {
error_str = "Friend is offline.";
goto on_error;
}
if ( !self->is_call ) {
if (!self->is_call) {
error_str = "Not in call!";
goto on_error;
}
if ( this_call->vin_idx == -1 )
if (this_call->vin_idx == -1) {
callback_video_starting(self->num);
else
} else {
callback_video_end(self->num);
}
return;
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])
{
const char *error_str;
if ( argc != 1 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else error_str = "Only one argument allowed!";
if (argc != 1) {
if (argc < 1) {
error_str = "Type must be specified!";
} else {
error_str = "Only one argument allowed!";
}
goto on_error;
}
VideoDeviceType type;
if ( strcasecmp(argv[1], "in") == 0 ) /* Input devices */
if (strcasecmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input;
}
else if ( strcasecmp(argv[1], "out") == 0 ) /* Output devices */
else if (strcasecmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output;
}
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -307,7 +316,7 @@ void cmd_list_video_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, c
return;
on_error:
print_err (self, error_str);
print_err(self, error_str);
}
/* This changes primary video device only */
@ -315,21 +324,27 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
{
const char *error_str;
if ( argc != 2 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else if ( argc < 2 ) error_str = "Must have id!";
else error_str = "Only two arguments allowed!";
if (argc != 2) {
if (argc < 1) {
error_str = "Type must be specified!";
} else if (argc < 2) {
error_str = "Must have id!";
} else {
error_str = "Only two arguments allowed!";
}
goto on_error;
}
VideoDeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
if (strcmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input;
}
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
else if (strcmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output;
}
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -340,40 +355,46 @@ void cmd_change_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc,
char *end;
long int selection = strtol(argv[2], &end, 10);
if ( *end ) {
if (*end) {
error_str = "Invalid input";
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!";
goto on_error;
}
return;
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])
{
const char *error_str;
if ( argc != 2 ) {
if ( argc < 1 ) error_str = "Type must be specified!";
else if ( argc < 2 ) error_str = "Must have id!";
else error_str = "Only two arguments allowed!";
if (argc != 2) {
if (argc < 1) {
error_str = "Type must be specified!";
} else if (argc < 2) {
error_str = "Must have id!";
} else {
error_str = "Only two arguments allowed!";
}
goto on_error;
}
VideoDeviceType type;
if ( strcmp(argv[1], "in") == 0 ) /* Input devices */
if (strcmp(argv[1], "in") == 0) { /* Input devices */
type = vdt_input;
}
else if ( strcmp(argv[1], "out") == 0 ) /* Output devices */
else if (strcmp(argv[1], "out") == 0) { /* Output devices */
type = vdt_output;
}
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid type: %s", argv[1]);
@ -384,23 +405,23 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
char *end;
long int selection = strtol(argv[2], &end, 10);
if ( *end ) {
if (*end) {
error_str = "Invalid input";
goto on_error;
}
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
if (video_selection_valid(type, selection) == vde_InvalidSelection) {
error_str = "Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->is_call ) {
if (self->is_call) {
Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) {
if (this_call->ttas) {
if ( type == vdt_output ) {
if (type == vdt_output) {
} else {
/* TODO: check for failure */
close_video_device(vdt_input, this_call->vin_idx);
@ -414,13 +435,16 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
return;
on_error:
print_err (self, error_str);
print_err(self, error_str);
}
void stop_video_stream(ToxWindow *self)
{
Call *this_call = &CallControl.calls[self->num];
if (this_call && this_call->vin_idx != -1)
if (this_call && this_call->vin_idx != -1) {
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 */
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 stop_video_transmission(Call *call, int friend_number);
void stop_video_stream(ToxWindow *self);

View File

@ -30,15 +30,19 @@
#include <vpx/vpx_image.h>
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__OSX__)
#import "osx_video.h"
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#if defined(__OpenBSD__) || defined(__NetBSD__)
#include <sys/videoio.h>
#else
#include <linux/videodev2.h>
#else /* __OSX__ */
#import "osx_video.h"
#endif
#endif /* defined(__OpenBSD__) || defined(__NetBSD__) */
#endif /* __OSX__ */
#include "line_info.h"
#include "settings.h"
@ -51,6 +55,8 @@
#include <unistd.h>
#include <stdlib.h>
#ifdef VIDEO
#define inline__ inline __attribute__((always_inline))
extern struct user_settings *user_settings;
@ -65,7 +71,7 @@ typedef struct VideoDevice {
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || defined(__FreeBSD__)
#if !defined(__OSX__)
int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt;
struct VideoBuffer *buffers;
@ -92,9 +98,7 @@ static int size[2]; /* Size of above containers */
VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_video_device[2]; /* Primary device */
#ifdef VIDEO
static ToxAV *av = NULL;
#endif /* VIDEO */
/* q_mutex */
#define lock pthread_mutex_lock(&video_mutex);
@ -132,7 +136,7 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
}
}
#if defined(__linux__) || defined(__FreeBSD__)
#if !defined(__OSX__)
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height)
{
@ -170,7 +174,7 @@ static int xioctl(int fh, unsigned long request, void *arg)
return r;
}
#endif /* __linux__ */
#endif
/* Meet devices */
#ifdef VIDEO
@ -181,7 +185,13 @@ VideoDeviceError init_video_devices()
{
size[vdt_input] = 0;
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__OSX__)
if (osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0) {
return vde_InternalError;
}
#else /* not __OSX__*/
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
int fd;
@ -190,14 +200,14 @@ VideoDeviceError init_video_devices()
fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
if ( fd == -1 ) {
if (fd == -1) {
break;
} else {
struct v4l2_capability cap;
char *video_input_name;
/* 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);
strcpy(video_input_name, (char *)cap.card);
strcat(video_input_name, " (");
@ -216,11 +226,6 @@ VideoDeviceError init_video_devices()
}
}
#else /* __OSX__ */
if ( osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#endif
size[vdt_output] = 1;
@ -228,13 +233,15 @@ VideoDeviceError init_video_devices()
video_devices_names[vdt_output][0] = video_output_name;
// Start poll thread
if ( pthread_mutex_init(&video_mutex, NULL) != 0 )
if (pthread_mutex_init(&video_mutex, NULL) != 0) {
return vde_InternalError;
}
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;
}
#ifdef VIDEO
av = av_;
@ -243,7 +250,7 @@ VideoDeviceError init_video_devices()
return (VideoDeviceError) vde_None;
}
VideoDeviceError terminate_video_devices()
VideoDeviceError terminate_video_devices(void)
{
/* Cleanup if needed */
lock;
@ -258,8 +265,9 @@ VideoDeviceError terminate_video_devices()
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;
}
#ifdef __OSX__
osx_video_release();
@ -271,16 +279,18 @@ VideoDeviceError terminate_video_devices()
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
VideoDataHandleCallback callback, void *data)
{
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__OSX__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]
|| !video_devices_running[vdt_input][device_idx]->fd )
if (size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx]) {
return vde_InvalidSelection;
}
#else /* __OSX__ */
#else /* not __OSX__ */
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) {
return vde_InvalidSelection;
}
#endif
@ -295,7 +305,9 @@ VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t
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;
@ -314,14 +326,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)
{
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
if (size[type] <= selection || selection < 0) {
return vde_InvalidSelection;
}
lock;
uint32_t i, temp_idx = -1;
for (i = 0; i < MAX_DEVICES; ++i) {
if ( !video_devices_running[type][i] ) {
if (!video_devices_running[type][i]) {
temp_idx = i;
break;
}
@ -333,7 +347,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 */
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][i]->ref_count++;
@ -346,23 +360,31 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
device->selection = selection;
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) {
if (pthread_mutex_init(device->mutex, NULL) != 0) {
free(device);
unlock;
return vde_InternalError;
}
if ( type == vdt_input ) {
if (type == vdt_input) {
video_thread_paused = true;
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__OSX__)
if (osx_video_open_device(selection, &device->video_width, &device->video_height) != 0) {
free(device);
unlock;
return vde_FailedStart;
}
#else /* not __OSX__*/
/* Open selected device */
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);
if ( device->fd == -1 ) {
if (device->fd == -1) {
unlock;
return vde_FailedStart;
}
@ -370,7 +392,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
/* Obtain video device capabilities */
struct v4l2_capability cap;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) {
if (-1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap)) {
close(device->fd);
free(device);
unlock;
@ -384,7 +406,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
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);
free(device);
unlock;
@ -401,14 +423,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
if (-1 == xioctl(device->fd, VIDIOC_REQBUFS, &req)) {
close(device->fd);
free(device);
unlock;
return vde_FailedStart;
}
if ( req.count < 2 ) {
if (req.count < 2) {
close(device->fd);
free(device);
unlock;
@ -425,7 +447,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf) ) {
if (-1 == xioctl(device->fd, VIDIOC_QUERYBUF, &buf)) {
close(device->fd);
free(device);
unlock;
@ -439,9 +461,10 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
MAP_SHARED /* recommended */,
device->fd, buf.m.offset);
if ( MAP_FAILED == device->buffers[i].start ) {
for (i = 0; i < buf.index; ++i)
if (MAP_FAILED == device->buffers[i].start) {
for (i = 0; i < buf.index; ++i) {
munmap(device->buffers[i].start, device->buffers[i].length);
}
close(device->fd);
free(device);
@ -462,9 +485,10 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
for (i = 0; i < device->n_buffers; ++i)
if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
for (i = 0; i < device->n_buffers; ++i) {
munmap(device->buffers[i].start, device->buffers[i].length);
}
close(device->fd);
free(device);
@ -476,24 +500,16 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/* 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);
unlock;
return vde_FailedStart;
}
#else /* __OSX__ */
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
free(device);
unlock;
return vde_FailedStart;
}
#endif
/* 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);
unlock;
return vde_FailedStart;
@ -501,9 +517,9 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display);
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),
BlackPixel(device->x_display, screen))) ) {
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),
BlackPixel(device->x_display, screen)))) {
close_video_device(vdt_input, temp_idx);
unlock;
return vde_FailedStart;
@ -512,7 +528,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
XStoreName(device->x_display, device->x_window, "Video Preview");
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);
unlock;
return vde_FailedStart;
@ -533,7 +549,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
} else { /* vdt_output */
/* 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);
unlock;
return vde_FailedStart;
@ -541,8 +557,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display);
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))) ) {
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)))) {
close_video_device(vdt_output, temp_idx);
unlock;
return vde_FailedStart;
@ -551,7 +567,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
XStoreName(device->x_display, device->x_window, "Video Receive");
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);
unlock;
return vde_FailedStart;
@ -575,21 +591,25 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
return vde_None;
}
__inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
VideoDeviceError write_video_out(uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
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);
/* 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_height = height;
XResizeWindow(device->x_display, device->x_window, width, height);
@ -634,7 +654,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
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.
@ -652,12 +672,13 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
unlock;
if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */
else {
if (video_thread_paused) {
usleep(10000); /* Wait for unpause. */
} else {
for (i = 0; i < size[vdt_input]; ++i) {
lock;
if ( video_devices_running[vdt_input][i] != NULL ) {
if (video_devices_running[vdt_input][i] != NULL) {
/* Obtain frame image data from device buffers */
VideoDevice *device = video_devices_running[vdt_input][i];
uint16_t video_width = device->video_width;
@ -666,14 +687,21 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
uint8_t *u = device->input.planes[1];
uint8_t *v = device->input.planes[2];
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__OSX__)
if (osx_video_read_device(y, u, v, &video_width, &video_height) != 0) {
unlock;
continue;
}
#else /* not __OSX__*/
struct v4l2_buffer buf;
memset(&(buf), 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if ( -1 == ioctl(device->fd, VIDIOC_DQBUF, &buf) ) {
if (-1 == ioctl(device->fd, VIDIOC_DQBUF, &buf)) {
unlock;
continue;
}
@ -683,18 +711,12 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
/* Convert frame image data to YUV420 for ToxAV */
yuv422to420(y, u, v, data, video_width, video_height);
#else /* __OSX__*/
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
unlock;
continue;
}
#endif
/* 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);
}
/* Convert YUV420 data to BGR */
uint8_t *img_data = malloc(video_width * video_height * 4);
@ -726,14 +748,14 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
XFlush(device->x_display);
free(img_data);
#if defined(__linux__) || defined(__FreeBSD__)
#if !defined(__OSX__)
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
if (-1 == xioctl(device->fd, VIDIOC_QBUF, &buf)) {
unlock;
continue;
}
#endif /* __linux__ */
#endif
}
@ -749,38 +771,41 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
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;
VideoDevice *device = video_devices_running[type][device_idx];
VideoDeviceError rc = vde_None;
if ( !device ) {
if (!device) {
unlock;
return vde_DeviceNotActive;
}
video_devices_running[type][device_idx] = NULL;
if ( !device->ref_count ) {
if (!device->ref_count) {
if ( type == vdt_input ) {
#if defined(__linux__) || defined(__FreeBSD__)
if (type == vdt_input) {
#if defined(__OSX__)
osx_video_close_device(device_idx);
#else /* not __OSX__ */
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;
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)) {
}
}
close(device->fd);
#else /* __OSX__ */
osx_video_close_device(device_idx);
#endif
vpx_img_free(&device->input);
XDestroyWindow(device->x_display, device->x_window);
@ -788,9 +813,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
XCloseDisplay(device->x_display);
pthread_mutex_destroy(device->mutex);
#if defined(__linux__) || defined(__FreeBSD__)
#if !defined(__OSX__)
free(device->buffers);
#endif /* __linux__ */
#endif /* not __OSX__ */
free(device);
} else {
@ -802,7 +827,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
free(device);
}
} else device->ref_count--;
} else {
device->ref_count--;
}
unlock;
return rc;
@ -812,8 +839,9 @@ void print_video_devices(ToxWindow *self, VideoDeviceType type)
{
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]);
}
return;
}
@ -822,3 +850,5 @@ VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection)
{
return (size[type] <= selection || selection < 0) ? vde_InvalidSelection : vde_None;
}
#endif /* VIDEO */

View File

@ -45,16 +45,16 @@ typedef enum VideoDeviceError {
vde_CaptureError = -9,
} VideoDeviceError;
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t *y, const uint8_t *u,
const uint8_t *v, void *data);
typedef void (*VideoDataHandleCallback)(int16_t width, int16_t height, const uint8_t *y, const uint8_t *u,
const uint8_t *v, void *data);
#ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV *av);
#else
VideoDeviceError init_video_devices();
VideoDeviceError init_video_devices(void);
#endif /* VIDEO */
VideoDeviceError terminate_video_devices();
VideoDeviceError terminate_video_devices(void);
/* Callback handles ready data from INPUT device */
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);
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);
#endif /* VIDEO_DEVICE_H */
#endif /* VIDEO_DEVICE_H */

View File

@ -39,165 +39,166 @@
extern char *DATA_FILE;
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 struct user_settings *user_settings;
static int num_active_windows;
/* 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)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) data, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendRequest != NULL) {
windows[i].onFriendRequest(&windows[i], m, (const char *) public_key, msg, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && 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;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onConnectionChange != NULL)
windows[i].onConnectionChange(&windows[i], m, friendnumber, connection_status);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onConnectionChange != NULL) {
windows[i]->onConnectionChange(windows[i], m, friendnumber, connection_status);
}
}
}
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)
if (user_settings->show_typing_other == SHOW_TYPING_OFF) {
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onTypingChange != NULL)
windows[i].onTypingChange(&windows[i], m, friendnumber, is_typing);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && 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 *userdata)
void on_friend_message(Tox *m, uint32_t friendnumber, Tox_Message_Type type, const uint8_t *string, size_t length,
void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onMessage != NULL)
windows[i].onMessage(&windows[i], m, friendnumber, type, msg, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && 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)
{
char nick[TOXIC_MAX_NAME_LENGTH + 1];
length = copy_tox_str(nick, sizeof(nick), (const char *) string, length);
filter_str(nick, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onNickChange != NULL)
windows[i].onNickChange(&windows[i], m, friendnumber, nick, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onNickChange != NULL) {
windows[i]->onNickChange(windows[i], m, friendnumber, nick, length);
}
}
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)
{
char msg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) string, length);
filter_str(msg, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusMessageChange != NULL)
windows[i].onStatusMessageChange(&windows[i], friendnumber, msg, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && 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;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onStatusChange != NULL)
windows[i].onStatusChange(&windows[i], m, friendnumber, status);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onStatusChange != NULL) {
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 (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFriendAdded != NULL)
windows[i].onFriendAdded(&windows[i], m, friendnumber, sort);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFriendAdded != NULL) {
windows[i]->onFriendAdded(windows[i], m, friendnumber, sort);
}
}
store_data(m, DATA_FILE);
}
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
const uint8_t *message, size_t length, void *userdata)
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)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, type, msg, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupMessage != NULL) {
windows[i]->onGroupMessage(windows[i], m, groupnumber, peernumber, type, msg, length);
}
}
}
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
size_t length, void *userdata)
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 i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupInvite != NULL)
windows[i].onGroupInvite(&windows[i], m, friendnumber, type, (char *) group_pub_key, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupInvite != NULL) {
windows[i]->onGroupInvite(windows[i], m, friendnumber, type, (char *) group_pub_key, length);
}
}
}
void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void *userdata)
void on_conference_peer_list_changed(Tox *m, uint32_t groupnumber, void *userdata)
{
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupNamelistChange != NULL)
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupNameListChange != NULL) {
windows[i]->onGroupNameListChange(windows[i], m, groupnumber);
}
}
}
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata)
void on_conference_peer_name(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
size_t length, void *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)
{
char data[MAX_STR_SIZE + 1];
length = copy_tox_str(data, sizeof(data), (const char *) title, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupTitleChange != NULL)
windows[i].onGroupTitleChange(&windows[i], m, groupnumber, peernumber, data, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onGroupTitleChange != NULL) {
windows[i]->onGroupTitleChange(windows[i], m, groupnumber, peernumber, data, length);
}
}
}
@ -206,19 +207,19 @@ void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, u
{
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
if (!ft) {
return;
}
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_chunk_request(m, ft, position, length);
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileChunkRequest != NULL)
windows[i].onFileChunkRequest(&windows[i], m, friendnumber, filenumber, position, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileChunkRequest != NULL) {
windows[i]->onFileChunkRequest(windows[i], m, friendnumber, filenumber, position, length);
}
}
}
@ -227,35 +228,35 @@ void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint
{
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
if (!ft) {
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileRecvChunk != NULL)
windows[i].onFileRecvChunk(&windows[i], m, friendnumber, filenumber, position, (char *) data, length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && 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 *userdata)
void on_file_recv_control(Tox *m, uint32_t friendnumber, uint32_t filenumber, Tox_File_Control control,
void *userdata)
{
struct FileTransfer *ft = get_file_transfer_struct(friendnumber, filenumber);
if (!ft)
if (!ft) {
return;
}
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
on_avatar_file_control(m, ft, control);
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileControl != NULL)
windows[i].onFileControl(&windows[i], m, friendnumber, filenumber, control);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileControl != NULL) {
windows[i]->onFileControl(windows[i], m, friendnumber, filenumber, control);
}
}
}
@ -268,50 +269,51 @@ void on_file_recv(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint32_t k
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onFileRecv != NULL)
windows[i].onFileRecv(&windows[i], m, friendnumber, filenumber, file_size, (char *) filename,
filename_length);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onFileRecv != NULL) {
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;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onReadReceipt != NULL)
windows[i].onReadReceipt(&windows[i], m, friendnumber, receipt);
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] != NULL && windows[i]->onReadReceipt != NULL) {
windows[i]->onReadReceipt(windows[i], m, friendnumber, receipt);
}
}
}
/* CALLBACKS END */
int add_window(Tox *m, ToxWindow w)
int add_window(Tox *m, ToxWindow *w)
{
if (LINES < 2)
if (LINES < 2) {
return -1;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; i++) {
if (windows[i].active)
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; i++) {
if (windows[i] != NULL) {
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;
}
#ifdef URXVT_FIX
/* Fixes text color problem on some terminals. */
wbkgd(w.window, COLOR_PAIR(6));
wbkgd(w->window, COLOR_PAIR(6));
#endif
windows[i] = w;
if (w.onInit)
w.onInit(&w, m);
if (w->onInit) {
w->onInit(w, m);
}
++num_active_windows;
@ -321,42 +323,46 @@ int add_window(Tox *m, ToxWindow w)
return -1;
}
void set_active_window(int index)
void set_active_window_index(uint8_t index)
{
if (index < 0 || index >= MAX_WINDOWS_NUM)
return;
active_window = windows + index;
if (index < MAX_WINDOWS_NUM) {
active_window_index = index;
}
}
/* Shows next window when tab or back-tab is pressed */
void set_next_window(int ch)
{
ToxWindow *end = windows + MAX_WINDOWS_NUM - 1;
ToxWindow *inf = active_window;
if (ch == user_settings->key_next_tab) {
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) {
if (ch == user_settings->key_next_tab) {
if (++active_window > end)
active_window = windows;
} 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);
for (uint8_t i = start; i > 0; --i) {
if (windows[i] != NULL) {
set_active_window_index(i);
return;
}
}
}
set_active_window_index(0);
}
/* Deletes window w and cleans up */
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);
memset(w, 0, sizeof(ToxWindow));
free(windows[idx]);
windows[idx] = NULL;
clear();
refresh();
@ -365,13 +371,18 @@ void del_window(ToxWindow *w)
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)
exit_toxic_err("failed in init_windows", FATALERR_WININIT);
if (n_prompt < 0) {
exit_toxic_err("add_window() for prompt failed in init_windows", FATALERR_WININIT);
}
prompt = &windows[n_prompt];
active_window = prompt;
if (add_window(m, new_friendlist()) == -1) {
exit_toxic_err("add_window() for friendlist failed in init_windows", FATALERR_WININIT);
}
set_active_window_index(n_prompt);
return prompt;
}
@ -387,25 +398,26 @@ void on_window_resize(void)
getmaxyx(stdscr, y2, x2);
y2 -= 2;
if (y2 <= 0 || x2 <= 0)
if (y2 <= 0 || x2 <= 0) {
return;
}
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) {
continue;
}
ToxWindow *w = &windows[i];
ToxWindow *w = windows[i];
if (windows[i].is_friendlist) {
if (windows[i]->is_friendlist) {
delwin(w->window);
w->window = newwin(y2, x2, 0, 0);
continue;
}
if (w->help->active)
if (w->help->active) {
wclear(w->help->win);
}
if (w->is_groupchat) {
delwin(w->chatwin->sidebar);
@ -427,8 +439,9 @@ void on_window_resize(void)
} else {
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);
}
}
#ifdef AUDIO
@ -438,7 +451,7 @@ void on_window_resize(void)
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
}
#endif /* AUDIO */
#endif /* AUDIO */
scrollok(w->chatwin->history, 0);
}
@ -448,7 +461,9 @@ static void draw_window_tab(ToxWindow *toxwin)
{
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);
@ -457,7 +472,9 @@ static void draw_window_tab(ToxWindow *toxwin)
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);
}
@ -466,8 +483,14 @@ static void draw_bar(void)
{
int y, x;
ToxWindow *w = windows[active_window_index];
if (w == NULL) {
return;
}
// save current cursor position
getyx(active_window->window, y, x);
getyx(w->window, y, x);
attron(COLOR_PAIR(BLUE));
mvhline(LINES - 2, 0, '_', COLS);
@ -475,35 +498,32 @@ static void draw_bar(void)
move(LINES - 1, 0);
attron(COLOR_PAIR(BLUE) | A_BOLD);
printw(" TOXIC " TOXICVER " |");
attroff(COLOR_PAIR(BLUE) | A_BOLD);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (!windows[i].active)
for (uint8_t i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i] == NULL) {
continue;
}
if (windows + i == active_window)
if (i == active_window_index) {
#ifdef URXVT_FIX
attron(A_BOLD | COLOR_PAIR(GREEN));
else
} else {
#endif
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
attroff(A_BOLD | COLOR_PAIR(GREEN));
else
} else {
#endif
attroff(A_BOLD);
}
}
// restore cursor position after drawing
@ -514,7 +534,11 @@ static void draw_bar(void)
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);
a->alert = WINDOW_ALERT_NONE;
@ -533,19 +557,22 @@ void draw_active_window(Tox *m)
#ifdef HAVE_WIDECHAR
int status = wget_wch(stdscr, &ch);
if (status == ERR)
if (status == ERR) {
return;
}
if (status == OK)
if (status == OK) {
ltr = iswprint(ch);
else /* if (status == KEY_CODE_YES) */
} else { /* if (status == KEY_CODE_YES) */
ltr = false;
}
#else
ch = getch();
if (ch == ERR)
if (ch == ERR) {
return;
}
/* TODO verify if this works */
ltr = isprint(ch);
@ -564,28 +591,37 @@ void draw_active_window(Tox *m)
call at least once per second */
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) {
ToxWindow *a = &windows[i];
if (toxwin == NULL) {
continue;
}
if (a->active && a != active_window && !a->is_friendlist) {
if (i != active_window_index && !toxwin->is_friendlist) {
pthread_mutex_lock(&Winthread.lock);
line_info_print(a);
line_info_print(toxwin);
pthread_mutex_unlock(&Winthread.lock);
}
}
}
/* returns a pointer to the ToxWindow in the ith index. Returns NULL if no ToxWindow exists */
ToxWindow *get_window_ptr(int i)
/* Returns a pointer to the ToxWindow in the ith index.
* 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)
toxwin = &windows[i];
return windows[index];
}
return toxwin;
/* Returns a pointer to the currently active ToxWindow. */
ToxWindow *get_active_window(void)
{
return windows[active_window_index];
}
void force_refresh(WINDOW *w)
@ -603,15 +639,19 @@ int get_num_active_windows(void)
/* destroys all chat and groupchat windows (should only be called on shutdown) */
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)
kill_chat_window(&windows[i], m);
else if (windows[i].is_groupchat)
close_groupchat(&windows[i], m, windows[i].num);
if (windows[i]->is_chat) {
kill_chat_window(windows[i], m);
} else if (windows[i]->is_groupchat) {
free_groupchat(windows[i], m, windows[i]->num);
}
}
kill_prompt_window(prompt);
kill_friendlist();
/* TODO: use enum instead of magic indices */
kill_friendlist(windows[1]);
kill_prompt_window(windows[0]);
}

View File

@ -36,13 +36,13 @@
#include "toxic.h"
#define MAX_WINDOWS_NUM 32
#define MAX_WINDOWS_NUM 16
#define MAX_WINDOW_NAME_LENGTH 22
#define CURS_Y_OFFSET 1 /* y-axis cursor offset for chat contexts */
#define CHATBOX_HEIGHT 2
/* Curses foreground colours (background is black) */
enum {
typedef enum {
WHITE,
GREEN,
CYAN,
@ -116,18 +116,19 @@ struct ToxWindow {
/* toxcore */
void(*onFriendRequest)(ToxWindow *, Tox *, const char *, const char *, size_t);
void(*onFriendAdded)(ToxWindow *, Tox *, uint32_t, bool);
void(*onConnectionChange)(ToxWindow *, Tox *, uint32_t, TOX_CONNECTION);
void(*onMessage)(ToxWindow *, Tox *, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onConnectionChange)(ToxWindow *, Tox *, uint32_t, Tox_Connection);
void(*onMessage)(ToxWindow *, Tox *, uint32_t, Tox_Message_Type, 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(*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(*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(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
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(*onTypingChange)(ToxWindow *, Tox *, uint32_t, bool);
void(*onReadReceipt)(ToxWindow *, Tox *, uint32_t, uint32_t);
@ -161,7 +162,7 @@ struct ToxWindow {
char name[TOXIC_MAX_NAME_LENGTH + 1];
uint32_t num; /* corresponds to friendnumber in chat windows */
bool active;
uint8_t index; /* This window's index in the windows array */
int x;
bool is_chat;
@ -186,8 +187,8 @@ struct StatusBar {
size_t statusmsg_len;
char nick[TOXIC_MAX_NAME_LENGTH + 1];
size_t nick_len;
TOX_USER_STATUS status;
TOX_CONNECTION connection;
Tox_User_Status status;
Tox_Connection connection;
};
#ifdef AUDIO
@ -251,17 +252,18 @@ struct Help {
ToxWindow *init_windows(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 set_active_window(int ch);
void set_active_window_index(uint8_t index);
int get_num_active_windows(void);
void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void);
void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(int i);
ToxWindow *get_window_ptr(size_t i);
ToxWindow *get_active_window(void);
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */
void refresh_inactive_windows(void);
#endif /* #define WINDOWS_H */
#endif /* WINDOWS_H */

View File

@ -47,7 +47,7 @@ static Atom XdndTypeList;
static Atom XdndActionCopy;
static Atom XdndFinished;
struct _Xtra {
struct Xtra {
drop_callback on_drop;
Display *display;
Window terminal_window;
@ -57,14 +57,14 @@ struct _Xtra {
Atom expecting_type;
} Xtra;
typedef struct _Property {
typedef struct Property {
unsigned char *data;
int read_format;
unsigned long read_num;
Atom read_type;
} Property;
Property read_property(Window s, Atom p)
static Property read_property(Window s, Atom p)
{
Atom read_type;
int read_format;
@ -76,7 +76,9 @@ Property read_property(Window s, Atom p)
/* Keep trying to read the property until there are no bytes unread */
do {
if (data) XFree(data);
if (data) {
XFree(data);
}
XGetWindowProperty(Xtra.display, s,
p, 0,
@ -93,12 +95,14 @@ Property read_property(Window s, Atom p)
return property;
}
Atom get_dnd_type(long *a, int l)
static Atom get_dnd_type(long *a, int l)
{
int i = 0;
for (; i < l; i ++) {
if (a[i] != XtraNil) return a[i]; /* Get first valid */
if (a[i] != XtraNil) {
return a[i]; /* Get first valid */
}
}
return XtraNil;
@ -193,10 +197,11 @@ static void handle_xdnd_selection(XSelectionEvent *e)
Property p = read_property(Xtra.proxy_window, XdndSelection);
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;
else /* text/uri-list */
} else { /* text/uri-list */
dt = DT_plain;
}
/* Call callback for every entry */
@ -204,13 +209,18 @@ static void handle_xdnd_selection(XSelectionEvent *e)
char *sptr;
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
if (str) Xtra.on_drop(str, dt);
while ((str = strtok_r(NULL, "\n\r", &sptr)))
if (str) {
Xtra.on_drop(str, dt);
}
while ((str = strtok_r(NULL, "\n\r", &sptr))) {
Xtra.on_drop(str, dt);
}
}
if (p.data) XFree(p.data);
if (p.data) {
XFree(p.data);
}
}
void *event_loop(void *p)
@ -227,7 +237,9 @@ void *event_loop(void *p)
XLockDisplay(Xtra.display);
if ((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if ((pending = XPending(Xtra.display))) {
XNextEvent(Xtra.display, &event);
}
if (!pending) {
XUnlockDisplay(Xtra.display);
@ -238,13 +250,22 @@ void *event_loop(void *p)
if (event.type == ClientMessage) {
Atom type = event.xclient.message_type;
if (type == XdndEnter) handle_xdnd_enter(&event.xclient);
else if (type == XdndPosition) handle_xdnd_position(&event.xclient);
else if (type == XdndDrop) handle_xdnd_drop(&event.xclient);
else if (type == XtraTerminate) break;
} else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection);
if (type == XdndEnter) {
handle_xdnd_enter(&event.xclient);
} else if (type == XdndPosition) {
handle_xdnd_position(&event.xclient);
} else if (type == XdndDrop) {
handle_xdnd_drop(&event.xclient);
} else if (type == XtraTerminate) {
break;
}
} else if (event.type == SelectionNotify) {
handle_xdnd_selection(&event.xselection);
}
/* AINNOBODYCANHANDLEDEMEVENTS*/
else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
else {
XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
}
XUnlockDisplay(Xtra.display);
}
@ -253,28 +274,53 @@ void *event_loop(void *p)
* Please call xtra_terminate() at exit
* otherwise HEWUSAGUDBOI happens
*/
if (Xtra.display) XCloseDisplay(Xtra.display);
if (Xtra.display) {
XCloseDisplay(Xtra.display);
}
return (Xtra.display = NULL);
}
static long unsigned int focused_window_id(void)
{
if (!Xtra.display) {
return 0;
}
Window focus;
int revert;
XLockDisplay(Xtra.display);
XGetInputFocus(Xtra.display, &focus, &revert);
XUnlockDisplay(Xtra.display);
return focus;
}
int is_focused(void)
{
return Xtra.proxy_window == focused_window_id() || Xtra.terminal_window == focused_window_id();
}
int init_xtra(drop_callback d)
{
memset(&Xtra, 0, sizeof(Xtra));
if (!d) return -1;
else Xtra.on_drop = d;
if (!d) {
return -1;
} else {
Xtra.on_drop = d;
}
XInitThreads();
if ( !(Xtra.display = XOpenDisplay(NULL))) return -1;
if (!(Xtra.display = XOpenDisplay(NULL))) {
return -1;
}
Xtra.terminal_window = focused_window_id();
/* OSX: if focused window is 0, it means toxic is ran from
* native terminal and not X11 terminal window, silently exit */
if (!Xtra.terminal_window)
if (!Xtra.terminal_window) {
return 0;
}
{
/* Create an invisible window which will act as proxy for the DnD operation. */
@ -299,17 +345,18 @@ int init_xtra(drop_callback d)
XDefaultRootWindow(Xtra.display),
&root, &x, &y, &wht, &hht, &b, &d);
if (! (Xtra.proxy_window = XCreateWindow
(Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */
wht, hht, /* Width + height */
0, /* Border width */
CopyFromParent, /* Depth */
InputOnly, /* Class */
CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */
&attr)) ) /* Attributes for value mask */
if (!(Xtra.proxy_window = XCreateWindow
(Xtra.display, Xtra.terminal_window, /* Parent */
0, 0, /* Position */
wht, hht, /* Width + height */
0, /* Border width */
CopyFromParent, /* Depth */
InputOnly, /* Class */
CopyFromParent, /* Visual */
CWEventMask | CWCursor, /* Value mask */
&attr))) { /* Attributes for value mask */
return -1;
}
}
XMapWindow(Xtra.display, Xtra.proxy_window); /* Show window (sandwich) */
@ -339,17 +386,20 @@ int init_xtra(drop_callback d)
pthread_t id;
if (pthread_create(&id, NULL, event_loop, NULL) != 0)
if (pthread_create(&id, NULL, event_loop, NULL) != 0) {
return -1;
}
pthread_detach(id);
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 = {
.xclient = {
@ -366,20 +416,3 @@ void terminate_xtra()
while (Xtra.display); /* Wait for termination */
}
long unsigned int focused_window_id()
{
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