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

Compare commits

..

88 Commits

Author SHA1 Message Date
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
600e013adc Bump to version 0.7.2 2016-12-18 00:11:24 -05:00
1d71e2eb18 Port to toktok-c-toxcore branch 2016-12-18 00:01:51 -05:00
f858714edd Fix small nodeslist parsing bug 2016-10-23 12:26:18 -04:00
4df44a7274 Merge branch 'tharvik-master' 2016-10-18 12:21:58 -04:00
a26ed9d28f add missing implicit include 2016-10-18 09:38:28 +02:00
2bd5083b8f Corrected brew errors (#413)
Corrected brew errors
2016-10-07 13:24:31 -04:00
8805f694b9 Merge branch 'Ansa89-qr_png' 2016-10-06 11:48:33 -04:00
71040355fd QR: close file descriptor on error 2016-10-06 11:54:27 +02:00
6bc5d8c543 QR PNG: convert some variables to macros 2016-10-06 11:52:26 +02:00
abb39ea6b5 Fix a few resource leaks 2016-10-05 15:17:56 -04:00
15846d2b50 Merge branch 'Ansa89-qr' 2016-10-05 14:58:10 -04:00
958df9f2e8 Add possibility to save QR code in PNG file format 2016-10-05 20:57:50 +02:00
2fd43aebee Merge branch 'Ansa89-fix_checks' 2016-10-05 12:23:23 -04:00
34c29745cc Makefile: use correct variable name when checking video libs 2016-10-05 09:09:22 +02:00
da6fe41d75 Makefile: fix video libs check 2016-10-04 10:25:49 +02:00
e17fa89d8f Update misc_tools.c (#408)
* Update misc_tools.c

Fix build on FreeBSD with clang.

Without this patch, compilation fails with these messages:

  CC    misc_tools.o
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:24: error: variable has incomplete type 'struct sockaddr_in'
    struct sockaddr_in s_addr;
                       ^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:12: note: forward declaration of 'struct sockaddr_in'
    struct sockaddr_in s_addr;
           ^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:480:22: error: use of undeclared identifier 'AF_INET'
    return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
                     ^
2 errors generated.

* Fix build on FreeBSD with clang

Without this patch, compilation fails with these messages:

CC misc_tools.o
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:24: error: variable has incomplete type 'struct sockaddr_in'
struct sockaddr_in s_addr;
^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:479:12: note: forward declaration of 'struct sockaddr_in'
struct sockaddr_in s_addr;
^
/usr/ports/net-im/toxic/work/toxic-0.7.1/src/misc_tools.c:480:22: error: use of undeclared identifier 'AF_INET'
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
^
2 errors generated.
2016-09-25 22:32:17 -04:00
f056f13329 Astyle everything and add an astyle options script to repo 2016-09-24 21:07:04 -04:00
83 changed files with 2905 additions and 1261 deletions

1
.gitignore vendored
View File

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

31
.travis.yml Normal file
View File

@ -0,0 +1,31 @@
language: python
python: nightly
addons:
apt:
packages:
- libalut-dev
- libconfig-dev
- libnotify-dev
- libopenal-dev
- libopus-dev
- libqrencode-dev
- libvpx-dev
cache:
directories:
- $HOME/cache
install:
# Where to find libraries.
- export LD_LIBRARY_PATH=$HOME/cache/usr/lib
- export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
# c-sodium
- git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium
- 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
- test -f $HOME/cache/usr/lib/libtoxcore.so || (cd c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
script:
- make ENABLE_PYTHON=1

View File

@ -1,57 +1,53 @@
# 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)
* [Compilation variables](#compilation-variables)
* [Packaging](#packaging)
<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* |
| [Tox Core AV](https://github.com/toktok/c-toxcore) | AUDIO | *None* |
| [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 https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install libnotify
```
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"
```
<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.
<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)
@ -60,11 +56,11 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
* `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
* Features excluded from the default build must be explicitly enabled using special variables:
* `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")

View File

@ -3,7 +3,7 @@ CFG_DIR = $(BASE_DIR)/cfg
-include $(CFG_DIR)/global_vars.mk
LIBS = libtoxcore ncursesw libconfig libqrencode
LIBS = toxcore ncursesw libconfig libqrencode
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
@ -30,6 +30,9 @@ endif
ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), NetBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk
endif

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

11
astylerc Normal file
View File

@ -0,0 +1,11 @@
--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

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

@ -34,6 +34,18 @@ ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.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_png.mk Normal file
View File

@ -0,0 +1,15 @@
# Variables for QR exported as PNG support
PNG_LIBS = libpng
PNG_CFLAGS = -DQRPNG
# Check if we can build QR exported as PNG support
CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error")
ifneq ($(CHECK_PNG_LIBS), error)
LIBS += $(PNG_LIBS)
CFLAGS += $(PNG_CFLAGS)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without QR exported as PNG support)
$(warning WARNING -- You need these libraries for QR exported as PNG support)
$(warning WARNING -- $(MISSING_PNG_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
@ -8,14 +8,14 @@ else
endif
# Check if we can build video support
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error")
ifneq ($(CHECK_VIDEO_LIBS), error)
LIBS += $(VIDEO_LIBS)
CFLAGS += $(VIDEO_CFLAGS)
OBJ += $(VIDEO_OBJ)
else ifneq ($(MAKECMDGOALS), clean)
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
$(warning WARNING -- Toxic will be compiled without video support)
$(warning WARNING -- You will need these libraries for video support)
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
endif
endif

View File

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

View File

@ -14,6 +14,8 @@ 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_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))\")"

View 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

@ -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";
};

210
src/api.c Normal file
View File

@ -0,0 +1,210 @@
/* 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 */

42
src/api.h Normal file
View File

@ -0,0 +1,42 @@
/* 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);
#endif /* #define API_H */

View File

@ -56,12 +56,13 @@
#endif
extern FriendsList Friends;
struct CallControl CallControl;
#define cbend pthread_exit(NULL)
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000)
static int set_call(Call* call, bool start)
static int set_call(Call *call, bool start)
{
call->in_idx = -1;
call->out_idx = -1;
@ -75,17 +76,18 @@ static int set_call(Call* call, bool start)
if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
return -1;
}
else {
} else {
call->ttid = 0;
if ( pthread_mutex_destroy(&call->mutex) != 0 )
return -1;
return -1;
}
return 0;
}
void call_cb ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled, void *user_data );
void call_cb ( ToxAV *av, uint32_t friend_number, bool audio_enabled, bool video_enabled,
void *user_data );
void callstate_cb ( ToxAV *av, uint32_t friend_number, uint32_t state, void *user_data );
void receive_audio_frame_cb ( ToxAV *av, uint32_t friend_number, int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data );
@ -103,7 +105,7 @@ void callback_call_canceled ( uint32_t friend_number );
void callback_call_rejected ( uint32_t friend_number );
void callback_call_ended ( uint32_t friend_number );
void write_device_callback( uint32_t friend_number, const int16_t* PCM, uint16_t sample_count, uint8_t channels,
void write_device_callback( uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate );
static void print_err (ToxWindow *self, const char *error_str)
@ -132,8 +134,6 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
CallControl.video_frame_duration = 0;
#endif /* VIDEO */
memset(CallControl.calls, 0, sizeof(CallControl.calls));
if ( !CallControl.av ) {
CallControl.audio_errors |= ae_StartingCoreAudio;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV");
@ -158,7 +158,8 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
void terminate_audio()
{
int i;
for (i = 0; i < MAX_CALLS; ++i)
for (i = 0; i < CallControl.max_calls; ++i)
stop_transmission(&CallControl.calls[i], i);
if ( CallControl.av )
@ -167,7 +168,7 @@ void terminate_audio()
terminate_devices();
}
void read_device_callback(const int16_t* captured, uint32_t size, void* data)
void read_device_callback(const int16_t *captured, uint32_t size, void *data)
{
TOXAV_ERR_SEND_FRAME error;
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
@ -175,13 +176,13 @@ void read_device_callback(const int16_t* captured, uint32_t size, void* data)
((int64_t) CallControl.audio_frame_duration) / 1000;
if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
captured, sample_count,
CallControl.audio_channels,
CallControl.audio_sample_rate, &error) == false )
{}
captured, sample_count,
CallControl.audio_channels,
CallControl.audio_sample_rate, &error) == false ) {
}
}
void write_device_callback(uint32_t friend_number, const int16_t* PCM, uint16_t sample_count, uint8_t channels,
void write_device_callback(uint32_t friend_number, const int16_t *PCM, uint16_t sample_count, uint8_t channels,
uint32_t sample_rate)
{
if ( CallControl.calls[friend_number].ttas )
@ -199,7 +200,7 @@ int start_transmission(ToxWindow *self, Call *call)
return -1;
DeviceError error = open_primary_device(input, &call->in_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels);
if ( error != de_None ) {
if ( error == de_FailedStart)
@ -210,12 +211,12 @@ int start_transmission(ToxWindow *self, Call *call)
}
if ( register_device_callback(self->num, call->in_idx,
read_device_callback, &self->num, true) != de_None)
read_device_callback, &self->num, true) != de_None)
/* Set VAD as true for all; TODO: Make it more dynamic */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
if ( open_primary_device(output, &call->out_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels) != de_None ) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
call->has_output = 0;
}
@ -286,7 +287,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
callback_call_ended(friend_number);
CallControl.pending_call = false;
break;
break;
case ( TOXAV_FRIEND_CALL_STATE_FINISHED ):
if ( CallControl.pending_call )
callback_call_rejected(friend_number);
@ -304,7 +306,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
CallControl.call_state = 0;
CallControl.pending_call = false;
break;
break;
default:
if ( CallControl.pending_call ) {
/* Start answered call */
@ -313,6 +316,7 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
} else {
#ifdef VIDEO
/* Handle receiving client video call states */
if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V )
callback_recv_video_starting(friend_number);
@ -322,21 +326,21 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
#endif /* VIDEO */
}
break;
break;
}
}
void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data)
int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data)
{
write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate);
}
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data)
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data)
{
CallControl.audio_bit_rate = audio_bit_rate;
toxav_audio_set_bit_rate(av, friend_number, audio_bit_rate, user_data);
}
void callback_recv_invite(Tox *m, uint32_t friend_number)
@ -373,14 +377,16 @@ void callback_recv_ringing(uint32_t friend_number)
}
void callback_recv_starting(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
ToxWindow *windows = CallControl.prompt;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) {
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return;
}
@ -399,12 +405,14 @@ void callback_recv_ending(uint32_t friend_number)
}
void callback_call_started(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
ToxWindow *windows = CallControl.prompt;
int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
return;
@ -479,6 +487,7 @@ void cmd_call(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
}
toxav_call(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
if ( error != TOXAV_ERR_CALL_OK ) {
if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!";
else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue";
@ -518,6 +527,7 @@ void cmd_answer(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
}
toxav_answer(CallControl.av, self->num, CallControl.audio_bit_rate, CallControl.video_bit_rate, &error);
if ( error != TOXAV_ERR_ANSWER_OK ) {
if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!";
else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!";
@ -621,6 +631,9 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
return;
}
// Refresh device list.
get_devices_names();
print_devices(self, type);
return;
@ -664,7 +677,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
}
if ( set_primary_device(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
@ -708,13 +721,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
}
if ( selection_valid(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) {
@ -722,15 +736,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
pthread_mutex_lock(&this_call->mutex);
close_device(output, this_call->out_idx);
this_call->has_output = open_device(output, selection, &this_call->out_idx,
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
== de_None ? 1 : 0;
CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
== de_None ? 1 : 0;
pthread_mutex_unlock(&this_call->mutex);
}
else {
} else {
/* TODO: check for failure */
close_device(input, this_call->in_idx);
open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate,
CallControl.audio_frame_duration, CallControl.audio_channels);
CallControl.audio_frame_duration, CallControl.audio_channels);
/* Set VAD as true for all; TODO: Make it more dynamic */
register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
}
@ -740,7 +753,7 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
self->device_selection[type] = selection;
return;
on_error:
on_error:
print_err (self, error_str);
}
@ -771,9 +784,10 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
/* If call is active, use this_call values */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
pthread_mutex_lock(&this_call->mutex);
if ( type == input ) {
device_mute(type, this_call->in_idx);
self->chatwin->infobox.in_is_muted ^= 1;
@ -781,12 +795,13 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
device_mute(type, this_call->out_idx);
self->chatwin->infobox.out_is_muted ^= 1;
}
pthread_mutex_unlock(&this_call->mutex);
}
return;
on_error:
on_error:
print_err (self, error_str);
}
@ -821,8 +836,57 @@ on_error:
print_err (self, error_str);
}
void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
char *error_str;
void stop_current_call(ToxWindow* self)
if ( argc != 1 ) {
error_str = "Must have value!";
goto on_error;
}
if ( self->is_call == false ) {
error_str = "Must be in a call";
goto on_error;
}
const uint32_t bitrate = strtol(argv[1], NULL, 10);
TOXAV_ERR_BIT_RATE_SET error;
audio_bit_rate_status_cb(CallControl.av, self->num, bitrate, &error);
if (error != TOXAV_ERR_BIT_RATE_SET_OK) {
switch (error) {
case TOXAV_ERR_BIT_RATE_SET_SYNC:
error_str = "Syncronization error occured";
break;
case TOXAV_ERR_BIT_RATE_SET_INVALID_BIT_RATE:
error_str = "Invalid AV bit rate value (valid is 6-510)";
break;
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND:
error_str = "Friend not found";
break;
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL:
error_str = "Friend is not in the call";
break;
default:
error_str = "Unknown error";
}
goto on_error;
}
return;
on_error:
print_err (self, error_str);
}
void stop_current_call(ToxWindow *self)
{
if ( CallControl.pending_call ) {
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);
@ -834,3 +898,47 @@ void stop_current_call(ToxWindow* self)
CallControl.pending_call = false;
}
/**
* Reallocates the Calls list according to n.
*/
static void realloc_calls(uint32_t n)
{
if (n <= 0) {
free(CallControl.calls);
CallControl.calls = NULL;
return;
}
Call *temp = realloc(CallControl.calls, n * sizeof(Call));
if (temp == NULL) {
exit_toxic_err("failed in realloc_calls", FATALERR_MEMORY);
}
CallControl.calls = temp;
}
/**
* Inits the call structure for a given friend. Called when a friend is added to the friends list.
* Index must be equivalent to the friend's friendlist index.
*/
void init_friend_AV(uint32_t index)
{
realloc_calls(CallControl.max_calls + 1);
memset(&CallControl.calls[CallControl.max_calls], 0, sizeof(Call));
if (index == CallControl.max_calls) {
++CallControl.max_calls;
}
}
/**
* Deletes a call structure from the Calls list. Called when a friend is deleted from the friends list.
* Index must be equivalent to the size of the Calls list.
*/
void del_friend_AV(uint32_t index)
{
realloc_calls(index);
CallControl.max_calls = index;
}

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,9 +79,9 @@ 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);
@ -89,5 +89,7 @@ void terminate_audio();
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

