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

Compare commits

..

71 Commits

Author SHA1 Message Date
b1d8ab102f Bump to version 0.8.1 2017-11-17 17:44:56 -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
78 changed files with 2564 additions and 1031 deletions

1
.gitignore vendored
View File

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

View File

@ -1,57 +1,53 @@
# Installation # Installation
* [Dependencies](#deps) * [Dependencies](#dependencies)
* [OS X Notes](#deps_osx) * [OS X Notes](#os-x-notes)
* [Compiling](#compiling) * [Compiling](#compiling)
* [Documentation](#docs) * [Documentation](#documentation)
* [Notes](#notes) * [Notes](#notes)
* [Compilation variables](#comp_vars) * [Compilation variables](#compilation-variables)
* [Packaging](#packaging) * [Packaging](#packaging)
<a name="deps" />
## Dependencies ## Dependencies
| Name | Needed by | Debian package | | 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 | | [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev | | [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
| [GNUmake](https://www.gnu.org/software/make) | BASE | make | | [GNUmake](https://www.gnu.org/software/make) | BASE | make |
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev| | [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev | | [libqrencode](https://fukuchi.org/works/qrencode/) | 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 | | [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev | | [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev | | [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc | | [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 #### OS X Notes
Using [Homebrew](http://brew.sh): Using [Homebrew](http://brew.sh):
``` ```
brew install openal-soft freealut libconfig brew install openal-soft freealut libconfig
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb brew install libnotify
``` ```
You can omit `libnotify` if you intend to build without desktop notifications enabled. You can omit `libnotify` if you intend to build without desktop notifications enabled.
<a name="Compiling">
## Compiling ## Compiling
``` ```
make PREFIX="/where/to/install" make PREFIX="/where/to/install"
sudo make install PREFIX="/where/to/install" sudo make install PREFIX="/where/to/install"
``` ```
<a name="docs" />
#### Documentation #### Documentation
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br /> Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br /> **NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them. **NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
<a name="notes" />
## Notes ## Notes
<a name="comp_vars" />
#### Compilation variables #### Compilation variables
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""` * You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones) * 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_AV=1` → build toxic without audio call support
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support * `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop 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 #### Packaging
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files * 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=""` can be used in addition to `PREFIX=""`:
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg") * `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") * `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")

View File

@ -30,6 +30,9 @@ endif
ifeq ($(UNAME_S), OpenBSD) ifeq ($(UNAME_S), OpenBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk -include $(CFG_DIR)/systems/FreeBSD.mk
endif endif
ifeq ($(UNAME_S), NetBSD)
-include $(CFG_DIR)/systems/FreeBSD.mk
endif
ifeq ($(UNAME_S), Darwin) ifeq ($(UNAME_S), Darwin)
-include $(CFG_DIR)/systems/Darwin.mk -include $(CFG_DIR)/systems/Darwin.mk
endif endif

View File

@ -3,7 +3,7 @@
src="https://scan.coverity.com/projects/4975/badge.svg"/> src="https://scan.coverity.com/projects/4975/badge.svg"/>
</a> </a>
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application. Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/toktok/c-toxcore), and is now available as a standalone application.
[![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png) [![Toxic Screenshot](https://i.imgur.com/san99Z2.png "Home Screen")](https://i.imgur.com/san99Z2.png)

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

@ -18,4 +18,4 @@ else ifneq ($(MAKECMDGOALS), clean)
$(warning WARNING -- Toxic will be compiled without audio support) $(warning WARNING -- Toxic will be compiled without audio support)
$(warning WARNING -- You need these libraries for audio support) $(warning WARNING -- You need these libraries for audio support)
$(warning WARNING -- $(MISSING_AUDIO_LIBS)) $(warning WARNING -- $(MISSING_AUDIO_LIBS))
endif endif

View File

@ -34,6 +34,18 @@ ifneq ($(DESK_NOTIFY), disabled)
-include $(CHECKS_DIR)/desktop_notifications.mk -include $(CHECKS_DIR)/desktop_notifications.mk
endif 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 if we can build Toxic
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error") CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
ifneq ($(CHECK_LIBS), error) ifneq ($(CHECK_LIBS), error)

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

@ -8,14 +8,14 @@ else
endif endif
# Check if we can build video support # 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) ifneq ($(CHECK_VIDEO_LIBS), error)
LIBS += $(VIDEO_LIBS) LIBS += $(VIDEO_LIBS)
CFLAGS += $(VIDEO_CFLAGS) CFLAGS += $(VIDEO_CFLAGS)
OBJ += $(VIDEO_OBJ) OBJ += $(VIDEO_OBJ)
else ifneq ($(MAKECMDGOALS), clean) 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 -- Toxic will be compiled without video support)
$(warning WARNING -- You will need these libraries for video support) $(warning WARNING -- You will need these libraries for video support)
$(warning WARNING -- $(MISSING_VIDEO_LIBS)) $(warning WARNING -- $(MISSING_VIDEO_LIBS))
endif endif

View File

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

View File

@ -14,6 +14,8 @@ help:
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support" @echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support" @echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support" @echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
@echo " DISABLE_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_CFLAGS: Add custom flags to default CFLAGS"
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS" @echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")" @echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"

View File

@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
mv temp_file $$file ;\ mv temp_file $$file ;\
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \ sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
mv temp_file $$file ;\ mv temp_file $$file ;\
gzip -f -9 $$file ;\ gzip -f -n -9 $$file ;\
done done
.PHONY: install .PHONY: install

View File

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

View File

@ -143,6 +143,9 @@ OPTIONS
*avatar_path*;; *avatar_path*;;
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB) 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*;; *chatlogs_path*;;
Default path for chatlogs. String value. Absolute path for chatlog files. 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) // Path for your avatar (file must be a .png and cannot exceed 64 KiB)
// avatar_path="/home/USERNAME/Pictures/youravatar.png"; // 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 // Path for chatlogs
// chatlogs_path="/home/USERNAME/toxic_chatlogs/"; // chatlogs_path="/home/USERNAME/toxic_chatlogs/";
}; };
@ -118,4 +121,3 @@ keys = {
toggle_peerlist="Ctrl+b"; toggle_peerlist="Ctrl+b";
toggle_paste_mode="Ctrl+T"; 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"
#endif /* PYTHON */
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);
}

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

@ -61,7 +61,7 @@ extern FriendsList Friends;
#define frame_size (CallControl.audio_sample_rate * CallControl.audio_frame_duration / 1000) #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->in_idx = -1;
call->out_idx = -1; call->out_idx = -1;
@ -75,17 +75,18 @@ static int set_call(Call* call, bool start)
if ( pthread_mutex_init(&call->mutex, NULL) != 0 ) if ( pthread_mutex_init(&call->mutex, NULL) != 0 )
return -1; return -1;
} } else {
else {
call->ttid = 0; call->ttid = 0;
if ( pthread_mutex_destroy(&call->mutex) != 0 ) if ( pthread_mutex_destroy(&call->mutex) != 0 )
return -1; return -1;
} }
return 0; 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 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, 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 ); uint8_t channels, uint32_t sampling_rate, void *user_data );
@ -103,7 +104,7 @@ void callback_call_canceled ( uint32_t friend_number );
void callback_call_rejected ( uint32_t friend_number ); void callback_call_rejected ( uint32_t friend_number );
void callback_call_ended ( 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 ); uint32_t sample_rate );
static void print_err (ToxWindow *self, const char *error_str) static void print_err (ToxWindow *self, const char *error_str)
@ -158,6 +159,7 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
void terminate_audio() void terminate_audio()
{ {
int i; int i;
for (i = 0; i < MAX_CALLS; ++i) for (i = 0; i < MAX_CALLS; ++i)
stop_transmission(&CallControl.calls[i], i); stop_transmission(&CallControl.calls[i], i);
@ -167,7 +169,7 @@ void terminate_audio()
terminate_devices(); 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; TOXAV_ERR_SEND_FRAME error;
uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */ uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
@ -175,13 +177,13 @@ void read_device_callback(const int16_t* captured, uint32_t size, void* data)
((int64_t) CallControl.audio_frame_duration) / 1000; ((int64_t) CallControl.audio_frame_duration) / 1000;
if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number, if ( sample_count <= 0 || toxav_audio_send_frame(CallControl.av, friend_number,
captured, sample_count, captured, sample_count,
CallControl.audio_channels, CallControl.audio_channels,
CallControl.audio_sample_rate, &error) == false ) 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) uint32_t sample_rate)
{ {
if ( CallControl.calls[friend_number].ttas ) if ( CallControl.calls[friend_number].ttas )
@ -199,7 +201,7 @@ int start_transmission(ToxWindow *self, Call *call)
return -1; return -1;
DeviceError error = open_primary_device(input, &call->in_idx, 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_None ) {
if ( error == de_FailedStart) if ( error == de_FailedStart)
@ -210,12 +212,12 @@ int start_transmission(ToxWindow *self, Call *call)
} }
if ( register_device_callback(self->num, call->in_idx, 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 */ /* 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!"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to register input handler!");
if ( open_primary_device(output, &call->out_idx, 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!"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to open output device!");
call->has_output = 0; call->has_output = 0;
} }
@ -286,7 +288,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
callback_call_ended(friend_number); callback_call_ended(friend_number);
CallControl.pending_call = false; CallControl.pending_call = false;
break; break;
case ( TOXAV_FRIEND_CALL_STATE_FINISHED ): case ( TOXAV_FRIEND_CALL_STATE_FINISHED ):
if ( CallControl.pending_call ) if ( CallControl.pending_call )
callback_call_rejected(friend_number); callback_call_rejected(friend_number);
@ -304,7 +307,8 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
CallControl.call_state = 0; CallControl.call_state = 0;
CallControl.pending_call = false; CallControl.pending_call = false;
break; break;
default: default:
if ( CallControl.pending_call ) { if ( CallControl.pending_call ) {
/* Start answered call */ /* Start answered call */
@ -313,6 +317,7 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
} else { } else {
#ifdef VIDEO #ifdef VIDEO
/* Handle receiving client video call states */ /* Handle receiving client video call states */
if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V ) if ( state & TOXAV_FRIEND_CALL_STATE_SENDING_V )
callback_recv_video_starting(friend_number); callback_recv_video_starting(friend_number);
@ -322,13 +327,13 @@ void callstate_cb(ToxAV *av, uint32_t friend_number, uint32_t state, void *user_
#endif /* VIDEO */ #endif /* VIDEO */
} }
break; break;
} }
} }
void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number, void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
int16_t const *pcm, size_t sample_count, int16_t const *pcm, size_t sample_count,
uint8_t channels, uint32_t sampling_rate, void *user_data) uint8_t channels, uint32_t sampling_rate, void *user_data)
{ {
write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate); write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate);
} }
@ -337,6 +342,7 @@ void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_
uint32_t video_bit_rate, void *user_data) uint32_t video_bit_rate, void *user_data)
{ {
CallControl.audio_bit_rate = audio_bit_rate; CallControl.audio_bit_rate = audio_bit_rate;
toxav_bit_rate_set(av, friend_number, audio_bit_rate, video_bit_rate, user_data);
} }
void callback_recv_invite(Tox *m, uint32_t friend_number) void callback_recv_invite(Tox *m, uint32_t friend_number)
@ -373,14 +379,16 @@ void callback_recv_ringing(uint32_t friend_number)
} }
void callback_recv_starting(uint32_t friend_number) void callback_recv_starting(uint32_t friend_number)
{ {
ToxWindow* windows = CallControl.prompt; ToxWindow *windows = CallControl.prompt;
int i; int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) { if ( windows[i].onStarting != NULL && windows[i].num == friend_number ) {
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state); windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */ 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; return;
} }
@ -399,12 +407,14 @@ void callback_recv_ending(uint32_t friend_number)
} }
void callback_call_started(uint32_t friend_number) void callback_call_started(uint32_t friend_number)
{ {
ToxWindow* windows = CallControl.prompt; ToxWindow *windows = CallControl.prompt;
int i; int i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) for (i = 0; i < MAX_WINDOWS_NUM; ++i)
if ( windows[i].onStart != NULL && windows[i].num == friend_number ) { if ( windows[i].onStart != NULL && windows[i].num == friend_number ) {
windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state); windows[i].onStart(&windows[i], CallControl.av, friend_number, CallControl.call_state);
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) {/* YEAH! */ 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; return;
@ -479,6 +489,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); 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_OK ) {
if ( error == TOXAV_ERR_CALL_FRIEND_ALREADY_IN_CALL ) error_str = "Already in a call!"; 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"; else if ( error == TOXAV_ERR_CALL_MALLOC ) error_str = "Memory allocation issue";
@ -518,6 +529,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); 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_OK ) {
if ( error == TOXAV_ERR_ANSWER_FRIEND_NOT_CALLING ) error_str = "No incoming call!"; 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!"; else if ( error == TOXAV_ERR_ANSWER_CODEC_INITIALIZATION ) error_str = "Failed to initialize codecs!";
@ -621,6 +633,9 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
return; return;
} }
// Refresh device list.
get_devices_names();
print_devices(self, type); print_devices(self, type);
return; return;
@ -664,7 +679,7 @@ void cmd_change_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (
} }
if ( set_primary_device(type, selection) == de_InvalidSelection ) { if ( set_primary_device(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!"; error_str = "Invalid selection!";
goto on_error; goto on_error;
} }
@ -708,13 +723,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
} }
if ( selection_valid(type, selection) == de_InvalidSelection ) { if ( selection_valid(type, selection) == de_InvalidSelection ) {
error_str="Invalid selection!"; error_str = "Invalid selection!";
goto on_error; goto on_error;
} }
/* If call is active, change device */ /* If call is active, change device */
if ( self->is_call ) { if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) { if ( this_call->ttas ) {
@ -722,15 +738,14 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
pthread_mutex_lock(&this_call->mutex); pthread_mutex_lock(&this_call->mutex);
close_device(output, this_call->out_idx); close_device(output, this_call->out_idx);
this_call->has_output = open_device(output, selection, &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) CallControl.audio_sample_rate, CallControl.audio_frame_duration, CallControl.audio_channels)
== de_None ? 1 : 0; == de_None ? 1 : 0;
pthread_mutex_unlock(&this_call->mutex); pthread_mutex_unlock(&this_call->mutex);
} } else {
else {
/* TODO: check for failure */ /* TODO: check for failure */
close_device(input, this_call->in_idx); close_device(input, this_call->in_idx);
open_device(input, selection, &this_call->in_idx, CallControl.audio_sample_rate, 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 */ /* 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); register_device_callback(self->num, this_call->in_idx, read_device_callback, &self->num, true);
} }
@ -740,7 +755,7 @@ void cmd_ccur_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
self->device_selection[type] = selection; self->device_selection[type] = selection;
return; return;
on_error: on_error:
print_err (self, error_str); print_err (self, error_str);
} }
@ -771,9 +786,10 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
/* If call is active, use this_call values */ /* If call is active, use this_call values */
if ( self->is_call ) { if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
pthread_mutex_lock(&this_call->mutex); pthread_mutex_lock(&this_call->mutex);
if ( type == input ) { if ( type == input ) {
device_mute(type, this_call->in_idx); device_mute(type, this_call->in_idx);
self->chatwin->infobox.in_is_muted ^= 1; self->chatwin->infobox.in_is_muted ^= 1;
@ -781,12 +797,13 @@ void cmd_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MA
device_mute(type, this_call->out_idx); device_mute(type, this_call->out_idx);
self->chatwin->infobox.out_is_muted ^= 1; self->chatwin->infobox.out_is_muted ^= 1;
} }
pthread_mutex_unlock(&this_call->mutex); pthread_mutex_unlock(&this_call->mutex);
} }
return; return;
on_error: on_error:
print_err (self, error_str); print_err (self, error_str);
} }
@ -821,8 +838,57 @@ on_error:
print_err (self, error_str); 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, -1, &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_AUDIO_BIT_RATE:
error_str = "Invalid audio 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 ) { if ( CallControl.pending_call ) {
toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL); toxav_call_control(CallControl.av, self->num, TOXAV_CALL_CONTROL_CANCEL, NULL);

View File

@ -56,7 +56,7 @@ typedef struct Device {
ALCdevice *dhndl; /* Handle of device selected/opened */ ALCdevice *dhndl; /* Handle of device selected/opened */
ALCcontext *ctx; /* Device context */ ALCcontext *ctx; /* Device context */
DataHandleCallback cb; /* Use this to handle data from input device usually */ 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 */ int32_t friend_number; /* ToxAV friend number */
uint32_t source, buffers[OPENAL_BUFS]; /* Playback source/buffers */ 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 */ uint32_t primary_device[2]; /* Primary device */
#ifdef AUDIO #ifdef AUDIO
static ToxAV* av = NULL; static ToxAV *av = NULL;
#endif /* AUDIO */ #endif /* AUDIO */
/* q_mutex */ /* q_mutex */
@ -90,47 +90,24 @@ pthread_mutex_t mutex;
bool thread_running = true, bool thread_running = true,
thread_paused = true; /* Thread control */ thread_paused = true; /* Thread control */
void* thread_poll(void*); void *thread_poll(void *);
/* Meet devices */ /* Meet devices */
#ifdef AUDIO #ifdef AUDIO
DeviceError init_devices(ToxAV* av_) DeviceError init_devices(ToxAV *av_)
#else #else
DeviceError init_devices() DeviceError init_devices()
#endif /* AUDIO */ #endif /* AUDIO */
{ {
const char *stringed_device_list; get_devices_names();
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;
}
}
// Start poll thread // Start poll thread
if (pthread_mutex_init(&mutex, NULL) != 0) if (pthread_mutex_init(&mutex, NULL) != 0)
return de_InternalError; return de_InternalError;
pthread_t thread_id; pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0) if ( pthread_create(&thread_id, NULL, thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0)
return de_InternalError; return de_InternalError;
@ -156,12 +133,45 @@ DeviceError terminate_devices()
return (DeviceError) de_None; 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) DeviceError device_mute(DeviceType type, uint32_t device_idx)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock; lock;
Device* device = running[type][device_idx]; Device *device = running[type][device_idx];
if (!device) { if (!device) {
unlock; 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) DeviceError device_set_VAD_treshold(uint32_t device_idx, float value)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock; lock;
Device* device = running[input][device_idx]; Device *device = running[input][device_idx];
if (!device) { if (!device) {
unlock; 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) DeviceError set_primary_device(DeviceType type, int32_t selection)
{ {
if (size[type] <= selection || selection < 0) return de_InvalidSelection; if (size[type] <= selection || selection < 0) return de_InvalidSelection;
primary_device[type] = selection; primary_device[type] = selection;
return de_None; 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); 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 // 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; 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); const uint32_t frame_size = (sample_rate * frame_duration / 1000);
uint32_t i; uint32_t i;
for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i); for (i = 0; i < MAX_DEVICES && running[type][i] != NULL; ++i);
if (i == MAX_DEVICES) { unlock; return de_AllDevicesBusy; } if (i == MAX_DEVICES) {
else *device_idx = i; unlock;
return de_AllDevicesBusy;
} else *device_idx = i;
for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */ for (i = 0; i < MAX_DEVICES; i ++) { /* Check if any device has the same selection */
if ( running[type][i] && running[type][i]->selection == selection ) { if ( running[type][i] && running[type][i]->selection == selection ) {
@ -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->selection = selection;
device->sample_rate = sample_rate; device->sample_rate = sample_rate;
@ -258,12 +275,12 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
if (type == input) { if (type == input) {
device->dhndl = alcCaptureOpenDevice(devices_names[type][selection], device->dhndl = alcCaptureOpenDevice(devices_names[type][selection],
sample_rate, device->sound_mode, frame_size * 2); sample_rate, device->sound_mode, frame_size * 2);
#ifdef AUDIO #ifdef AUDIO
device->VAD_treshold = user_settings->VAD_treshold; device->VAD_treshold = user_settings->VAD_treshold;
#endif #endif
} } else {
else {
device->dhndl = alcOpenDevice(devices_names[type][selection]); device->dhndl = alcOpenDevice(devices_names[type][selection]);
if ( !device->dhndl ) { if ( !device->dhndl ) {
free(device); free(device);
running[type][*device_idx] = NULL; running[type][*device_idx] = NULL;
@ -279,10 +296,10 @@ DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx
alSourcei(device->source, AL_LOOPING, AL_FALSE); alSourcei(device->source, AL_LOOPING, AL_FALSE);
uint16_t zeros[frame_size]; uint16_t zeros[frame_size];
memset(zeros, 0, frame_size*2); memset(zeros, 0, frame_size * 2);
for ( i = 0; i < OPENAL_BUFS; ++i ) { for ( i = 0; i < OPENAL_BUFS; ++i ) {
alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size*2, sample_rate); alBufferData(device->buffers[i], device->sound_mode, zeros, frame_size * 2, sample_rate);
} }
alSourceQueueBuffers(device->source, OPENAL_BUFS, device->buffers); 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; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
lock; lock;
Device* device = running[type][device_idx]; Device *device = running[type][device_idx];
DeviceError rc = de_None; DeviceError rc = de_None;
if (!device) { if (!device) {
@ -323,27 +340,28 @@ DeviceError close_device(DeviceType type, uint32_t device_idx)
if ( !device->ref_count ) { if ( !device->ref_count ) {
if (type == input) { if (type == input) {
if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError; if ( !alcCaptureCloseDevice(device->dhndl) ) rc = de_AlError;
} } else {
else {
if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx); if (alcGetCurrentContext() != device->ctx) alcMakeContextCurrent(device->ctx);
alDeleteSources(1, &device->source); alDeleteSources(1, &device->source);
alDeleteBuffers(OPENAL_BUFS, device->buffers); alDeleteBuffers(OPENAL_BUFS, device->buffers);
alcMakeContextCurrent(NULL); alcMakeContextCurrent(NULL);
if ( device->ctx ) alcDestroyContext(device->ctx); if ( device->ctx ) alcDestroyContext(device->ctx);
if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError; if ( !alcCloseDevice(device->dhndl) ) rc = de_AlError;
} }
free(device); free(device);
} } else device->ref_count--;
else device->ref_count--;
unlock; unlock;
return rc; return rc;
} }
DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD) DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void *data, bool enable_VAD)
{ {
if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL) if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return de_InvalidSelection; return de_InvalidSelection;
@ -358,12 +376,12 @@ DeviceError register_device_callback( int32_t friend_number, uint32_t device_idx
return de_None; 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) uint32_t sample_rate)
{ {
if (device_idx >= MAX_DEVICES) return de_InvalidSelection; if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
Device* device = running[output][device_idx]; Device *device = running[output][device_idx];
if (!device || device->muted) return de_DeviceNotActive; if (!device || device->muted) return de_DeviceNotActive;
@ -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_PROCESSED, &processed);
alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued); alGetSourcei(device->source, AL_BUFFERS_QUEUED, &queued);
if(processed) { if (processed) {
ALuint bufids[processed]; ALuint bufids[processed];
alSourceUnqueueBuffers(device->source, processed, bufids); alSourceUnqueueBuffers(device->source, processed, bufids);
alDeleteBuffers(processed - 1, bufids + 1); alDeleteBuffers(processed - 1, bufids + 1);
bufid = bufids[0]; bufid = bufids[0];
} } else if (queued < 16) alGenBuffers(1, &bufid);
else if(queued < 16) alGenBuffers(1, &bufid);
else { else {
pthread_mutex_unlock(device->mutex); pthread_mutex_unlock(device->mutex);
return de_Busy; 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); alSourceQueueBuffers(device->source, 1, &bufid);
ALint state; ALint state;
alGetSourcei(device->source, AL_SOURCE_STATE, &state); alGetSourcei(device->source, AL_SOURCE_STATE, &state);
if(state != AL_PLAYING) alSourcePlay(device->source); if (state != AL_PLAYING) alSourcePlay(device->source);
pthread_mutex_unlock(device->mutex); pthread_mutex_unlock(device->mutex);
return de_None; return de_None;
} }
void* thread_poll (void* arg) // TODO: maybe use thread for every input source void *thread_poll (void *arg) // TODO: maybe use thread for every input source
{ {
/* /*
* NOTE: We only need to poll input devices for data. * NOTE: We only need to poll input devices for data.
@ -411,9 +429,9 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
int32_t sample = 0; int32_t sample = 0;
while (1) while (1) {
{
lock; lock;
if (!thread_running) { if (!thread_running) {
unlock; unlock;
break; break;
@ -422,7 +440,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
bool paused = thread_paused; bool paused = thread_paused;
unlock; unlock;
/* Wait for unpause. */ /* Wait for unpause. */
if (paused) { if (paused) {
usleep(10000); usleep(10000);
} }
@ -430,6 +448,7 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
else { else {
for (i = 0; i < size[input]; ++i) { for (i = 0; i < size[input]; ++i) {
lock; lock;
if (running[input][i] != NULL) { if (running[input][i] != NULL) {
alcGetIntegerv(running[input][i]->dhndl, ALC_CAPTURE_SAMPLES, sizeof(int32_t), &sample); 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; unlock;
continue; continue;
} }
Device* device = running[input][i];
Device *device = running[input][i];
int16_t frame[16000]; int16_t frame[16000];
alcCaptureSamples(device->dhndl, frame, f_size); 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); if ( device->cb ) device->cb(frame, f_size, device->cb_data);
} }
unlock; unlock;
} }
usleep(5000); usleep(5000);
} }
} }
@ -460,12 +482,13 @@ void* thread_poll (void* arg) // TODO: maybe use thread for every input source
pthread_exit(NULL); pthread_exit(NULL);
} }
void print_devices(ToxWindow* self, DeviceType type) void print_devices(ToxWindow *self, DeviceType type)
{ {
int i; int i;
for (i = 0; i < size[type]; ++i) for (i = 0; i < size[type]; ++i) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]); line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i, devices_names[type][i]);
}
return; return;
} }
@ -475,7 +498,7 @@ DeviceError selection_valid(DeviceType type, int32_t selection)
return (size[type] <= selection || selection < 0) ? de_InvalidSelection : de_None; 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) if (size[input] <= device_idx || !running[input][device_idx] || running[input][device_idx]->dhndl == NULL)
return NULL; return NULL;

View File

@ -52,20 +52,22 @@ typedef enum DeviceError {
de_AlError = -9, de_AlError = -9,
} DeviceError; } DeviceError;
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data); typedef void (*DataHandleCallback) (const int16_t *, uint32_t size, void *data);
#ifdef AUDIO #ifdef AUDIO
DeviceError init_devices(ToxAV* av); DeviceError init_devices(ToxAV *av);
#else #else
DeviceError init_devices(); DeviceError init_devices();
#endif /* AUDIO */ #endif /* AUDIO */
void get_devices_names();
DeviceError terminate_devices(); DeviceError terminate_devices();
/* Callback handles ready data from INPUT device */ /* Callback handles ready data from INPUT device */
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD); DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback,
void* get_device_callback_data(uint32_t device_idx); void *data, bool enable_VAD);
void *get_device_callback_data(uint32_t device_idx);
/* toggle device mute */ /* toggle device mute */
DeviceError device_mute(DeviceType type, uint32_t device_idx); 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 #endif
DeviceError set_primary_device(DeviceType type, int32_t selection); DeviceError set_primary_device(DeviceType type, int32_t selection);
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels); DeviceError open_primary_device(DeviceType type, uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration,
uint8_t channels);
/* Start device */ /* 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 */ /* Stop device */
DeviceError close_device(DeviceType type, uint32_t device_idx); DeviceError close_device(DeviceType type, uint32_t device_idx);
/* Write data to device */ /* 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); void get_primary_device_name(DeviceType type, char *buf, int size);
DeviceError selection_valid(DeviceType type, int32_t selection); DeviceError selection_valid(DeviceType type, int32_t selection);

View File

@ -25,10 +25,10 @@
#include <limits.h> #include <limits.h>
#ifdef __APPLE__ #ifdef __APPLE__
#include <sys/types.h> #include <sys/types.h>
#include <sys/dir.h> #include <sys/dir.h>
#else #else
#include <dirent.h> #include <dirent.h>
#endif /* ifdef __APPLE__ */ #endif /* ifdef __APPLE__ */
#include "windows.h" #include "windows.h"
@ -108,7 +108,11 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
/* TODO: generalize this */ /* TODO: generalize this */
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile")) 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 */ /* isolate substring from space behind pos to pos */
char tmp[MAX_STR_SIZE]; char tmp[MAX_STR_SIZE];
@ -185,6 +189,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
int n_endchrs = strlen(endchrs); int n_endchrs = strlen(endchrs);
int strt = ctx->pos - s_len; int strt = ctx->pos - s_len;
int diff = match_len - s_len + n_endchrs; int diff = match_len - s_len + n_endchrs;
if (ctx->len + diff >= MAX_STR_SIZE) if (ctx->len + diff >= MAX_STR_SIZE)
return -1; return -1;
@ -282,13 +287,13 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
if (dp == NULL) if (dp == NULL)
return -1; return -1;
char dirnames[MAX_DIRS][NAME_MAX]; char dirnames[MAX_DIRS][NAME_MAX + 1];
struct dirent *entry; struct dirent *entry;
int dircount = 0; int dircount = 0;
while ((entry = readdir(dp)) && dircount < MAX_DIRS) { while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
if (strncmp(entry->d_name, b_name, b_name_len) == 0 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); snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
++dircount; ++dircount;
} }

View File

@ -55,6 +55,7 @@ int avatar_send(Tox *m, uint32_t friendnum)
TOX_ERR_FILE_SEND err; TOX_ERR_FILE_SEND err;
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size, uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err); NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
if (Avatar.size == 0) if (Avatar.size == 0)
return 0; return 0;
@ -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) { } else if (ft->state == FILE_TRANSFER_PAUSED) {
ft->state = FILE_TRANSFER_STARTED; ft->state = FILE_TRANSFER_STARTED;
} }
break; break;
case TOX_FILE_CONTROL_PAUSE: case TOX_FILE_CONTROL_PAUSE:

View File

@ -251,6 +251,7 @@ static int update_DHT_nodeslist(const char *nodes_path)
} }
struct Recv_Curl_Data recv_data; struct Recv_Curl_Data recv_data;
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data)); memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
if (curl_fetch_nodes_JSON(&recv_data) == -1) { if (curl_fetch_nodes_JSON(&recv_data) == -1) {
@ -476,7 +477,7 @@ void *load_nodeslist_thread(void *data)
/* If nodeslist does not contain any valid entries we set the last_scan value /* 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. * 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}"; const char *s = "{\"last_scan\":0}";
rewind(fp); rewind(fp);
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
@ -523,6 +524,7 @@ int load_DHT_nodeslist(void)
} }
thread_data.active = true; thread_data.active = true;
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) { if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
thread_data.active = false; thread_data.active = false;
return -5; return -5;

View File

@ -48,9 +48,9 @@
#include "message_queue.h" #include "message_queue.h"
#ifdef AUDIO #ifdef AUDIO
#include "audio_call.h" #include "audio_call.h"
#ifdef VIDEO #ifdef VIDEO
#include "video_call.h" #include "video_call.h"
#endif /* VIDEO */ #endif /* VIDEO */
#endif /* AUDIO */ #endif /* AUDIO */
@ -65,8 +65,12 @@ static void init_infobox(ToxWindow *self);
static void kill_infobox(ToxWindow *self); static void kill_infobox(ToxWindow *self);
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef AUDIO #if defined(AUDIO) && defined(PYTHON)
#define AC_NUM_CHAT_COMMANDS 30 #define AC_NUM_CHAT_COMMANDS 32
#elif AUDIO
#define AC_NUM_CHAT_COMMANDS 31
#elif PYTHON
#define AC_NUM_CHAT_COMMANDS 23
#else #else
#define AC_NUM_CHAT_COMMANDS 22 #define AC_NUM_CHAT_COMMANDS 22
#endif /* AUDIO */ #endif /* AUDIO */
@ -106,8 +110,15 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/mute" }, { "/mute" },
{ "/sense" }, { "/sense" },
{ "/video" }, { "/video" },
{ "/bitrate" },
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
}; };
static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing) 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, 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; 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) if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, 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 else
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, 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, 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) if (self->active_box != -1)
box_notify2(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, 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 else
box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message, box_notify(self, generic_message, NT_WNDALERT_1 | NT_NOFOCUS | user_settings->bell_on_message,
&self->active_box, self->name, "* %s %s", nick, action ); &self->active_box, self->name, "* %s %s", nick, action );
} }
static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len) static void chat_onMessage(ToxWindow *self, Tox *m, uint32_t num, TOX_MESSAGE_TYPE type, const char *msg, size_t len)
@ -454,7 +465,7 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
if (ft->state == FILE_TRANSFER_PENDING) { if (ft->state == FILE_TRANSFER_PENDING) {
ft->state = FILE_TRANSFER_STARTED; ft->state = FILE_TRANSFER_STARTED;
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer [%d] for '%s' accepted.", 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]; char progline[MAX_STR_SIZE];
init_progress_bar(progline); init_progress_bar(progline);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
@ -466,10 +477,12 @@ static void chat_onFileControl(ToxWindow *self, Tox *m, uint32_t friendnum, uint
break; break;
} }
case TOX_FILE_CONTROL_PAUSE: { case TOX_FILE_CONTROL_PAUSE: {
ft->state = FILE_TRANSFER_PAUSED; ft->state = FILE_TRANSFER_PAUSED;
break; break;
} }
case TOX_FILE_CONTROL_CANCEL: { case TOX_FILE_CONTROL_CANCEL: {
snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name); snprintf(msg, sizeof(msg), "File transfer for '%s' was aborted.", ft->file_name);
close_file_transfer(self, m, ft, -1, msg, notif_error); close_file_transfer(self, m, ft, -1, msg, notif_error);
@ -548,7 +561,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
bytes_convert_str(sizestr, sizeof(sizestr), file_size); bytes_convert_str(sizestr, sizeof(sizestr), file_size);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr); line_info_add(self, 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; size_t path_len = name_length;
/* use specified download path in config if possible */ /* use specified download path in config if possible */
@ -601,10 +614,10 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
if (self->active_box != -1) if (self->active_box != -1)
box_notify2(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, 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 else
box_notify(self, transfer_pending, NT_WNDALERT_0 | NT_NOFOCUS | user_settings->bell_on_filetrans, 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, static void chat_onGroupInvite(ToxWindow *self, Tox *m, int32_t friendnumber, uint8_t type, const char *group_pub_key,
@ -671,8 +684,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."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Ringing...type \"/hangup\" to cancel it.");
#ifdef SOUND_NOTIFY #ifdef SOUND_NOTIFY
if (self->ringing_sound == -1) if (self->ringing_sound == -1)
sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound); sound_notify(self, call_outgoing, NT_LOOP, &self->ringing_sound);
#endif /* SOUND_NOTIFY */ #endif /* SOUND_NOTIFY */
} }
@ -881,8 +896,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action); int id = 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); cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id);
} }
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr) static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
@ -927,11 +942,20 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
diff = dir_match(self, m, ctx->line, L"/sendfile"); diff = dir_match(self, m, ctx->line, L"/sendfile");
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar"); 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] = { const char status_cmd_list[3][8] = {
{"online"}, {"online"},
{"away"}, {"away"},
{"busy"}, {"busy"},
}; };
diff = complete_line(self, status_cmd_list, 3, 8); diff = complete_line(self, status_cmd_list, 3, 8);
} else { } else {
@ -950,8 +974,7 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line)) if (!wstring_is_empty(ctx->line)) {
{
add_line_to_hist(ctx); add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n'); wstrsubst(ctx->line, L'', L'\n');
@ -980,8 +1003,8 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line); int id = 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); cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
} }
} }
@ -1029,9 +1052,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
colour = GREEN; colour = GREEN;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
colour = YELLOW; colour = YELLOW;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
colour = RED; colour = RED;
break; break;
@ -1107,9 +1132,11 @@ static void chat_onDraw(ToxWindow *self, Tox *m)
wnoutrefresh(self->window); wnoutrefresh(self->window);
#ifdef AUDIO #ifdef AUDIO
if (ctx->infobox.active) { if (ctx->infobox.active) {
draw_infobox(self); draw_infobox(self);
} }
#endif #endif
if (self->help->active) if (self->help->active)

View File

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

View File

@ -33,6 +33,7 @@
#include "line_info.h" #include "line_info.h"
#include "misc_tools.h" #include "misc_tools.h"
#include "notify.h" #include "notify.h"
#include "api.h"
struct cmd_func { struct cmd_func {
const char *name; const char *name;
@ -65,8 +66,11 @@ static struct cmd_func global_commands[] = {
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
{ "/lsvdev", cmd_list_video_devices }, { "/lsvdev", cmd_list_video_devices },
{ "/svdev" , cmd_change_video_device }, { "/svdev", cmd_change_video_device },
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
{ "/run", cmd_run },
#endif /* PYTHON */
{ NULL, NULL }, { NULL, NULL },
}; };
@ -83,6 +87,7 @@ static struct cmd_func chat_commands[] = {
{ "/hangup", cmd_hangup }, { "/hangup", cmd_hangup },
{ "/mute", cmd_mute }, { "/mute", cmd_mute },
{ "/sense", cmd_sense }, { "/sense", cmd_sense },
{ "/bitrate", cmd_bitrate },
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
{ "/video", cmd_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: case GROUPCHAT_COMMAND_MODE:
if (do_command(w, self, m, num_args, group_commands, args) == 0) if (do_command(w, self, m, num_args, group_commands, args) == 0)
return; return;
break; break;
} }
if (do_command(w, self, m, num_args, global_commands, args) == 0) if (do_command(w, self, m, num_args, global_commands, args) == 0)
return; return;
#ifdef PYTHON
if (do_plugin_command(num_args, args) == 0)
return;
#endif
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command."); 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. * Returns NULL on failure.
*/ */
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction) FILE_TRANSFER_DIRECTION direction)
{ {
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
return NULL; return NULL;
@ -145,8 +145,8 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
for (i = 0; i < MAX_FILES; ++i) { for (i = 0; i < MAX_FILES; ++i) {
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ? struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
&Friends.list[friendnum].file_sender[i] : &Friends.list[friendnum].file_sender[i] :
&Friends.list[friendnum].file_receiver[i]; &Friends.list[friendnum].file_receiver[i];
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
return ft; return ft;

View File

@ -87,7 +87,7 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
* Returns NULL on failure. * Returns NULL on failure.
*/ */
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index, struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
FILE_TRANSFER_DIRECTION direction); FILE_TRANSFER_DIRECTION direction);
/* Initializes an unused file transfer and returns its pointer. /* Initializes an unused file transfer and returns its pointer.
* Returns NULL on failure. * 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].active) {
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
continue;
}
BlockedFriend tmp; BlockedFriend tmp;
memset(&tmp, 0, sizeof(BlockedFriend)); memset(&tmp, 0, sizeof(BlockedFriend));
tmp.namelength = htons(Blocked.list[i].namelength); 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); memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)]; uint8_t lastonline[sizeof(uint64_t)];
@ -250,10 +254,15 @@ int load_blocklist(char *path)
memset(&Blocked.list[i], 0, sizeof(BlockedFriend)); memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
memcpy(&tmp, data + i * sizeof(BlockedFriend), 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].active = true;
Blocked.list[i].num = i; 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); // copy null byte
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE); memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
uint8_t lastonline[sizeof(uint64_t)]; 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) 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.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 */ /* if the format changes make sure TIME_STR_SIZE is the correct size */
const char *t = user_settings->timestamp_format; const char *t = user_settings->timestamp_format;
@ -538,7 +547,7 @@ static void friendlist_onGroupInvite(ToxWindow *self, Tox *m, int32_t num, uint8
get_nick_truncate(m, nick, num); get_nick_truncate(m, nick, num);
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, line_info_add(prompt, 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); sound_notify(prompt, notif_error, NT_WNDALERT_1, NULL);
} }
@ -560,6 +569,7 @@ static void select_friend(ToxWindow *self, wint_t key, int *selected, int num)
static void delete_friend(Tox *m, uint32_t f_num) static void delete_friend(Tox *m, uint32_t f_num)
{ {
TOX_ERR_FRIEND_DELETE err; TOX_ERR_FRIEND_DELETE err;
if (tox_friend_delete(m, f_num, &err) != true) { if (tox_friend_delete(m, f_num, &err) != true) {
fprintf(stderr, "tox_friend_delete failed with error %d\n", err); fprintf(stderr, "tox_friend_delete failed with error %d\n", err);
return; return;
@ -645,16 +655,18 @@ static void draw_del_popup(void)
wattron(PendingDelete.popup, A_BOLD); wattron(PendingDelete.popup, A_BOLD);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (blocklist_view == 0) if (blocklist_view == 0)
wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name); wprintw(PendingDelete.popup, "%s", Friends.list[PendingDelete.num].name);
else else
wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name); wprintw(PendingDelete.popup, "%s", Blocked.list[PendingDelete.num].name);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
wattroff(PendingDelete.popup, A_BOLD); wattroff(PendingDelete.popup, A_BOLD);
wprintw(PendingDelete.popup, "? y/n"); wprintw(PendingDelete.popup, "? y/n");
wrefresh(PendingDelete.popup); wnoutrefresh(PendingDelete.popup);
} }
/* deletes contact from blocked list */ /* deletes contact from blocked list */
@ -799,6 +811,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
block_friend(m, f); block_friend(m, f);
else else
unblock_friend(m, f); unblock_friend(m, f);
break; break;
case KEY_RIGHT: case KEY_RIGHT:
@ -811,6 +824,7 @@ static void friendlist_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
select_friend(self, key, &Friends.num_selected, Friends.num_friends); select_friend(self, key, &Friends.num_selected, Friends.num_friends);
else else
select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked); select_friend(self, key, &Blocked.num_selected, Blocked.num_blocked);
break; break;
} }
} }
@ -881,7 +895,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); wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
} }
wrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active)
@ -970,9 +984,11 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
case TOX_USER_STATUS_NONE: case TOX_USER_STATUS_NONE:
colour = GREEN; colour = GREEN;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
colour = YELLOW; colour = YELLOW;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
colour = RED; colour = RED;
break; break;
@ -1054,9 +1070,9 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
int day_dist = ( int day_dist = (
cur_loc_tm.tm_yday - Friends.list[f].last_online.tm.tm_yday 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_year - Friends.list[f].last_online.tm.tm_year) * 365)
); );
const char *hourmin = Friends.list[f].last_online.hour_min_str; const char *hourmin = Friends.list[f].last_online.hour_min_str;
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
@ -1096,7 +1112,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff); wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
} }
wrefresh(self->window); wnoutrefresh(self->window);
draw_del_popup(); draw_del_popup();
if (self->help->active) if (self->help->active)
@ -1112,7 +1128,8 @@ void disable_chatwin(uint32_t f_num)
static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state) static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number, int state)
{ {
assert(0); assert(0);
if( friend_number >= Friends.max_idx)
if ( friend_number >= Friends.max_idx)
return; return;
assert(0); assert(0);
@ -1120,7 +1137,7 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
if (Friends.list[friend_number].chatwin == -1) { if (Friends.list[friend_number].chatwin == -1) {
if (get_num_active_windows() < MAX_WINDOWS_NUM) { if (get_num_active_windows() < MAX_WINDOWS_NUM) {
if(state != TOXAV_FRIEND_CALL_STATE_FINISHED) { if (state != TOXAV_FRIEND_CALL_STATE_FINISHED) {
Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num)); Friends.list[friend_number].chatwin = add_window(m, new_chat(m, Friends.list[friend_number].num));
set_active_window(Friends.list[friend_number].chatwin); set_active_window(Friends.list[friend_number].chatwin);
} }

View File

@ -44,7 +44,7 @@ struct GroupChatInvite {
typedef struct { typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength; uint16_t namelength;
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1]; char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
size_t statusmsg_len; size_t statusmsg_len;
char pub_key[TOX_PUBLIC_KEY_SIZE]; char pub_key[TOX_PUBLIC_KEY_SIZE];
@ -65,7 +65,7 @@ typedef struct {
typedef struct { typedef struct {
char name[TOXIC_MAX_NAME_LENGTH + 1]; char name[TOXIC_MAX_NAME_LENGTH + 1];
int namelength; uint16_t namelength;
char pub_key[TOX_PUBLIC_KEY_SIZE]; char pub_key[TOX_PUBLIC_KEY_SIZE];
uint32_t num; uint32_t num;
bool active; bool active;

View File

@ -130,6 +130,7 @@ void cmd_add_helper(ToxWindow *self, Tox *m, const char *id_bin, const char *msg
break; break;
case TOX_ERR_FRIEND_ADD_NULL: case TOX_ERR_FRIEND_ADD_NULL:
/* fallthrough */ /* fallthrough */
default: default:
errmsg = "Faile to add friend: Unknown error."; errmsg = "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]; char key_binary[TOX_PUBLIC_KEY_SIZE * 2 + 1];
if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) { if (hex_string_to_bin(ascii_key, strlen(ascii_key), key_binary, TOX_PUBLIC_KEY_SIZE) == -1) {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid key.");
return; return;
@ -281,6 +283,7 @@ void cmd_connect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)
case TOX_ERR_BOOTSTRAP_NULL: case TOX_ERR_BOOTSTRAP_NULL:
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Bootstrap failed.");
break; break;
default: default:
break; break;
} }
@ -333,31 +336,31 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
uint8_t type; uint8_t type;
if (!strcasecmp(argv[1], "audio")) if (!strcasecmp(argv[1], "audio"))
type = TOX_GROUPCHAT_TYPE_AV; type = TOX_CONFERENCE_TYPE_AV;
else if (!strcasecmp(argv[1], "text")) else if (!strcasecmp(argv[1], "text"))
type = TOX_GROUPCHAT_TYPE_TEXT; type = TOX_CONFERENCE_TYPE_TEXT;
else { else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
return; return;
} }
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) TOX_ERR_CONFERENCE_NEW err;
groupnum = tox_add_groupchat(m);
/*#ifdef AUDIO
else
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
#endif*/
if (groupnum == -1) { uint32_t groupnum = tox_conference_new(m, &err);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
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; return;
} }
if (init_groupchat_win(prompt, m, groupnum, type) == -1) { 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."); 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; 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]; char dir[data_file_len + 1];
size_t dir_len = get_base_dir(DATA_FILE, data_file_len, dir); 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]; #ifdef QRPNG
snprintf(qr_path, sizeof(qr_path), "%s%s%s", dir, nick, QRCODE_FILENAME_EXT);
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) { #endif /* QRPNG */
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to create QR code."); 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; return;
} }
if (ID_to_QRcode(id_string, output) == -1) { #endif /* QRPNG */
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);
} }
void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]) void cmd_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
@ -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:"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your new Tox ID is:");
cmd_myid(window, self, m, 0, NULL); cmd_myid(window, self, m, 0, NULL);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); line_info_add(self, 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,
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); "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]) 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'; msg[len] = '\0';
prompt_update_statusmessage(prompt, m, msg); prompt_update_statusmessage(prompt, m, msg);
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s: \"%s\".", status_str, msg);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
} }
finish: finish:

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]); void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
#endif
#endif /* #define GLOBAL_COMMANDS_H */ #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]) 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]; char title[MAX_STR_SIZE];
if (argc < 1) { 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) { if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
title[tlen] = '\0';
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
} else {
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set"); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
return;
} }
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; return;
} }
@ -55,8 +62,8 @@ void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
int len = strlen(title) - 1; int len = strlen(title) - 1;
title[len] = '\0'; title[len] = '\0';
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) { 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."); line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
return; return;
} }

View File

@ -69,8 +69,12 @@ static int max_groupchat_index = 0;
extern struct user_settings *user_settings; extern struct user_settings *user_settings;
extern struct Winthread Winthread; extern struct Winthread Winthread;
#ifdef AUDIO #if defined(AUDIO) && defined(PYTHON)
#define AC_NUM_GROUP_COMMANDS 25
#elif AUDIO
#define AC_NUM_GROUP_COMMANDS 24 #define AC_NUM_GROUP_COMMANDS 24
#elif PYTHON
#define AC_NUM_GROUP_COMMANDS 21
#else #else
#define AC_NUM_GROUP_COMMANDS 20 #define AC_NUM_GROUP_COMMANDS 20
#endif /* AUDIO */ #endif /* AUDIO */
@ -98,25 +102,18 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
{ "/status" }, { "/status" },
{ "/title" }, { "/title" },
#ifdef AUDIO #ifdef PYTHON
{ "/lsdev" }, { "/run" },
{ "/sdev" },
{ "/mute" },
{ "/sense" },
#endif /* AUDIO */ #endif /* PYTHON */
}; };
#ifdef AUDIO int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
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)
{ {
if (groupnum > MAX_GROUPCHAT_NUM) if (groupnum > MAX_GROUPCHAT_NUM) {
return -1; return -1;
}
ToxWindow self = new_group_chat(m, groupnum); ToxWindow self = new_group_chat(m, groupnum);
int i; int i;
@ -135,18 +132,12 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
groupchats[i].oldpeer_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 if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL) || groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY); exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME)); memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(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); set_active_window(groupchats[i].chatwin);
if (i == max_groupchat_index) if (i == max_groupchat_index)
@ -174,12 +165,9 @@ static void kill_groupchat_window(ToxWindow *self)
del_window(self); del_window(self);
} }
void close_groupchat(ToxWindow *self, Tox *m, int groupnum) void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
{ {
tox_del_groupchat(m, groupnum); tox_conference_delete(m, groupnum, NULL);
#ifdef AUDIO
group_audio_close_out_device(groupnum);
#endif
free(groupchats[groupnum].peer_names); free(groupchats[groupnum].peer_names);
free(groupchats[groupnum].oldpeer_names); free(groupchats[groupnum].oldpeer_names);
@ -237,8 +225,8 @@ void redraw_groupchat_win(ToxWindow *self)
} }
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum, static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
const char *msg, uint16_t len) TOX_MESSAGE_TYPE type, const char *msg, size_t len)
{ {
if (self->num != groupnum) if (self->num != groupnum)
return; return;
@ -266,56 +254,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); box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
nick_clr = RED; nick_clr = RED;
} } else {
else {
sound_notify(self, silent, NT_WNDALERT_1, NULL); sound_notify(self, silent, NT_WNDALERT_1, NULL);
} }
char timefrmt[TIME_STR_SIZE]; char timefrmt[TIME_STR_SIZE];
get_time_str(timefrmt, sizeof(timefrmt)); 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); write_to_log(msg, nick, ctx->log, false);
} }
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action, static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
uint16_t len) const char *title,
{ size_t length)
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)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -341,7 +293,7 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum,
} }
/* Puts two copies of peerlist/lengths in chat instance */ /* 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 copy_peernames(Tox *m, uint32_t gnum, size_t npeers)
{ {
/* Assumes these are initiated in init_groupchat_win */ /* Assumes these are initiated in init_groupchat_win */
free(groupchats[gnum].peer_names); free(groupchats[gnum].peer_names);
@ -351,27 +303,34 @@ static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], ui
int N = TOX_MAX_NAME_LENGTH; int N = TOX_MAX_NAME_LENGTH;
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N); groupchats[gnum].peer_names = calloc(1, sizeof(uint8_t) * npeers * N);
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N); groupchats[gnum].oldpeer_names = calloc(1, sizeof(uint8_t) * npeers * N);
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers); groupchats[gnum].peer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers); groupchats[gnum].oldpeer_name_lengths = calloc(1, sizeof(uint16_t) * npeers);
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) { || groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY); exit_toxic_err("failed in copy_peernames()", FATALERR_MEMORY);
} }
uint16_t u_len = strlen(UNKNOWN_NAME); uint16_t u_len = strlen(UNKNOWN_NAME);
int i; int i;
for (i = 0; i < npeers; ++i) { for (i = 0; i < npeers; ++i) {
if (!lengths[i]) { uint8_t name[TOX_MAX_NAME_LENGTH];
TOX_ERR_CONFERENCE_PEER_QUERY err;
size_t n_len = tox_conference_peer_get_name_size(m, gnum, i, &err);
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len); memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
groupchats[gnum].peer_names[i * N + u_len] = '\0'; groupchats[gnum].peer_names[i * N + u_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = u_len; groupchats[gnum].peer_name_lengths[i] = u_len;
} else { } else {
uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1); tox_conference_peer_get_name(m, gnum, i, name, NULL);
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], n_len); n_len = MIN(n_len, TOXIC_MAX_NAME_LENGTH - 1);
memcpy(&groupchats[gnum].peer_names[i * N], name, n_len);
groupchats[gnum].peer_names[i * N + n_len] = '\0'; groupchats[gnum].peer_names[i * N + n_len] = '\0';
groupchats[gnum].peer_name_lengths[i] = n_len; groupchats[gnum].peer_name_lengths[i] = n_len;
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len); filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
@ -385,8 +344,8 @@ static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], ui
struct group_add_thrd { struct group_add_thrd {
Tox *m; Tox *m;
ToxWindow *self; ToxWindow *self;
int peernum; uint32_t peernum;
int groupnum; uint32_t groupnum;
time_t timestamp; time_t timestamp;
pthread_t tid; pthread_t tid;
pthread_attr_t attr; pthread_attr_t attr;
@ -429,7 +388,8 @@ void *group_add_wait(void *data)
pthread_exit(NULL); pthread_exit(NULL);
} }
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change) static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
TOX_CONFERENCE_STATE_CHANGE change)
{ {
if (self->num != groupnum) if (self->num != groupnum)
return; return;
@ -437,8 +397,15 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
if (groupnum > max_groupchat_index) if (groupnum > max_groupchat_index)
return; return;
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum); TOX_ERR_CONFERENCE_PEER_QUERY err;
int num_peers = groupchats[groupnum].num_peers;
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
groupchats[groupnum].num_peers = num_peers;
} else {
num_peers = groupchats[groupnum].num_peers;
}
if (peernum > num_peers) if (peernum > num_peers)
return; return;
@ -446,28 +413,19 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
/* get old peer name before updating name list */ /* get old peer name before updating name list */
uint8_t oldpeername[TOX_MAX_NAME_LENGTH]; uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_ADD) { if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN) {
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH], memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(oldpeername));
sizeof(oldpeername));
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum]; uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
oldpeername[old_n_len] = '\0'; oldpeername[old_n_len] = '\0';
} }
/* Update name/len lists */ /* Update name/len lists */
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH]; copy_peernames(m, groupnum, num_peers);
uint16_t tmp_peerlens[num_peers];
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));
}
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
/* get current peername then sort namelist */ /* get current peername then sort namelist */
uint8_t peername[TOX_MAX_NAME_LENGTH]; uint8_t peername[TOX_MAX_NAME_LENGTH];
if (change != TOX_CHAT_CHANGE_PEER_DEL) { if (change != TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT) {
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum]; uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername)); memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
peername[n_len] = '\0'; peername[n_len] = '\0';
@ -482,7 +440,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
get_time_str(timefrmt, sizeof(timefrmt)); get_time_str(timefrmt, sizeof(timefrmt));
switch (change) { switch (change) {
case TOX_CHAT_CHANGE_PEER_ADD: case TOX_CONFERENCE_STATE_CHANGE_PEER_JOIN:
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT)) if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
break; break;
@ -512,7 +470,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
break; break;
case TOX_CHAT_CHANGE_PEER_DEL: case TOX_CONFERENCE_STATE_CHANGE_PEER_EXIT:
event = "has left the room"; event = "has left the room";
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event); line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
@ -522,7 +480,7 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
write_to_log(event, (char *) oldpeername, ctx->log, true); write_to_log(event, (char *) oldpeername, ctx->log, true);
break; break;
case TOX_CHAT_CHANGE_PEER_NAME: case TOX_CONFERENCE_STATE_CHANGE_PEER_NAME_CHANGE:
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT)) if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
return; return;
@ -549,9 +507,10 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
return; return;
} }
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) { TOX_ERR_CONFERENCE_SEND_MESSAGE err;
const char *errmsg = " * Failed to send action.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg); 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);
} }
} }
@ -595,7 +554,16 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
TOX_MAX_NAME_LENGTH); TOX_MAX_NAME_LENGTH);
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) { } else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
} 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); diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
} }
@ -621,8 +589,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line)) if (!wstring_is_empty(ctx->line)) {
{
add_line_to_hist(ctx); add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n'); wstrsubst(ctx->line, L'', L'\n');
@ -642,9 +609,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE); execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
} }
} else { } else {
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) { TOX_ERR_CONFERENCE_SEND_MESSAGE err;
const char *errmsg = " * Failed to send message.";
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg); 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 +670,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
wmove(ctx->sidebar, i + 2, 1); wmove(ctx->sidebar, i + 2, 1);
pthread_mutex_lock(&Winthread.lock); 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); pthread_mutex_unlock(&Winthread.lock);
/* truncate nick to fit in side panel without modifying list */ /* truncate nick to fit in side panel without modifying list */
@ -725,7 +693,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos)); int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
wmove(self->window, y + 1, new_x); wmove(self->window, y + 1, new_x);
wrefresh(self->window); wnoutrefresh(self->window);
if (self->help->active) if (self->help->active)
help_onDraw(self); help_onDraw(self);
@ -767,125 +735,7 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
wmove(self->window, y2 - CURS_Y_OFFSET, 0); wmove(self->window, y2 - CURS_Y_OFFSET, 0);
} }
ToxWindow new_group_chat(Tox *m, uint32_t groupnum)
#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 ret; ToxWindow ret;
memset(&ret, 0, sizeof(ret)); memset(&ret, 0, sizeof(ret));
@ -898,13 +748,8 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
ret.onInit = &groupchat_onInit; ret.onInit = &groupchat_onInit;
ret.onGroupMessage = &groupchat_onGroupMessage; ret.onGroupMessage = &groupchat_onGroupMessage;
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange; ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
ret.onGroupAction = &groupchat_onGroupAction;
ret.onGroupTitleChange = &groupchat_onGroupTitleChange; ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
#ifdef AUDIO
ret.onWriteDevice = &groupchat_onWriteDevice;
#endif
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum); snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
ChatContext *chatwin = calloc(1, sizeof(ChatContext)); ChatContext *chatwin = calloc(1, sizeof(ChatContext));

View File

@ -26,61 +26,30 @@
#include "toxic.h" #include "toxic.h"
#include "windows.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 SIDEBAR_WIDTH 16
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */ #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 MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
#define GROUP_EVENT_WAIT 3 #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 { typedef struct {
int chatwin; int chatwin;
bool active; bool active;
uint8_t type; uint8_t type;
int num_peers; uint32_t num_peers;
int side_pos; /* current position of the sidebar - used for scrolling up and down */ int side_pos; /* current position of the sidebar - used for scrolling up and down */
time_t start_time; time_t start_time;
uint8_t *peer_names; uint8_t *peer_names;
uint8_t *oldpeer_names; uint8_t *oldpeer_names;
uint16_t *peer_name_lengths; uint16_t *peer_name_lengths;
uint16_t *oldpeer_name_lengths; uint16_t *oldpeer_name_lengths;
#ifdef AUDIO
struct GAudio audio;
#endif
} GroupChat; } GroupChat;
void close_groupchat(ToxWindow *self, Tox *m, int groupnum); void close_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
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);
/* destroys and re-creates groupchat window with or without the peerlist */ /* destroys and re-creates groupchat window with or without the peerlist */
void redraw_groupchat_win(ToxWindow *self); void redraw_groupchat_win(ToxWindow *self);
ToxWindow new_group_chat(Tox *m, int groupnum); ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
#endif /* #define GROUPCHAT_H */ #endif /* #define GROUPCHAT_H */

View File

@ -27,7 +27,15 @@
#include "help.h" #include "help.h"
#include "misc_tools.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 #define HELP_MENU_HEIGHT 9
#endif /* PYTHON */
#define HELP_MENU_WIDTH 26 #define HELP_MENU_WIDTH 26
void help_init_menu(ToxWindow *self) void help_init_menu(ToxWindow *self)
@ -95,6 +103,13 @@ static void help_draw_menu(ToxWindow *self)
wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, "oup commands\n"); 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)); wattron(win, A_BOLD | COLOR_PAIR(BLUE));
wprintw(win, " f"); wprintw(win, " f");
wattroff(win, A_BOLD | COLOR_PAIR(BLUE)); wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
@ -112,7 +127,7 @@ static void help_draw_menu(ToxWindow *self)
wprintw(win, "it menu\n"); wprintw(win, "it menu\n");
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_bottom_menu(WINDOW *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, " /log <on> or <off> : Enable/disable logging\n");
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n"); wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
wprintw(win, " /myid : Print your Tox ID\n"); wprintw(win, " /myid : Print your Tox ID\n");
#ifdef 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"); 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, " /clear : Clear window history\n");
wprintw(win, " /close : Close the current chat window\n"); wprintw(win, " /close : Close the current chat window\n");
wprintw(win, " /quit or /exit : Exit Toxic\n"); wprintw(win, " /quit or /exit : Exit Toxic\n");
@ -181,10 +200,18 @@ static void help_draw_global(ToxWindow *self)
wprintw(win, " /svdev <type> <id> : Set active video device\n"); wprintw(win, " /svdev <type> <id> : Set active video device\n");
#endif /* VIDEO */ #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); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_chat(ToxWindow *self) 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, " /sdev <type> <id> : Change active device\n");
wprintw(win, " /mute <type> : Mute active device if in call\n"); wprintw(win, " /mute <type> : Mute active device if in call\n");
wprintw(win, " /sense <n> : VAD sensitivity threshold\n"); wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef VIDEO #ifdef VIDEO
@ -227,7 +255,7 @@ static void help_draw_chat(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_keys(ToxWindow *self) static void help_draw_keys(ToxWindow *self)
@ -253,7 +281,7 @@ static void help_draw_keys(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
static void help_draw_group(ToxWindow *self) static void help_draw_group(ToxWindow *self)
@ -271,9 +299,29 @@ static void help_draw_group(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); 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) static void help_draw_contacts(ToxWindow *self)
{ {
WINDOW *win = self->help->win; WINDOW *win = self->help->win;
@ -293,12 +341,14 @@ static void help_draw_contacts(ToxWindow *self)
help_draw_bottom_menu(win); help_draw_bottom_menu(win);
box(win, ACS_VLINE, ACS_HLINE); box(win, ACS_VLINE, ACS_HLINE);
wrefresh(win); wnoutrefresh(win);
} }
void help_onKey(ToxWindow *self, wint_t key) void help_onKey(ToxWindow *self, wint_t key)
{ {
switch(key) { int height;
switch (key) {
case 'x': case 'x':
case T_KEY_ESC: case T_KEY_ESC:
help_exit(self); help_exit(self);
@ -306,9 +356,9 @@ void help_onKey(ToxWindow *self, wint_t key)
case 'c': case 'c':
#ifdef VIDEO #ifdef VIDEO
help_init_window(self, 22, 80); help_init_window(self, 23, 80);
#elif AUDIO #elif AUDIO
help_init_window(self, 19, 80); help_init_window(self, 20, 80);
#else #else
help_init_window(self, 10, 80); help_init_window(self, 10, 80);
#endif #endif
@ -316,13 +366,16 @@ void help_onKey(ToxWindow *self, wint_t key)
break; break;
case 'g': case 'g':
height = 22;
#ifdef VIDEO #ifdef VIDEO
help_init_window(self, 30, 80); height += 8;
#elif AUDIO #elif AUDIO
help_init_window(self, 26, 80); height += 4;
#else
help_init_window(self, 22, 80);
#endif #endif
#ifdef PYTHON
height += 2;
#endif
help_init_window(self, height, 80);
self->help->type = HELP_GLOBAL; self->help->type = HELP_GLOBAL;
break; break;
@ -331,6 +384,14 @@ void help_onKey(ToxWindow *self, wint_t key)
self->help->type = HELP_GROUP; self->help->type = HELP_GROUP;
break; 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': case 'f':
help_init_window(self, 10, 80); help_init_window(self, 10, 80);
self->help->type = HELP_CONTACTS; self->help->type = HELP_CONTACTS;
@ -350,9 +411,7 @@ void help_onKey(ToxWindow *self, wint_t key)
void help_onDraw(ToxWindow *self) void help_onDraw(ToxWindow *self)
{ {
curs_set(0); switch (self->help->type) {
switch(self->help->type) {
case HELP_MENU: case HELP_MENU:
help_draw_menu(self); help_draw_menu(self);
return; return;
@ -376,5 +435,12 @@ void help_onDraw(ToxWindow *self)
case HELP_GROUP: case HELP_GROUP:
help_draw_group(self); help_draw_group(self);
break; break;
#ifdef PYTHON
case HELP_PLUGIN:
help_draw_plugin(self);
break;
#endif /* PYTHON */
} }
} }

View File

@ -33,6 +33,9 @@ typedef enum {
HELP_GROUP, HELP_GROUP,
HELP_KEYS, HELP_KEYS,
HELP_CONTACTS, HELP_CONTACTS,
#ifdef PYTHON
HELP_PLUGIN,
#endif
} HELP_TYPES; } HELP_TYPES;
void help_onDraw(ToxWindow *self); 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; self->show_peerlist ^= 1;
redraw_groupchat_win(self); redraw_groupchat_win(self);
} }
match = true; match = true;
} } else if (key == user_settings->key_toggle_pastemode) {
else if (key == user_settings->key_toggle_pastemode) {
self->chatwin->pastemode ^= 1; self->chatwin->pastemode ^= 1;
match = true; match = true;
} }

View File

@ -129,17 +129,21 @@ static struct line_info *line_info_ret_queue(struct history *hst)
return line; return line;
} }
/* creates new line_info line and puts it in the queue. */ /* 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, ...) * 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) if (!self)
return; return -1;
struct history *hst = self->chatwin->hst; struct history *hst = self->chatwin->hst;
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE) if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
return; return -1;
struct line_info *new_line = calloc(1, sizeof(struct line_info)); 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 */ /* for type-specific formatting in print function */
switch (type) { switch (type) {
case IN_ACTION: case IN_ACTION:
/* fallthrough */ /* fallthrough */
case OUT_ACTION: case OUT_ACTION:
len += strlen(user_settings->line_normal) + 2; len += strlen(user_settings->line_normal) + 2;
break; break;
case IN_MSG: case IN_MSG:
/* fallthrough */ /* fallthrough */
case OUT_MSG: case OUT_MSG:
len += strlen(user_settings->line_normal) + 3; len += strlen(user_settings->line_normal) + 3;
@ -220,6 +226,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
len += strlen(new_line->name2); len += strlen(new_line->name2);
} }
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
new_line->len = len; new_line->len = len;
new_line->type = type; new_line->type = type;
new_line->bold = bold; 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(); new_line->timestamp = get_unix_time();
hst->queue[hst->queue_sz++] = new_line; 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() */ /* 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) if (hst->start_id > user_settings->history_size)
line_info_root_fwd(hst); line_info_root_fwd(hst);
line->id = hst->line_end->id + 1;
line->prev = hst->line_end; line->prev = hst->line_end;
hst->line_end->next = line; hst->line_end->next = line;
hst->line_end = line; hst->line_end = line;
hst->line_end->id = line->id;
int y, y2, x, x2; int y, y2, x, x2;
getmaxyx(self->window, y2, x2); getmaxyx(self->window, y2, x2);
@ -304,8 +313,10 @@ void line_info_print(ToxWindow *self)
switch (type) { switch (type) {
case OUT_MSG: case OUT_MSG:
/* fallthrough */ /* fallthrough */
case OUT_MSG_READ: case OUT_MSG_READ:
/* fallthrough */ /* fallthrough */
case IN_MSG: case IN_MSG:
wattron(win, COLOR_PAIR(BLUE)); 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); wprintw(win, "%s %s: ", user_settings->line_normal, line->name1);
wattroff(win, COLOR_PAIR(nameclr)); wattroff(win, COLOR_PAIR(nameclr));
char* msg = line->msg; char *msg = line->msg;
while (msg)
{ while (msg) {
char* line = strsep(&msg, "\n"); char *line = strsep(&msg, "\n");
if (line[0] == '>') if (line[0] == '>')
wattron(win, COLOR_PAIR(GREEN)); wattron(win, COLOR_PAIR(GREEN));
@ -360,8 +371,10 @@ void line_info_print(ToxWindow *self)
break; break;
case OUT_ACTION_READ: case OUT_ACTION_READ:
/* fallthrough */ /* fallthrough */
case OUT_ACTION: case OUT_ACTION:
/* fallthrough */ /* fallthrough */
case IN_ACTION: case IN_ACTION:
wattron(win, COLOR_PAIR(BLUE)); 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) { if (key == user_settings->key_half_page_up) {
line_info_page_up(self, hst); 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); 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); 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); 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); line_info_reset_start(self, hst);
} } else {
else {
match = false; match = false;
} }

View File

@ -74,9 +74,13 @@ struct history {
int queue_sz; int queue_sz;
}; };
/* creates new line_info line and puts it in the queue. */ /* 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, ...); * 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 */ /* Prints a section of history starting at line_start */
void line_info_print(ToxWindow *self); 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; return 0;
on_error: on_error:
if (log_on) if (log_on)
log_enable(src, selfkey, otherkey, log, LOG_CHAT); log_enable(src, selfkey, otherkey, log, LOG_CHAT);

View File

@ -42,8 +42,12 @@ void cqueue_cleanup(struct chat_queue *q)
free(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)); struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
if (new_m == NULL) if (new_m == NULL)

View File

@ -40,7 +40,7 @@ struct chat_queue {
}; };
void cqueue_cleanup(struct chat_queue *q); 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. */ /* Tries to send the oldest unsent message in queue. */
void cqueue_try_send(ToxWindow *self, Tox *m); void cqueue_try_send(ToxWindow *self, Tox *m);

View File

@ -26,9 +26,12 @@
#include <time.h> #include <time.h>
#include <limits.h> #include <limits.h>
#include <dirent.h> #include <dirent.h>
#if SYSTEM == BSD
#include <sys/stat.h> #include <netinet/in.h>
#include <sys/socket.h>
#endif /* BSD! */
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/stat.h>
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
@ -70,7 +73,7 @@ struct tm *get_time(void)
{ {
struct tm *timeinfo; struct tm *timeinfo;
time_t t = get_unix_time(); time_t t = get_unix_time();
timeinfo = localtime((const time_t*) &t); timeinfo = localtime((const time_t *) &t);
return timeinfo; return timeinfo;
} }
@ -156,7 +159,7 @@ int bin_id_to_string(const char *bin_id, size_t bin_id_size, char *output, size_
size_t i; size_t i;
for (i = 0; i < TOX_ADDRESS_SIZE; ++i) for (i = 0; i < TOX_ADDRESS_SIZE; ++i)
snprintf(&output[i*2], output_size - (i * 2), "%02X", bin_id[i] & 0xff); snprintf(&output[i * 2], output_size - (i * 2), "%02X", bin_id[i] & 0xff);
return 0; return 0;
} }
@ -187,7 +190,7 @@ int mbs_to_wcs_buf(wchar_t *buf, const char *string, size_t n)
if (n < len) if (n < len)
return -1; return -1;
if ((len = mbstowcs(buf, string, n)) == (size_t) -1) if ((len = mbstowcs(buf, string, n)) == (size_t) - 1)
return -1; return -1;
return len; return len;
@ -201,7 +204,7 @@ int wcs_to_mbs_buf(char *buf, const wchar_t *string, size_t n)
if (n < len) if (n < len)
return -1; return -1;
if ((len = wcstombs(buf, string, n)) == (size_t) -1) if ((len = wcstombs(buf, string, n)) == (size_t) - 1)
return -1; return -1;
return len; return len;
@ -228,11 +231,11 @@ int valid_nick(const char *nick)
for (i = 0; nick[i]; ++i) { for (i = 0; nick[i]; ++i) {
if ((nick[i] == ' ' && nick[i + 1] == ' ') if ((nick[i] == ' ' && nick[i + 1] == ' ')
|| nick[i] == '/' || nick[i] == '/'
|| nick[i] == '\n' || nick[i] == '\n'
|| nick[i] == '\t' || nick[i] == '\t'
|| nick[i] == '\v' || nick[i] == '\v'
|| nick[i] == '\r') || nick[i] == '\r')
return 0; return 0;
} }
@ -319,45 +322,71 @@ void str_to_lower(char *str)
Returns nick len */ Returns nick len */
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum) size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
{ {
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) { if (err != TOX_ERR_FRIEND_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME); goto on_error;
len = strlen(UNKNOWN_NAME);
} else { } 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); len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0'; buf[len] = '\0';
filter_str(buf, len); filter_str(buf, len);
return 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 */ /* 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) { if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
strcpy(buf, UNKNOWN_NAME); goto on_error;
len = strlen(UNKNOWN_NAME); } 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); len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
buf[len] = '\0'; buf[len] = '\0';
filter_str(buf, len); filter_str(buf, len);
return 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 */ 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 copy_tox_str(char *msg, size_t size, const char *data, size_t length)
{ {
size_t len = MIN(length, size - 1); size_t i;
memcpy(msg, data, len); size_t j = 0;
msg[len] = '\0';
return len; 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. /* 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); size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
/* same as get_nick_truncate but for groupchats */ /* same as get_nick_truncate but for groupchats */
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum); int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
/* copies data to msg buffer. /* copies data to msg buffer.
returns length of msg, which will be no larger than size-1 */ returns length of msg, which will be no larger than size-1 */

View File

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

View File

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

View File

@ -31,13 +31,16 @@
#endif /* __OBJC__ */ #endif /* __OBJC__ */
#define RELEASE_CHK(func, obj) if ((obj))\ #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); 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__ #ifdef __OBJC__
@interface OSXVideo : NSObject <AVCaptureVideoDataOutputSampleBufferDelegate> @interface OSXVideo :
- (instancetype)initWithDeviceNames:(char **)device_names AmtDevices:(int *)size; NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>
- (instancetype)initWithDeviceNames:
(char **)device_names AmtDevices:
(int *)size;
@end @end
#endif /* __OBJC__ */ #endif /* __OBJC__ */

View File

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

View File

@ -49,10 +49,16 @@ extern struct Winthread Winthread;
extern FriendsList Friends; extern FriendsList Friends;
FriendRequests FrndRequests; 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 #define AC_NUM_GLOB_COMMANDS 22
#elif AUDIO #elif AUDIO
#define AC_NUM_GLOB_COMMANDS 20 #define AC_NUM_GLOB_COMMANDS 20
#elif PYTHON
#define AC_NUM_GLOB_COMMANDS 19
#else #else
#define AC_NUM_GLOB_COMMANDS 18 #define AC_NUM_GLOB_COMMANDS 18
#endif #endif
@ -92,6 +98,12 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
#endif /* VIDEO */ #endif /* VIDEO */
#ifdef PYTHON
{ "/run" },
#endif /* PYTHON */
}; };
void kill_prompt_window(ToxWindow *self) void kill_prompt_window(ToxWindow *self)
@ -214,11 +226,18 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
diff = dir_match(self, m, ctx->line, L"/avatar"); diff = dir_match(self, m, ctx->line, L"/avatar");
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] = { const char status_cmd_list[3][8] = {
{"online"}, {"online"},
{"away"}, {"away"},
{"busy"}, {"busy"},
}; };
diff = complete_line(self, status_cmd_list, 3, 8); diff = complete_line(self, status_cmd_list, 3, 8);
} else } else
@ -238,8 +257,7 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
} else if (key == '\r') { } else if (key == '\r') {
rm_trailing_spaces_buf(ctx); rm_trailing_spaces_buf(ctx);
if (!wstring_is_empty(ctx->line)) if (!wstring_is_empty(ctx->line)) {
{
add_line_to_hist(ctx); add_line_to_hist(ctx);
wstrsubst(ctx->line, L'', L'\n'); wstrsubst(ctx->line, L'', L'\n');
@ -301,10 +319,12 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
status_text = "Online"; status_text = "Online";
colour = GREEN; colour = GREEN;
break; break;
case TOX_USER_STATUS_AWAY: case TOX_USER_STATUS_AWAY:
status_text = "Away"; status_text = "Away";
colour = YELLOW; colour = YELLOW;
break; break;
case TOX_USER_STATUS_BUSY: case TOX_USER_STATUS_BUSY:
status_text = "Busy"; status_text = "Busy";
colour = RED; colour = RED;
@ -335,7 +355,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m); size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) statusmsg); tox_self_get_status_message (m, (uint8_t *) statusmsg);
statusmsg[slen] = '\0'; statusmsg[slen] = '\0';
snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg); snprintf(statusbar->statusmsg, sizeof(statusbar->statusmsg), "%s", statusmsg);
statusbar->statusmsg_len = strlen(statusbar->statusmsg); statusbar->statusmsg_len = strlen(statusbar->statusmsg);
@ -375,7 +395,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
help_onDraw(self); help_onDraw(self);
} }
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status) static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, TOX_CONNECTION connection_status)
{ {
ChatContext *ctx = self->chatwin; ChatContext *ctx = self->chatwin;
@ -404,8 +424,7 @@ static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnu
else else
box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box, box_notify(self, user_log_in, NT_WNDALERT_2 | NT_NOTIFWND | NT_RESTOL, &self->active_box,
"Toxic", "%s has come online", nick ); "Toxic", "%s has come online", nick );
} } else if (connection_status == TOX_CONNECTION_NONE) {
else if (connection_status == TOX_CONNECTION_NONE) {
msg = "has gone offline"; msg = "has gone offline";
line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg); line_info_add(self, timefrmt, nick, NULL, DISCONNECTION, 0, RED, msg);
write_to_log(msg, nick, ctx->log, true); write_to_log(msg, nick, ctx->log, true);

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"
#endif /* PYTHON */
#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;
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);
}
}

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 <stdlib.h>
#include <qrencode.h> #include <qrencode.h>
#include <stdbool.h> #include <stdbool.h>
#include <string.h>
#include "toxic.h" #include "toxic.h"
#include "windows.h" #include "windows.h"
#include "qr_code.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 BORDER_LEN 1
#define CHAR_1 "\342\226\210" #define CHAR_1 "\342\226\210"
#define CHAR_2 "\342\226\204" #define CHAR_2 "\342\226\204"
#define CHAR_3 "\342\226\200" #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 0 on success.
* Returns -1 on failure. * 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) if (fp == NULL)
return -1; return -1;
QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0); QRcode *qr_obj = QRcode_encodeString(tox_id, 0, QR_ECLEVEL_L, QR_MODE_8, 0);
if (qr_obj == NULL) if (qr_obj == NULL) {
fclose(fp);
return -1; return -1;
}
size_t width = qr_obj->width; size_t width = qr_obj->width;
size_t i, j; size_t i, j;
@ -83,7 +95,116 @@ int ID_to_QRcode(const char *tox_id, FILE *fp)
fprintf(fp, "\n"); fprintf(fp, "\n");
} }
fclose(fp);
QRcode_free(qr_obj); QRcode_free(qr_obj);
return 0; 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" #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 0 on success.
* Returns -1 on failure. * 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 */ #endif /* QR_CODE */

View File

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

View File

@ -63,6 +63,7 @@ struct user_settings {
char download_path[PATH_MAX]; char download_path[PATH_MAX];
char chatlogs_path[PATH_MAX]; char chatlogs_path[PATH_MAX];
char avatar_path[PATH_MAX]; char avatar_path[PATH_MAX];
char autorun_path[PATH_MAX];
char password_eval[PASSWORD_EVAL_MAX]; char password_eval[PASSWORD_EVAL_MAX];
int key_next_tab; int key_next_tab;

View File

@ -52,8 +52,7 @@ extern struct Winthread Winthread;
#define PATH_SEP_S "/" #define PATH_SEP_S "/"
#define PATH_SEP_C '/' #define PATH_SEP_C '/'
typedef enum typedef enum {
{
MPLEX_NONE, MPLEX_NONE,
MPLEX_SCREEN, MPLEX_SCREEN,
MPLEX_TMUX, MPLEX_TMUX,
@ -97,13 +96,14 @@ static char *read_into_dyn_buffer (FILE *stream)
char *dyn_buffer = NULL; char *dyn_buffer = NULL;
int dyn_buffer_size = 1; /* account for the \0 */ int dyn_buffer_size = 1; /* account for the \0 */
while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) while ((input_ptr = fgets (buffer, BUFFER_SIZE, stream)) != NULL) {
{
int length = dyn_buffer_size + strlen (input_ptr); int length = dyn_buffer_size + strlen (input_ptr);
if (dyn_buffer) if (dyn_buffer)
dyn_buffer = (char*) realloc (dyn_buffer, length); dyn_buffer = (char *) realloc (dyn_buffer, length);
else else
dyn_buffer = (char*) malloc (length); dyn_buffer = (char *) malloc (length);
strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr); strcpy (dyn_buffer + dyn_buffer_size - 1, input_ptr);
dyn_buffer_size = length; dyn_buffer_size = length;
} }
@ -116,26 +116,29 @@ static char *extract_socket_path (const char *info)
const char *search_str = " Socket"; const char *search_str = " Socket";
const char *pos = strstr (info, search_str); const char *pos = strstr (info, search_str);
char *end = NULL; char *end = NULL;
char* path = NULL; char *path = NULL;
if (!pos) if (!pos)
return NULL; return NULL;
pos += strlen (search_str); pos += strlen (search_str);
pos = strchr (pos, PATH_SEP_C); pos = strchr (pos, PATH_SEP_C);
if (!pos) if (!pos)
return NULL; return NULL;
end = strchr (pos, '\n'); end = strchr (pos, '\n');
if (!end) if (!end)
return NULL; return NULL;
*end = '\0'; *end = '\0';
end = strrchr (pos, '.'); end = strrchr (pos, '.');
if (!end) if (!end)
return NULL; return NULL;
path = (char*) malloc (end - pos + 1); path = (char *) malloc (end - pos + 1);
*end = '\0'; *end = '\0';
return strcpy (path, pos); return strcpy (path, pos);
} }
@ -147,14 +150,17 @@ static int detect_gnu_screen ()
char *dyn_buffer = NULL; char *dyn_buffer = NULL;
socket_name = getenv ("STY"); socket_name = getenv ("STY");
if (!socket_name) if (!socket_name)
goto nomplex; goto nomplex;
session_info_stream = popen ("env LC_ALL=C screen -ls", "r"); session_info_stream = popen ("env LC_ALL=C screen -ls", "r");
if (!session_info_stream) if (!session_info_stream)
goto nomplex; goto nomplex;
dyn_buffer = read_into_dyn_buffer (session_info_stream); dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer) if (!dyn_buffer)
goto nomplex; goto nomplex;
@ -162,6 +168,7 @@ static int detect_gnu_screen ()
session_info_stream = NULL; session_info_stream = NULL;
socket_path = extract_socket_path (dyn_buffer); socket_path = extract_socket_path (dyn_buffer);
if (!socket_path) if (!socket_path)
goto nomplex; goto nomplex;
@ -181,28 +188,34 @@ static int detect_gnu_screen ()
return 1; return 1;
nomplex: nomplex:
if (session_info_stream) if (session_info_stream)
pclose (session_info_stream); pclose (session_info_stream);
if (dyn_buffer) if (dyn_buffer)
free (dyn_buffer); free (dyn_buffer);
if (socket_path) if (socket_path)
free(socket_path); free(socket_path);
return 0; return 0;
} }
static int detect_tmux () static int detect_tmux ()
{ {
char *tmux_env = getenv ("TMUX"), *pos; char *tmux_env = getenv ("TMUX"), *pos;
if (!tmux_env) if (!tmux_env)
return 0; return 0;
/* find second separator */ /* find second separator */
pos = strrchr (tmux_env, ','); pos = strrchr (tmux_env, ',');
if (!pos) if (!pos)
return 0; return 0;
/* store the session number string for later use */ /* store the session id for later use */
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1); snprintf (mplex_data, sizeof(mplex_data), "$%s", pos + 1);
mplex = MPLEX_TMUX; mplex = MPLEX_TMUX;
return 1; return 1;
} }
@ -230,6 +243,7 @@ static int gnu_screen_is_detached ()
return 0; return 0;
struct stat sb; struct stat sb;
if (stat (mplex_data, &sb) != 0) if (stat (mplex_data, &sb) != 0)
return 0; return 0;
@ -238,14 +252,8 @@ static int gnu_screen_is_detached ()
} }
/* Detects tmux attached/detached by getting session data and finding the /* Detects tmux attached/detached by getting session data and finding the
current session's entry. An attached entry ends with "(attached)". Example: current session's entry.
*/
$ tmux list-sessions
0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached)
1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23]
In this example, session 0 is attached and session 1 is detached.
*/
static int tmux_is_detached () static int tmux_is_detached ()
{ {
if (mplex != MPLEX_TMUX) if (mplex != MPLEX_TMUX)
@ -253,14 +261,18 @@ static int tmux_is_detached ()
FILE *session_info_stream = NULL; FILE *session_info_stream = NULL;
char *dyn_buffer = NULL, *search_str = NULL; char *dyn_buffer = NULL, *search_str = NULL;
char *entry_pos, *nl_pos, *attached_pos; char *entry_pos;
int detached;
const int numstr_len = strlen (mplex_data); const int numstr_len = strlen (mplex_data);
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r"); /* get the number of attached clients for each session */
session_info_stream = popen ("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r");
if (!session_info_stream) if (!session_info_stream)
goto fail; goto fail;
dyn_buffer = read_into_dyn_buffer (session_info_stream); dyn_buffer = read_into_dyn_buffer (session_info_stream);
if (!dyn_buffer) if (!dyn_buffer)
goto fail; goto fail;
@ -268,13 +280,12 @@ static int tmux_is_detached ()
session_info_stream = NULL; session_info_stream = NULL;
/* prepare search string, for finding the current session's entry */ /* prepare search string, for finding the current session's entry */
search_str = (char*) malloc (numstr_len + 4); search_str = (char *) malloc (numstr_len + 2);
search_str[0] = '\n'; search_str[0] = '\n';
strcpy (search_str + 1, mplex_data); strcpy (search_str + 1, mplex_data);
strcat (search_str, ": ");
/* do the search */ /* 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; entry_pos = dyn_buffer;
else else
entry_pos = strstr (dyn_buffer, search_str); entry_pos = strstr (dyn_buffer, search_str);
@ -282,9 +293,8 @@ static int tmux_is_detached ()
if (! entry_pos) if (! entry_pos)
goto fail; goto fail;
/* find the next \n and look for the "(attached)" before it */ entry_pos = strchr (entry_pos, ' ') + 1;
nl_pos = strchr (entry_pos + 1, '\n'); detached = strncmp (entry_pos, "0\n", 2) == 0;
attached_pos = strstr (entry_pos + 1, "(attached)\n");
free (search_str); free (search_str);
search_str = NULL; search_str = NULL;
@ -292,15 +302,19 @@ static int tmux_is_detached ()
free (dyn_buffer); free (dyn_buffer);
dyn_buffer = NULL; dyn_buffer = NULL;
return attached_pos == NULL || attached_pos > nl_pos; return detached;
fail: fail:
if (session_info_stream) if (session_info_stream)
pclose (session_info_stream); pclose (session_info_stream);
if (dyn_buffer) if (dyn_buffer)
free (dyn_buffer); free (dyn_buffer);
if (search_str) if (search_str)
free (search_str); free (search_str);
return 0; return 0;
} }
@ -332,26 +346,21 @@ static void mplex_timer_handler (Tox *m)
current_status = tox_self_get_status (m); current_status = tox_self_get_status (m);
pthread_mutex_unlock (&Winthread.lock); pthread_mutex_unlock (&Winthread.lock);
if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) if (auto_away_active && current_status == TOX_USER_STATUS_AWAY && !detached) {
{
auto_away_active = false; auto_away_active = false;
new_status = prev_status; new_status = prev_status;
new_note = prev_note; 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; auto_away_active = true;
prev_status = current_status; prev_status = current_status;
new_status = TOX_USER_STATUS_AWAY; new_status = TOX_USER_STATUS_AWAY;
pthread_mutex_lock (&Winthread.lock); pthread_mutex_lock (&Winthread.lock);
size_t slen = tox_self_get_status_message_size(m); size_t slen = tox_self_get_status_message_size(m);
tox_self_get_status_message (m, (uint8_t*) prev_note); tox_self_get_status_message (m, (uint8_t *) prev_note);
prev_note[slen] = '\0'; prev_note[slen] = '\0';
pthread_mutex_unlock (&Winthread.lock); pthread_mutex_unlock (&Winthread.lock);
new_note = user_settings->mplex_away_note; new_note = user_settings->mplex_away_note;
} } else
else
return; return;
char argv[3][MAX_STR_SIZE]; char argv[3][MAX_STR_SIZE];

View File

@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stdarg.h>
#include <signal.h> #include <signal.h>
#include <locale.h> #include <locale.h>
#include <string.h> #include <string.h>
@ -64,7 +65,7 @@
#include "bootstrap.h" #include "bootstrap.h"
#ifdef X11 #ifdef X11
#include "xtra.h" #include "xtra.h"
#endif #endif
#ifdef AUDIO #ifdef AUDIO
@ -75,8 +76,13 @@
ToxAV *av; ToxAV *av;
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
#include "api.h"
#include "python_api.h"
#endif
#ifndef PACKAGE_DATADIR #ifndef PACKAGE_DATADIR
#define PACKAGE_DATADIR "." #define PACKAGE_DATADIR "."
#endif #endif
/* Export for use in Callbacks */ /* Export for use in Callbacks */
@ -104,9 +110,17 @@ static struct user_password {
int len; int len;
} user_password; } user_password;
static time_t last_signal_time;
static void catch_SIGINT(int sig) 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) static void catch_SIGSEGV(int sig)
@ -162,6 +176,10 @@ void exit_toxic_success(Tox *m)
terminate_audio(); terminate_audio();
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
terminate_python();
#endif /* PYTHON */
free_global_data(); free_global_data();
tox_kill(m); tox_kill(m);
endwin(); endwin();
@ -320,8 +338,10 @@ static int password_prompt(char *buf, int size)
/* eat overflowed stdin and return error */ /* eat overflowed stdin and return error */
if (buf[--len] != '\n') { if (buf[--len] != '\n') {
int ch; int ch;
while ((ch = getchar()) != '\n' && ch > 0) while ((ch = getchar()) != '\n' && ch > 0)
; ;
return 0; return 0;
} }
@ -338,6 +358,7 @@ static int password_eval(char *buf, int size)
/* Run password_eval command */ /* Run password_eval command */
FILE *f = popen(user_settings->password_eval, "r"); FILE *f = popen(user_settings->password_eval, "r");
if (f == NULL) { if (f == NULL) {
fprintf(stderr, "Executing password_eval failed\n"); fprintf(stderr, "Executing password_eval failed\n");
return 0; return 0;
@ -345,6 +366,7 @@ static int password_eval(char *buf, int size)
/* Get output from command */ /* Get output from command */
char *ret = fgets(buf, size, f); char *ret = fgets(buf, size, f);
if (ret == NULL) { if (ret == NULL) {
fprintf(stderr, "Reading password from password_eval command failed\n"); fprintf(stderr, "Reading password from password_eval command failed\n");
pclose(f); pclose(f);
@ -353,6 +375,7 @@ static int password_eval(char *buf, int size)
/* Get exit status */ /* Get exit status */
int status = pclose(f); int status = pclose(f);
if (status != 0) { if (status != 0) {
fprintf(stderr, "password_eval command returned error %d\n", status); fprintf(stderr, "password_eval command returned error %d\n", status);
return 0; return 0;
@ -360,6 +383,7 @@ static int password_eval(char *buf, int size)
/* Removez whitespace or \n at end */ /* Removez whitespace or \n at end */
int i, len = strlen(buf); int i, len = strlen(buf);
for (i = len - 1; i > 0 && isspace(buf[i]); i--) { for (i = len - 1; i > 0 && isspace(buf[i]); i--) {
buf[i] = 0; buf[i] = 0;
len--; len--;
@ -376,9 +400,10 @@ static void first_time_encrypt(const char *msg)
do { do {
system("clear"); system("clear");
printf("%s ", msg); printf("%s ", msg);
fflush(stdout);
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n") if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n")) || !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
break; break;
} while (fgets(ch, sizeof(ch), stdin)); } while (fgets(ch, sizeof(ch), stdin));
@ -396,6 +421,7 @@ static void first_time_encrypt(const char *msg)
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN); printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
while (valid_password == false) { while (valid_password == false) {
fflush(stdout); // Flush all before user input
len = password_prompt(user_password.pass, sizeof(user_password.pass)); len = password_prompt(user_password.pass, sizeof(user_password.pass));
user_password.len = len; user_password.len = len;
@ -514,24 +540,23 @@ int store_data(Tox *m, const char *path)
static void init_tox_callbacks(Tox *m) static void init_tox_callbacks(Tox *m)
{ {
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange, NULL); tox_callback_self_connection_status(m, prompt_onSelfConnectionChange);
tox_callback_friend_connection_status(m, on_connectionchange, NULL); tox_callback_friend_connection_status(m, on_connectionchange);
tox_callback_friend_typing(m, on_typing_change, NULL); tox_callback_friend_typing(m, on_typing_change);
tox_callback_friend_request(m, on_request, NULL); tox_callback_friend_request(m, on_request);
tox_callback_friend_message(m, on_message, NULL); tox_callback_friend_message(m, on_message);
tox_callback_friend_name(m, on_nickchange, NULL); tox_callback_friend_name(m, on_nickchange);
tox_callback_friend_status(m, on_statuschange, NULL); tox_callback_friend_status(m, on_statuschange);
tox_callback_friend_status_message(m, on_statusmessagechange, NULL); tox_callback_friend_status_message(m, on_statusmessagechange);
tox_callback_friend_read_receipt(m, on_read_receipt, NULL); tox_callback_friend_read_receipt(m, on_read_receipt);
tox_callback_group_invite(m, on_groupinvite, NULL); tox_callback_conference_invite(m, on_groupinvite);
tox_callback_group_message(m, on_groupmessage, NULL); tox_callback_conference_message(m, on_groupmessage);
tox_callback_group_action(m, on_groupaction, NULL); tox_callback_conference_namelist_change(m, on_group_namelistchange);
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL); tox_callback_conference_title(m, on_group_titlechange);
tox_callback_group_title(m, on_group_titlechange, NULL); tox_callback_file_recv(m, on_file_recv);
tox_callback_file_recv(m, on_file_recv, NULL); tox_callback_file_chunk_request(m, on_file_chunk_request);
tox_callback_file_chunk_request(m, on_file_chunk_request, NULL); tox_callback_file_recv_control(m, on_file_control);
tox_callback_file_recv_control(m, on_file_control, NULL); tox_callback_file_recv_chunk(m, on_file_recv_chunk);
tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL);
} }
static void init_tox_options(struct Tox_Options *tox_opts) static void init_tox_options(struct Tox_Options *tox_opts)
@ -554,7 +579,7 @@ static void init_tox_options(struct Tox_Options *tox_opts)
tox_opts->proxy_host = arg_opts.proxy_address; tox_opts->proxy_host = arg_opts.proxy_address;
const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP"; const char *ps = tox_opts->proxy_type == 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); snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port);
queue_init_message("%s", tmp); queue_init_message("%s", tmp);
} }
@ -612,6 +637,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
size_t pwlen = 0; size_t pwlen = 0;
int pweval = user_settings->password_eval[0]; int pweval = user_settings->password_eval[0];
if (!pweval) { if (!pweval) {
system("clear"); // TODO: is this portable? system("clear"); // TODO: is this portable?
printf("Enter password (q to quit) "); printf("Enter password (q to quit) ");
@ -621,11 +647,14 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
char plain[plain_len]; char plain[plain_len];
while (true) { while (true) {
fflush(stdout); // Flush before prompts so the user sees the question/message
if (pweval) { if (pweval) {
pwlen = password_eval(user_password.pass, sizeof(user_password.pass)); pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
} else { } else {
pwlen = password_prompt(user_password.pass, sizeof(user_password.pass)); pwlen = password_prompt(user_password.pass, sizeof(user_password.pass));
} }
user_password.len = pwlen; user_password.len = pwlen;
if (strcasecmp(user_password.pass, "q") == 0) { if (strcasecmp(user_password.pass, "q") == 0) {
@ -739,7 +768,7 @@ static void do_toxic(Tox *m)
return; return;
} }
tox_iterate(m); tox_iterate(m, NULL);
do_tox_connection(m); do_tox_connection(m);
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
@ -785,7 +814,7 @@ void *thread_cqueue(void *data)
ToxWindow *toxwin = get_window_ptr(i); ToxWindow *toxwin = get_window_ptr(i);
if (toxwin != NULL && toxwin->is_chat if (toxwin != NULL && toxwin->is_chat
&& tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE) && tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE)
cqueue_try_send(toxwin, m); cqueue_try_send(toxwin, m);
} }
@ -828,6 +857,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, --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, " -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, " -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) static void set_default_opts(void)
@ -858,10 +894,11 @@ static void parse_args(int argc, char *argv[])
{"SOCKS5-proxy", required_argument, 0, 'p'}, {"SOCKS5-proxy", required_argument, 0, 'p'},
{"HTTP-proxy", required_argument, 0, 'P'}, {"HTTP-proxy", required_argument, 0, 'P'},
{"unencrypt-data", no_argument, 0, 'u'}, {"unencrypt-data", no_argument, 0, 'u'},
{"version", no_argument, 0, 'v'},
{NULL, no_argument, NULL, 0}, {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; int opt, indexptr;
long int port = 0; long int port = 0;
@ -933,10 +970,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5; arg_opts.proxy_type = TOX_PROXY_TYPE_SOCKS5;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-') if (++optind > argc || argv[optind - 1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
port = strtol(argv[optind-1], NULL, 10); port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -948,10 +985,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP; arg_opts.proxy_type = TOX_PROXY_TYPE_HTTP;
snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg); snprintf(arg_opts.proxy_address, sizeof(arg_opts.proxy_address), "%s", optarg);
if (++optind > argc || argv[optind-1][0] == '-') if (++optind > argc || argv[optind - 1][0] == '-')
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
port = strtol(argv[optind-1], NULL, 10); port = strtol(argv[optind - 1], NULL, 10);
if (port <= 0 || port > MAX_PORT_RANGE) if (port <= 0 || port > MAX_PORT_RANGE)
exit_toxic_err("Proxy error", FATALERR_PROXY); exit_toxic_err("Proxy error", FATALERR_PROXY);
@ -984,6 +1021,10 @@ static void parse_args(int argc, char *argv[])
arg_opts.unencrypt_data = 1; arg_opts.unencrypt_data = 1;
break; break;
case 'v':
print_version();
exit(EXIT_SUCCESS);
case 'h': case 'h':
default: default:
print_usage(); print_usage();
@ -1077,7 +1118,7 @@ static void init_default_data_files(void)
// this doesn't do anything (yet) // this doesn't do anything (yet)
#ifdef X11 #ifdef X11
void DnD_callback(const char* asdv, DropType dt) void DnD_callback(const char *asdv, DropType dt)
{ {
// if (dt != DT_plain) // if (dt != DT_plain)
// return; // return;
@ -1124,7 +1165,7 @@ int main(int argc, char **argv)
const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL; const char *p = arg_opts.config_path[0] ? arg_opts.config_path : NULL;
if (settings_load(user_settings, p) == -1) { 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); int curl_init = curl_global_init(CURL_GLOBAL_ALL);
@ -1139,8 +1180,10 @@ int main(int argc, char **argv)
} }
#ifdef X11 #ifdef X11
if (init_xtra(DnD_callback) == -1) if (init_xtra(DnD_callback) == -1)
queue_init_message("X failed to initialize"); queue_init_message("X failed to initialize");
#endif #endif
Tox *m = load_toxic(DATA_FILE); Tox *m = load_toxic(DATA_FILE);
@ -1182,11 +1225,19 @@ int main(int argc, char **argv)
set_primary_device(output, user_settings->audio_out_dev); set_primary_device(output, user_settings->audio_out_dev);
#elif SOUND_NOTIFY #elif SOUND_NOTIFY
if ( init_devices() == de_InternalError ) if ( init_devices() == de_InternalError )
queue_init_message("Failed to init audio devices"); queue_init_message("Failed to init audio devices");
#endif /* AUDIO */ #endif /* AUDIO */
#ifdef PYTHON
init_python(m);
invoke_autoruns(prompt->chatwin->history, prompt);
#endif /* PYTHON */
init_notify(60, 3000); init_notify(60, 3000);
/* screen/tmux auto-away timer */ /* screen/tmux auto-away timer */
@ -1206,7 +1257,7 @@ int main(int argc, char **argv)
cleanup_init_messages(); cleanup_init_messages();
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */ /* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
char avatarstr[MAX_STR_SIZE]; char avatarstr[PATH_MAX + 11];
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path); snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path);
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE); execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
@ -1219,8 +1270,10 @@ int main(int argc, char **argv)
if (timed_out(last_save, AUTOSAVE_FREQ)) { if (timed_out(last_save, AUTOSAVE_FREQ)) {
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (store_data(m, DATA_FILE) != 0) if (store_data(m, DATA_FILE) != 0)
line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file"); line_info_add(prompt, NULL, NULL, NULL, SYS_MSG, 0, RED, "WARNING: Failed to save to data file");
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
last_save = cur_time; last_save = cur_time;

View File

@ -107,18 +107,23 @@ int store_data(Tox *m, const char *path);
/* callbacks */ /* callbacks */
void on_request(Tox *m, const uint8_t *public_key, const uint8_t *data, size_t length, void *userdata); void on_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_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_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_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_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_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_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_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata); const uint8_t *message, size_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_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata); size_t length, void *userdata);
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void *userdata); void on_group_namelistchange(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length, void *userdata); void *userdata);
void on_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, void on_file_recv_chunk(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, const uint8_t *data,
size_t length, void *userdata); size_t length, void *userdata);
void on_file_control (Tox *m, uint32_t friendnumber, uint32_t filenumber, TOX_FILE_CONTROL control, void *userdata); void on_file_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; int i = ctx->pos, count = 0;
/* traverse past empty space */ /* traverse past empty space */
while (i > 0 && ctx->line[i-1] == L' ') { while (i > 0 && ctx->line[i - 1] == L' ') {
++count; ++count;
--i; --i;
} }
/* traverse past last entered word */ /* traverse past last entered word */
while (i > 0 && ctx->line[i-1] != L' ') { while (i > 0 && ctx->line[i - 1] != L' ') {
++count; ++count;
--i; --i;
} }
@ -243,18 +243,20 @@ void fetch_hist_item(ChatContext *ctx, int key_dir)
ctx->len = h_len; ctx->len = h_len;
} }
void strsubst(char* str, char old, char new) void strsubst(char *str, char old, char new)
{ {
int i; int i;
for (i = 0; str[i] != '\0'; ++i) for (i = 0; str[i] != '\0'; ++i)
if (str[i] == old) if (str[i] == old)
str[i] = new; str[i] = new;
} }
void wstrsubst(wchar_t* str, wchar_t old, wchar_t new) void wstrsubst(wchar_t *str, wchar_t old, wchar_t new)
{ {
int i; int i;
for (i = 0; str[i] != L'\0'; ++i) for (i = 0; str[i] != L'\0'; ++i)
if (str[i] == old) if (str[i] == old)
str[i] = new; str[i] = new;
} }

View File

@ -49,7 +49,7 @@ void reset_buf(ChatContext *ctx);
Return 0 on success, -1 if yank buffer is empty or too long */ Return 0 on success, -1 if yank buffer is empty or too long */
int yank_buf(ChatContext *ctx); int yank_buf(ChatContext *ctx);
/* 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. until we find a space or run out of characters.
Return 0 on success, -1 if no line or already at the beginning */ Return 0 on success, -1 if no line or already at the beginning */
int del_word_buf(ChatContext *ctx); int del_word_buf(ChatContext *ctx);

View File

@ -40,13 +40,13 @@
#define default_video_bit_rate 5000 #define default_video_bit_rate 5000
void receive_video_frame_cb( ToxAV *av, uint32_t friend_number, void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height, uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data ); void *user_data );
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data); uint32_t video_bit_rate, void *user_data);
static void print_err (ToxWindow *self, const char *error_str) static void print_err (ToxWindow *self, const char *error_str)
{ {
@ -82,22 +82,24 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
void terminate_video() void terminate_video()
{ {
int i; int i;
for (i = 0; i < MAX_CALLS; ++i) { for (i = 0; i < MAX_CALLS; ++i) {
Call* this_call = &CallControl.calls[i]; Call *this_call = &CallControl.calls[i];
stop_video_transmission(this_call, i); stop_video_transmission(this_call, i);
if( this_call->vout_idx != -1 ) if ( this_call->vout_idx != -1 )
close_video_device(vdt_output, this_call->vout_idx); close_video_device(vdt_output, this_call->vout_idx);
} }
terminate_video_devices(); terminate_video_devices();
} }
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 */ uint32_t friend_number = *((uint32_t *)data); /* TODO: Or pass an array of call_idx's */
Call* this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_SEND_FRAME error; TOXAV_ERR_SEND_FRAME error;
/* Drop frame if video sending is disabled */ /* Drop frame if video sending is disabled */
@ -117,9 +119,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, 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, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data); write_video_out(width, height, y, u, v, ystride, ustride, vstride, user_data);
} }
@ -175,16 +177,16 @@ int stop_video_transmission(Call *call, int friend_number)
* Callbacks * Callbacks
*/ */
void receive_video_frame_cb(ToxAV *av, uint32_t friend_number, void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
uint16_t width, uint16_t height, uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data); write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
} }
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
uint32_t video_bit_rate, void *user_data) uint32_t video_bit_rate, void *user_data)
{ {
CallControl.video_bit_rate = video_bit_rate; CallControl.video_bit_rate = video_bit_rate;
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL); toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
@ -192,7 +194,7 @@ void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_
void callback_recv_video_starting(uint32_t friend_number) void callback_recv_video_starting(uint32_t friend_number)
{ {
Call* this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
if ( this_call->vout_idx != -1 ) if ( this_call->vout_idx != -1 )
return; return;
@ -201,21 +203,22 @@ void callback_recv_video_starting(uint32_t friend_number)
} }
void callback_recv_video_end(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); close_video_device(vdt_output, this_call->vout_idx);
this_call->vout_idx = -1; this_call->vout_idx = -1;
} }
void callback_video_starting(uint32_t friend_number) void callback_video_starting(uint32_t friend_number)
{ {
ToxWindow* windows = CallControl.prompt; ToxWindow *windows = CallControl.prompt;
Call* this_call = &CallControl.calls[friend_number]; Call *this_call = &CallControl.calls[friend_number];
TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK; TOXAV_ERR_CALL_CONTROL error = TOXAV_ERR_CALL_CONTROL_OK;
toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error); toxav_call_control(CallControl.av, friend_number, TOXAV_CALL_CONTROL_SHOW_VIDEO, &error);
if (error == TOXAV_ERR_CALL_CONTROL_OK) { if (error == TOXAV_ERR_CALL_CONTROL_OK) {
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if ( windows[i].is_call && windows[i].num == friend_number ) { if ( windows[i].is_call && windows[i].num == friend_number ) {
if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) { if ( 0 != start_video_transmission(&windows[i], CallControl.av, this_call) ) {
@ -244,7 +247,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]) void cmd_video(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
{ {
const char *error_str; const char *error_str;
Call* this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if ( argc != 0 ) { if ( argc != 0 ) {
error_str = "Unknown arguments."; error_str = "Unknown arguments.";
@ -387,18 +390,18 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
} }
if ( video_selection_valid(type, selection) == vde_InvalidSelection ) { if ( video_selection_valid(type, selection) == vde_InvalidSelection ) {
error_str="Invalid selection!"; error_str = "Invalid selection!";
goto on_error; goto on_error;
} }
/* If call is active, change device */ /* If call is active, change device */
if ( self->is_call ) { if ( self->is_call ) {
Call* this_call = &CallControl.calls[self->num]; Call *this_call = &CallControl.calls[self->num];
if ( this_call->ttas ) { if ( this_call->ttas ) {
if ( type == vdt_output ) { if ( type == vdt_output ) {
} } else {
else {
/* TODO: check for failure */ /* TODO: check for failure */
close_video_device(vdt_input, this_call->vin_idx); close_video_device(vdt_input, this_call->vin_idx);
open_video_device(vdt_input, selection, &this_call->vin_idx); open_video_device(vdt_input, selection, &this_call->vin_idx);
@ -410,7 +413,7 @@ void cmd_ccur_video_device(WINDOW *window, ToxWindow *self, Tox *m, int argc, ch
self->video_device_selection[type] = selection; self->video_device_selection[type] = selection;
return; return;
on_error: on_error:
print_err (self, error_str); print_err (self, error_str);
} }

View File

@ -30,14 +30,18 @@
#include <vpx/vpx_image.h> #include <vpx/vpx_image.h>
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__OSX__)
#import "osx_video.h"
#else
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mman.h> #include <sys/mman.h>
#include <fcntl.h> #include <fcntl.h>
#if defined(__linux__)
#include <linux/videodev2.h> #include <linux/videodev2.h>
#else /* __OSX__ */ #else
#import "osx_video.h" #include <sys/videoio.h>
#endif
#endif #endif
#include "line_info.h" #include "line_info.h"
@ -62,10 +66,10 @@ struct VideoBuffer {
typedef struct VideoDevice { typedef struct VideoDevice {
VideoDataHandleCallback cb; /* Use this to handle data from input device usually */ 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 */ int32_t friend_number; /* ToxAV friend number */
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
int fd; /* File descriptor of video device selected/opened */ int fd; /* File descriptor of video device selected/opened */
struct v4l2_format fmt; struct v4l2_format fmt;
struct VideoBuffer *buffers; struct VideoBuffer *buffers;
@ -93,7 +97,7 @@ VideoDevice *video_devices_running[2][MAX_DEVICES] = {{NULL}}; /* Running de
uint32_t primary_video_device[2]; /* Primary device */ uint32_t primary_video_device[2]; /* Primary device */
#ifdef VIDEO #ifdef VIDEO
static ToxAV* av = NULL; static ToxAV *av = NULL;
#endif /* VIDEO */ #endif /* VIDEO */
/* q_mutex */ /* q_mutex */
@ -102,15 +106,16 @@ static ToxAV* av = NULL;
pthread_mutex_t video_mutex; pthread_mutex_t video_mutex;
bool video_thread_running = true, 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, static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
const uint8_t *u, const uint8_t *v, unsigned int ystride, const uint8_t *u, const uint8_t *v, unsigned int ystride,
unsigned int ustride, unsigned int vstride, uint8_t *out) unsigned int ustride, unsigned int vstride, uint8_t *out)
{ {
unsigned long int i, j; unsigned long int i, j;
for (i = 0; i < height; ++i) { for (i = 0; i < height; ++i) {
for (j = 0; j < width; ++j) { for (j = 0; j < width; ++j) {
uint8_t *point = out + 4 * ((i * 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 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; int b = (298 * (t_y - 16) + 516 * (t_u - 128) + 128) >> 8;
point[2] = r>255? 255 : r<0 ? 0 : r; point[2] = r > 255 ? 255 : r < 0 ? 0 : r;
point[1] = g>255? 255 : g<0 ? 0 : g; point[1] = g > 255 ? 255 : g < 0 ? 0 : g;
point[0] = b>255? 255 : b<0 ? 0 : b; point[0] = b > 255 ? 255 : b < 0 ? 0 : b;
point[3] = ~0; point[3] = ~0;
} }
} }
} }
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
uint8_t *input, uint16_t width, uint16_t height) uint8_t *input, uint16_t width, uint16_t height)
{ {
uint8_t *end = input + width * height * 2; uint8_t *end = input + width * height * 2;
while (input != end) { while (input != end) {
uint8_t *line_end = input + width * 2; uint8_t *line_end = input + width * 2;
while (input != line_end) { while (input != line_end) {
*plane_y++ = *input++; *plane_y++ = *input++;
*plane_u++ = *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; line_end = input + width * 2;
while (input != line_end) { while (input != line_end) {
*plane_y++ = *input++; *plane_y++ = *input++;
input++;//u input++;//u
@ -166,56 +174,59 @@ static int xioctl(int fh, unsigned long request, void *arg)
return r; return r;
} }
#endif /* __linux__ */ #endif
/* Meet devices */ /* Meet devices */
#ifdef VIDEO #ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV* av_) VideoDeviceError init_video_devices(ToxAV *av_)
#else #else
VideoDeviceError init_video_devices() VideoDeviceError init_video_devices()
#endif /* VIDEO */ #endif /* VIDEO */
{ {
size[vdt_input] = 0; 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]) { for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
int fd; int fd;
char device_address[] = "/dev/videoXX"; char device_address[] = "/dev/videoXX";
snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]); snprintf(device_address + 10, sizeof(char) * strlen(device_address) - 10, "%i", size[vdt_input]);
fd = open(device_address, O_RDWR | O_NONBLOCK, 0); fd = open(device_address, O_RDWR | O_NONBLOCK, 0);
if ( fd == -1 ) { if ( fd == -1 ) {
break; break;
} else { } else {
struct v4l2_capability cap; struct v4l2_capability cap;
char* video_input_name; char *video_input_name;
/* Query V4L for capture capabilities */ /* Query V4L for capture capabilities */
if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) { if ( -1 != ioctl(fd, VIDIOC_QUERYCAP, &cap) ) {
video_input_name = (char*)malloc(strlen((const char*)cap.card) + strlen(device_address) + 4); video_input_name = (char *)malloc(strlen((const char *)cap.card) + strlen(device_address) + 4);
strcpy(video_input_name, (char*)cap.card); strcpy(video_input_name, (char *)cap.card);
strcat(video_input_name, " ("); strcat(video_input_name, " (");
strcat(video_input_name, (char*)device_address); strcat(video_input_name, (char *)device_address);
strcat(video_input_name, ")"); strcat(video_input_name, ")");
} else { } else {
video_input_name = (char*)malloc(strlen(device_address) + 3); video_input_name = (char *)malloc(strlen(device_address) + 3);
strcpy(video_input_name, "("); strcpy(video_input_name, "(");
strcat(video_input_name, device_address); strcat(video_input_name, device_address);
strcat(video_input_name, ")"); strcat(video_input_name, ")");
} }
video_devices_names[vdt_input][size[vdt_input]] = video_input_name; video_devices_names[vdt_input][size[vdt_input]] = video_input_name;
close(fd); close(fd);
} }
} }
#else /* __OSX__ */
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
return vde_InternalError;
#endif #endif
size[vdt_output] = 1; 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; video_devices_names[vdt_output][0] = video_output_name;
// Start poll thread // Start poll thread
@ -223,6 +234,7 @@ VideoDeviceError init_video_devices()
return vde_InternalError; return vde_InternalError;
pthread_t thread_id; pthread_t thread_id;
if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 ) if ( pthread_create(&thread_id, NULL, video_thread_poll, NULL) != 0 || pthread_detach(thread_id) != 0 )
return vde_InternalError; return vde_InternalError;
@ -243,8 +255,9 @@ VideoDeviceError terminate_video_devices()
usleep(20000); usleep(20000);
int i; int i;
for (i = 0; i < size[vdt_input]; ++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 ) 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, 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 defined(__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;
#else /* __OSX__ */
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] ) if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
return vde_InvalidSelection; 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 #endif
lock; lock;
@ -286,7 +304,7 @@ VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selectio
return vde_None; 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); 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); 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; 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; device->selection = selection;
if ( pthread_mutex_init(device->mutex, NULL) != 0 ) { 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 ) { if ( type == vdt_input ) {
video_thread_paused = true; 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 */ /* Open selected device */
char device_address[] = "/dev/videoXX"; char device_address[] = "/dev/videoXX";
snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection); snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection);
device->fd = open(device_address, O_RDWR); device->fd = open(device_address, O_RDWR);
if ( device->fd == -1 ) { if ( device->fd == -1 ) {
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
@ -352,6 +377,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
/* Obtain video device capabilities */ /* Obtain video device capabilities */
struct v4l2_capability cap; struct v4l2_capability cap;
if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) { if ( -1 == xioctl(device->fd, VIDIOC_QUERYCAP, &cap) ) {
close(device->fd); close(device->fd);
free(device); free(device);
@ -365,7 +391,8 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
if( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) {
if ( -1 == xioctl(device->fd, VIDIOC_S_FMT, &fmt) ) {
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -381,6 +408,7 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
req.count = 4; req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP; req.memory = V4L2_MEMORY_MMAP;
if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) { if ( -1 == xioctl(device->fd, VIDIOC_REQBUFS, &req) ) {
close(device->fd); close(device->fd);
free(device); free(device);
@ -414,20 +442,22 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
device->buffers[i].length = buf.length; device->buffers[i].length = buf.length;
device->buffers[i].start = mmap(NULL /* start anywhere */, device->buffers[i].start = mmap(NULL /* start anywhere */,
buf.length, buf.length,
PROT_READ | PROT_WRITE /* required */, PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */, MAP_SHARED /* recommended */,
device->fd, buf.m.offset); device->fd, buf.m.offset);
if ( MAP_FAILED == device->buffers[i].start ) { if ( MAP_FAILED == device->buffers[i].start ) {
for (i = 0; i < buf.index; ++i) for (i = 0; i < buf.index; ++i)
munmap(device->buffers[i].start, device->buffers[i].length); munmap(device->buffers[i].start, device->buffers[i].length);
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
} }
device->n_buffers = i; device->n_buffers = i;
enum v4l2_buf_type type; 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) ) { if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
for (i = 0; i < device->n_buffers; ++i) for (i = 0; i < device->n_buffers; ++i)
munmap(device->buffers[i].start, device->buffers[i].length); munmap(device->buffers[i].start, device->buffers[i].length);
close(device->fd); close(device->fd);
free(device); free(device);
unlock; unlock;
@ -459,12 +490,6 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
return vde_FailedStart; 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 #endif
/* Create X11 window associated to device */ /* 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); int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen), device->video_width, device->video_height, 0, BlackPixel(device->x_display, screen),
BlackPixel(device->x_display, screen))) ) { BlackPixel(device->x_display, screen))) ) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
XStoreName(device->x_display, device->x_window, "Video Preview"); XStoreName(device->x_display, device->x_window, "Video Preview");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask); XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_input, temp_idx); close_video_device(vdt_input, temp_idx);
@ -517,14 +542,14 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
int screen = DefaultScreen(device->x_display); int screen = DefaultScreen(device->x_display);
if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0, if ( !(device->x_window = XCreateSimpleWindow(device->x_display, RootWindow(device->x_display, screen), 0, 0,
100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) { 100, 100, 0, BlackPixel(device->x_display, screen), BlackPixel(device->x_display, screen))) ) {
close_video_device(vdt_output, temp_idx); close_video_device(vdt_output, temp_idx);
unlock; unlock;
return vde_FailedStart; return vde_FailedStart;
} }
XStoreName(device->x_display, device->x_window, "Video Receive"); XStoreName(device->x_display, device->x_window, "Video Receive");
XSelectInput(device->x_display, device->x_window, ExposureMask|ButtonPressMask|KeyPressMask); XSelectInput(device->x_display, device->x_window, ExposureMask | ButtonPressMask | KeyPressMask);
if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) { if ( (device->x_gc = DefaultGC(device->x_display, screen)) == NULL ) {
close_video_device(vdt_output, temp_idx); close_video_device(vdt_output, temp_idx);
@ -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, __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
uint8_t const *y, uint8_t const *u, uint8_t const *v, uint8_t const *y, uint8_t const *u, uint8_t const *v,
int32_t ystride, int32_t ustride, int32_t vstride, int32_t ystride, int32_t ustride, int32_t vstride,
void *user_data) void *user_data)
{ {
VideoDevice* device = video_devices_running[vdt_output][0]; VideoDevice *device = video_devices_running[vdt_output][0];
if ( !device ) return vde_DeviceNotActive; if ( !device ) return vde_DeviceNotActive;
if( !device->x_window ) return vde_DeviceNotActive; if ( !device->x_window ) return vde_DeviceNotActive;
pthread_mutex_lock(device->mutex); pthread_mutex_lock(device->mutex);
@ -594,7 +619,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
.red_mask = 0xFF0000, .red_mask = 0xFF0000,
.green_mask = 0xFF00, .green_mask = 0xFF00,
.blue_mask = 0xFF, .blue_mask = 0xFF,
.data = (char*)img_data .data = (char *)img_data
}; };
/* Render image data */ /* Render image data */
@ -609,7 +634,7 @@ __inline VideoDeviceError write_video_out(uint16_t width, uint16_t height,
return vde_None; return vde_None;
} }
void* video_thread_poll (void* arg) // TODO: maybe use thread for every input source void *video_thread_poll (void *arg) // TODO: maybe use thread for every input source
{ {
/* /*
* NOTE: We only need to poll input devices for data. * NOTE: We only need to poll input devices for data.
@ -619,26 +644,34 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
while (1) { while (1) {
lock; lock;
if (!video_thread_running) { if (!video_thread_running) {
unlock; unlock;
break; break;
} }
unlock; unlock;
if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */ if ( video_thread_paused ) usleep(10000); /* Wait for unpause. */
else { else {
for (i = 0; i < size[vdt_input]; ++i) { for (i = 0; i < size[vdt_input]; ++i) {
lock; lock;
if ( video_devices_running[vdt_input][i] != NULL ) { if ( video_devices_running[vdt_input][i] != NULL ) {
/* Obtain frame image data from device buffers */ /* Obtain frame image data from device buffers */
VideoDevice* device = video_devices_running[vdt_input][i]; VideoDevice *device = video_devices_running[vdt_input][i];
uint16_t video_width = device->video_width; uint16_t video_width = device->video_width;
uint16_t video_height = device->video_height; uint16_t video_height = device->video_height;
uint8_t *y = device->input.planes[0]; uint8_t *y = device->input.planes[0];
uint8_t *u = device->input.planes[1]; uint8_t *u = device->input.planes[1];
uint8_t *v = device->input.planes[2]; uint8_t *v = device->input.planes[2];
#if defined(__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; struct v4l2_buffer buf;
memset(&(buf), 0, sizeof(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; 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 */ /* Convert frame image data to YUV420 for ToxAV */
yuv422to420(y, u, v, data, video_width, video_height); 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 #endif
/* Send frame data to friend through ToxAV */ /* 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 */ /* Convert YUV420 data to BGR */
uint8_t *img_data = malloc(video_width * video_height * 4); uint8_t *img_data = malloc(video_width * video_height * 4);
yuv420tobgr(video_width, video_height, y, u, v, 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 */ /* Allocate image data in X11 */
XImage image = { XImage image = {
@ -685,7 +713,7 @@ void* video_thread_poll (void* arg) // TODO: maybe use thread for every input so
.red_mask = 0xFF0000, .red_mask = 0xFF0000,
.green_mask = 0xFF00, .green_mask = 0xFF00,
.blue_mask = 0xFF, .blue_mask = 0xFF,
.data = (char*)img_data .data = (char *)img_data
}; };
/* Render image 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); XFlush(device->x_display);
free(img_data); free(img_data);
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) { if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
unlock; unlock;
continue; continue;
} }
#endif /* __linux__ */
#endif /* __linux__ / BSD */
} }
unlock; unlock;
} }
usleep(1000 * 1000 / 24); usleep(1000 * 1000 / 24);
} }
} }
@ -731,19 +763,23 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
if ( !device->ref_count ) { if ( !device->ref_count ) {
if ( type == vdt_input ) { 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; enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
int i; int i;
for (i = 0; i < device->n_buffers; ++i) { for (i = 0; i < device->n_buffers; ++i) {
if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) { if ( -1 == munmap(device->buffers[i].start, device->buffers[i].length) ) {
} }
} }
close(device->fd); close(device->fd);
#else /* __OSX__ */
osx_video_close_device(device_idx);
#endif #endif
vpx_img_free(&device->input); vpx_img_free(&device->input);
XDestroyWindow(device->x_display, device->x_window); 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); XCloseDisplay(device->x_display);
pthread_mutex_destroy(device->mutex); pthread_mutex_destroy(device->mutex);
#if defined(__linux__) || defined(__FreeBSD__) #if defined(__linux__) || SYSTEM == BSD
free(device->buffers); free(device->buffers);
#endif /* __linux__ */ #endif /* __linux__ / BSD */
free(device); free(device);
} else { } else {
@ -765,14 +801,13 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
free(device); free(device);
} }
} } else device->ref_count--;
else device->ref_count--;
unlock; unlock;
return rc; return rc;
} }
void print_video_devices(ToxWindow* self, VideoDeviceType type) void print_video_devices(ToxWindow *self, VideoDeviceType type)
{ {
int i; int i;

View File

@ -45,10 +45,11 @@ typedef enum VideoDeviceError {
vde_CaptureError = -9, vde_CaptureError = -9,
} VideoDeviceError; } VideoDeviceError;
typedef void (*VideoDataHandleCallback) (int16_t width, int16_t height, const uint8_t* y, const uint8_t* u, 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 #ifdef VIDEO
VideoDeviceError init_video_devices(ToxAV* av); VideoDeviceError init_video_devices(ToxAV *av);
#else #else
VideoDeviceError init_video_devices(); VideoDeviceError init_video_devices();
#endif /* VIDEO */ #endif /* VIDEO */
@ -56,20 +57,22 @@ VideoDeviceError init_video_devices();
VideoDeviceError terminate_video_devices(); VideoDeviceError terminate_video_devices();
/* Callback handles ready data from INPUT device */ /* Callback handles ready data from INPUT device */
VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback, void* data); VideoDeviceError register_video_device_callback(int32_t call_idx, uint32_t device_idx, VideoDataHandleCallback callback,
void* get_video_device_callback_data(uint32_t device_idx); void *data);
void *get_video_device_callback_data(uint32_t device_idx);
VideoDeviceError set_primary_video_device(VideoDeviceType type, int32_t selection); 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 */ /* 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 */ /* Stop device */
VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx); VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx);
/* Write data to device */ /* 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); void get_primary_video_device_name(VideoDeviceType type, char *buf, int size);
VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection); VideoDeviceError video_selection_valid(VideoDeviceType type, int32_t selection);

View File

@ -151,8 +151,8 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
store_data(m, DATA_FILE); store_data(m, DATA_FILE);
} }
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
void *userdata) const uint8_t *message, size_t length, void *userdata)
{ {
char msg[MAX_STR_SIZE + 1]; char msg[MAX_STR_SIZE + 1];
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length); length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
@ -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) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
if (windows[i].onGroupMessage != NULL) 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 on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
void *userdata) size_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)
{ {
size_t i; size_t i;
@ -190,7 +176,8 @@ 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, uint32_t peernumber, TOX_CONFERENCE_STATE_CHANGE change,
void *userdata)
{ {
size_t i; size_t i;
@ -200,7 +187,7 @@ void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t ch
} }
} }
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length, void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
void *userdata) void *userdata)
{ {
char data[MAX_STR_SIZE + 1]; char data[MAX_STR_SIZE + 1];
@ -445,10 +432,12 @@ void on_window_resize(void)
} }
#ifdef AUDIO #ifdef AUDIO
if (w->chatwin->infobox.active) { if (w->chatwin->infobox.active) {
delwin(w->chatwin->infobox.win); delwin(w->chatwin->infobox.win);
w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH); w->chatwin->infobox.win = newwin(INFOBOX_HEIGHT, INFOBOX_WIDTH + 1, 1, x2 - INFOBOX_WIDTH);
} }
#endif /* AUDIO */ #endif /* AUDIO */
scrollok(w->chatwin->history, 0); scrollok(w->chatwin->history, 0);
@ -458,20 +447,24 @@ void on_window_resize(void)
static void draw_window_tab(ToxWindow *toxwin) static void draw_window_tab(ToxWindow *toxwin)
{ {
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert)); if (toxwin->alert != WINDOW_ALERT_NONE) attron(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
clrtoeol(); clrtoeol();
printw(" [%s]", toxwin->name); printw(" [%s]", toxwin->name);
pthread_mutex_lock(&Winthread.lock); pthread_mutex_lock(&Winthread.lock);
if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert)); if (toxwin->alert != WINDOW_ALERT_NONE) attroff(COLOR_PAIR(toxwin->alert));
pthread_mutex_unlock(&Winthread.lock); pthread_mutex_unlock(&Winthread.lock);
} }
static void draw_bar(void) static void draw_bar(void)
{ {
int y,x; int y, x;
// save current cursor position // save current cursor position
getyx(active_window->window, y, x); getyx(active_window->window, y, x);
@ -482,10 +475,6 @@ static void draw_bar(void)
move(LINES - 1, 0); move(LINES - 1, 0);
attron(COLOR_PAIR(BLUE) | A_BOLD);
printw(" TOXIC " TOXICVER " |");
attroff(COLOR_PAIR(BLUE) | A_BOLD);
size_t i; size_t i;
for (i = 0; i < MAX_WINDOWS_NUM; ++i) { for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
@ -595,6 +584,12 @@ ToxWindow *get_window_ptr(int i)
return toxwin; return toxwin;
} }
/* returns a pointer to the currently open ToxWindow. */
ToxWindow *get_active_window(void)
{
return active_window;
}
void force_refresh(WINDOW *w) void force_refresh(WINDOW *w)
{ {
wclear(w); wclear(w);

View File

@ -121,11 +121,10 @@ struct ToxWindow {
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t); void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS); void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t); void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t); void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t); void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t); void(*onGroupNamelistChange)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_CONFERENCE_STATE_CHANGE);
void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t); void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t); void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t); void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL); void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
@ -260,6 +259,7 @@ void kill_all_windows(Tox *m); /* should only be called on shutdown */
void on_window_resize(void); void on_window_resize(void);
void force_refresh(WINDOW *w); void force_refresh(WINDOW *w);
ToxWindow *get_window_ptr(int i); ToxWindow *get_window_ptr(int i);
ToxWindow *get_active_window(void);
/* refresh inactive windows to prevent scrolling bugs. /* refresh inactive windows to prevent scrolling bugs.
call at least once per second */ call at least once per second */

View File

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

View File

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