@ -56,7 +56,7 @@ typedef struct Device {
ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */
@ -80,7 +80,7 @@ Device *running[2][MAX_DEVICES] = {{NULL}}; /* Running devices */
uint32_t primary_device[2]; /* Primary device */
#ifdef AUDIO
static ToxAV* av = NULL;
static ToxAV *av = NULL;
#endif /* AUDIO */
/* q_mutex */
@ -90,47 +90,24 @@ pthread_mutex_t mutex;
bool thread_running = true,
thread_paused = true; /* Thread control */
thread_paused = true; /* Thread control */
void* thread_poll(void*);
void *thread_poll(void *);
/* Meet devices */
#ifdef AUDIO
DeviceError init_devices(ToxAV* av_)
DeviceError init_devices(ToxAV *av_)
#else
DeviceError init_devices()
#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)
return de_InternalError;
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError;
@ -156,12 +133,45 @@ DeviceError terminate_devices()
return (DeviceError) de_None;
}
void get_devices_names() {
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;
lock;
Device* device = running[type][device_idx];
Device *device = running[type][device_idx];
if (!device) {
unlock;
@ -178,9 +188,10 @@ DeviceError device_mute(DeviceType type, uint32_t device_idx)
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[input][device_idx];
Device *device = running[input][device_idx];
if (!device) {
unlock;
@ -198,12 +209,14 @@ 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;
primary_device[type] = selection;
return de_None;
}
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels)
{
return open_device(type, primary_device[type], device_idx, sample_rate, frame_duration, channels);
}
@ -214,7 +227,8 @@ void get_primary_device_name(DeviceType type, char *buf, int size)
}
// TODO: generate buffers separately
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels)
DeviceError open_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;
@ -225,10 +239,13 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
const uint32_t frame_size = (sample_rate * frame_duration / 1000);
uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; }
else *device_idx = i;
if (i == MAX_DEVICES) {
unlock;
return de_AllDevicesBusy;
} 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 ) {
@ -242,7 +259,7 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
}
}
Device* device = running[type][*device_idx] = calloc(1, sizeof(Device));
Device *device = running[type][*device_idx] = calloc(1, sizeof(Device));
device->selection = selection;
device->sample_rate = sample_rate;
@ -258,12 +275,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO
#ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold;
#endif
}
else {
#endif
} else {
device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) {
free(device);
running[type][*device_idx] = NULL;
@ -279,10 +296,10 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
alSourcei(device->source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size];
memset(zeros, 0, frame_size*2);
memset(zeros, 0, frame_size * 2);
for ( i = 0; i < OPENAL_BUFS; ++i ) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate);
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
}
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers);
@ -310,7 +327,7 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock;
Device* device = running[type][device_idx];
Device *device = running[type][device_idx];
DeviceError rc = de_None;
if (!device) {
@ -323,27 +340,28 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
if ( !device->ref_count ) {
if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
}
else {
} else {
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 ( !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)
return de_InvalidSelection;
@ -358,12 +376,12 @@ DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx
return de_None;
}
inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t sample_count, uint8_t channels,
inline__ DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t sample_count, uint8_t channels,
uint32_t sample_rate)
{
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx];
Device *device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive;
@ -375,33 +393,33 @@ inline__ DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_
alGetSourcei(device->source, AL_BUFFERS_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) {
if (processed) {
ALuint bufids[processed];
alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0];
}
else if(queued < 16) alGenBuffers(1, &bufid);
} else if (queued < 16) alGenBuffers(1, &bufid);
else {
pthread_mutex_unlock(device->mutex);
return de_Busy;
}
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels, sample_rate);
alBufferData(bufid, channels == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, data, sample_count * 2 * channels,
sample_rate);
alSourceQueueBuffers(device->source, 1, &bufid);
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.
@ -411,9 +429,9 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
int32_t sample = 0;
while (1)
{
while (1) {
lock;
if (!thread_running) {
unlock;
break;
@ -422,7 +440,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
bool paused = thread_paused;
unlock;
/* Wait for unpause. */
/* Wait for unpause. */
if (paused) {
usleep(10000);
}
@ -430,6 +448,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
else {
for (i = 0; i < size[input]; ++i) {
lock;
if (running[input][i] != NULL) {
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample);
@ -439,7 +458,8 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
unlock;
continue;
}
Device* device = running[input][i];
Device *device = running[input][i];
int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size);
@ -451,8 +471,10 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
if ( device->cb ) device->cb(frame, f_size, device->cb_data);
}
unlock;
}
usleep(5000);
}
}
@ -460,12 +482,13 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
pthread_exit(NULL);
}
void print_devices(ToxWindow* self, DeviceType type)
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;
}
@ -475,7 +498,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None;
}
void* get_device_callback_data(uint32_t device_idx)
void *get_device_callback_data(uint32_t device_idx)
{
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return NULL;

View File

@ -52,20 +52,22 @@ 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);
DeviceError init_devices(ToxAV *av);
#else
DeviceError init_devices();
#endif /* AUDIO */
void get_devices_names();
DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
void* get_device_callback_data(uint32_t device_idx);
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD);
void *get_device_callback_data(uint32_t device_idx);
/* toggle device mute */
DeviceError device_mute(DeviceType type, uint32_t device_idx);
@ -75,16 +77,19 @@ DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
#endif
DeviceError set_primary_device(DeviceType type, int32_t selection);
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels);
/* Start device */
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
DeviceError open_device(DeviceType type, int32_t selection, uint32_t *device_idx, uint32_t sample_rate,
uint32_t frame_duration, uint8_t channels);
/* Stop device */
DeviceError close_device(DeviceType type, uint32_t device_idx);
/* Write data to device */
DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels, uint32_t sample_rate);
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
uint32_t sample_rate);
void print_devices(ToxWindow* self, DeviceType type);
void print_devices(ToxWindow *self, DeviceType type);
void get_primary_device_name(DeviceType type, char *buf, int size);
DeviceError selection_valid(DeviceType type, int32_t selection);

View File

@ -25,10 +25,10 @@
#include <limits.h>
#ifdef __APPLE__
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/types.h>
#include <sys/dir.h>
#else
#include <dirent.h>
#include <dirent.h>
#endif /* ifdef __APPLE__ */
#include "windows.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 */
}
@ -91,7 +93,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
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)
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
{
ChatContext *ctx = self->chatwin;
@ -108,7 +110,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
#ifdef PYTHON
dir_search = dir_search || !strncmp(ubuf, "/run", strlen("/run"));
#endif
/* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE];
@ -144,7 +150,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
}
int s_len = strlen(sub);
int n_matches = 0;
size_t n_matches = 0;
char matches[n_items][MAX_STR_SIZE];
int i = 0;
@ -185,6 +191,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int n_endchrs = strlen(endchrs);
int strt = ctx->pos - s_len;
int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE)
return -1;
@ -282,13 +289,13 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
if (dp == NULL)
return -1;
char dirnames[MAX_DIRS][NAME_MAX];
char dirnames[MAX_DIRS][NAME_MAX+1];
struct dirent *entry;
int dircount = 0;
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount;
}
@ -300,9 +307,9 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
return -1;
if (dircount > 1) {
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX);
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
print_matches(self, m, dirnames, dircount, NAME_MAX + 1);
}
return complete_line(self, dirnames, dircount, NAME_MAX);
return complete_line(self, dirnames, dircount, NAME_MAX + 1);
}

View File

@ -31,7 +31,7 @@
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);
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
/* attempts to match /command "<incomplete-dir>" line to matching directories.

View File

@ -55,6 +55,7 @@ int avatar_send(Tox *m, uint32_t friendnum)
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)
return 0;
@ -150,6 +151,7 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
} else if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED;
}
break;
case TOX_FILE_CONTROL_PAUSE:

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,
@ -251,6 +255,7 @@ static int update_DHT_nodeslist(const char *nodes_path)
}
struct Recv_Curl_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
@ -376,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;
}
@ -476,7 +481,7 @@ void *load_nodeslist_thread(void *data)
/* If nodeslist does not contain any valid entries we set the last_scan value
* to 0 so that it will fetch a new list the next time this function is called.
*/
if (idx == 0) {
if (Nodes.count == 0) {
const char *s = "{\"last_scan\":0}";
rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
@ -523,6 +528,7 @@ int load_DHT_nodeslist(void)
}
thread_data.active = true;
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
thread_data.active = false;
return -5;
@ -575,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

@ -48,9 +48,9 @@
#include "message_queue.h"
#ifdef AUDIO
#include "audio_call.h"
#include "audio_call.h"
#ifdef VIDEO
#include "video_call.h"
#include "video_call.h"
#endif /* VIDEO */
#endif /* AUDIO */
@ -65,8 +65,12 @@ static void init_infobox(ToxWindow *self);
static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */
#ifdef AUDIO
#define AC_NUM_CHAT_COMMANDS 30
#if defined(AUDIO) && defined(PYTHON)
#define AC_NUM_CHAT_COMMANDS 32
#elif AUDIO
#define AC_NUM_CHAT_COMMANDS 31
#elif PYTHON
#define AC_NUM_CHAT_COMMANDS 23
#else
#define AC_NUM_CHAT_COMMANDS 22
#endif /* AUDIO */
@ -106,8 +110,15 @@ 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)
@ -152,7 +163,7 @@ void kill_chat_window(ToxWindow *self, Tox *m)
}
static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const char *msg, size_t len,
const char *nick, const char *timefrmt)
const char *nick, const char *timefrmt)
{
ChatContext *ctx = self->chatwin;
@ -161,10 +172,10 @@ static void recv_message_helper(ToxWindow *self, Tox *m, uint32_t num, const cha
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);
self->active_box, "%s", msg);
else
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, nick, "%s", msg);
&self->active_box, nick, "%s", msg);
}
static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char *action, size_t len,
@ -177,10 +188,10 @@ static void recv_action_helper(ToxWindow *self, Tox *m, uint32_t num, const char
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 );
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)
@ -227,8 +238,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";
@ -454,7 +463,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.",
ft->index, ft->file_name);
ft->index, ft->file_name);
char progline[MAX_STR_SIZE];
init_progress_bar(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
@ -466,10 +475,12 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
break;
}
case TOX_FILE_CONTROL_PAUSE: {
ft->state = FILE_TRANSFER_PAUSED;
break;
}
case TOX_FILE_CONTROL_CANCEL: {
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
close_file_transfer(self, m, ft, -1, msg, notif_error);
@ -548,7 +559,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,10 +612,10 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
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 );
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,
@ -671,8 +682,10 @@ void chat_onRinging (ToxWindow *self, ToxAV *av, uint32_t friend_number, int sta
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)
sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound);
#endif /* SOUND_NOTIFY */
}
@ -881,8 +894,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)
@ -927,11 +940,20 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
diff = dir_match(self, m, ctx->line, L"/sendfile");
} 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"},
{"busy"},
{"online"},
{"away"},
{"busy"},
};
diff = complete_line(self, status_cmd_list, 3, 8);
} else {
@ -950,8 +972,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line))
{
if (!wstring_is_empty(ctx->line)) {
add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n');
@ -980,8 +1001,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);
}
}
@ -1029,9 +1050,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE:
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
colour = RED;
break;
@ -1107,9 +1130,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
wnoutrefresh(self->window);
#ifdef AUDIO
if (ctx->infobox.active) {
draw_infobox(self);
}
#endif
if (self->help->active)
@ -1133,9 +1158,8 @@ static void chat_onInit(ToxWindow *self, Tox *m)
/* 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);

View File

@ -92,8 +92,10 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
return;
}
if (tox_invite_friend(m, self->num, groupnum) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
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);
return;
}
@ -116,24 +118,23 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
return;
}
int groupnum = -1;
if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
return;
}
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
/*#ifdef AUDIO
else
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
NULL, NULL);
#endif*/
TOX_ERR_CONFERENCE_JOIN err;
if (groupnum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
uint32_t groupnum = tox_conference_join(m, self->num, (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);
return;
}
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_conference_delete(m, groupnum, NULL);
return;
}
@ -190,6 +191,7 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
on_recv_error:
switch (err) {
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
@ -258,7 +260,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
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);
(uint8_t *) file_name, namelen, &err);
if (err != TOX_ERR_FILE_SEND_OK)
goto on_send_error;
@ -282,6 +284,7 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
return;
on_send_error:
switch (err) {
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
errmsg = "File transfer failed: Invalid friend.";

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

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;
@ -65,8 +66,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 +87,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 },
@ -186,11 +191,19 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
case GROUPCHAT_COMMAND_MODE:
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)
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

@ -136,7 +136,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction)
FILE_TRANSFER_DIRECTION direction)
{
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL;
@ -145,8 +145,8 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
&Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
return ft;

View File

@ -87,7 +87,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
* Returns NULL on failure.
*/
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction);
FILE_TRANSFER_DIRECTION direction);
/* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure.

View File

@ -153,10 +153,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)];
@ -250,10 +254,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)];
@ -317,7 +326,7 @@ static void sort_blocklist_index(void)
static void update_friend_last_online(uint32_t num, time_t timestamp)
{
Friends.list[num].last_online.last_on = timestamp;
Friends.list[num].last_online.tm = *localtime((const time_t*)&timestamp);
Friends.list[num].last_online.tm = *localtime((const time_t *)&timestamp);
/* if the format changes make sure TIME_STR_SIZE is the correct size */
const char *t = user_settings->timestamp_format;
@ -457,6 +466,10 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
if (sort)
sort_friendlist_index();
#ifdef AUDIO
init_friend_AV(i);
#endif
return;
}
}
@ -538,7 +551,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
get_nick_truncate(m, nick, num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED,
"* Group chat invite from %s failed: too many windows are open.", nick);
"* Group chat invite from %s failed: too many windows are open.", nick);
sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
}
@ -560,6 +573,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;
if (tox_friend_delete(m, f_num, &err) != true) {
fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
return;
@ -595,6 +609,10 @@ static void delete_friend(Tox *m, uint32_t f_num)
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)
--Friends.num_selected;
@ -645,16 +663,18 @@ static void draw_del_popup(void)
wattron(PendingDelete.popup, A_BOLD);
pthread_mutex_lock(&Winthread.lock);
if (blocklist_view == 0)
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
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 */
@ -799,6 +819,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
block_friend(m, f);
else
unblock_friend(m, f);
break;
case KEY_RIGHT:
@ -811,6 +832,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
select_friend(self, key, &Friends.num_selected, Friends.num_friends);
else
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
break;
}
}
@ -881,7 +903,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
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)
@ -970,9 +992,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE:
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
colour = RED;
break;
@ -1054,9 +1078,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock);
int day_dist = (
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
+ ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
);
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday
+ ((cur_loc_tm.tm_year - Friends.list[f].last_online.tm.tm_year) * 365)
);
const char *hourmin = Friends.list[f].last_online.hour_min_str;
pthread_mutex_unlock(&Winthread.lock);
@ -1096,7 +1120,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
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)
@ -1112,7 +1136,8 @@ void disable_chatwin(uint32_t f_num)
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{
assert(0);
if( friend_number >= Friends.max_idx)
if ( friend_number >= Friends.max_idx)
return;
assert(0);
@ -1120,7 +1145,7 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
if (Friends.list[friend_number].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
set_active_window(Friends.list[friend_number].chatwin);
}
@ -1138,6 +1163,18 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
}
#endif /* AUDIO */
/* Returns a friend's status */
TOX_USER_STATUS get_friend_status(uint32_t friendnumber)
{
return Friends.list[friendnumber].status;
}
/* Returns a friend's connection status */
TOX_CONNECTION get_friend_connection_status(uint32_t friendnumber)
{
return Friends.list[friendnumber].connection_status;
}
ToxWindow new_friendlist(void)
{
ToxWindow ret;

View File

@ -44,7 +44,7 @@ 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];
@ -54,7 +54,7 @@ typedef struct {
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;
@ -87,6 +87,8 @@ int get_friendnum(uint8_t *name);
int load_blocklist(char *data);
void kill_friendlist(void);
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

@ -130,6 +130,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
break;
case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */
default:
errmsg = "Faile to add friend: Unknown error.";
@ -260,6 +261,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
}
char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
return;
@ -281,6 +283,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
case TOX_ERR_BOOTSTRAP_NULL:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
break;
default:
break;
}
@ -333,31 +336,31 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
uint8_t type;
if (!strcasecmp(argv[1], "audio"))
type = TOX_GROUPCHAT_TYPE_AV;
type = TOX_CONFERENCE_TYPE_AV;
else if (!strcasecmp(argv[1], "text"))
type = TOX_GROUPCHAT_TYPE_TEXT;
type = TOX_CONFERENCE_TYPE_TEXT;
else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return;
}
int groupnum = -1;
if (type != TOX_CONFERENCE_TYPE_TEXT) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
return;
}
if (type == TOX_GROUPCHAT_TYPE_TEXT)
groupnum = tox_add_groupchat(m);
/*#ifdef AUDIO
else
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
#endif*/
TOX_ERR_CONFERENCE_NEW err;
if (groupnum == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
uint32_t groupnum = tox_conference_new(m, &err);
if (err != TOX_ERR_CONFERENCE_NEW_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize (error %d)", err);
return;
}
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
tox_del_groupchat(m, groupnum);
tox_conference_delete(m, groupnum, NULL);
return;
}
@ -448,25 +451,42 @@ void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
char dir[data_file_len + 1];
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir);
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
#ifdef QRPNG
FILE *output = fopen(qr_path, "wb");
if (argc == 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Required 'txt' or 'png'");
return;
} else if (!strcmp(argv[1], "txt")) {
if (output == NULL) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
#endif /* QRPNG */
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT) + 1];
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
if (ID_to_QRcode_txt(id_string, qr_path) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
#ifdef QRPNG
} else if (!strcmp(argv[1], "png")) {
char qr_path[dir_len + nick_len + strlen(QRCODE_FILENAME_EXT_PNG) + 1];
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT_PNG);
if (ID_to_QRcode_png(id_string, qr_path) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Unknown option '%s' -- Required 'txt' or 'png'", argv[1]);
return;
}
if (ID_to_QRcode(id_string, output) == -1) {
fclose(output);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code.");
return;
}
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "QR code has been printed to the file '%s'", qr_path);
fclose(output);
#endif /* QRPNG */
}
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -543,8 +563,10 @@ void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
cmd_myid(window, self, m, 0, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Any services that relied on your old ID will need to be updated manually.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'", old_nospam);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0,
"Any services that relied on your old ID will need to be updated manually.");
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "If you ever want your old Tox ID back, type '/nospam %X'",
old_nospam);
}
void cmd_prompt_help(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -633,6 +655,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s: \"%s\".", status_str, msg);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
}
finish:

View File

@ -56,4 +56,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 */
#ifdef PYTHON
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif
#endif /* #define GLOBAL_COMMANDS_H */

View File

@ -30,18 +30,25 @@
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
TOX_ERR_CONFERENCE_TITLE err;
char title[MAX_STR_SIZE];
if (argc < 1) {
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
if (tlen != -1) {
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
} else {
if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
}
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
return;
}
@ -55,8 +62,8 @@ void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
int len = strlen(title) - 1;
title[len] = '\0';
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
return;
}

View File

@ -69,8 +69,12 @@ static int max_groupchat_index = 0;
extern struct user_settings *user_settings;
extern struct Winthread Winthread;
#ifdef AUDIO
#if defined(AUDIO) && defined(PYTHON)
#define AC_NUM_GROUP_COMMANDS 25
#elif AUDIO
#define AC_NUM_GROUP_COMMANDS 24
#elif PYTHON
#define AC_NUM_GROUP_COMMANDS 21
#else
#define AC_NUM_GROUP_COMMANDS 20
#endif /* AUDIO */
@ -98,25 +102,18 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/status" },
{ "/title" },
#ifdef AUDIO
#ifdef PYTHON
{ "/lsdev" },
{ "/sdev" },
{ "/mute" },
{ "/sense" },
{ "/run" },
#endif /* AUDIO */
#endif /* PYTHON */
};
#ifdef AUDIO
static int group_audio_open_out_device(int groupnum);
static int group_audio_close_out_device(int groupnum);
#endif /* AUDIO */
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
{
if (groupnum > MAX_GROUPCHAT_NUM)
if (groupnum > MAX_GROUPCHAT_NUM) {
return -1;
}
ToxWindow self = new_group_chat(m, groupnum);
int i;
@ -129,24 +126,6 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
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);
#ifdef AUDIO
if (type == TOX_GROUPCHAT_TYPE_AV)
if (group_audio_open_out_device(i) == -1)
fprintf(stderr, "Group Audio failed to init\n");
#endif /* AUDIO */
set_active_window(groupchats[i].chatwin);
if (i == max_groupchat_index)
@ -174,17 +153,10 @@ static void kill_groupchat_window(ToxWindow *self)
del_window(self);
}
void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
void free_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
{
tox_del_groupchat(m, groupnum);
#ifdef AUDIO
group_audio_close_out_device(groupnum);
#endif
free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names);
free(groupchats[groupnum].peer_name_lengths);
free(groupchats[groupnum].oldpeer_name_lengths);
free(groupchats[groupnum].name_list);
free(groupchats[groupnum].peer_list);
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
int i;
@ -198,6 +170,12 @@ void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
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)
{
@ -237,8 +215,8 @@ void redraw_groupchat_win(ToxWindow *self)
}
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
const char *msg, uint16_t len)
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
TOX_MESSAGE_TYPE type, const char *msg, size_t len)
{
if (self->num != groupnum)
return;
@ -266,56 +244,20 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
nick_clr = RED;
}
else {
} else {
sound_notify(self, silent, NT_WNDALERT_1, NULL);
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
line_info_add(self, timefrmt, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg);
write_to_log(msg, nick, ctx->log, false);
}
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
uint16_t len)
{
if (self->num != groupnum)
return;
ChatContext *ctx = self->chatwin;
char nick[TOX_MAX_NAME_LENGTH];
get_group_nick_truncate(m, nick, peernum, groupnum);
char selfnick[TOX_MAX_NAME_LENGTH];
tox_self_get_name(m, (uint8_t *) selfnick);
size_t n_len = tox_self_get_name_size(m);
selfnick[n_len] = '\0';
if (strcasestr(action, selfnick)) {
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
if (self->active_box != -1)
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
else
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
}
else {
sound_notify(self, silent, NT_WNDALERT_1, NULL);
}
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
write_to_log(action, nick, ctx->log, true);
}
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title,
uint8_t length)
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
const char *title,
size_t length)
{
ChatContext *ctx = self->chatwin;
@ -340,96 +282,102 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum,
write_to_log(tmp_event, nick, ctx->log, true);
}
/* Puts two copies of peerlist/lengths in chat instance */
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int 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 = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = malloc(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);
int i;
if (chat->name_list) {
free(chat->name_list);
}
for (i = 0; i < npeers; ++i) {
if (!lengths[i]) {
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 {
uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1);
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], 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);
chat->name_list = malloc(sizeof(char *) * chat->num_peers * TOX_MAX_NAME_LENGTH);
if (chat->name_list == NULL) {
exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY);
}
uint32_t i, count = 0;
for (i = 0; i < chat->max_idx; ++i) {
if (chat->peer_list[i].active) {
memcpy(&chat->name_list[count * TOX_MAX_NAME_LENGTH], chat->peer_list[i].name, chat->peer_list[i].name_length + 1);
++count;
}
}
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);
qsort(chat->name_list, count, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
}
struct group_add_thrd {
Tox *m;
ToxWindow *self;
int peernum;
int 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)
/* 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)
{
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
ToxWindow *self = thrd->self;
Tox *m = thrd->m;
char peername[TOX_MAX_NAME_LENGTH];
/* keep polling for a name that differs from the default until we run out of time */
while (true) {
usleep(100000);
pthread_mutex_lock(&Winthread.lock);
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
pthread_mutex_unlock(&Winthread.lock);
break;
}
pthread_mutex_unlock(&Winthread.lock);
if (!chat) {
return -1;
}
const char *event = "has joined the room";
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
if (num_peers == 0) {
free(chat->peer_list);
chat->peer_list = NULL;
return 0;
}
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);
struct GroupPeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct GroupPeer));
pthread_attr_destroy(&thrd->attr);
free(thrd);
pthread_exit(NULL);
if (!tmp_list) {
return -1;
}
chat->peer_list = tmp_list;
return 0;
}
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
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) {
continue;
}
peer->active = true;
peer->name_length = length;
peer->peernumber = i;
}
group_update_name_list(groupnum);
}
static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum)
{
if (self->num != groupnum)
return;
@ -437,109 +385,57 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
if (groupnum > max_groupchat_index)
return;
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
int num_peers = groupchats[groupnum].num_peers;
GroupChat *chat = &groupchats[groupnum];
TOX_ERR_CONFERENCE_PEER_QUERY err;
if (peernum > num_peers)
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
uint32_t old_num = chat->num_peers;
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
chat->num_peers = num_peers;
} else {
num_peers = old_num;
}
chat->max_idx = num_peers;
update_peer_list(m, groupnum, num_peers);
}
static void groupchat_onGroupPeerNameChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
const char *name, size_t length)
{
if (self->num != groupnum) {
return;
/* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
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 */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
uint16_t tmp_peerlens[num_peers];
GroupChat *chat = &groupchats[groupnum];
if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
if (!chat) {
return;
}
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
size_t i;
/* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH];
for (i = 0; i < chat->max_idx; ++i) {
GroupPeer *peer = &chat->peer_list[i];
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0';
}
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
ChatContext *ctx = self->chatwin;
const char *event;
char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt));
switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD:
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_CHAT_CHANGE_PEER_DEL:
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_CHAT_CHANGE_PEER_NAME:
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 && strcmp(peer->name, "Tox User")) {
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", (char *) name);
write_to_log(tmp_event, peer->name, ctx->log, true);
line_info_add(self, timefrmt, peer->name, (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)
@ -549,9 +445,10 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
return;
}
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
const char *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
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);
}
}
@ -591,11 +488,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) {
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);
}
@ -621,8 +527,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line))
{
if (!wstring_is_empty(ctx->line)) {
add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n');
@ -634,7 +539,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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 "));
@ -642,9 +547,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
}
} else {
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
const char *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
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);
}
}
}
@ -702,7 +608,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
wmove(ctx->sidebar, i + 2, 1);
pthread_mutex_lock(&Winthread.lock);
int peer = i + groupchats[self->num].side_pos;
uint32_t peer = i + groupchats[self->num].side_pos;
pthread_mutex_unlock(&Winthread.lock);
/* truncate nick to fit in side panel without modifying list */
@ -710,7 +616,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';
@ -725,7 +631,7 @@ 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)
help_onDraw(self);
@ -767,125 +673,7 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
}
#ifdef AUDIO
static int group_audio_open_out_device(int groupnum)
{
char dname[MAX_STR_SIZE];
get_primary_device_name(output, dname, sizeof(dname));
dname[MAX_STR_SIZE - 1] = '\0';
groupchats[groupnum].audio.dvhandle = alcOpenDevice(dname);
if (groupchats[groupnum].audio.dvhandle == NULL)
return -1;
groupchats[groupnum].audio.dvctx = alcCreateContext(groupchats[groupnum].audio.dvhandle, NULL);
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
alGenBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alGenSources((uint32_t) 1, &groupchats[groupnum].audio.source);
alSourcei(groupchats[groupnum].audio.source, AL_LOOPING, AL_FALSE);
if (alcGetError(groupchats[groupnum].audio.dvhandle) != AL_NO_ERROR) {
group_audio_close_out_device(groupnum);
groupchats[groupnum].audio.dvhandle = NULL;
groupchats[groupnum].audio.dvctx = NULL;
return -1;
}
alSourceQueueBuffers(groupchats[groupnum].audio.source, OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alSourcePlay(groupchats[groupnum].audio.source);
return 0;
}
static int group_audio_close_out_device(int groupnum)
{
if (!groupchats[groupnum].audio.dvhandle)
return -1;
if (!groupchats[groupnum].audio.dvctx)
return -1;
if (alcGetCurrentContext() != groupchats[groupnum].audio.dvctx)
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
alDeleteSources((uint32_t) 1, &groupchats[groupnum].audio.source);
alDeleteBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
alcMakeContextCurrent(NULL);
alcDestroyContext(groupchats[groupnum].audio.dvctx);
if (!alcCloseDevice(groupchats[groupnum].audio.dvhandle))
return -1;
return 0;
}
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
// unsigned int sample_rate)
// {
// if (!pcm)
// return -1;
// if (channels == 0 || channels > 2)
// return -2;
// ALuint bufid;
// ALint processed = 0, queued = 0;
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
// fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
// if (processed) {
// ALuint bufids[processed];
// alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
// alDeleteBuffers(processed - 1, bufids + 1);
// bufid = bufids[0];
// } else if (queued < 16) {
// alGenBuffers(1, &bufid);
// } else {
// return -3;
// }
// int length = samples * channels * sizeof(int16_t);
// alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
// alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
// ALint state;
// alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
// if (state != AL_PLAYING)
// alSourcePlay(groupchats[groupnum].audio.source);
// return 0;
// }
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, uint32_t groupnum, int peernum, const int16_t *pcm,
unsigned int samples, uint8_t channels, unsigned int sample_rate)
{
return;
// if (groupnum != self->num)
// return;
// if (peernum < 0)
// return;
// if (groupchats[groupnum].audio.dvhandle == NULL)
// fprintf(stderr, "dvhandle is null)\n");
// if (groupchats[groupnum].audio.dvctx == NULL)
// fprintf(stderr, "ctx is null\n");
// int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
// fprintf(stderr, "write: %d\n", ret);
}
#endif /* AUDIO */
ToxWindow new_group_chat(Tox *m, int groupnum)
ToxWindow new_group_chat(Tox *m, uint32_t groupnum)
{
ToxWindow ret;
memset(&ret, 0, sizeof(ret));
@ -897,14 +685,10 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
ret.onDraw = &groupchat_onDraw;
ret.onInit = &groupchat_onInit;
ret.onGroupMessage = &groupchat_onGroupMessage;
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupAction = &groupchat_onGroupAction;
ret.onGroupNameListChange = &groupchat_onGroupNameListChange;
ret.onGroupPeerNameChange = &groupchat_onGroupPeerNameChange;
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
#ifdef AUDIO
ret.onWriteDevice = &groupchat_onWriteDevice;
#endif
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
ChatContext *chatwin = calloc(1, sizeof(ChatContext));

View File

@ -26,61 +26,41 @@
#include "toxic.h"
#include "windows.h"
#ifdef AUDIO
#include "audio_call.h"
#endif
#ifdef AUDIO
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif /* ALC_ALL_DEVICES_SPECIFIER */
#endif /* __APPLE__ */
#endif /* AUDIO */
#define SIDEBAR_WIDTH 16
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3
#ifdef AUDIO
struct GAudio {
ALCdevice *dvhandle; /* Handle of device selected/opened */
ALCcontext *dvctx;
ALuint source;
ALuint buffers[OPENAL_BUFS];
};
#endif /* AUDIO */
typedef struct GroupPeer {
bool active;
char name[TOX_MAX_NAME_LENGTH];
size_t name_length;
uint32_t peernumber;
} GroupPeer;
typedef struct {
int chatwin;
bool active;
uint8_t type;
int 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;
#ifdef AUDIO
struct GAudio audio;
#endif
GroupPeer *peer_list;
size_t max_idx;
char *name_list;
size_t num_peers;
} GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
/* 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, int groupnum);
ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
#endif /* #define GROUPCHAT_H */

View File

@ -27,7 +27,15 @@
#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)
@ -95,6 +103,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 +127,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,7 +173,11 @@ 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 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 */
wprintw(win, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -181,10 +200,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)
@ -215,6 +242,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
@ -227,7 +255,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)
@ -253,7 +281,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)
@ -271,9 +299,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
static void help_draw_contacts(ToxWindow *self)
{
WINDOW *win = self->help->win;
@ -293,12 +341,14 @@ 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)
{
switch(key) {
int height;
switch (key) {
case 'x':
case T_KEY_ESC:
help_exit(self);
@ -306,9 +356,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
@ -316,13 +366,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;
@ -331,6 +384,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
case 'f':
help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS;
@ -350,9 +411,7 @@ void help_onKey(ToxWindow *self, wint_t key)
void help_onDraw(ToxWindow *self)
{
curs_set(0);
switch(self->help->type) {
switch (self->help->type) {
case HELP_MENU:
help_draw_menu(self);
return;
@ -376,5 +435,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,6 +33,9 @@ typedef enum {
HELP_GROUP,
HELP_KEYS,
HELP_CONTACTS,
#ifdef PYTHON
HELP_PLUGIN,
#endif
} HELP_TYPES;
void help_onDraw(ToxWindow *self);

View File

@ -275,9 +275,9 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
self->show_peerlist ^= 1;
redraw_groupchat_win(self);
}
match = true;
}
else if (key == user_settings->key_toggle_pastemode) {
} else if (key == user_settings->key_toggle_pastemode) {
self->chatwin->pastemode ^= 1;
match = true;
}

View File

@ -129,17 +129,21 @@ static struct line_info *line_info_ret_queue(struct history *hst)
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;
return -1;
struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return;
return -1;
struct line_info *new_line = calloc(1, sizeof(struct line_info));
@ -158,12 +162,14 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
/* for type-specific formatting in print function */
switch (type) {
case IN_ACTION:
/* fallthrough */
case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2;
break;
case IN_MSG:
/* fallthrough */
case OUT_MSG:
len += strlen(user_settings->line_normal) + 3;
@ -220,6 +226,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;
@ -228,6 +235,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() */
@ -242,10 +251,10 @@ static void line_info_check_queue(ToxWindow *self)
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);
@ -304,8 +313,10 @@ void line_info_print(ToxWindow *self)
switch (type) {
case OUT_MSG:
/* fallthrough */
case OUT_MSG_READ:
/* fallthrough */
case IN_MSG:
wattron(win, COLOR_PAIR(BLUE));
@ -323,10 +334,10 @@ void line_info_print(ToxWindow *self)
wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
wattroff(win, COLOR_PAIR(nameclr));
char* msg = line->msg;
while (msg)
{
char* line = strsep(&msg, "\n");
char *msg = line->msg;
while (msg) {
char *line = strsep(&msg, "\n");
if (line[0] == '>')
wattron(win, COLOR_PAIR(GREEN));
@ -360,8 +371,10 @@ void line_info_print(ToxWindow *self)
break;
case OUT_ACTION_READ:
/* fallthrough */
case OUT_ACTION:
/* fallthrough */
case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE));
@ -548,20 +561,15 @@ bool line_info_onKey(ToxWindow *self, wint_t key)
if (key == user_settings->key_half_page_up) {
line_info_page_up(self, hst);
}
else if (key == user_settings->key_half_page_down) {
} else if (key == user_settings->key_half_page_down) {
line_info_page_down(self, hst);
}
else if (key == user_settings->key_scroll_line_up) {
} else if (key == user_settings->key_scroll_line_up) {
line_info_scroll_up(hst);
}
else if (key == user_settings->key_scroll_line_down) {
} else if (key == user_settings->key_scroll_line_down) {
line_info_scroll_down(hst);
}
else if (key == user_settings->key_page_bottom) {
} else if (key == user_settings->key_page_bottom) {
line_info_reset_start(self, hst);
}
else {
} else {
match = false;
}

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);

View File

@ -262,6 +262,7 @@ int rename_logfile(char *src, char *dest, const char *selfkey, const char *other
return 0;
on_error:
if (log_on)
log_enable(src, selfkey, otherkey, log, LOG_CHAT);

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,

View File

@ -42,8 +42,12 @@ 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)

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);

View File

@ -26,9 +26,10 @@
#include <time.h>
#include <limits.h>
#include <dirent.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include "toxic.h"
#include "windows.h"
@ -70,7 +71,7 @@ struct tm *get_time(void)
{
struct tm *timeinfo;
time_t t = get_unix_time();
timeinfo = localtime((const time_t*) &t);
timeinfo = localtime((const time_t *) &t);
return timeinfo;
}
@ -156,7 +157,7 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
return 0;
}
@ -187,7 +188,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
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;
@ -201,7 +202,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
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;
@ -228,11 +229,11 @@ int valid_nick(const char *nick)
for (i = 0; nick[i]; ++i) {
if ((nick[i] == ' ' && nick[i + 1] == ' ')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
|| nick[i] == '/'
|| nick[i] == '\n'
|| nick[i] == '\t'
|| nick[i] == '\v'
|| nick[i] == '\r')
return 0;
}
@ -319,45 +320,71 @@ void str_to_lower(char *str)
Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
TOX_ERR_FRIEND_QUERY err;
size_t len = tox_friend_get_name_size(m, friendnum, &err);
if (len == 0) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
if (err != TOX_ERR_FRIEND_QUERY_OK) {
goto on_error;
} else {
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
goto on_error;
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
return len;
}
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
{
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
TOX_ERR_CONFERENCE_PEER_QUERY err;
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
if (len == -1) {
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
goto on_error;
} else {
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
goto on_error;
}
}
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0';
filter_str(buf, len);
return len;
on_error:
strcpy(buf, UNKNOWN_NAME);
len = strlen(UNKNOWN_NAME);
buf[len] = '\0';
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.

View File

@ -126,7 +126,7 @@ void str_to_lower(char *str);
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
/* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
/* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */

View File

@ -21,6 +21,7 @@
*/
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <curl/curl.h>
@ -249,21 +250,29 @@ void *lookup_thread_func(void *data)
}
struct Recv_Curl_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
char post_data[MAX_STR_SIZE];
snprintf(post_data, sizeof(post_data), "{\"action\": 3, \"name\": \"%s\"}", name);
struct curl_slist *headers = NULL;
headers = curl_slist_append(headers, "Content-Type: application/json");
headers = curl_slist_append(headers, "charsets: utf-8");
curl_easy_setopt(c_handle, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(c_handle, CURLOPT_URL, real_domain);
curl_easy_setopt(c_handle, CURLOPT_WRITEFUNCTION, curl_cb_write_data);
curl_easy_setopt(c_handle, CURLOPT_WRITEDATA, &recv_data);
curl_easy_setopt(c_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
curl_easy_setopt(c_handle, CURLOPT_POSTFIELDS, post_data);
int proxy_ret = set_curl_proxy(c_handle, arg_opts.proxy_address, arg_opts.proxy_port, arg_opts.proxy_type);

View File

@ -38,24 +38,24 @@
#include "xtra.h"
#if defined(AUDIO) || defined(SOUND_NOTIFY)
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#ifdef __APPLE__
#include <OpenAL/al.h>
#include <OpenAL/alc.h>
#else
#include <AL/al.h>
#include <AL/alc.h>
/* compatibility with older versions of OpenAL */
#ifndef ALC_ALL_DEVICES_SPECIFIER
#include <AL/alext.h>
#endif
#endif
#ifdef SOUND_NOTIFY
#include <AL/alut.h> /* freealut packet */
#endif
#endif /* AUDIO */
#ifdef BOX_NOTIFY
#include <libnotify/notify.h>
#include <libnotify/notify.h>
#endif
#define MAX_BOX_MSG_LEN 127
@ -75,7 +75,7 @@ struct Control {
#ifdef SOUND_NOTIFY
uint32_t device_idx; /* index of output device */
char* sounds[SOUNDS_SIZE];
char *sounds[SOUNDS_SIZE];
#endif /* SOUND_NOTIFY */
} Control = {0};
@ -88,7 +88,7 @@ struct _ActiveNotifications {
bool active;
int *id_indicator;
#ifdef BOX_NOTIFY
NotifyNotification* box;
NotifyNotification *box;
char messages[MAX_BOX_MSG_LEN + 1][MAX_BOX_MSG_LEN + 1];
char title[64];
size_t size;
@ -188,15 +188,17 @@ void graceful_clear()
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].active) {
#ifdef BOX_NOTIFY
#ifdef BOX_NOTIFY
if (actives[i].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
#endif
if(actives[i].id_indicator)
#endif
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
if ( actives[i].looping ) {
@ -221,11 +223,11 @@ void graceful_clear()
control_unlock();
}
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -240,59 +242,67 @@ void* do_playing(void* _p)
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].looping) has_looping = true;
test_active_notify = actives[i].active && !actives[i].looping;
#ifdef BOX_NOTIFY
test_active_notify = test_active_notify && !actives[i].box;
#endif
#ifdef BOX_NOTIFY
test_active_notify = test_active_notify && !actives[i].box;
#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 */
/* Close */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#ifdef BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
#ifdef BOX_NOTIFY
else if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
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 */
/* stop source if not looping or playing, just terminate box */
alSourceStop(actives[i].source);
alDeleteSources(1, &actives[i].source);
alDeleteBuffers(1, &actives[i].buffer);
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
#endif
#endif
}
/* device is opened and no activity in under DEVICE_COOLDOWN time, close device*/
if (device_opened && !has_looping &&
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
(time(NULL) - last_opened_update) > DEVICE_COOLDOWN) {
m_close_device();
}
has_looping = false;
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
int play_source(uint32_t source, uint32_t buffer, bool looping)
{
int i = 0;
for (; i < ACTIVE_NOTIFS_MAX && actives[i].active; i ++);
if ( i == ACTIVE_NOTIFS_MAX ) {
return -1; /* Full */
}
@ -308,11 +318,11 @@ int play_source(uint32_t source, uint32_t buffer, bool looping)
}
#elif BOX_NOTIFY
void* do_playing(void* _p)
void *do_playing(void *_p)
{
(void)_p;
while(true) {
while (true) {
control_lock();
if (!Control.poll_active) {
@ -323,20 +333,22 @@ void* do_playing(void* _p)
int i;
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box && time(NULL) >= actives[i].n_timeout)
{
GError* ignore;
if (actives[i].box && time(NULL) >= actives[i].n_timeout) {
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
if(actives[i].id_indicator)
if (actives[i].id_indicator)
*actives[i].id_indicator = -1; /* reset indicator value */
memset(&actives[i], 0, sizeof(struct _ActiveNotifications));
}
}
control_unlock();
usleep(10000);
}
pthread_exit(NULL);
}
@ -347,7 +359,7 @@ void graceful_clear()
for (i = 0; i < ACTIVE_NOTIFS_MAX; i ++) {
if (actives[i].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[i].box, &ignore);
actives[i].box = NULL;
}
@ -378,6 +390,7 @@ int init_notify(int login_cooldown, int notification_timeout)
#endif /* SOUND_NOTIFY */
#if defined(SOUND_NOTIFY) || defined(BOX_NOTIFY)
if (pthread_mutex_init(Control.poll_mutex, NULL) != 0)
return -1;
@ -419,7 +432,9 @@ void terminate_notify()
#ifdef SOUND_NOTIFY
int i = 0;
for (; i < SOUNDS_SIZE; i ++) free(Control.sounds[i]);
alutExit();
#endif /* SOUND_NOTIFY */
@ -429,7 +444,7 @@ void terminate_notify()
}
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char* value)
int set_sound(Notification sound, const char *value)
{
if (sound == silent) return 0;
@ -457,10 +472,11 @@ int play_sound_internal(Notification what, bool loop)
alSourcei(source, AL_LOOPING, loop);
int rc = play_source(source, buffer, loop);
if (rc < 0) {
alSourceStop(source);
alDeleteSources(1, &source);
alDeleteBuffers(1,&buffer);
alDeleteBuffers(1, &buffer);
return -1;
}
@ -472,6 +488,7 @@ int play_notify_sound(Notification notif, uint64_t flags)
int rc = -1;
if (flags & NT_BEEP) beep();
if (notif != silent) {
if ( !Control.poll_active || !Control.sounds[notif] )
return -1;
@ -487,17 +504,21 @@ void stop_sound(int id)
{
if (id >= 0 && id < ACTIVE_NOTIFS_MAX && actives[id].looping && actives[id].active ) {
#ifdef BOX_NOTIFY
if (actives[id].box) {
GError* ignore;
GError *ignore;
notify_notification_close(actives[id].box, &ignore);
}
#endif
if (actives[id].id_indicator)
*actives[id].id_indicator = -1;
// alSourcei(actives[id].source, AL_LOOPING, false);
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alDeleteBuffers(1, &actives[id].buffer);
memset(&actives[id], 0, sizeof(struct _ActiveNotifications));
}
}
@ -508,6 +529,7 @@ static int m_play_sound(Notification notif, uint64_t flags)
#ifdef SOUND_NOTIFY
return play_notify_sound(notif, flags);
#else
if (notif != silent)
beep();
@ -516,12 +538,12 @@ static int m_play_sound(Notification notif, uint64_t flags)
}
#ifdef BOX_NOTIFY
void m_notify_action(NotifyNotification *box, char *action, void* data)
void m_notify_action(NotifyNotification *box, char *action, void *data)
{
}
#endif
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator)
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator)
{
tab_notify(self, flags);
@ -540,6 +562,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
if (id == -1) {
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].box; id++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -558,7 +581,7 @@ int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_in
return id;
}
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id)
{
tab_notify(self, flags);
@ -566,6 +589,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
return -1;
if (id < 0 || id >= ACTIVE_NOTIFS_MAX) return -1;
#ifdef SOUND_NOTIFY
control_lock();
@ -578,7 +602,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
alSourceStop(actives[id].source);
alDeleteSources(1, &actives[id].source);
alDeleteBuffers(1,&actives[id].buffer);
alDeleteBuffers(1, &actives[id].buffer);
alGenSources(1, &actives[id].source);
alGenBuffers(1, &actives[id].buffer);
@ -592,6 +616,7 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
return id;
#else
if (notif != silent)
beep();
@ -599,7 +624,8 @@ int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id)
#endif /* SOUND_NOTIFY */
}
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title,
const char *format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
@ -613,9 +639,11 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
control_lock();
#ifdef SOUND_NOTIFY
if (id == -1) { /* Could not play */
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -623,17 +651,23 @@ 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;
}
#else
if (id == -1)
return -1;
#endif /* SOUND_NOTIFY */
snprintf(actives[id].title, sizeof(actives[id].title), "%s", title);
if (strlen(title) > 23) strcpy(actives[id].title + 20, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -656,7 +690,7 @@ int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indi
#endif /* BOX_NOTIFY */
}
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...)
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...)
{
if (notifications_are_disabled(flags)) {
tab_notify(self, flags);
@ -675,7 +709,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
return -1;
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -688,7 +723,8 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
for (; i < actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}
@ -706,7 +742,7 @@ int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, con
#endif
}
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...)
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...)
{
tab_notify(self, flags);
@ -718,7 +754,9 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
control_lock();
int id;
for (id = 0; id < ACTIVE_NOTIFS_MAX && actives[id].active; id ++);
if ( id == ACTIVE_NOTIFS_MAX ) {
control_unlock();
return -1; /* Full */
@ -730,9 +768,11 @@ 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, "...");
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[0], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -756,7 +796,7 @@ int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const
#endif
}
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...)
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...)
{
tab_notify(self, flags);
@ -772,7 +812,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
}
va_list __ARGS__; va_start (__ARGS__, format);
va_list __ARGS__;
va_start (__ARGS__, format);
vsnprintf (actives[id].messages[actives[id].size], MAX_BOX_MSG_LEN, format, __ARGS__);
va_end (__ARGS__);
@ -785,7 +826,8 @@ int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* form
char formated[128 * 129] = {'\0'};
int i = 0;
for (; i <actives[id].size; i ++) {
for (; i < actives[id].size; i ++) {
strcat(formated, actives[id].messages[i]);
strcat(formated, "\n");
}

View File

@ -26,8 +26,7 @@
#include <inttypes.h>
#include "windows.h"
typedef enum _Notification
{
typedef enum _Notification {
silent = -1,
notif_error,
self_log_in,
@ -63,18 +62,19 @@ typedef enum _Flags {
int init_notify(int login_cooldown, int notification_timeout);
void terminate_notify();
int sound_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator);
int sound_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id);
int sound_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator);
int sound_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id);
void stop_sound(int id);
int box_notify(ToxWindow* self, Notification notif, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
int box_notify2(ToxWindow* self, Notification notif, uint64_t flags, int id, const char* format, ...);
int box_silent_notify(ToxWindow* self, uint64_t flags, int* id_indicator, const char* title, const char* format, ...);
int box_silent_notify2(ToxWindow* self, uint64_t flags, int id, const char* format, ...);
int box_notify(ToxWindow *self, Notification notif, uint64_t flags, int *id_indicator, const char *title,
const char *format, ...);
int box_notify2(ToxWindow *self, Notification notif, uint64_t flags, int id, const char *format, ...);
int box_silent_notify(ToxWindow *self, uint64_t flags, int *id_indicator, const char *title, const char *format, ...);
int box_silent_notify2(ToxWindow *self, uint64_t flags, int id, const char *format, ...);
#ifdef SOUND_NOTIFY
int set_sound(Notification sound, const char* value);
int set_sound(Notification sound, const char *value);
#endif /* SOUND_NOTIFY */
#endif /* NOTIFY_H */

View File

@ -31,13 +31,16 @@
#endif /* __OBJC__ */
#define RELEASE_CHK(func, obj) if ((obj))\
func((obj));
func((obj));
void bgrtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height);
#ifdef __OBJC__
@interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
- (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size;
@interface OSXVideo :
NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size;
@end
#endif /* __OBJC__ */

View File

@ -44,19 +44,19 @@
static uint8_t rgb_to_y(int r, int g, int b)
{
int y = ((9798 * r + 19235 * g + 3736 * b) >> 15);
return y>255? 255 : y<0 ? 0 : y;
return y > 255 ? 255 : y < 0 ? 0 : y;
}
static uint8_t rgb_to_u(int r, int g, int b)
{
int u = ((-5538 * r + -10846 * g + 16351 * b) >> 15) + 128;
return u>255? 255 : u<0 ? 0 : u;
return u > 255 ? 255 : u < 0 ? 0 : u;
}
static uint8_t rgb_to_v(int r, int g, int b)
{
int v = ((16351 * r + -13697 * g + -2664 * b) >> 15) + 128;
return v>255? 255 : v<0 ? 0 : v;
return v > 255 ? 255 : v < 0 ? 0 : v;
}
void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t *rgb, uint16_t width, uint16_t height)
@ -65,9 +65,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
uint8_t *p;
uint8_t r, g, b;
for(y = 0; y != height; y += 2) {
for (y = 0; y != height; y += 2) {
p = rgb;
for(x = 0; x != width; x++) {
for (x = 0; x != width; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
@ -76,7 +77,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
*plane_y++ = rgb_to_y(r, g, b);
}
for(x = 0; x != width / 2; x++) {
for (x = 0; x != width / 2; x++) {
b = *rgb++;
g = *rgb++;
r = *rgb++;
@ -91,9 +92,12 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
*plane_y++ = rgb_to_y(r, g, b);
b = ((int)b + (int)*(rgb - 8) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
g = ((int)g + (int)*(rgb - 7) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
r = ((int)r + (int)*(rgb - 6) + (int)*p + (int)*(p + 4) + 2) / 4; p++;
b = ((int)b + (int) * (rgb - 8) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
g = ((int)g + (int) * (rgb - 7) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
r = ((int)r + (int) * (rgb - 6) + (int) * p + (int) * (p + 4) + 2) / 4;
p++;
p++;
*plane_u++ = rgb_to_u(r, g, b);
@ -122,28 +126,34 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
BOOL _shouldMangleDimensions;
}
- (instancetype)initWithDeviceNames: (char **)device_names AmtDevices: (int *)size {
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size
{
_session = [[AVCaptureSession alloc] init];
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
int i;
for (i = 0; i < [devices count]; ++i) {
AVCaptureDevice *device = [devices objectAtIndex:i];
AVCaptureDevice *device = [devices objectAtIndex: i];
char *video_input_name;
NSString *localizedName = [device localizedName];
video_input_name = (char*)malloc(strlen([localizedName cStringUsingEncoding:NSUTF8StringEncoding]) + 1);
strcpy(video_input_name, (char*)[localizedName cStringUsingEncoding:NSUTF8StringEncoding]);
video_input_name = (char *)malloc(strlen([localizedName cStringUsingEncoding: NSUTF8StringEncoding]) + 1);
strcpy(video_input_name, (char *)[localizedName cStringUsingEncoding: NSUTF8StringEncoding]);
device_names[i] = video_input_name;
}
if ( i <= 0 )
return nil;
*size = i;
return self;
}
- (void)dealloc {
- (void)dealloc
{
pthread_mutex_destroy(&_frameLock);
[_session release];
[_linkerVideo release];
@ -151,14 +161,18 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
[super dealloc];
}
- (int)openVideoDeviceIndex: (uint32_t)device_idx Width: (uint16_t *)width Height: (uint16_t *)height {
- (int)openVideoDeviceIndex:
(uint32_t)device_idx Width:
(uint16_t *)width Height:
(uint16_t *)height
{
pthread_mutex_init(&_frameLock, NULL);
pthread_mutex_lock(&_frameLock);
_processingQueue = dispatch_queue_create("Toxic processing queue", DISPATCH_QUEUE_SERIAL);
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex: device_idx];
NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
if ( error != NULL ) {
[input release];
@ -166,7 +180,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
}
[_session beginConfiguration];
[_session addInput:input];
[_session addInput: input];
//_session.sessionPreset = AVCaptureSessionPreset640x480;
//*width = 640;
//*height = 480;
@ -176,8 +190,9 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
[device release];
/* Obtain device resolution */
AVCaptureInputPort *port = [input.ports objectAtIndex:0];
AVCaptureInputPort *port = [input.ports objectAtIndex: 0];
CMFormatDescriptionRef format_description = port.formatDescription;
if ( format_description ) {
CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(format_description);
*width = dimensions.width;
@ -188,36 +203,47 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
}
_linkerVideo = [[AVCaptureVideoDataOutput alloc] init];
[_linkerVideo setSampleBufferDelegate:self queue:_processingQueue];
[_linkerVideo setSampleBufferDelegate: self queue: _processingQueue];
// TODO possibly get a better pixel format
if (_shouldMangleDimensions) {
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480}];
[_linkerVideo setVideoSettings: @ {
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(id)kCVPixelBufferWidthKey: @640,
(id)kCVPixelBufferHeightKey: @480
}];
} else {
[_linkerVideo setVideoSettings:@{(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
}
[_session addOutput:_linkerVideo];
[_session addOutput: _linkerVideo];
[_session startRunning];
pthread_mutex_unlock(&_frameLock);
return 0;
}
- (void)closeVideoDeviceIndex: (uint32_t)device_idx {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex:device_idx];
- (void)closeVideoDeviceIndex:
(uint32_t)device_idx
{
NSArray *devices = [AVCaptureDevice devicesWithMediaType: AVMediaTypeVideo];
AVCaptureDevice *device = [devices objectAtIndex: device_idx];
NSError *error = NULL;
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice:device error:&error];
AVCaptureInput *input = [[AVCaptureDeviceInput alloc] initWithDevice: device error: &error];
[_session stopRunning];
[_session removeOutput:_linkerVideo];
[_session removeInput:input];
[_session removeOutput: _linkerVideo];
[_session removeInput: input];
[_linkerVideo release];
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
- (void)captureOutput:
(AVCaptureOutput *)captureOutput didOutputSampleBuffer:
(CMSampleBufferRef)sampleBuffer fromConnection:
(AVCaptureConnection *)connection
{
pthread_mutex_lock(&_frameLock);
CVImageBufferRef img = CMSampleBufferGetImageBuffer(sampleBuffer);
if (!img) {
NSLog(@"Toxic WARNING: Bad sampleBuffer from AVfoundation!");
} else {
@ -228,10 +254,17 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
// we're not going to do anything to it, so it's safe to lock it always
CVPixelBufferLockBaseAddress(_currentFrame, kCVPixelBufferLock_ReadOnly);
}
pthread_mutex_unlock(&_frameLock);
}
- (int)getVideoFrameY: (uint8_t *)y U: (uint8_t *)u V: (uint8_t *)v Width: (uint16_t *)width Height: (uint16_t *)height {
- (int)getVideoFrameY:
(uint8_t *)y U:
(uint8_t *)u V:
(uint8_t *)v Width:
(uint16_t *)width Height:
(uint16_t *)height
{
if (!_currentFrame) {
return -1;
}
@ -240,6 +273,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
CFRetain(_currentFrame);
CFTypeID imageType = CFGetTypeID(_currentFrame);
if (imageType == CVPixelBufferGetTypeID()) {
// TODO maybe handle other formats
bgrxtoyuv420(y, u, v, CVPixelBufferGetBaseAddress(_currentFrame), *width, *height);
@ -263,7 +297,7 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
/*
* C-interface for OSXVideo
*/
static OSXVideo* _OSXVideo = nil;
static OSXVideo *_OSXVideo = nil;
int osx_video_init(char **device_names, int *size)
{

View File

@ -49,10 +49,16 @@ extern struct Winthread Winthread;
extern FriendsList Friends;
FriendRequests FrndRequests;
#ifdef VIDEO
#if defined(PYTHON) && defined(VIDEO)
#define AC_NUM_GLOB_COMMANDS 23
#elif defined(PYTHON) && defined(AUDIO)
#define AC_NUM_GLOB_COMMANDS 21
#elif VIDEO
#define AC_NUM_GLOB_COMMANDS 22
#elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20
#elif PYTHON
#define AC_NUM_GLOB_COMMANDS 19
#else
#define AC_NUM_GLOB_COMMANDS 18
#endif
@ -92,6 +98,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)
@ -151,6 +163,13 @@ void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
statusbar->status = status;
}
/* Returns our own connection status */
TOX_CONNECTION prompt_selfConnectionStatus(void)
{
StatusBar *statusbar = prompt->stb;
return statusbar->connection;
}
/* Adds friend request to pending friend requests.
Returns request number on success, -1 if queue is full. */
static int add_friend_request(const char *public_key, const char *data)
@ -214,11 +233,18 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
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"},
{"busy"},
{"online"},
{"away"},
{"busy"},
};
diff = complete_line(self, status_cmd_list, 3, 8);
} else
@ -238,8 +264,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') {
rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line))
{
if (!wstring_is_empty(ctx->line)) {
add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n');
@ -301,10 +326,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
status_text = "Online";
colour = GREEN;
break;
case TOX_USER_STATUS_AWAY:
status_text = "Away";
colour = YELLOW;
break;
case TOX_USER_STATUS_BUSY:
status_text = "Busy";
colour = RED;
@ -335,7 +362,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);
@ -375,7 +402,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
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;
@ -404,8 +431,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
else
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick );
}
else if (connection_status == TOX_CONNECTION_NONE) {
} 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);

View File

@ -53,4 +53,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);
/* Returns our own connection status */
TOX_CONNECTION prompt_selfConnectionStatus(void);
#endif /* end of include guard: PROMPT_H */

347
src/python_api.c Normal file
View File

@ -0,0 +1,347 @@
/* 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 /* #define PYTHON_API_H */

View File

@ -23,30 +23,42 @@
#include <stdlib.h>
#include <qrencode.h>
#include <stdbool.h>
#include <string.h>
#include "toxic.h"
#include "windows.h"
#include "qr_code.h"
#ifdef QRPNG
#include <png.h>
#define INCHES_PER_METER (100.0/2.54)
#define DPI 72
#define SQUARE_SIZE 5
#endif /* QRPNG */
#define BORDER_LEN 1
#define CHAR_1 "\342\226\210"
#define CHAR_2 "\342\226\204"
#define CHAR_3 "\342\226\200"
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
/* Converts a tox ID string into a QRcode and prints it into the given filename.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode(const char *tox_id, FILE *fp)
int ID_to_QRcode_txt(const char *tox_id, const char *outfile)
{
FILE *fp = fopen(outfile, "wb");
if (fp == NULL)
return -1;
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
if (qr_obj == NULL)
if (qr_obj == NULL) {
fclose(fp);
return -1;
}
size_t width = qr_obj->width;
size_t i, j;
@ -83,7 +95,116 @@ int ID_to_QRcode(const char *tox_id, FILE *fp)
fprintf(fp, "\n");
}
fclose(fp);
QRcode_free(qr_obj);
return 0;
}
#ifdef QRPNG
/* Converts a tox ID string into a QRcode and prints it into the given filename as png.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode_png(const char *tox_id, const char *outfile)
{
static FILE *fp;
unsigned char *p;
unsigned char black[4] = {0, 0, 0, 255};
size_t x, y, xx, yy, real_width;
png_structp png_ptr;
png_infop info_ptr;
fp = fopen(outfile, "wb");
if (fp == NULL) {
return -1;
}
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
if (qr_obj == NULL) {
fclose(fp);
return -1;
}
real_width = (qr_obj->width + BORDER_LEN * 2) * SQUARE_SIZE;
size_t row_size = real_width * 4;
unsigned char row[row_size];
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (png_ptr == NULL) {
fclose(fp);
QRcode_free(qr_obj);
return -1;
}
info_ptr = png_create_info_struct(png_ptr);
if (info_ptr == NULL) {
fclose(fp);
QRcode_free(qr_obj);
return -1;
}
if (setjmp(png_jmpbuf(png_ptr))) {
fclose(fp);
QRcode_free(qr_obj);
png_destroy_write_struct(&png_ptr, &info_ptr);
return -1;
}
png_init_io(png_ptr, fp);
png_set_IHDR(png_ptr, info_ptr, real_width, real_width, 8,
PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
png_set_pHYs(png_ptr, info_ptr, DPI * INCHES_PER_METER,
DPI * INCHES_PER_METER, PNG_RESOLUTION_METER);
png_write_info(png_ptr, info_ptr);
/* top margin */
memset(row, 0xff, row_size);
for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) {
png_write_row(png_ptr, row);
}
/* data */
p = qr_obj->data;
for (y = 0; y < qr_obj->width; y++) {
memset(row, 0xff, row_size);
for (x = 0; x < qr_obj->width; x++) {
for (xx = 0; xx < SQUARE_SIZE; xx++) {
if (*p & 1) {
memcpy(&row[((BORDER_LEN + x) * SQUARE_SIZE + xx) * 4], black, 4);
}
}
p++;
}
for (yy = 0; yy < SQUARE_SIZE; yy++) {
png_write_row(png_ptr, row);
}
}
/* bottom margin */
memset(row, 0xff, row_size);
for (y = 0; y < BORDER_LEN * SQUARE_SIZE; y++) {
png_write_row(png_ptr, row);
}
png_write_end(png_ptr, info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
fclose(fp);
QRcode_free(qr_obj);
return 0;
}
#endif /* QRPNG */

View File

@ -25,11 +25,21 @@
#define QRCODE_FILENAME_EXT ".QRcode"
/* Converts a tox ID string into a QRcode and prints it to the given file stream.
/* Converts a tox ID string into a QRcode and prints it into the given filename.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode(const char *tox_id, FILE *fp);
int ID_to_QRcode_txt(const char *tox_id, const char *outfile);
#ifdef QRPNG
#define QRCODE_FILENAME_EXT_PNG ".QRcode.png"
/* Converts a tox ID string into a QRcode and prints it into the given filename as png.
*
* Returns 0 on success.
* Returns -1 on failure.
*/
int ID_to_QRcode_png(const char *tox_id, const char *outfile);
#endif /* QRPNG */
#endif /* QR_CODE */

View File

@ -32,45 +32,45 @@
#include "misc_tools.h"
#ifdef AUDIO
#include "audio_device.h"
#include "audio_device.h"
#endif /* AUDIO */
#include "settings.h"
#include "line_info.h"
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#define PACKAGE_DATADIR "."
#endif
#define NO_SOUND "silent"
static struct ui_strings {
const char* self;
const char* timestamps;
const char* time_format;
const char* timestamp_format;
const char* log_timestamp_format;
const char* alerts;
const char* bell_on_message;
const char* bell_on_filetrans;
const char* bell_on_filetrans_accept;
const char* bell_on_invite;
const char* native_colors;
const char* autolog;
const char* history_size;
const char* show_typing_self;
const char* show_typing_other;
const char* show_welcome_msg;
const char* show_connection_msg;
const char* nodeslist_update_freq;
const char *self;
const char *timestamps;
const char *time_format;
const char *timestamp_format;
const char *log_timestamp_format;
const char *alerts;
const char *bell_on_message;
const char *bell_on_filetrans;
const char *bell_on_filetrans_accept;
const char *bell_on_invite;
const char *native_colors;
const char *autolog;
const char *history_size;
const char *show_typing_self;
const char *show_typing_other;
const char *show_welcome_msg;
const char *show_connection_msg;
const char *nodeslist_update_freq;
const char* line_join;
const char* line_quit;
const char* line_alert;
const char* line_normal;
const char *line_join;
const char *line_quit;
const char *line_alert;
const char *line_normal;
const char* mplex_away;
const char* mplex_away_note;
const char *mplex_away;
const char *mplex_away_note;
} ui_strings = {
"ui",
"timestamps",
@ -98,7 +98,7 @@ static struct ui_strings {
"mplex_away_note",
};
static void ui_defaults(struct user_settings* settings)
static void ui_defaults(struct user_settings *settings)
{
settings->timestamps = TIMESTAMPS_ON;
snprintf(settings->timestamp_format, sizeof(settings->timestamp_format), "%s", TIMESTAMP_DEFAULT);
@ -131,18 +131,18 @@ static void ui_defaults(struct user_settings* settings)
}
static const struct keys_strings {
const char* self;
const char* next_tab;
const char* prev_tab;
const char* scroll_line_up;
const char* scroll_line_down;
const char* half_page_up;
const char* half_page_down;
const char* page_bottom;
const char* peer_list_up;
const char* peer_list_down;
const char* toggle_peerlist;
const char* toggle_pastemode;
const char *self;
const char *next_tab;
const char *prev_tab;
const char *scroll_line_up;
const char *scroll_line_down;
const char *half_page_up;
const char *half_page_down;
const char *page_bottom;
const char *peer_list_up;
const char *peer_list_down;
const char *toggle_peerlist;
const char *toggle_pastemode;
} key_strings = {
"keys",
"next_tab",
@ -159,7 +159,7 @@ static const struct keys_strings {
};
/* defines from toxic.h */
static void key_defaults(struct user_settings* settings)
static void key_defaults(struct user_settings *settings)
{
settings->key_next_tab = T_KEY_NEXT;
settings->key_prev_tab = T_KEY_PREV;
@ -175,33 +175,36 @@ static void key_defaults(struct user_settings* settings)
}
static const struct tox_strings {
const char* self;
const char* download_path;
const char* chatlogs_path;
const char* avatar_path;
const char* password_eval;
const char *self;
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",
};
static void tox_defaults(struct user_settings* settings)
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, "");
}
#ifdef AUDIO
static const struct audio_strings {
const char* self;
const char* input_device;
const char* output_device;
const char* VAD_treshold;
const char *self;
const char *input_device;
const char *output_device;
const char *VAD_treshold;
} audio_strings = {
"audio",
"input_device",
@ -209,7 +212,7 @@ static const struct audio_strings {
"VAD_treshold",
};
static void audio_defaults(struct user_settings* settings)
static void audio_defaults(struct user_settings *settings)
{
settings->audio_in_dev = 0;
settings->audio_out_dev = 0;
@ -219,17 +222,17 @@ static void audio_defaults(struct user_settings* settings)
#ifdef SOUND_NOTIFY
static const struct sound_strings {
const char* self;
const char* notif_error;
const char* self_log_in;
const char* self_log_out;
const char* user_log_in;
const char* user_log_out;
const char* call_incoming;
const char* call_outgoing;
const char* generic_message;
const char* transfer_pending;
const char* transfer_completed;
const char *self;
const char *notif_error;
const char *self_log_in;
const char *self_log_out;
const char *user_log_in;
const char *user_log_out;
const char *call_incoming;
const char *call_outgoing;
const char *generic_message;
const char *transfer_pending;
const char *transfer_completed;
} sound_strings = {
"sounds",
"notif_error",
@ -245,11 +248,12 @@ static const struct sound_strings {
};
#endif
static int key_parse(const char **bind) {
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;
}
@ -262,7 +266,8 @@ static int key_parse(const char **bind) {
return -1;
}
static void set_key_binding(int *key, const char **bind) {
static void set_key_binding(int *key, const char **bind)
{
int code = key_parse(bind);
if (code != -1) {
@ -318,6 +323,7 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_bool(setting, ui_strings.timestamps, &s->timestamps);
int time = 24;
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");
@ -338,12 +344,15 @@ int settings_load(struct user_settings *s, const char *patharg)
if (config_setting_lookup_bool(setting, ui_strings.bell_on_message, &s->bell_on_message)) {
s->bell_on_message = s->bell_on_message ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans, &s->bell_on_filetrans)) {
s->bell_on_filetrans = s->bell_on_filetrans ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_filetrans_accept, &s->bell_on_filetrans_accept)) {
s->bell_on_filetrans_accept = s->bell_on_filetrans_accept ? NT_BEEP : 0;
}
if (config_setting_lookup_bool(setting, ui_strings.bell_on_invite, &s->bell_on_invite)) {
s->bell_on_invite = s->bell_on_invite ? NT_BEEP : 0;
}
@ -361,12 +370,15 @@ int settings_load(struct user_settings *s, const char *patharg)
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) ) {
snprintf(s->line_quit, sizeof(s->line_quit), "%s", 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) ) {
snprintf(s->line_normal, sizeof(s->line_normal), "%s", str);
}
@ -409,6 +421,20 @@ int settings_load(struct user_settings *s, const char *patharg)
s->avatar_path[0] = '\0';
}
#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);
@ -420,32 +446,44 @@ int settings_load(struct user_settings *s, const char *patharg)
/* keys */
if ((setting = config_lookup(cfg, key_strings.self)) != NULL) {
const char* tmp = NULL;
const char *tmp = NULL;
if (config_setting_lookup_string(setting, key_strings.next_tab, &tmp))
set_key_binding(&s->key_next_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))
set_key_binding(&s->key_scroll_line_up, &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))
set_key_binding(&s->key_half_page_up, &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))
set_key_binding(&s->key_page_bottom, &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))
set_key_binding(&s->key_peer_list_down, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_peerlist, &tmp))
set_key_binding(&s->key_toggle_peerlist, &tmp);
if (config_setting_lookup_string(setting, key_strings.toggle_pastemode, &tmp))
set_key_binding(&s->key_toggle_pastemode, &tmp);
}
#ifdef AUDIO
if ((setting = config_lookup(cfg, audio_strings.self)) != NULL) {
config_setting_lookup_int(setting, audio_strings.input_device, &s->audio_in_dev);
s->audio_in_dev = s->audio_in_dev < 0 || s->audio_in_dev > MAX_DEVICES ? 0 : s->audio_in_dev;
@ -455,9 +493,11 @@ int settings_load(struct user_settings *s, const char *patharg)
config_setting_lookup_float(setting, audio_strings.VAD_treshold, &s->VAD_treshold);
}
#endif
#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) ) {
@ -506,8 +546,7 @@ int settings_load(struct user_settings *s, const char *patharg)
if (str && strcasecmp(str, NO_SOUND) != 0)
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
}
else {
} else {
set_sound(notif_error, PACKAGE_DATADIR "/sounds/ToxicError.wav");
set_sound(user_log_in, PACKAGE_DATADIR "/sounds/ToxicContactOnline.wav");
set_sound(user_log_out, PACKAGE_DATADIR "/sounds/ToxicContactOffline.wav");
@ -517,6 +556,7 @@ int settings_load(struct user_settings *s, const char *patharg)
set_sound(transfer_pending, PACKAGE_DATADIR "/sounds/ToxicTransferStart.wav");
set_sound(transfer_completed, PACKAGE_DATADIR "/sounds/ToxicTransferComplete.wav");
}
#endif
config_destroy(cfg);

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

View File

@ -52,8 +52,7 @@ extern struct Winthread Winthread;
#define PATH_SEP_S "/"
#define PATH_SEP_C '/'
typedef enum
{
typedef enum {
MPLEX_NONE,
MPLEX_SCREEN,
MPLEX_TMUX,
@ -97,13 +96,14 @@ static char *read_into_dyn_buffer (FILE *stream)
char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL)
{
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) {
int length = dyn_buffer_size + strlen (input_ptr);
if (dyn_buffer)
dyn_buffer = (char*) realloc (dyn_buffer, length);
dyn_buffer = (char *) realloc (dyn_buffer, length);
else
dyn_buffer = (char*) malloc (length);
dyn_buffer = (char *) malloc (length);
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
dyn_buffer_size = length;
}
@ -116,26 +116,29 @@ static char *extract_socket_path (const char *info)
const char *search_str = " Socket";
const char *pos = strstr (info, search_str);
char *end = NULL;
char* path = NULL;
char *path = NULL;
if (!pos)
return NULL;
pos += strlen (search_str);
pos = strchr (pos, PATH_SEP_C);
if (!pos)
return NULL;
end = strchr (pos, '\n');
if (!end)
return NULL;
*end = '\0';
end = strrchr (pos, '.');
if (!end)
return NULL;
path = (char*) malloc (end - pos + 1);
path = (char *) malloc (end - pos + 1);
*end = '\0';
return strcpy (path, pos);
}
@ -147,14 +150,17 @@ static int detect_gnu_screen ()
char *dyn_buffer = NULL;
socket_name = getenv ("STY");
if (!socket_name)
goto nomplex;
session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
if (!session_info_stream)
goto nomplex;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto nomplex;
@ -162,6 +168,7 @@ static int detect_gnu_screen ()
session_info_stream = NULL;
socket_path = extract_socket_path (dyn_buffer);
if (!socket_path)
goto nomplex;
@ -181,28 +188,34 @@ static int detect_gnu_screen ()
return 1;
nomplex:
if (session_info_stream)
pclose (session_info_stream);
if (dyn_buffer)
free (dyn_buffer);
if (socket_path)
free(socket_path);
return 0;
}
static int detect_tmux ()
{
char *tmux_env = getenv ("TMUX"), *pos;
if (!tmux_env)
return 0;
/* find second separator */
pos = strrchr (tmux_env, ',');
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;
}
@ -230,6 +243,7 @@ static int gnu_screen_is_detached ()
return 0;
struct stat sb;
if (stat (mplex_data, &sb) != 0)
return 0;
@ -238,14 +252,8 @@ static int gnu_screen_is_detached ()
}
/* 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.
*/
current session's entry.
*/
static int tmux_is_detached ()
{
if (mplex != MPLEX_TMUX)
@ -253,14 +261,18 @@ static int tmux_is_detached ()
FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos, *nl_pos, *attached_pos;
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)
goto fail;
dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer)
goto fail;
@ -268,13 +280,12 @@ static int tmux_is_detached ()
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, ": ");
/* 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);
@ -282,9 +293,8 @@ static int tmux_is_detached ()
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);
search_str = NULL;
@ -292,15 +302,19 @@ static int tmux_is_detached ()
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 (dyn_buffer)
free (dyn_buffer);
if (search_str)
free (search_str);
return 0;
}
@ -332,26 +346,21 @@ static void mplex_timer_handler (Tox *m)
current_status = tox_self_get_status (m);
pthread_mutex_unlock (&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached)
{
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) {
auto_away_active = false;
new_status = prev_status;
new_note = prev_note;
}
else
if (current_status == TOX_USER_STATUS_NONE && detached)
{
} else if (current_status == TOX_USER_STATUS_NONE && detached) {
auto_away_active = true;
prev_status = current_status;
new_status = TOX_USER_STATUS_AWAY;
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);
new_note = user_settings->mplex_away_note;
}
else
} else
return;
char argv[3][MAX_STR_SIZE];

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"
@ -64,7 +66,7 @@
#include "bootstrap.h"
#ifdef X11
#include "xtra.h"
#include "xtra.h"
#endif
#ifdef AUDIO
@ -75,8 +77,13 @@
ToxAV *av;
#endif /* AUDIO */
#ifdef PYTHON
#include "api.h"
#include "python_api.h"
#endif
#ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "."
#define PACKAGE_DATADIR "."
#endif
/* Export for use in Callbacks */
@ -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();
@ -287,12 +306,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)
{
@ -320,8 +373,10 @@ static int password_prompt(char *buf, int size)
/* eat overflowed stdin and return error */
if (buf[--len] != '\n') {
int ch;
while ((ch = getchar()) != '\n' && ch > 0)
;
return 0;
}
@ -338,6 +393,7 @@ static int password_eval(char *buf, int size)
/* Run password_eval command */
FILE *f = popen(user_settings->password_eval, "r");
if (f == NULL) {
fprintf(stderr, "Executing password_eval failed\n");
return 0;
@ -345,6 +401,7 @@ static int password_eval(char *buf, int size)
/* Get output from command */
char *ret = fgets(buf, size, f);
if (ret == NULL) {
fprintf(stderr, "Reading password from password_eval command failed\n");
pclose(f);
@ -353,6 +410,7 @@ static int password_eval(char *buf, int size)
/* Get exit status */
int status = pclose(f);
if (status != 0) {
fprintf(stderr, "password_eval command returned error %d\n", status);
return 0;
@ -360,6 +418,7 @@ static int password_eval(char *buf, int size)
/* Removez whitespace or \n at end */
int i, len = strlen(buf);
for (i = len - 1; i > 0 && isspace(buf[i]); i--) {
buf[i] = 0;
len--;
@ -376,9 +435,10 @@ 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));
@ -396,6 +456,7 @@ 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;
@ -514,54 +575,54 @@ int store_data(Tox *m, const char *path)
static void init_tox_callbacks(Tox *m)
{
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange, NULL);
tox_callback_friend_connection_status(m, on_connectionchange, NULL);
tox_callback_friend_typing(m, on_typing_change, NULL);
tox_callback_friend_request(m, on_request, NULL);
tox_callback_friend_message(m, on_message, NULL);
tox_callback_friend_name(m, on_nickchange, NULL);
tox_callback_friend_status(m, on_statuschange, NULL);
tox_callback_friend_status_message(m, on_statusmessagechange, NULL);
tox_callback_friend_read_receipt(m, on_read_receipt, NULL);
tox_callback_group_invite(m, on_groupinvite, NULL);
tox_callback_group_message(m, on_groupmessage, NULL);
tox_callback_group_action(m, on_groupaction, NULL);
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
tox_callback_group_title(m, on_group_titlechange, NULL);
tox_callback_file_recv(m, on_file_recv, NULL);
tox_callback_file_chunk_request(m, on_file_chunk_request, NULL);
tox_callback_file_recv_control(m, on_file_control, NULL);
tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL);
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_peer_list_changed(m, on_group_namelistchange);
tox_callback_conference_peer_name(m, on_group_peernamechange);
tox_callback_conference_title(m, on_group_titlechange);
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_chunk(m, on_file_recv_chunk);
}
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.";
@ -612,6 +673,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
size_t pwlen = 0;
int pweval = user_settings->password_eval[0];
if (!pweval) {
system("clear"); // TODO: is this portable?
printf("Enter password (q to quit) ");
@ -621,11 +683,14 @@ 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 {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
}
user_password.len = pwlen;
if (strcasecmp(user_password.pass, "q") == 0) {
@ -646,9 +711,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);
@ -669,9 +733,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);
@ -686,7 +749,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
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);
@ -702,16 +765,19 @@ 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);
if (!tox_opts)
exit_toxic_err("tox_options_new returned fatal error", options_new_err);
init_tox_options(tox_opts);
TOX_ERR_NEW new_err;
Tox *m = load_tox(data_path, &tox_opts, &new_err);
Tox *m = load_tox(data_path, tox_opts, &new_err);
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_opts.ipv6_enabled) {
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_options_get_ipv6_enabled(tox_opts)) {
queue_init_message("Falling back to ipv4");
tox_opts.ipv6_enabled = false;
m = load_tox(data_path, &tox_opts, &new_err);
tox_options_set_ipv6_enabled(tox_opts, false);
m = load_tox(data_path, tox_opts, &new_err);
}
if (!m)
@ -727,6 +793,7 @@ static Tox *load_toxic(char *data_path)
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;
}
@ -739,7 +806,7 @@ static void do_toxic(Tox *m)
return;
}
tox_iterate(m);
tox_iterate(m, NULL);
do_tox_connection(m);
pthread_mutex_unlock(&Winthread.lock);
}
@ -785,7 +852,7 @@ 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);
}
@ -828,6 +895,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)
@ -858,10 +932,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;
@ -933,10 +1008,10 @@ 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);
port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -948,10 +1023,10 @@ 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);
port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -984,6 +1059,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();
@ -1077,7 +1156,7 @@ static void init_default_data_files(void)
// this doesn't do anything (yet)
#ifdef X11
void DnD_callback(const char* asdv, DropType dt)
void DnD_callback(const char *asdv, DropType dt)
{
// if (dt != DT_plain)
// return;
@ -1124,7 +1203,7 @@ int main(int argc, char **argv)
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
if (settings_load(user_settings, p) == -1) {
queue_init_message("Failed to load user settings");
queue_init_message("Failed to load user settings");
}
int curl_init = curl_global_init(CURL_GLOBAL_ALL);
@ -1139,8 +1218,10 @@ int main(int argc, char **argv)
}
#ifdef X11
if (init_xtra(DnD_callback) == -1)
queue_init_message("X failed to initialize");
#endif
Tox *m = load_toxic(DATA_FILE);
@ -1153,6 +1234,7 @@ int main(int argc, char **argv)
prompt = init_windows(m);
prompt_init_statusbar(prompt, m);
load_groups(prompt, m);
/* thread for ncurses stuff */
if (pthread_mutex_init(&Winthread.lock, NULL) != 0)
@ -1182,11 +1264,19 @@ int main(int argc, char **argv)
set_primary_device(output, user_settings->audio_out_dev);
#elif SOUND_NOTIFY
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 */
@ -1206,7 +1296,7 @@ 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];
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);
@ -1219,8 +1309,10 @@ 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)
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
pthread_mutex_unlock(&Winthread.lock);
last_save = cur_time;

View File

@ -107,18 +107,24 @@ 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_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, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length, void *userdata);
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_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_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, void *userdata);
void on_group_peernamechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
size_t length, void *userdata);
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *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);

View File

@ -136,13 +136,13 @@ int del_word_buf(ChatContext *ctx)
int i = ctx->pos, count = 0;
/* traverse past empty space */
while (i > 0 && ctx->line[i-1] == L' ') {
while (i > 0 && ctx->line[i - 1] == L' ') {
++count;
--i;
}
/* traverse past last entered word */
while (i > 0 && ctx->line[i-1] != L' ') {
while (i > 0 && ctx->line[i - 1] != L' ') {
++count;
--i;
}
@ -243,18 +243,20 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
ctx->len = h_len;
}
void strsubst(char* str, char old, char new)
void strsubst(char *str, char old, char new)
{
int i;
for (i = 0; str[i] != '\0'; ++i)
if (str[i] == old)
str[i] = new;
str[i] = new;
}
void wstrsubst(wchar_t* str, wchar_t old, wchar_t new)
void wstrsubst(wchar_t *str, wchar_t old, wchar_t new)
{
int i;
for (i = 0; str[i] != L'\0'; ++i)
if (str[i] == old)
str[i] = new;
str[i] = new;
}

View File

@ -49,7 +49,7 @@ void reset_buf(ChatContext *ctx);
Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx);
/* Deletes all characters from line starting at pos and going backwards
/* Deletes all characters from line starting at pos and going backwards
until we find a space or run out of characters.
Return 0 on success, -1 if no line or already at the beginning */
int del_word_buf(ChatContext *ctx);

View File

@ -40,13 +40,12 @@
#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 );
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 video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data);
static void print_err (ToxWindow *self, const char *error_str)
{
@ -74,7 +73,7 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
}
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_bit_rate(CallControl.av, video_bit_rate_status_cb, &CallControl);
return CallControl.av;
}
@ -82,22 +81,24 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
void terminate_video()
{
int i;
for (i = 0; i < MAX_CALLS; ++i) {
Call* this_call = &CallControl.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();
}
void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, const uint8_t* v, void* data)
void read_video_device_callback(int16_t width, int16_t height, const uint8_t *y, const uint8_t *u, const uint8_t *v,
void *data)
{
uint32_t friend_number = *((uint32_t*)data); /* TODO: Or pass an array of call_idx's */
Call* this_call = &CallControl.calls[friend_number];
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;
/* Drop frame if video sending is disabled */
@ -117,9 +118,9 @@ void read_video_device_callback(int16_t width, int16_t height, const uint8_t* y,
}
void write_video_device_callback(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)
uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data)
{
write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
}
@ -133,7 +134,7 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
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;
}
@ -154,7 +155,7 @@ 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 ) {
close_video_device(vdt_input, call->vin_idx);
@ -175,24 +176,23 @@ int stop_video_transmission(Call *call, int friend_number)
* Callbacks
*/
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)
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)
{
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 video_bit_rate_status_cb(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];
Call *this_call = &CallControl.calls[friend_number];
if ( this_call->vout_idx != -1 )
return;
@ -201,21 +201,22 @@ void callback_recv_video_starting(uint32_t friend_number)
}
void callback_recv_video_end(uint32_t friend_number)
{
Call* this_call = &CallControl.calls[friend_number];
Call *this_call = &CallControl.calls[friend_number];
close_video_device(vdt_output, this_call->vout_idx);
this_call->vout_idx = -1;
}
void callback_video_starting(uint32_t friend_number)
{
ToxWindow* windows = CallControl.prompt;
Call* this_call = &CallControl.calls[friend_number];
ToxWindow *windows = CallControl.prompt;
Call *this_call = &CallControl.calls[friend_number];
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) ) {
@ -244,7 +245,7 @@ void callback_video_end(uint32_t friend_number)
void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{
const char *error_str;
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( argc != 0 ) {
error_str = "Unknown arguments.";
@ -387,18 +388,18 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
}
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
error_str="Invalid selection!";
error_str = "Invalid selection!";
goto on_error;
}
/* If call is active, change device */
if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num];
Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) {
if ( type == vdt_output ) {
}
else {
} else {
/* TODO: check for failure */
close_video_device(vdt_input, this_call->vin_idx);
open_video_device(vdt_input, selection, &this_call->vin_idx);
@ -410,7 +411,7 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
self->video_device_selection[type] = selection;
return;
on_error:
on_error:
print_err (self, error_str);
}

View File

@ -30,14 +30,18 @@
#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(__linux__)
#include <linux/videodev2.h>
#else /* __OSX__ */
#import "osx_video.h"
#else
#include <sys/videoio.h>
#endif
#endif
#include "line_info.h"
@ -62,10 +66,10 @@ struct VideoBuffer {
typedef struct VideoDevice {
VideoDataHandleCallback cb; /* Use this to handle data from input device usually */
void* cb_data; /* Data to be passed to callback */
void *cb_data; /* Data to be passed to callback */
int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt;
struct VideoBuffer *buffers;
@ -93,7 +97,7 @@ VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running de
uint32_t primary_video_device[2]; /* Primary device */
#ifdef VIDEO
static ToxAV* av = NULL;
static ToxAV *av = NULL;
#endif /* VIDEO */
/* q_mutex */
@ -102,15 +106,16 @@ static ToxAV* av = NULL;
pthread_mutex_t video_mutex;
bool video_thread_running = true,
video_thread_paused = true; /* Thread control */
video_thread_paused = true; /* Thread control */
void* video_thread_poll(void*);
void *video_thread_poll(void *);
static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
const uint8_t *u, const uint8_t *v, unsigned int ystride,
unsigned int ustride, unsigned int vstride, uint8_t *out)
const uint8_t *u, const uint8_t *v, unsigned int ystride,
unsigned int ustride, unsigned int vstride, uint8_t *out)
{
unsigned long int i, j;
for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) {
uint8_t *point = out + 4 * ((i * width) + j);
@ -123,21 +128,23 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
int g = (298 * (t_y - 16) - 100 * (t_u - 128) - 208 * (t_v - 128) + 128) >> 8;
int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
point[2] = r>255? 255 : r<0 ? 0 : r;
point[1] = g>255? 255 : g<0 ? 0 : g;
point[0] = b>255? 255 : b<0 ? 0 : b;
point[2] = r > 255 ? 255 : r < 0 ? 0 : r;
point[1] = g > 255 ? 255 : g < 0 ? 0 : g;
point[0] = b > 255 ? 255 : b < 0 ? 0 : b;
point[3] = ~0;
}
}
}
#if defined(__linux__) || defined(__FreeBSD__)
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height)
uint8_t *input, uint16_t width, uint16_t height)
{
uint8_t *end = input + width * height * 2;
while (input != end) {
uint8_t *line_end = input + width * 2;
while (input != line_end) {
*plane_y++ = *input++;
*plane_u++ = *input++;
@ -146,6 +153,7 @@ static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
}
line_end = input + width * 2;
while (input != line_end) {
*plane_y++ = *input++;
input++;//u
@ -166,56 +174,59 @@ static int xioctl(int fh, unsigned long request, void *arg)
return r;
}
#endif /* __linux__ */
#endif
/* Meet devices */
#ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV* av_)
VideoDeviceError init_video_devices(ToxAV *av_)
#else
VideoDeviceError init_video_devices()
#endif /* VIDEO */
{
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;
char device_address[] = "/dev/videoXX";
snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
if ( fd == -1 ) {
break;
} else {
struct v4l2_capability cap;
char* video_input_name;
char *video_input_name;
/* Query V4L for capture capabilities */
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);
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, " (");
strcat(video_input_name, (char*)device_address);
strcat(video_input_name, (char *)device_address);
strcat(video_input_name, ")");
} else {
video_input_name = (char*)malloc(strlen(device_address) + 3);
video_input_name = (char *)malloc(strlen(device_address) + 3);
strcpy(video_input_name, "(");
strcat(video_input_name, device_address);
strcat(video_input_name, ")");
}
video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
close(fd);
}
}
#else /* __OSX__ */
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#endif
size[vdt_output] = 1;
char* video_output_name = "Toxic Video Receiver";
char *video_output_name = "Toxic Video Receiver";
video_devices_names[vdt_output][0] = video_output_name;
// Start poll thread
@ -223,6 +234,7 @@ VideoDeviceError init_video_devices()
return vde_InternalError;
pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 )
return vde_InternalError;
@ -243,8 +255,9 @@ VideoDeviceError terminate_video_devices()
usleep(20000);
int i;
for (i = 0; i < size[vdt_input]; ++i) {
free((void*)video_devices_names[vdt_input][i]);
free((void *)video_devices_names[vdt_input][i]);
}
if ( pthread_mutex_destroy(&video_mutex) != 0 )
@ -258,14 +271,19 @@ VideoDeviceError terminate_video_devices()
}
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
VideoDataHandleCallback callback, void* data)
VideoDataHandleCallback callback, void *data)
{
#if defined(__linux__) || defined(__FreeBSD__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] || !video_devices_running[vdt_input][device_idx]->fd )
return vde_InvalidSelection;
#else /* __OSX__ */
#if defined(__OSX__)
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
return vde_InvalidSelection;
#else /* not __OSX__ */
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
lock;
@ -286,7 +304,7 @@ VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selectio
return vde_None;
}
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx)
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx)
{
return open_video_device(type, primary_video_device[type], device_idx);
}
@ -296,7 +314,7 @@ void get_primary_video_device_name(VideoDeviceType type, char *buf, int size)
memcpy(buf, dvideo_device_names[type], size);
}
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx)
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx)
{
if ( size[type] <= selection || selection < 0 ) return vde_InvalidSelection;
@ -327,7 +345,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
}
VideoDevice* device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
VideoDevice *device = video_devices_running[type][temp_idx] = calloc(1, sizeof(VideoDevice));
device->selection = selection;
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) {
@ -339,12 +357,19 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
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 ) {
unlock;
return vde_FailedStart;
@ -352,6 +377,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) ) {
close(device->fd);
free(device);
@ -365,7 +391,8 @@ 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;
@ -381,6 +408,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
close(device->fd);
free(device);
@ -414,20 +442,22 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
device->buffers[i].length = buf.length;
device->buffers[i].start = mmap(NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
device->fd, buf.m.offset);
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
device->fd, buf.m.offset);
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);
unlock;
return vde_FailedStart;
}
}
device->n_buffers = i;
enum v4l2_buf_type type;
@ -443,6 +473,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
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);
unlock;
@ -459,12 +490,6 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
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 */
@ -477,15 +502,15 @@ 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))) ) {
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;
}
XStoreName(device->x_display, device->x_window, "Video Preview");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_input, temp_idx);
@ -517,14 +542,14 @@ 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))) ) {
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
close_video_device(vdt_output, temp_idx);
unlock;
return vde_FailedStart;
}
XStoreName(device->x_display, device->x_window, "Video Receive");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask);
XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_output, temp_idx);
@ -551,15 +576,15 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
}
__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)
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];
VideoDevice *device = video_devices_running[vdt_output][0];
if ( !device ) return vde_DeviceNotActive;
if( !device->x_window ) return vde_DeviceNotActive;
if ( !device->x_window ) return vde_DeviceNotActive;
pthread_mutex_lock(device->mutex);
@ -594,7 +619,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
.red_mask = 0xFF0000,
.green_mask = 0xFF00,
.blue_mask = 0xFF,
.data = (char*)img_data
.data = (char *)img_data
};
/* Render image data */
@ -609,7 +634,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.
@ -619,26 +644,34 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
while (1) {
lock;
if (!video_thread_running) {
unlock;
break;
}
unlock;
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 ) {
/* Obtain frame image data from device buffers */
VideoDevice* device = video_devices_running[vdt_input][i];
VideoDevice *device = video_devices_running[vdt_input][i];
uint16_t video_width = device->video_width;
uint16_t video_height = device->video_height;
uint8_t *y = device->input.planes[0];
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));
@ -650,16 +683,11 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
continue;
}
void *data = (void*)device->buffers[buf.index].start;
void *data = (void *)device->buffers[buf.index].start;
/* 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 */
@ -669,7 +697,7 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
/* Convert YUV420 data to BGR */
uint8_t *img_data = malloc(video_width * video_height * 4);
yuv420tobgr(video_width, video_height, y, u, v,
video_width, video_width/2, video_width/2, img_data);
video_width, video_width / 2, video_width / 2, img_data);
/* Allocate image data in X11 */
XImage image = {
@ -685,7 +713,7 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
.red_mask = 0xFF0000,
.green_mask = 0xFF00,
.blue_mask = 0xFF,
.data = (char*)img_data
.data = (char *)img_data
};
/* Render image data */
@ -696,16 +724,20 @@ 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(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
unlock;
continue;
}
#endif /* __linux__ */
#endif
}
unlock;
}
usleep(1000 * 1000 / 24);
}
}
@ -731,19 +763,23 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
if ( !device->ref_count ) {
if ( type == vdt_input ) {
#if defined(__linux__) || defined(__FreeBSD__)
#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) ) {
}
}
close(device->fd);
#else /* __OSX__ */
osx_video_close_device(device_idx);
#endif
vpx_img_free(&device->input);
XDestroyWindow(device->x_display, device->x_window);
@ -751,9 +787,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(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
free(device->buffers);
#endif /* __linux__ */
#endif /* __linux__ / BSD */
free(device);
} else {
@ -765,14 +801,13 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
free(device);
}
}
else device->ref_count--;
} else device->ref_count--;
unlock;
return rc;
}
void print_video_devices(ToxWindow* self, VideoDeviceType type)
void print_video_devices(ToxWindow *self, VideoDeviceType type)
{
int i;

View File

@ -45,10 +45,11 @@ 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);
VideoDeviceError init_video_devices(ToxAV *av);
#else
VideoDeviceError init_video_devices();
#endif /* VIDEO */
@ -56,20 +57,22 @@ VideoDeviceError init_video_devices();
VideoDeviceError terminate_video_devices();
/* Callback handles ready data from INPUT device */
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, void* data);
void* get_video_device_callback_data(uint32_t device_idx);
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback,
void *data);
void *get_video_device_callback_data(uint32_t device_idx);
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection);
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t* device_idx);
VideoDeviceError open_primary_video_device(VideoDeviceType type, uint32_t *device_idx);
/* Start device */
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t* device_idx);
VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint32_t *device_idx);
/* Stop device */
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
/* Write data to device */
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);
void print_video_devices(ToxWindow* self, VideoDeviceType type);
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);

View File

@ -151,8 +151,8 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
store_data(m, DATA_FILE);
}
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
void *userdata)
void on_groupmessage(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);
@ -161,26 +161,12 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *mes
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL)
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, msg, length);
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, type, msg, length);
}
}
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
void *userdata)
{
char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) action, length);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupAction != NULL)
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length);
}
}
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_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)
{
size_t i;
@ -190,17 +176,32 @@ void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *g
}
}
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata)
void on_group_namelistchange(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);
if (windows[i].onGroupNameListChange != NULL)
windows[i].onGroupNameListChange(&windows[i], m, groupnumber);
}
}
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
void on_group_peernamechange(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);
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupPeerNameChange != NULL)
windows[i].onGroupPeerNameChange(&windows[i], m, groupnumber, peernumber, nick, length);
}
}
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata)
{
char data[MAX_STR_SIZE + 1];
@ -445,10 +446,12 @@ void on_window_resize(void)
}
#ifdef AUDIO
if (w->chatwin->infobox.active) {
delwin(w->chatwin->infobox.win);
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
}
#endif /* AUDIO */
scrollok(w->chatwin->history, 0);
@ -458,20 +461,24 @@ void on_window_resize(void)
static void draw_window_tab(ToxWindow *toxwin)
{
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
clrtoeol();
printw(" [%s]", toxwin->name);
pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock);
}
static void draw_bar(void)
{
int y,x;
int y, x;
// save current cursor position
getyx(active_window->window, y, x);
@ -482,10 +489,6 @@ 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) {
@ -595,6 +598,12 @@ ToxWindow *get_window_ptr(int i)
return toxwin;
}
/* returns a pointer to the currently open ToxWindow. */
ToxWindow *get_active_window(void)
{
return active_window;
}
void force_refresh(WINDOW *w)
{
wclear(w);
@ -613,10 +622,11 @@ void kill_all_windows(Tox *m)
size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].is_chat)
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);
} else if (windows[i].is_groupchat) {
free_groupchat(&windows[i], m, windows[i].num);
}
}
kill_prompt_window(prompt);

View File

@ -42,7 +42,7 @@
#define CHATBOX_HEIGHT 2
/* Curses foreground colours (background is black) */
enum {
typedef enum {
WHITE,
GREEN,
CYAN,
@ -121,11 +121,11 @@ struct ToxWindow {
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_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 *, int, int, uint8_t);
void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t);
void(*onGroupNameListChange)(ToxWindow *, Tox *, uint32_t);
void(*onGroupPeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*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);
@ -260,6 +260,7 @@ 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_active_window(void);
/* refresh inactive windows to prevent scrolling bugs.
call at least once per second */

View File

@ -57,8 +57,7 @@ struct _Xtra {
Atom expecting_type;
} Xtra;
typedef struct _Property
{
typedef struct _Property {
unsigned char *data;
int read_format;
unsigned long read_num;
@ -97,21 +96,23 @@ Property read_property(Window s, Atom p)
Atom get_dnd_type(long *a, int l)
{
int i = 0;
for (; i < l; i ++) {
if (a[i] != XtraNil) return a[i]; /* Get first valid */
}
return XtraNil;
}
/* TODO maybe support only certain types in the future */
static void handle_xdnd_enter(XClientMessageEvent* e)
static void handle_xdnd_enter(XClientMessageEvent *e)
{
Xtra.handling_version = (e->data.l[1] >> 24);
if ((e->data.l[1] & 1)) {
// Fetch the list of possible conversions
Property p = read_property(e->data.l[0], XdndTypeList);
Xtra.expecting_type = get_dnd_type((long*)p.data, p.read_num);
Xtra.expecting_type = get_dnd_type((long *)p.data, p.read_num);
XFree(p.data);
} else {
// Use the available list
@ -119,7 +120,7 @@ static void handle_xdnd_enter(XClientMessageEvent* e)
}
}
static void handle_xdnd_position(XClientMessageEvent* e)
static void handle_xdnd_position(XClientMessageEvent *e)
{
XEvent ev = {
.xclient = {
@ -143,7 +144,7 @@ static void handle_xdnd_position(XClientMessageEvent* e)
XFlush(Xtra.display);
}
static void handle_xdnd_drop(XClientMessageEvent* e)
static void handle_xdnd_drop(XClientMessageEvent *e)
{
/* Not expecting any type */
if (Xtra.expecting_type == XtraNil) {
@ -172,7 +173,7 @@ static void handle_xdnd_drop(XClientMessageEvent* e)
}
}
static void handle_xdnd_selection(XSelectionEvent* e)
static void handle_xdnd_selection(XSelectionEvent *e)
{
/* DnD succesfully finished, send finished and call callback */
XEvent ev = {
@ -199,12 +200,12 @@ static void handle_xdnd_selection(XSelectionEvent* e)
/* Call callback for every entry */
if (Xtra.on_drop && p.read_num)
{
if (Xtra.on_drop && p.read_num) {
char *sptr;
char *str = strtok_r((char *) p.data, "\n\r", &sptr);
if (str) Xtra.on_drop(str, dt);
while ((str = strtok_r(NULL, "\n\r", &sptr)))
Xtra.on_drop(str, dt);
}
@ -212,7 +213,7 @@ static void handle_xdnd_selection(XSelectionEvent* e)
if (p.data) XFree(p.data);
}
void *event_loop(void* p)
void *event_loop(void *p)
{
/* Handle events like a real nigga */
@ -221,30 +222,27 @@ void *event_loop(void* p)
XEvent event;
int pending;
while (Xtra.display)
{
while (Xtra.display) {
/* NEEDMOEVENTSFODEMPROGRAMS */
XLockDisplay(Xtra.display);
if((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if (!pending)
{
if ((pending = XPending(Xtra.display))) XNextEvent(Xtra.display, &event);
if (!pending) {
XUnlockDisplay(Xtra.display);
usleep(10000);
continue;
}
if (event.type == ClientMessage)
{
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);
} else if (event.type == SelectionNotify) handle_xdnd_selection(&event.xselection);
/* AINNOBODYCANHANDLEDEMEVENTS*/
else XSendEvent(Xtra.display, Xtra.terminal_window, 0, 0, &event);
@ -256,6 +254,7 @@ void *event_loop(void* p)
* otherwise HEWUSAGUDBOI happens
*/
if (Xtra.display) XCloseDisplay(Xtra.display);
return (Xtra.display = NULL);
}
@ -267,6 +266,7 @@ int init_xtra(drop_callback d)
else Xtra.on_drop = d;
XInitThreads();
if ( !(Xtra.display = XOpenDisplay(NULL))) return -1;
Xtra.terminal_window = focused_window_id();
@ -274,7 +274,7 @@ int init_xtra(drop_callback d)
/* OSX: if focused window is 0, it means toxic is ran from
* native terminal and not X11 terminal window, silently exit */
if (!Xtra.terminal_window)
return 0;
return 0;
{
/* Create an invisible window which will act as proxy for the DnD operation. */
@ -300,15 +300,15 @@ int init_xtra(drop_callback d)
&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 */
(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;
}
@ -335,9 +335,10 @@ int init_xtra(drop_callback d)
XA_ATOM,
32,
PropModeReplace,
(unsigned char*)&XdndVersion, 1);
(unsigned char *)&XdndVersion, 1);
pthread_t id;
if (pthread_create(&id, NULL, event_loop, NULL) != 0)
return -1;

View File

@ -28,10 +28,10 @@
typedef enum {
DT_plain,
DT_file_list
}
}
DropType;
typedef void (*drop_callback) (const char*, DropType);
typedef void (*drop_callback) (const char *, DropType);
int init_xtra(drop_callback d);
void terminate_xtra();