mirror of
https://github.com/Tha14/toxic.git
synced 2025-06-27 16:36:45 +02:00
Compare commits
77 Commits
new_groupc
...
v0.8.2
Author | SHA1 | Date | |
---|---|---|---|
a223329815 | |||
3fec11d5f9 | |||
221edb0012 | |||
2710ab6034 | |||
bc3ffac0ba | |||
29f55c5277 | |||
a290f0f7f8 | |||
5cd196a769 | |||
b14d983a8c | |||
51f1daeec8 | |||
b799c6a8d7 | |||
b9f9546e2b | |||
846bc4613e | |||
a5a1f6015d | |||
fe6a7074ea | |||
db7c9fe426 | |||
b1d8ab102f | |||
0bd5b4ddee | |||
c387df35f8 | |||
351a50c214 | |||
93175314b5 | |||
b905a1a3c5 | |||
c4386b195f | |||
ed1e617380 | |||
1382adb1f6 | |||
ecf1c317b7 | |||
cf0b99f1e5 | |||
3605a296a9 | |||
9375d220f9 | |||
8f94b0a218 | |||
85a0becbf9 | |||
fec36ad9e6 | |||
ecdf6f01d2 | |||
e1bfa30769 | |||
ebcbc7497b | |||
e844ece28b | |||
8508451ba6 | |||
5cc83a7cb5 | |||
febc725763 | |||
f2c116feb3 | |||
52dd60dc86 | |||
80c0500299 | |||
ab490d28b4 | |||
a9f7f85617 | |||
1bfc1ba371 | |||
2ede39369a | |||
922c184195 | |||
56a9571509 | |||
0136f22076 | |||
c4ace288af | |||
6d3fbfee59 | |||
be5e7906da | |||
369f26932e | |||
22ea522baf | |||
4f60d546e6 | |||
76d1eafdc0 | |||
37912f2d88 | |||
09710327c5 | |||
acee4615f8 | |||
5ed26eda9b | |||
6d2b90ac9f | |||
02ea0fac44 | |||
7d3d129624 | |||
b3ed8bc35c | |||
90210daca7 | |||
0e13a1f1bc | |||
09e2690211 | |||
e65e3af274 | |||
c6c60d018e | |||
451d4ced80 | |||
7a7402ff86 | |||
600e013adc | |||
1d71e2eb18 | |||
f858714edd | |||
4df44a7274 | |||
a26ed9d28f | |||
2bd5083b8f |
1
.gitignore
vendored
1
.gitignore
vendored
@ -16,3 +16,4 @@ stamp-h1
|
||||
build/toxic
|
||||
build/*.o
|
||||
build/*.d
|
||||
apidoc/python/build
|
||||
|
31
.travis.yml
Normal file
31
.travis.yml
Normal file
@ -0,0 +1,31 @@
|
||||
language: python
|
||||
python: nightly
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libalut-dev
|
||||
- libconfig-dev
|
||||
- libnotify-dev
|
||||
- libopenal-dev
|
||||
- libopus-dev
|
||||
- libqrencode-dev
|
||||
- libvpx-dev
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/cache
|
||||
|
||||
install:
|
||||
# Where to find libraries.
|
||||
- export LD_LIBRARY_PATH=$HOME/cache/usr/lib
|
||||
- export PKG_CONFIG_PATH=$HOME/cache/usr/lib/pkgconfig
|
||||
# c-sodium
|
||||
- git clone --depth=1 --branch=stable https://github.com/jedisct1/libsodium
|
||||
- test -f $HOME/cache/usr/lib/libsodium.so || (cd libsodium && ./configure --prefix=$HOME/cache/usr && make install -j$(nproc))
|
||||
# c-toxcore
|
||||
- git clone --depth=1 https://github.com/TokTok/c-toxcore
|
||||
- test -f $HOME/cache/usr/lib/libtoxcore.so || (cd c-toxcore && cmake -B_build -H. -DCMAKE_INSTALL_PREFIX:PATH=$HOME/cache/usr && make -C_build install -j$(nproc))
|
||||
|
||||
script:
|
||||
- make ENABLE_PYTHON=1
|
30
INSTALL.md
30
INSTALL.md
@ -1,57 +1,53 @@
|
||||
# Installation
|
||||
* [Dependencies](#deps)
|
||||
* [OS X Notes](#deps_osx)
|
||||
* [Dependencies](#dependencies)
|
||||
* [OS X Notes](#os-x-notes)
|
||||
* [Compiling](#compiling)
|
||||
* [Documentation](#docs)
|
||||
* [Documentation](#documentation)
|
||||
* [Notes](#notes)
|
||||
* [Compilation variables](#comp_vars)
|
||||
* [Compilation variables](#compilation-variables)
|
||||
* [Packaging](#packaging)
|
||||
|
||||
<a name="deps" />
|
||||
## Dependencies
|
||||
| Name | Needed by | Debian package |
|
||||
|------------------------------------------------------|----------------------------|---------------------|
|
||||
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
|
||||
| [Tox Core](https://github.com/toktok/c-toxcore) | BASE | *None* |
|
||||
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
||||
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
||||
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
||||
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
|
||||
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
|
||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
|
||||
| [Tox Core AV](https://github.com/toktok/c-toxcore) | AUDIO | *None* |
|
||||
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
|
||||
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||
<sup>1</sup>: see [Documentation](#docs)
|
||||
|
||||
<a name="deps_osx" />
|
||||
<sup>1</sup>: see [Documentation](#documentation)
|
||||
|
||||
#### OS X Notes
|
||||
Using [Homebrew](http://brew.sh):
|
||||
```
|
||||
brew install openal-soft freealut libconfig
|
||||
brew install https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||
brew install https://raw.githubusercontent.com/Homebrew/homebrew-x11/master/libnotify.rb
|
||||
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||
brew install libnotify
|
||||
```
|
||||
|
||||
You can omit `libnotify` if you intend to build without desktop notifications enabled.
|
||||
|
||||
<a name="Compiling">
|
||||
## Compiling
|
||||
```
|
||||
make PREFIX="/where/to/install"
|
||||
sudo make install PREFIX="/where/to/install"
|
||||
```
|
||||
|
||||
<a name="docs" />
|
||||
#### Documentation
|
||||
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
|
||||
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
|
||||
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||
|
||||
<a name="notes" />
|
||||
## Notes
|
||||
|
||||
<a name="comp_vars" />
|
||||
#### Compilation variables
|
||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
|
||||
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
|
||||
@ -60,11 +56,11 @@ Run `make doc` in the build directory after editing the asciidoc files to regene
|
||||
* `DISABLE_AV=1` → build toxic without audio call support
|
||||
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
|
||||
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
|
||||
* Features excluded from the default build must be explicitly enabled using special variables:
|
||||
* `ENABLE_PYTHON=1` → build toxic with Python scripting support
|
||||
|
||||
<a name="packaging" />
|
||||
#### Packaging
|
||||
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
|
||||
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
|
||||
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
|
||||
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
|
||||
|
||||
|
5
Makefile
5
Makefile
@ -3,7 +3,7 @@ CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
-include $(CFG_DIR)/global_vars.mk
|
||||
|
||||
LIBS = libtoxcore ncursesw libconfig libqrencode
|
||||
LIBS = toxcore ncursesw libconfig libqrencode
|
||||
|
||||
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
|
||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||
@ -30,6 +30,9 @@ endif
|
||||
ifeq ($(UNAME_S), OpenBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), NetBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
-include $(CFG_DIR)/systems/Darwin.mk
|
||||
endif
|
||||
|
@ -3,13 +3,12 @@
|
||||
src="https://scan.coverity.com/projects/4975/badge.svg"/>
|
||||
</a>
|
||||
|
||||
Toxic is a [Tox](https://tox.chat)-based instant messenging client which formerly resided in the [Tox core repository](https://github.com/irungentoo/toxcore), and is now available as a standalone application.
|
||||
Toxic is a [Tox](https://tox.chat)-based instant messenging and video chat client.
|
||||
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
|
||||
## Installation
|
||||
[Use our repositories](https://wiki.tox.chat/binaries#other_linux)<br />
|
||||
[Compile it yourself](/INSTALL.md)
|
||||
[See the install instructions](/INSTALL.md)
|
||||
|
||||
## Settings
|
||||
Running Toxic for the first time creates an empty file called toxic.conf in your home configuration directory ("~/.config/tox" for Linux users). Adding options to this file allows you to enable auto-logging, change the time format (12/24 hour), and much more.
|
||||
|
20
apidoc/python/Makefile
Normal file
20
apidoc/python/Makefile
Normal 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)
|
157
apidoc/python/source/conf.py
Normal file
157
apidoc/python/source/conf.py
Normal 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'),
|
||||
]
|
||||
|
||||
|
||||
|
8
apidoc/python/source/examples.rst
Normal file
8
apidoc/python/source/examples.rst
Normal file
@ -0,0 +1,8 @@
|
||||
============
|
||||
API Examples
|
||||
============
|
||||
|
||||
Fortune
|
||||
=======
|
||||
.. literalinclude:: fortune.py
|
||||
:language: python
|
37
apidoc/python/source/fortune.py
Normal file
37
apidoc/python/source/fortune.py
Normal 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)
|
9
apidoc/python/source/index.rst
Normal file
9
apidoc/python/source/index.rst
Normal file
@ -0,0 +1,9 @@
|
||||
Toxic Scripting Interface Documentation
|
||||
=======================================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
intro
|
||||
reference
|
||||
examples
|
12
apidoc/python/source/intro.rst
Normal file
12
apidoc/python/source/intro.rst
Normal 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.
|
73
apidoc/python/source/reference.rst
Normal file
73
apidoc/python/source/reference.rst
Normal 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
|
@ -1,5 +1,5 @@
|
||||
# Variables for audio call support
|
||||
AUDIO_LIBS = libtoxav openal
|
||||
AUDIO_LIBS = openal
|
||||
AUDIO_CFLAGS = -DAUDIO
|
||||
ifneq (, $(findstring audio_device.o, $(OBJ)))
|
||||
AUDIO_OBJ = audio_call.o
|
||||
@ -18,4 +18,4 @@ else ifneq ($(MAKECMDGOALS), clean)
|
||||
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||
$(warning WARNING -- You need these libraries for audio support)
|
||||
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
||||
endif
|
||||
endif
|
||||
|
@ -40,6 +40,12 @@ ifneq ($(QR_PNG), disabled)
|
||||
-include $(CHECKS_DIR)/qr_png.mk
|
||||
endif
|
||||
|
||||
# Check if we want build Python scripting support
|
||||
PYTHON = $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(PYTHON), disabled)
|
||||
-include $(CHECKS_DIR)/python.mk
|
||||
endif
|
||||
|
||||
# Check if we can build Toxic
|
||||
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_LIBS), error)
|
||||
|
15
cfg/checks/python.mk
Normal file
15
cfg/checks/python.mk
Normal 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
|
@ -1,5 +1,5 @@
|
||||
# Variables for video call support
|
||||
VIDEO_LIBS = libtoxav vpx x11
|
||||
VIDEO_LIBS = vpx x11
|
||||
VIDEO_CFLAGS = -DVIDEO
|
||||
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||
VIDEO_OBJ = video_call.o
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.7.1
|
||||
TOXIC_VERSION = 0.8.1
|
||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||
ifneq (, $(findstring error, $(REV)))
|
||||
VERSION = $(TOXIC_VERSION)
|
||||
|
@ -15,6 +15,7 @@ help:
|
||||
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
|
||||
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
|
||||
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||
|
@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
|
||||
mv temp_file $$file ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
gzip -f -9 $$file ;\
|
||||
gzip -f -n -9 $$file ;\
|
||||
done
|
||||
|
||||
.PHONY: install
|
||||
|
@ -1,13 +1,13 @@
|
||||
'\" t
|
||||
.\" Title: toxic.conf
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2016-07-21
|
||||
.\" Generator: DocBook XSL Stylesheets v1.79.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2016-09-20
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC\&.CONF" "5" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -227,6 +227,11 @@ Default path for downloads\&. String value\&. Absolute path for downloaded files
|
||||
Path for your avatar (file must be a \&.png and cannot exceed 16\&.3 KiB)
|
||||
.RE
|
||||
.PP
|
||||
\fBautorun_path\fR
|
||||
.RS 4
|
||||
Path for any scripts that should be run on startup
|
||||
.RE
|
||||
.PP
|
||||
\fBchatlogs_path\fR
|
||||
.RS 4
|
||||
Default path for chatlogs\&. String value\&. Absolute path for chatlog files\&.
|
||||
|
@ -143,6 +143,9 @@ OPTIONS
|
||||
*avatar_path*;;
|
||||
Path for your avatar (file must be a .png and cannot exceed 16.3 KiB)
|
||||
|
||||
*autorun_path*;;
|
||||
Path for any scripts that should be run on startup
|
||||
|
||||
*chatlogs_path*;;
|
||||
Default path for chatlogs. String value. Absolute path for chatlog files.
|
||||
|
||||
|
@ -87,6 +87,9 @@ tox = {
|
||||
// Path for your avatar (file must be a .png and cannot exceed 64 KiB)
|
||||
// avatar_path="/home/USERNAME/Pictures/youravatar.png";
|
||||
|
||||
// Path for scripts that should be run on startup
|
||||
// autorun_path="/home/USERNAME/toxic_scripts/";
|
||||
|
||||
// Path for chatlogs
|
||||
// chatlogs_path="/home/USERNAME/toxic_chatlogs/";
|
||||
};
|
||||
@ -118,4 +121,3 @@ keys = {
|
||||
toggle_peerlist="Ctrl+b";
|
||||
toggle_paste_mode="Ctrl+T";
|
||||
};
|
||||
|
||||
|
210
src/api.c
Normal file
210
src/api.c
Normal file
@ -0,0 +1,210 @@
|
||||
/* api.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "execute.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "message_queue.h"
|
||||
#include "misc_tools.h"
|
||||
#include "settings.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef PYTHON
|
||||
#include "python_api.h"
|
||||
|
||||
Tox *user_tox;
|
||||
static WINDOW *cur_window;
|
||||
static ToxWindow *self_window;
|
||||
|
||||
extern FriendsList Friends;
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
void api_display(const char *const msg)
|
||||
{
|
||||
if (msg == NULL)
|
||||
return;
|
||||
|
||||
self_window = get_active_window();
|
||||
line_info_add(self_window, NULL, NULL, NULL, SYS_MSG, 0, 0, msg);
|
||||
}
|
||||
|
||||
FriendsList api_get_friendslist(void)
|
||||
{
|
||||
return Friends;
|
||||
}
|
||||
|
||||
char *api_get_nick(void)
|
||||
{
|
||||
size_t len = tox_self_get_name_size(user_tox);
|
||||
uint8_t *name = malloc(len + 1);
|
||||
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
tox_self_get_name(user_tox, name);
|
||||
name[len] = '\0';
|
||||
return (char *) name;
|
||||
}
|
||||
|
||||
TOX_USER_STATUS api_get_status(void)
|
||||
{
|
||||
return tox_self_get_status(user_tox);
|
||||
}
|
||||
|
||||
char *api_get_status_message(void)
|
||||
{
|
||||
size_t len = tox_self_get_status_message_size(user_tox);
|
||||
uint8_t *status = malloc(len + 1);
|
||||
|
||||
if (status == NULL)
|
||||
return NULL;
|
||||
|
||||
tox_self_get_status_message(user_tox, status);
|
||||
status[len] = '\0';
|
||||
return (char *) status;
|
||||
}
|
||||
|
||||
void api_send(const char *msg)
|
||||
{
|
||||
if (msg == NULL || self_window->chatwin->cqueue == NULL)
|
||||
return;
|
||||
|
||||
char *name = api_get_nick();
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
|
||||
if (name == NULL)
|
||||
return;
|
||||
|
||||
self_window = get_active_window();
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
strncpy((char *) self_window->chatwin->line, msg, sizeof(self_window->chatwin->line));
|
||||
add_line_to_hist(self_window->chatwin);
|
||||
int id = line_info_add(self_window, timefrmt, name, NULL, OUT_MSG, 0, 0, "%s", msg);
|
||||
cqueue_add(self_window->chatwin->cqueue, msg, strlen(msg), OUT_MSG, id);
|
||||
free(name);
|
||||
}
|
||||
|
||||
void api_execute(const char *input, int mode)
|
||||
{
|
||||
self_window = get_active_window();
|
||||
execute(cur_window, self_window, user_tox, input, mode);
|
||||
}
|
||||
|
||||
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
return do_python_command(num_args, args);
|
||||
}
|
||||
|
||||
int num_registered_handlers(void)
|
||||
{
|
||||
return python_num_registered_handlers();
|
||||
}
|
||||
|
||||
int help_max_width(void)
|
||||
{
|
||||
return python_help_max_width();
|
||||
}
|
||||
|
||||
void draw_handler_help(WINDOW *win)
|
||||
{
|
||||
python_draw_handler_help(win);
|
||||
}
|
||||
|
||||
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
FILE *fp;
|
||||
const char *error_str;
|
||||
|
||||
cur_window = window;
|
||||
self_window = self;
|
||||
|
||||
if ( argc != 1 ) {
|
||||
if ( argc < 1 ) error_str = "Path must be specified!";
|
||||
else error_str = "Only one argument allowed!";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
return;
|
||||
}
|
||||
|
||||
fp = fopen(argv[1], "r");
|
||||
|
||||
if ( fp == NULL ) {
|
||||
error_str = "Path does not exist!";
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
return;
|
||||
}
|
||||
|
||||
run_python(fp, argv[1]);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||
{
|
||||
struct dirent *dir;
|
||||
char abspath_buf[PATH_MAX + 1], err_buf[PATH_MAX + 1];
|
||||
size_t path_len;
|
||||
DIR *d;
|
||||
FILE *fp;
|
||||
|
||||
if (user_settings->autorun_path[0] == '\0')
|
||||
return;
|
||||
|
||||
d = opendir(user_settings->autorun_path);
|
||||
|
||||
if (d == NULL) {
|
||||
snprintf(err_buf, PATH_MAX + 1, "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||
api_display(err_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
cur_window = window;
|
||||
self_window = self;
|
||||
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
path_len = strlen(dir->d_name);
|
||||
|
||||
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||
snprintf(abspath_buf, PATH_MAX + 1, "%s%s", user_settings->autorun_path, dir->d_name);
|
||||
fp = fopen(abspath_buf, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
snprintf(err_buf, PATH_MAX + 1, "Invalid path: %s", abspath_buf);
|
||||
api_display(err_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
run_python(fp, abspath_buf);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
#endif /* PYTHON */
|
42
src/api.h
Normal file
42
src/api.h
Normal 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 */
|
107
src/audio_call.c
107
src/audio_call.c
@ -56,6 +56,7 @@
|
||||
#endif
|
||||
|
||||
extern FriendsList Friends;
|
||||
struct CallControl CallControl;
|
||||
|
||||
#define cbend pthread_exit(NULL)
|
||||
|
||||
@ -133,8 +134,6 @@ ToxAV *init_audio(ToxWindow *self, Tox *tox)
|
||||
CallControl.video_frame_duration = 0;
|
||||
#endif /* VIDEO */
|
||||
|
||||
memset(CallControl.calls, 0, sizeof(CallControl.calls));
|
||||
|
||||
if ( !CallControl.av ) {
|
||||
CallControl.audio_errors |= ae_StartingCoreAudio;
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to init ToxAV");
|
||||
@ -160,7 +159,7 @@ void terminate_audio()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CALLS; ++i)
|
||||
for (i = 0; i < CallControl.max_calls; ++i)
|
||||
stop_transmission(&CallControl.calls[i], i);
|
||||
|
||||
if ( CallControl.av )
|
||||
@ -338,10 +337,10 @@ void receive_audio_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
write_device_callback(friend_number, pcm, sample_count, channels, sampling_rate);
|
||||
}
|
||||
|
||||
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||
uint32_t video_bit_rate, void *user_data)
|
||||
void audio_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate, void *user_data)
|
||||
{
|
||||
CallControl.audio_bit_rate = audio_bit_rate;
|
||||
toxav_audio_set_bit_rate(av, friend_number, audio_bit_rate, user_data);
|
||||
}
|
||||
|
||||
void callback_recv_invite(Tox *m, uint32_t friend_number)
|
||||
@ -387,7 +386,7 @@ void callback_recv_starting(uint32_t friend_number)
|
||||
windows[i].onStarting(&windows[i], CallControl.av, friend_number, CallControl.call_state);
|
||||
|
||||
if ( 0 != start_transmission(&windows[i], &CallControl.calls[friend_number]) ) /* YEAH! */
|
||||
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0 , "Error starting transmission!");
|
||||
line_info_add(&windows[i], NULL, NULL, NULL, SYS_MSG, 0, 0, "Error starting transmission!");
|
||||
|
||||
return;
|
||||
}
|
||||
@ -632,6 +631,9 @@ void cmd_list_devices(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*
|
||||
return;
|
||||
}
|
||||
|
||||
// Refresh device list.
|
||||
get_devices_names();
|
||||
|
||||
print_devices(self, type);
|
||||
|
||||
return;
|
||||
@ -834,6 +836,55 @@ on_error:
|
||||
print_err (self, error_str);
|
||||
}
|
||||
|
||||
void cmd_bitrate(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char *error_str;
|
||||
|
||||
if ( argc != 1 ) {
|
||||
error_str = "Must have value!";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ( self->is_call == false ) {
|
||||
error_str = "Must be in a call";
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
const uint32_t bitrate = strtol(argv[1], NULL, 10);
|
||||
|
||||
TOXAV_ERR_BIT_RATE_SET error;
|
||||
audio_bit_rate_status_cb(CallControl.av, self->num, bitrate, &error);
|
||||
|
||||
if (error != TOXAV_ERR_BIT_RATE_SET_OK) {
|
||||
switch (error) {
|
||||
case TOXAV_ERR_BIT_RATE_SET_SYNC:
|
||||
error_str = "Syncronization error occured";
|
||||
break;
|
||||
|
||||
case TOXAV_ERR_BIT_RATE_SET_INVALID_BIT_RATE:
|
||||
error_str = "Invalid AV bit rate value (valid is 6-510)";
|
||||
break;
|
||||
|
||||
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_FOUND:
|
||||
error_str = "Friend not found";
|
||||
break;
|
||||
|
||||
case TOXAV_ERR_BIT_RATE_SET_FRIEND_NOT_IN_CALL:
|
||||
error_str = "Friend is not in the call";
|
||||
break;
|
||||
|
||||
default:
|
||||
error_str = "Unknown error";
|
||||
}
|
||||
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
on_error:
|
||||
print_err (self, error_str);
|
||||
}
|
||||
|
||||
void stop_current_call(ToxWindow *self)
|
||||
{
|
||||
@ -847,3 +898,47 @@ void stop_current_call(ToxWindow *self)
|
||||
|
||||
CallControl.pending_call = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reallocates the Calls list according to n.
|
||||
*/
|
||||
static void realloc_calls(uint32_t n)
|
||||
{
|
||||
if (n <= 0) {
|
||||
free(CallControl.calls);
|
||||
CallControl.calls = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
Call *temp = realloc(CallControl.calls, n * sizeof(Call));
|
||||
|
||||
if (temp == NULL) {
|
||||
exit_toxic_err("failed in realloc_calls", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
CallControl.calls = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the call structure for a given friend. Called when a friend is added to the friends list.
|
||||
* Index must be equivalent to the friend's friendlist index.
|
||||
*/
|
||||
void init_friend_AV(uint32_t index)
|
||||
{
|
||||
realloc_calls(CallControl.max_calls + 1);
|
||||
memset(&CallControl.calls[CallControl.max_calls], 0, sizeof(Call));
|
||||
|
||||
if (index == CallControl.max_calls) {
|
||||
++CallControl.max_calls;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a call structure from the Calls list. Called when a friend is deleted from the friends list.
|
||||
* Index must be equivalent to the size of the Calls list.
|
||||
*/
|
||||
void del_friend_AV(uint32_t index)
|
||||
{
|
||||
realloc_calls(index);
|
||||
CallControl.max_calls = index;
|
||||
}
|
||||
|
@ -27,8 +27,6 @@
|
||||
|
||||
#include "audio_device.h"
|
||||
|
||||
#define MAX_CALLS 10
|
||||
|
||||
typedef enum _AudioError {
|
||||
ae_None = 0,
|
||||
ae_StartingCaptureDevice = 1 << 0,
|
||||
@ -65,7 +63,9 @@ struct CallControl {
|
||||
ToxAV *av;
|
||||
ToxWindow *prompt;
|
||||
|
||||
Call calls[MAX_CALLS];
|
||||
Call *calls;
|
||||
uint32_t max_calls;
|
||||
|
||||
uint32_t call_state;
|
||||
bool pending_call;
|
||||
bool audio_enabled;
|
||||
@ -79,9 +79,9 @@ struct CallControl {
|
||||
uint32_t video_bit_rate;
|
||||
int32_t video_frame_duration;
|
||||
|
||||
} CallControl;
|
||||
};
|
||||
|
||||
struct CallControl CallControl;
|
||||
extern struct CallControl CallControl;
|
||||
|
||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||
@ -89,5 +89,7 @@ void terminate_audio();
|
||||
int start_transmission(ToxWindow *self, Call *call);
|
||||
int stop_transmission(Call *call, uint32_t friend_number);
|
||||
void stop_current_call(ToxWindow *self);
|
||||
void init_friend_AV(uint32_t index);
|
||||
void del_friend_AV(uint32_t index);
|
||||
|
||||
#endif /* AUDIO_CALL_H */
|
||||
|
@ -100,34 +100,7 @@ DeviceError init_devices(ToxAV *av_)
|
||||
DeviceError init_devices()
|
||||
#endif /* AUDIO */
|
||||
{
|
||||
const char *stringed_device_list;
|
||||
|
||||
size[input] = 0;
|
||||
|
||||
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
|
||||
devices_names[input][size[input]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
size[output] = 0;
|
||||
|
||||
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
|
||||
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
else
|
||||
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
|
||||
if (stringed_device_list) {
|
||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
||||
devices_names[output][size[output]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
get_devices_names();
|
||||
|
||||
// Start poll thread
|
||||
if (pthread_mutex_init(&mutex, NULL) != 0)
|
||||
@ -160,6 +133,38 @@ DeviceError terminate_devices()
|
||||
return (DeviceError) de_None;
|
||||
}
|
||||
|
||||
void get_devices_names() {
|
||||
|
||||
const char *stringed_device_list;
|
||||
|
||||
size[input] = 0;
|
||||
|
||||
if ( (stringed_device_list = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER)) ) {
|
||||
ddevice_names[input] = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
for ( ; *stringed_device_list && size[input] < MAX_DEVICES; ++size[input] ) {
|
||||
devices_names[input][size[input]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
size[output] = 0;
|
||||
|
||||
if (alcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT") != AL_FALSE)
|
||||
stringed_device_list = alcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
|
||||
else
|
||||
stringed_device_list = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
|
||||
|
||||
if (stringed_device_list) {
|
||||
ddevice_names[output] = alcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
|
||||
|
||||
for ( ; *stringed_device_list && size[output] < MAX_DEVICES; ++size[output] ) {
|
||||
devices_names[output][size[output]] = stringed_device_list;
|
||||
stringed_device_list += strlen( stringed_device_list ) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DeviceError device_mute(DeviceType type, uint32_t device_idx)
|
||||
{
|
||||
if (device_idx >= MAX_DEVICES) return de_InvalidSelection;
|
||||
@ -481,8 +486,9 @@ void print_devices(ToxWindow *self, DeviceType type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size[type]; ++i)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%d: %s", i, devices_names[type][i]);
|
||||
for (i = 0; i < size[type]; ++i) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, i == primary_device[type] ? 1 : 0, 0, "%d: %s", i, devices_names[type][i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -61,6 +61,7 @@ DeviceError init_devices(ToxAV *av);
|
||||
DeviceError init_devices();
|
||||
#endif /* AUDIO */
|
||||
|
||||
void get_devices_names();
|
||||
DeviceError terminate_devices();
|
||||
|
||||
/* Callback handles ready data from INPUT device */
|
||||
|
@ -38,16 +38,18 @@
|
||||
#include "execute.h"
|
||||
#include "configdir.h"
|
||||
|
||||
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
|
||||
static void print_matches(ToxWindow *self, Tox *m, const void *list, size_t n_items, size_t size)
|
||||
{
|
||||
if (m)
|
||||
if (m) {
|
||||
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
|
||||
}
|
||||
|
||||
const char *L = (char *) list;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_items; ++i)
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", &L[i * size]);
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, ""); /* formatting */
|
||||
}
|
||||
@ -91,7 +93,7 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -110,6 +112,10 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|
||||
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|
||||
|
||||
#ifdef PYTHON
|
||||
dir_search = dir_search || !strncmp(ubuf, "/run", strlen("/run"));
|
||||
#endif
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
@ -144,7 +150,7 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
int n_matches = 0;
|
||||
size_t n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
@ -283,7 +289,7 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
if (dp == NULL)
|
||||
return -1;
|
||||
|
||||
char dirnames[MAX_DIRS][NAME_MAX];
|
||||
char dirnames[MAX_DIRS][NAME_MAX+1];
|
||||
struct dirent *entry;
|
||||
int dircount = 0;
|
||||
|
||||
@ -301,9 +307,9 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
return -1;
|
||||
|
||||
if (dircount > 1) {
|
||||
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
|
||||
print_matches(self, m, dirnames, dircount, NAME_MAX);
|
||||
qsort(dirnames, dircount, NAME_MAX + 1, qsort_strcasecmp_hlpr);
|
||||
print_matches(self, m, dirnames, dircount, NAME_MAX + 1);
|
||||
}
|
||||
|
||||
return complete_line(self, dirnames, dircount, NAME_MAX);
|
||||
return complete_line(self, dirnames, dircount, NAME_MAX + 1);
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
|
||||
int complete_line(ToxWindow *self, const void *list, size_t n_items, size_t size);
|
||||
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
|
||||
|
@ -34,6 +34,7 @@
|
||||
#include "configdir.h"
|
||||
#include "curl_util.h"
|
||||
#include "settings.h"
|
||||
#include "prompt.h"
|
||||
|
||||
extern struct arg_opts arg_opts;
|
||||
extern struct user_settings *user_settings;
|
||||
@ -108,7 +109,10 @@ static struct DHT_Nodes {
|
||||
/* Determine if a node is offline by comparing the age of the nodeslist
|
||||
* to the last time the node was successfully pinged.
|
||||
*/
|
||||
#define NODE_IS_OFFLINE(last_scan, last_ping) ((last_ping + NODE_OFFLINE_TIMOUT) <= (last_ping))
|
||||
static bool node_is_offline(unsigned long long int last_ping)
|
||||
{
|
||||
return last_ping + NODE_OFFLINE_TIMOUT <= last_ping;
|
||||
}
|
||||
|
||||
/* Return true if nodeslist pointed to by fp needs to be updated.
|
||||
* This will be the case if the file is empty, has an invalid format,
|
||||
@ -377,7 +381,7 @@ static int extract_node(const char *line, struct Node *node)
|
||||
|
||||
long long int last_pinged = extract_val_last_pinged(last_pinged_str + LAST_PING_JSON_KEY_LEN);
|
||||
|
||||
if (last_pinged <= 0 || NODE_IS_OFFLINE(Nodes.last_scan, last_pinged)) {
|
||||
if (last_pinged <= 0 || node_is_offline(last_pinged)) {
|
||||
return -3;
|
||||
}
|
||||
|
||||
@ -477,7 +481,7 @@ void *load_nodeslist_thread(void *data)
|
||||
/* If nodeslist does not contain any valid entries we set the last_scan value
|
||||
* to 0 so that it will fetch a new list the next time this function is called.
|
||||
*/
|
||||
if (idx == 0) {
|
||||
if (Nodes.count == 0) {
|
||||
const char *s = "{\"last_scan\":0}";
|
||||
rewind(fp);
|
||||
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
|
||||
@ -577,7 +581,7 @@ static void DHT_bootstrap(Tox *m)
|
||||
void do_tox_connection(Tox *m)
|
||||
{
|
||||
static time_t last_bootstrap_time = 0;
|
||||
bool connected = tox_self_get_connection_status(m) != TOX_CONNECTION_NONE;
|
||||
bool connected = prompt_selfConnectionStatus() != TOX_CONNECTION_NONE;
|
||||
|
||||
if (!connected && timed_out(last_bootstrap_time, TRY_BOOTSTRAP_INTERVAL)) {
|
||||
DHT_bootstrap(m);
|
||||
|
43
src/chat.c
43
src/chat.c
@ -65,8 +65,12 @@ static void init_infobox(ToxWindow *self);
|
||||
static void kill_infobox(ToxWindow *self);
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef AUDIO
|
||||
#define AC_NUM_CHAT_COMMANDS 30
|
||||
#if defined(AUDIO) && defined(PYTHON)
|
||||
#define AC_NUM_CHAT_COMMANDS 32
|
||||
#elif AUDIO
|
||||
#define AC_NUM_CHAT_COMMANDS 31
|
||||
#elif PYTHON
|
||||
#define AC_NUM_CHAT_COMMANDS 23
|
||||
#else
|
||||
#define AC_NUM_CHAT_COMMANDS 22
|
||||
#endif /* AUDIO */
|
||||
@ -106,8 +110,15 @@ static const char chat_cmd_list[AC_NUM_CHAT_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/mute" },
|
||||
{ "/sense" },
|
||||
{ "/video" },
|
||||
{ "/bitrate" },
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
{ "/run" },
|
||||
|
||||
#endif /* PYTHON */
|
||||
};
|
||||
|
||||
static void set_self_typingstatus(ToxWindow *self, Tox *m, bool is_typing)
|
||||
@ -227,8 +238,6 @@ static void chat_onConnectionChange(ToxWindow *self, Tox *m, uint32_t num, TOX_C
|
||||
}
|
||||
|
||||
if (prev_status == TOX_CONNECTION_NONE) {
|
||||
Friends.list[num].is_typing = user_settings->show_typing_other == SHOW_TYPING_ON
|
||||
? tox_friend_get_typing(m, num, NULL) : false;
|
||||
chat_resume_file_senders(self, m, num);
|
||||
|
||||
msg = "has come online";
|
||||
@ -550,7 +559,7 @@ static void chat_onFileRecv(ToxWindow *self, Tox *m, uint32_t friendnum, uint32_
|
||||
bytes_convert_str(sizestr, sizeof(sizestr), file_size);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer request for '%s' (%s)", filename, sizestr);
|
||||
|
||||
char file_path[MAX_STR_SIZE];
|
||||
char file_path[PATH_MAX + name_length + 1];
|
||||
size_t path_len = name_length;
|
||||
|
||||
/* use specified download path in config if possible */
|
||||
@ -885,8 +894,8 @@ static void send_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
|
||||
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, ctx->hst->line_end->id + 1);
|
||||
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_ACTION, 0, 0, "%s", action);
|
||||
cqueue_add(ctx->cqueue, action, strlen(action), OUT_ACTION, id);
|
||||
}
|
||||
|
||||
static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
@ -931,7 +940,16 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
diff = dir_match(self, m, ctx->line, L"/sendfile");
|
||||
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
} else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/run");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
|
||||
const char status_cmd_list[3][8] = {
|
||||
{"online"},
|
||||
{"away"},
|
||||
@ -983,8 +1001,8 @@ static void chat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
||||
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, ctx->hst->line_end->id + 1);
|
||||
int id = line_info_add(self, timefrmt, selfname, NULL, OUT_MSG, 0, 0, "%s", line);
|
||||
cqueue_add(ctx->cqueue, line, strlen(line), OUT_MSG, id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1140,9 +1158,8 @@ static void chat_onInit(ToxWindow *self, Tox *m)
|
||||
|
||||
/* Init statusbar info */
|
||||
StatusBar *statusbar = self->stb;
|
||||
|
||||
statusbar->status = tox_friend_get_status(m, self->num, NULL);
|
||||
statusbar->connection = tox_friend_get_connection_status(m, self->num, NULL);
|
||||
statusbar->status = get_friend_status(self->num);
|
||||
statusbar->connection = get_friend_connection_status(self->num);
|
||||
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH];
|
||||
tox_friend_get_status_message(m, self->num, (uint8_t *) statusmsg, NULL);
|
||||
|
@ -92,8 +92,10 @@ void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*a
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_invite_friend(m, self->num, groupnum) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group.");
|
||||
TOX_ERR_CONFERENCE_INVITE err;
|
||||
|
||||
if (!tox_conference_invite(m, self->num, groupnum, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -116,25 +118,23 @@ void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = -1;
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
||||
TOX_ERR_CONFERENCE_JOIN err;
|
||||
|
||||
/*#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
||||
NULL, NULL);
|
||||
#endif*/
|
||||
uint32_t groupnum = tox_conference_join(m, self->num, (uint8_t *) groupkey, length, &err);
|
||||
|
||||
if (groupnum == -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -41,6 +41,7 @@ void cmd_cancel(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZ
|
||||
void cmd_ccur_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_mute(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sense(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_bitrate(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "api.h"
|
||||
|
||||
struct cmd_func {
|
||||
const char *name;
|
||||
@ -65,8 +66,11 @@ static struct cmd_func global_commands[] = {
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev" , cmd_change_video_device },
|
||||
{ "/svdev", cmd_change_video_device },
|
||||
#endif /* VIDEO */
|
||||
#ifdef PYTHON
|
||||
{ "/run", cmd_run },
|
||||
#endif /* PYTHON */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
@ -83,6 +87,7 @@ static struct cmd_func chat_commands[] = {
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/bitrate", cmd_bitrate },
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/video", cmd_video },
|
||||
@ -193,5 +198,12 @@ void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
if (do_command(w, self, m, num_args, global_commands, args) == 0)
|
||||
return;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
if (do_plugin_command(num_args, args) == 0)
|
||||
return;
|
||||
|
||||
#endif
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
@ -153,10 +153,14 @@ static int save_blocklist(char *path)
|
||||
}
|
||||
|
||||
if (Blocked.list[i].active) {
|
||||
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BlockedFriend tmp;
|
||||
memset(&tmp, 0, sizeof(BlockedFriend));
|
||||
tmp.namelength = htons(Blocked.list[i].namelength);
|
||||
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1);
|
||||
memcpy(tmp.name, Blocked.list[i].name, Blocked.list[i].namelength + 1); // Include null byte
|
||||
memcpy(tmp.pub_key, Blocked.list[i].pub_key, TOX_PUBLIC_KEY_SIZE);
|
||||
|
||||
uint8_t lastonline[sizeof(uint64_t)];
|
||||
@ -250,10 +254,15 @@ int load_blocklist(char *path)
|
||||
memset(&Blocked.list[i], 0, sizeof(BlockedFriend));
|
||||
|
||||
memcpy(&tmp, data + i * sizeof(BlockedFriend), sizeof(BlockedFriend));
|
||||
Blocked.list[i].namelength = ntohs(tmp.namelength);
|
||||
|
||||
if (Blocked.list[i].namelength > TOXIC_MAX_NAME_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Blocked.list[i].active = true;
|
||||
Blocked.list[i].num = i;
|
||||
Blocked.list[i].namelength = MIN(TOXIC_MAX_NAME_LENGTH, ntohs(tmp.namelength));
|
||||
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1);
|
||||
memcpy(Blocked.list[i].name, tmp.name, Blocked.list[i].namelength + 1); // copy null byte
|
||||
memcpy(Blocked.list[i].pub_key, tmp.pub_key, TOX_PUBLIC_KEY_SIZE);
|
||||
|
||||
uint8_t lastonline[sizeof(uint64_t)];
|
||||
@ -457,6 +466,10 @@ void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort)
|
||||
if (sort)
|
||||
sort_friendlist_index();
|
||||
|
||||
#ifdef AUDIO
|
||||
init_friend_AV(i);
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -596,6 +609,10 @@ static void delete_friend(Tox *m, uint32_t f_num)
|
||||
Friends.max_idx = i;
|
||||
realloc_friends(i);
|
||||
|
||||
#ifdef AUDIO
|
||||
del_friend_AV(i);
|
||||
#endif
|
||||
|
||||
/* make sure num_selected stays within Friends.num_friends range */
|
||||
if (Friends.num_friends && Friends.num_selected == Friends.num_friends)
|
||||
--Friends.num_selected;
|
||||
@ -657,7 +674,7 @@ static void draw_del_popup(void)
|
||||
wattroff(PendingDelete.popup, A_BOLD);
|
||||
wprintw(PendingDelete.popup, "? y/n");
|
||||
|
||||
wrefresh(PendingDelete.popup);
|
||||
wnoutrefresh(PendingDelete.popup);
|
||||
}
|
||||
|
||||
/* deletes contact from blocked list */
|
||||
@ -886,7 +903,7 @@ static void blocklist_onDraw(ToxWindow *self, Tox *m, int y2, int x2)
|
||||
wprintw(self->window, "%02X", Blocked.list[selected_num].pub_key[i] & 0xff);
|
||||
}
|
||||
|
||||
wrefresh(self->window);
|
||||
wnoutrefresh(self->window);
|
||||
draw_del_popup();
|
||||
|
||||
if (self->help->active)
|
||||
@ -1103,7 +1120,7 @@ static void friendlist_onDraw(ToxWindow *self, Tox *m)
|
||||
wprintw(self->window, "%02X", Friends.list[selected_num].pub_key[i] & 0xff);
|
||||
}
|
||||
|
||||
wrefresh(self->window);
|
||||
wnoutrefresh(self->window);
|
||||
draw_del_popup();
|
||||
|
||||
if (self->help->active)
|
||||
@ -1146,6 +1163,18 @@ static void friendlist_onAV(ToxWindow *self, ToxAV *av, uint32_t friend_number,
|
||||
}
|
||||
#endif /* AUDIO */
|
||||
|
||||
/* Returns a friend's status */
|
||||
TOX_USER_STATUS get_friend_status(uint32_t friendnumber)
|
||||
{
|
||||
return Friends.list[friendnumber].status;
|
||||
}
|
||||
|
||||
/* Returns a friend's connection status */
|
||||
TOX_CONNECTION get_friend_connection_status(uint32_t friendnumber)
|
||||
{
|
||||
return Friends.list[friendnumber].connection_status;
|
||||
}
|
||||
|
||||
ToxWindow new_friendlist(void)
|
||||
{
|
||||
ToxWindow ret;
|
||||
|
@ -44,7 +44,7 @@ struct GroupChatInvite {
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
uint16_t namelength;
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
|
||||
size_t statusmsg_len;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
@ -54,7 +54,7 @@ typedef struct {
|
||||
TOX_CONNECTION connection_status;
|
||||
bool is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
TOX_USER_STATUS status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct GroupChatInvite group_invite;
|
||||
@ -65,7 +65,7 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
uint16_t namelength;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
bool active;
|
||||
@ -87,6 +87,8 @@ int get_friendnum(uint8_t *name);
|
||||
int load_blocklist(char *data);
|
||||
void kill_friendlist(void);
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||
TOX_USER_STATUS get_friend_status(uint32_t friendnumber);
|
||||
TOX_CONNECTION get_friend_connection_status(uint32_t friendnumber);
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
void sort_friendlist_index(void);
|
||||
|
@ -336,32 +336,31 @@ void cmd_groupchat(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
||||
uint8_t type;
|
||||
|
||||
if (!strcasecmp(argv[1], "audio"))
|
||||
type = TOX_GROUPCHAT_TYPE_AV;
|
||||
type = TOX_CONFERENCE_TYPE_AV;
|
||||
else if (!strcasecmp(argv[1], "text"))
|
||||
type = TOX_GROUPCHAT_TYPE_TEXT;
|
||||
type = TOX_CONFERENCE_TYPE_TEXT;
|
||||
else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Valid group types are: text | audio");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = -1;
|
||||
if (type != TOX_CONFERENCE_TYPE_TEXT) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Toxic does not support audio groups.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_add_groupchat(m);
|
||||
TOX_ERR_CONFERENCE_NEW err;
|
||||
|
||||
/*#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_add_av_groupchat(m, NULL, NULL);
|
||||
#endif*/
|
||||
uint32_t groupnum = tox_conference_new(m, &err);
|
||||
|
||||
if (groupnum == -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_del_groupchat(m, groupnum);
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -656,6 +655,9 @@ void cmd_status(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[
|
||||
msg[len] = '\0';
|
||||
|
||||
prompt_update_statusmessage(prompt, m, msg);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s: \"%s\".", status_str, msg);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Your status has been changed to %s.", status_str);
|
||||
}
|
||||
|
||||
finish:
|
||||
|
@ -56,4 +56,8 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)
|
||||
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#ifdef PYTHON
|
||||
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif
|
||||
|
||||
#endif /* #define GLOBAL_COMMANDS_H */
|
||||
|
@ -30,18 +30,25 @@
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
TOX_ERR_CONFERENCE_TITLE err;
|
||||
char title[MAX_STR_SIZE];
|
||||
|
||||
if (argc < 1) {
|
||||
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
|
||||
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||
|
||||
if (tlen != -1) {
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
} else {
|
||||
if (err != TOX_ERR_CONFERENCE_TITLE_OK) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,8 +62,8 @@ void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*arg
|
||||
int len = strlen(title) - 1;
|
||||
title[len] = '\0';
|
||||
|
||||
if (tox_group_set_title(m, self->num, (uint8_t *) title, len) != 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title.");
|
||||
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
|
551
src/groupchat.c
551
src/groupchat.c
@ -69,8 +69,12 @@ static int max_groupchat_index = 0;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
#ifdef AUDIO
|
||||
#if defined(AUDIO) && defined(PYTHON)
|
||||
#define AC_NUM_GROUP_COMMANDS 25
|
||||
#elif AUDIO
|
||||
#define AC_NUM_GROUP_COMMANDS 24
|
||||
#elif PYTHON
|
||||
#define AC_NUM_GROUP_COMMANDS 21
|
||||
#else
|
||||
#define AC_NUM_GROUP_COMMANDS 20
|
||||
#endif /* AUDIO */
|
||||
@ -98,25 +102,18 @@ static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/status" },
|
||||
{ "/title" },
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef PYTHON
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
{ "/mute" },
|
||||
{ "/sense" },
|
||||
{ "/run" },
|
||||
|
||||
#endif /* AUDIO */
|
||||
#endif /* PYTHON */
|
||||
};
|
||||
|
||||
#ifdef AUDIO
|
||||
static int group_audio_open_out_device(int groupnum);
|
||||
static int group_audio_close_out_device(int groupnum);
|
||||
#endif /* AUDIO */
|
||||
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type)
|
||||
{
|
||||
if (groupnum > MAX_GROUPCHAT_NUM)
|
||||
if (groupnum > MAX_GROUPCHAT_NUM) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ToxWindow self = new_group_chat(m, groupnum);
|
||||
int i;
|
||||
@ -129,26 +126,6 @@ int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type)
|
||||
groupchats[i].type = type;
|
||||
groupchats[i].start_time = get_unix_time();
|
||||
|
||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
|
||||
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
||||
|
||||
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|
||||
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
|
||||
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
|
||||
|
||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_AV)
|
||||
if (group_audio_open_out_device(i) == -1)
|
||||
fprintf(stderr, "Group Audio failed to init\n");
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
set_active_window(groupchats[i].chatwin);
|
||||
|
||||
if (i == max_groupchat_index)
|
||||
@ -176,17 +153,10 @@ static void kill_groupchat_window(ToxWindow *self)
|
||||
del_window(self);
|
||||
}
|
||||
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||
void free_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||
{
|
||||
tox_del_groupchat(m, groupnum);
|
||||
#ifdef AUDIO
|
||||
group_audio_close_out_device(groupnum);
|
||||
#endif
|
||||
|
||||
free(groupchats[groupnum].peer_names);
|
||||
free(groupchats[groupnum].oldpeer_names);
|
||||
free(groupchats[groupnum].peer_name_lengths);
|
||||
free(groupchats[groupnum].oldpeer_name_lengths);
|
||||
free(groupchats[groupnum].name_list);
|
||||
free(groupchats[groupnum].peer_list);
|
||||
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
||||
|
||||
int i;
|
||||
@ -200,6 +170,12 @@ void close_groupchat(ToxWindow *self, Tox *m, int groupnum)
|
||||
kill_groupchat_window(self);
|
||||
}
|
||||
|
||||
static void delete_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||
{
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
free_groupchat(self, m, groupnum);
|
||||
}
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self)
|
||||
{
|
||||
@ -239,8 +215,8 @@ void redraw_groupchat_win(ToxWindow *self)
|
||||
|
||||
}
|
||||
|
||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||
const char *msg, uint16_t len)
|
||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
TOX_MESSAGE_TYPE type, const char *msg, size_t len)
|
||||
{
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
@ -275,47 +251,13 @@ static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
|
||||
line_info_add(self, timefrmt, nick, NULL, type == TOX_MESSAGE_TYPE_NORMAL ? IN_MSG : IN_ACTION, 0, nick_clr, "%s", msg);
|
||||
write_to_log(msg, nick, ctx->log, false);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupAction(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *action,
|
||||
uint16_t len)
|
||||
{
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t n_len = tox_self_get_name_size(m);
|
||||
selfnick[n_len] = '\0';
|
||||
|
||||
if (strcasestr(action, selfnick)) {
|
||||
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "* %s %s", nick, action );
|
||||
else
|
||||
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "* %s %s", nick, action);
|
||||
} else {
|
||||
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, IN_ACTION, 0, 0, "%s", action);
|
||||
write_to_log(action, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum, int peernum, const char *title,
|
||||
uint8_t length)
|
||||
static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
const char *title,
|
||||
size_t length)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -340,96 +282,102 @@ static void groupchat_onGroupTitleChange(ToxWindow *self, Tox *m, int groupnum,
|
||||
write_to_log(tmp_event, nick, ctx->log, true);
|
||||
}
|
||||
|
||||
/* Puts two copies of peerlist/lengths in chat instance */
|
||||
static void copy_peernames(int gnum, uint8_t peerlist[][TOX_MAX_NAME_LENGTH], uint16_t lengths[], int npeers)
|
||||
static void group_update_name_list(uint32_t groupnum)
|
||||
{
|
||||
/* Assumes these are initiated in init_groupchat_win */
|
||||
free(groupchats[gnum].peer_names);
|
||||
free(groupchats[gnum].oldpeer_names);
|
||||
free(groupchats[gnum].peer_name_lengths);
|
||||
free(groupchats[gnum].oldpeer_name_lengths);
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
int N = TOX_MAX_NAME_LENGTH;
|
||||
|
||||
groupchats[gnum].peer_names = malloc(sizeof(uint8_t) * npeers * N);
|
||||
groupchats[gnum].oldpeer_names = malloc(sizeof(uint8_t) * npeers * N);
|
||||
groupchats[gnum].peer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
||||
groupchats[gnum].oldpeer_name_lengths = malloc(sizeof(uint16_t) * npeers);
|
||||
|
||||
if (groupchats[gnum].peer_names == NULL || groupchats[gnum].oldpeer_names == NULL
|
||||
|| groupchats[gnum].peer_name_lengths == NULL || groupchats[gnum].oldpeer_name_lengths == NULL) {
|
||||
exit_toxic_err("failed in copy_peernames", FATALERR_MEMORY);
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t u_len = strlen(UNKNOWN_NAME);
|
||||
int i;
|
||||
if (chat->name_list) {
|
||||
free(chat->name_list);
|
||||
}
|
||||
|
||||
for (i = 0; i < npeers; ++i) {
|
||||
if (!lengths[i]) {
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], UNKNOWN_NAME, u_len);
|
||||
groupchats[gnum].peer_names[i * N + u_len] = '\0';
|
||||
groupchats[gnum].peer_name_lengths[i] = u_len;
|
||||
} else {
|
||||
uint16_t n_len = MIN(lengths[i], TOXIC_MAX_NAME_LENGTH - 1);
|
||||
memcpy(&groupchats[gnum].peer_names[i * N], peerlist[i], n_len);
|
||||
groupchats[gnum].peer_names[i * N + n_len] = '\0';
|
||||
groupchats[gnum].peer_name_lengths[i] = n_len;
|
||||
filter_str((char *) &groupchats[gnum].peer_names[i * N], n_len);
|
||||
chat->name_list = malloc(sizeof(char *) * chat->num_peers * TOX_MAX_NAME_LENGTH);
|
||||
|
||||
if (chat->name_list == NULL) {
|
||||
exit_toxic_err("failed in group_update_name_list", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
uint32_t i, count = 0;
|
||||
|
||||
for (i = 0; i < chat->max_idx; ++i) {
|
||||
if (chat->peer_list[i].active) {
|
||||
memcpy(&chat->name_list[count * TOX_MAX_NAME_LENGTH], chat->peer_list[i].name, chat->peer_list[i].name_length + 1);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
|
||||
qsort(chat->name_list, count, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
||||
}
|
||||
|
||||
struct group_add_thrd {
|
||||
Tox *m;
|
||||
ToxWindow *self;
|
||||
int peernum;
|
||||
int groupnum;
|
||||
time_t timestamp;
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
};
|
||||
|
||||
/* Waits GROUP_EVENT_WAIT seconds for a new peer to set their name before announcing them */
|
||||
void *group_add_wait(void *data)
|
||||
/* Reallocates groupnum's peer list. Increase is true if the list needs to grow.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
static int realloc_peer_list(GroupChat *chat, uint32_t num_peers)
|
||||
{
|
||||
struct group_add_thrd *thrd = (struct group_add_thrd *) data;
|
||||
ToxWindow *self = thrd->self;
|
||||
Tox *m = thrd->m;
|
||||
char peername[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
/* keep polling for a name that differs from the default until we run out of time */
|
||||
while (true) {
|
||||
usleep(100000);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
get_group_nick_truncate(m, peername, thrd->peernum, thrd->groupnum);
|
||||
|
||||
if (strcmp(peername, DEFAULT_TOX_NAME) || timed_out(thrd->timestamp, GROUP_EVENT_WAIT)) {
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
break;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
if (!chat) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *event = "has joined the room";
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
if (num_peers == 0) {
|
||||
free(chat->peer_list);
|
||||
chat->peer_list = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
|
||||
write_to_log(event, (char *) peername, self->chatwin->log, true);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
struct GroupPeer *tmp_list = realloc(chat->peer_list, num_peers * sizeof(struct GroupPeer));
|
||||
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
pthread_exit(NULL);
|
||||
if (!tmp_list) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
chat->peer_list = tmp_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
|
||||
static void update_peer_list(Tox *m, uint32_t groupnum, uint32_t num_peers)
|
||||
{
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
realloc_peer_list(chat, num_peers);
|
||||
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < num_peers; ++i) {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||
size_t length = tox_conference_peer_get_name_size(m, groupnum, i, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK || length >= TOX_MAX_NAME_LENGTH) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tox_conference_peer_get_name(m, groupnum, i, (uint8_t *) peer->name, &err);
|
||||
peer->name[length] = 0;
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer->active = true;
|
||||
peer->name_length = length;
|
||||
peer->peernumber = i;
|
||||
}
|
||||
|
||||
group_update_name_list(groupnum);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNameListChange(ToxWindow *self, Tox *m, uint32_t groupnum)
|
||||
{
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
@ -437,109 +385,57 @@ static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnu
|
||||
if (groupnum > max_groupchat_index)
|
||||
return;
|
||||
|
||||
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
||||
int num_peers = groupchats[groupnum].num_peers;
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||
|
||||
if (peernum > num_peers)
|
||||
uint32_t num_peers = tox_conference_peer_count(m, groupnum, &err);
|
||||
uint32_t old_num = chat->num_peers;
|
||||
|
||||
if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
chat->num_peers = num_peers;
|
||||
} else {
|
||||
num_peers = old_num;
|
||||
}
|
||||
|
||||
chat->max_idx = num_peers;
|
||||
update_peer_list(m, groupnum, num_peers);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupPeerNameChange(ToxWindow *self, Tox *m, uint32_t groupnum, uint32_t peernum,
|
||||
const char *name, size_t length)
|
||||
{
|
||||
if (self->num != groupnum) {
|
||||
return;
|
||||
|
||||
/* get old peer name before updating name list */
|
||||
uint8_t oldpeername[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
if (change != TOX_CHAT_CHANGE_PEER_ADD) {
|
||||
memcpy(oldpeername, &groupchats[groupnum].oldpeer_names[peernum * TOX_MAX_NAME_LENGTH],
|
||||
sizeof(oldpeername));
|
||||
uint16_t old_n_len = groupchats[groupnum].oldpeer_name_lengths[peernum];
|
||||
oldpeername[old_n_len] = '\0';
|
||||
}
|
||||
|
||||
/* Update name/len lists */
|
||||
uint8_t tmp_peerlist[num_peers][TOX_MAX_NAME_LENGTH];
|
||||
uint16_t tmp_peerlens[num_peers];
|
||||
GroupChat *chat = &groupchats[groupnum];
|
||||
|
||||
if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
|
||||
memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
|
||||
memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||
size_t i;
|
||||
|
||||
/* get current peername then sort namelist */
|
||||
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
||||
for (i = 0; i < chat->max_idx; ++i) {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
if (change != TOX_CHAT_CHANGE_PEER_DEL) {
|
||||
uint16_t n_len = groupchats[groupnum].peer_name_lengths[peernum];
|
||||
memcpy(peername, &groupchats[groupnum].peer_names[peernum * TOX_MAX_NAME_LENGTH], sizeof(peername));
|
||||
peername[n_len] = '\0';
|
||||
}
|
||||
|
||||
qsort(groupchats[groupnum].peer_names, groupchats[groupnum].num_peers, TOX_MAX_NAME_LENGTH, qsort_strcasecmp_hlpr);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
const char *event;
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
switch (change) {
|
||||
case TOX_CHAT_CHANGE_PEER_ADD:
|
||||
if (!timed_out(groupchats[groupnum].start_time, GROUP_EVENT_WAIT))
|
||||
break;
|
||||
|
||||
struct group_add_thrd *thrd = malloc(sizeof(struct group_add_thrd));
|
||||
thrd->m = m;
|
||||
thrd->peernum = peernum;
|
||||
thrd->groupnum = groupnum;
|
||||
thrd->self = self;
|
||||
thrd->timestamp = get_unix_time();
|
||||
|
||||
if (pthread_attr_init(&thrd->attr) != 0) {
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_attr_setdetachstate(&thrd->attr, PTHREAD_CREATE_DETACHED) != 0) {
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pthread_create(&thrd->tid, &thrd->attr, group_add_wait, (void *) thrd) != 0) {
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TOX_CHAT_CHANGE_PEER_DEL:
|
||||
event = "has left the room";
|
||||
line_info_add(self, timefrmt, (char *) oldpeername, NULL, DISCONNECTION, 0, RED, event);
|
||||
|
||||
if (groupchats[self->num].side_pos > 0)
|
||||
--groupchats[self->num].side_pos;
|
||||
|
||||
write_to_log(event, (char *) oldpeername, ctx->log, true);
|
||||
break;
|
||||
|
||||
case TOX_CHAT_CHANGE_PEER_NAME:
|
||||
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||
return;
|
||||
|
||||
/* ignore initial name change (TODO: this is a bad way to do this) */
|
||||
if (strcmp((char *) oldpeername, DEFAULT_TOX_NAME) == 0)
|
||||
return;
|
||||
|
||||
event = " is now known as ";
|
||||
line_info_add(self, timefrmt, (char *) oldpeername, (char *) peername, NAME_CHANGE, 0, 0, event);
|
||||
// Test against default tox name to prevent nick change spam on initial join (TODO: this is disgusting)
|
||||
if (peer->active && peer->peernumber == peernum && strcmp(peer->name, "Tox User")) {
|
||||
ChatContext *ctx = self->chatwin;
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
char tmp_event[TOXIC_MAX_NAME_LENGTH * 2 + 32];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) peername);
|
||||
write_to_log(tmp_event, (char *) oldpeername, ctx->log, true);
|
||||
snprintf(tmp_event, sizeof(tmp_event), "is now known as %s", (char *) name);
|
||||
|
||||
write_to_log(tmp_event, peer->name, ctx->log, true);
|
||||
line_info_add(self, timefrmt, peer->name, (char *) name, NAME_CHANGE, 0, 0, " is now known as ");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sound_notify(self, silent, NT_WNDALERT_2, NULL);
|
||||
groupchat_onGroupNameListChange(self, m, groupnum);
|
||||
}
|
||||
|
||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||
@ -549,9 +445,10 @@ static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *a
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_group_action_send(m, self->num, (uint8_t *) action, strlen(action)) == -1) {
|
||||
const char *errmsg = " * Failed to send action.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
|
||||
TOX_ERR_CONFERENCE_SEND_MESSAGE err;
|
||||
|
||||
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_ACTION, (uint8_t *) action, strlen(action), &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send action (error %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
@ -591,11 +488,20 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
/* TODO: make this not suck */
|
||||
if (ctx->line[0] != L'/' || wcscmp(ctx->line, L"/me") == 0) {
|
||||
diff = complete_line(self, groupchats[self->num].peer_names, groupchats[self->num].num_peers,
|
||||
diff = complete_line(self, groupchats[self->num].name_list, groupchats[self->num].num_peers,
|
||||
TOX_MAX_NAME_LENGTH);
|
||||
} else if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
} else {
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0) {
|
||||
diff = dir_match(self, m, ctx->line, L"/run");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
else {
|
||||
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
}
|
||||
|
||||
@ -633,7 +539,7 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
if (line[0] == '/') {
|
||||
if (strcmp(line, "/close") == 0) {
|
||||
close_groupchat(self, m, self->num);
|
||||
delete_groupchat(self, m, self->num);
|
||||
return;
|
||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
||||
send_group_action(self, ctx, m, line + strlen("/me "));
|
||||
@ -641,9 +547,10 @@ static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
execute(ctx->history, self, m, line, GROUPCHAT_COMMAND_MODE);
|
||||
}
|
||||
} else {
|
||||
if (tox_group_message_send(m, self->num, (uint8_t *) line, strlen(line)) == -1) {
|
||||
const char *errmsg = " * Failed to send message.";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, errmsg);
|
||||
TOX_ERR_CONFERENCE_SEND_MESSAGE err;
|
||||
|
||||
if (!tox_conference_send_message(m, self->num, TOX_MESSAGE_TYPE_NORMAL, (uint8_t *) line, strlen(line), &err)) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Failed to send message (error %d)", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -701,7 +608,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
int peer = i + groupchats[self->num].side_pos;
|
||||
uint32_t peer = i + groupchats[self->num].side_pos;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
@ -709,7 +616,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
int maxlen = SIDEBAR_WIDTH - 2;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||
memcpy(tmpnck, &groupchats[self->num].name_list[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
tmpnck[maxlen] = '\0';
|
||||
@ -724,7 +631,7 @@ static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wrefresh(self->window);
|
||||
wnoutrefresh(self->window);
|
||||
|
||||
if (self->help->active)
|
||||
help_onDraw(self);
|
||||
@ -766,125 +673,7 @@ static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
}
|
||||
|
||||
|
||||
#ifdef AUDIO
|
||||
static int group_audio_open_out_device(int groupnum)
|
||||
{
|
||||
char dname[MAX_STR_SIZE];
|
||||
get_primary_device_name(output, dname, sizeof(dname));
|
||||
dname[MAX_STR_SIZE - 1] = '\0';
|
||||
|
||||
groupchats[groupnum].audio.dvhandle = alcOpenDevice(dname);
|
||||
|
||||
if (groupchats[groupnum].audio.dvhandle == NULL)
|
||||
return -1;
|
||||
|
||||
groupchats[groupnum].audio.dvctx = alcCreateContext(groupchats[groupnum].audio.dvhandle, NULL);
|
||||
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
|
||||
alGenBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
alGenSources((uint32_t) 1, &groupchats[groupnum].audio.source);
|
||||
alSourcei(groupchats[groupnum].audio.source, AL_LOOPING, AL_FALSE);
|
||||
|
||||
if (alcGetError(groupchats[groupnum].audio.dvhandle) != AL_NO_ERROR) {
|
||||
group_audio_close_out_device(groupnum);
|
||||
groupchats[groupnum].audio.dvhandle = NULL;
|
||||
groupchats[groupnum].audio.dvctx = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
alSourceQueueBuffers(groupchats[groupnum].audio.source, OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
alSourcePlay(groupchats[groupnum].audio.source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int group_audio_close_out_device(int groupnum)
|
||||
{
|
||||
if (!groupchats[groupnum].audio.dvhandle)
|
||||
return -1;
|
||||
|
||||
if (!groupchats[groupnum].audio.dvctx)
|
||||
return -1;
|
||||
|
||||
if (alcGetCurrentContext() != groupchats[groupnum].audio.dvctx)
|
||||
alcMakeContextCurrent(groupchats[groupnum].audio.dvctx);
|
||||
|
||||
alDeleteSources((uint32_t) 1, &groupchats[groupnum].audio.source);
|
||||
alDeleteBuffers(OPENAL_BUFS, groupchats[groupnum].audio.buffers);
|
||||
|
||||
alcMakeContextCurrent(NULL);
|
||||
alcDestroyContext(groupchats[groupnum].audio.dvctx);
|
||||
|
||||
if (!alcCloseDevice(groupchats[groupnum].audio.dvhandle))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// static int group_audio_write(int peernum, int groupnum, const int16_t *pcm, unsigned int samples, uint8_t channels,
|
||||
// unsigned int sample_rate)
|
||||
// {
|
||||
// if (!pcm)
|
||||
// return -1;
|
||||
|
||||
// if (channels == 0 || channels > 2)
|
||||
// return -2;
|
||||
|
||||
// ALuint bufid;
|
||||
// ALint processed = 0, queued = 0;
|
||||
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_PROCESSED, &processed);
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_BUFFERS_QUEUED, &queued);
|
||||
// fprintf(stderr, "source: %d, queued: %d, processed: %d\n", groupchats[groupnum].audio.source, queued, processed);
|
||||
|
||||
// if (processed) {
|
||||
// ALuint bufids[processed];
|
||||
// alSourceUnqueueBuffers(groupchats[groupnum].audio.source, processed, bufids);
|
||||
// alDeleteBuffers(processed - 1, bufids + 1);
|
||||
// bufid = bufids[0];
|
||||
// } else if (queued < 16) {
|
||||
// alGenBuffers(1, &bufid);
|
||||
// } else {
|
||||
// return -3;
|
||||
// }
|
||||
|
||||
// int length = samples * channels * sizeof(int16_t);
|
||||
|
||||
// alBufferData(bufid, (channels == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16, pcm, length, sample_rate);
|
||||
// alSourceQueueBuffers(groupchats[groupnum].audio.source, 1, &bufid);
|
||||
|
||||
// ALint state;
|
||||
// alGetSourcei(groupchats[groupnum].audio.source, AL_SOURCE_STATE, &state);
|
||||
|
||||
// if (state != AL_PLAYING)
|
||||
// alSourcePlay(groupchats[groupnum].audio.source);
|
||||
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
static void groupchat_onWriteDevice(ToxWindow *self, Tox *m, uint32_t groupnum, int peernum, const int16_t *pcm,
|
||||
unsigned int samples, uint8_t channels, unsigned int sample_rate)
|
||||
{
|
||||
return;
|
||||
|
||||
// if (groupnum != self->num)
|
||||
// return;
|
||||
|
||||
// if (peernum < 0)
|
||||
// return;
|
||||
|
||||
// if (groupchats[groupnum].audio.dvhandle == NULL)
|
||||
// fprintf(stderr, "dvhandle is null)\n");
|
||||
|
||||
// if (groupchats[groupnum].audio.dvctx == NULL)
|
||||
// fprintf(stderr, "ctx is null\n");
|
||||
|
||||
// int ret = group_audio_write(peernum, groupnum, pcm, samples, channels, sample_rate);
|
||||
// fprintf(stderr, "write: %d\n", ret);
|
||||
}
|
||||
#endif /* AUDIO */
|
||||
|
||||
ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||
ToxWindow new_group_chat(Tox *m, uint32_t groupnum)
|
||||
{
|
||||
ToxWindow ret;
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
@ -896,14 +685,10 @@ ToxWindow new_group_chat(Tox *m, int groupnum)
|
||||
ret.onDraw = &groupchat_onDraw;
|
||||
ret.onInit = &groupchat_onInit;
|
||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||
ret.onGroupAction = &groupchat_onGroupAction;
|
||||
ret.onGroupNameListChange = &groupchat_onGroupNameListChange;
|
||||
ret.onGroupPeerNameChange = &groupchat_onGroupPeerNameChange;
|
||||
ret.onGroupTitleChange = &groupchat_onGroupTitleChange;
|
||||
|
||||
#ifdef AUDIO
|
||||
ret.onWriteDevice = &groupchat_onWriteDevice;
|
||||
#endif
|
||||
|
||||
snprintf(ret.name, sizeof(ret.name), "Group %d", groupnum);
|
||||
|
||||
ChatContext *chatwin = calloc(1, sizeof(ChatContext));
|
||||
|
@ -26,61 +26,41 @@
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef AUDIO
|
||||
#include "audio_call.h"
|
||||
#endif
|
||||
|
||||
#ifdef AUDIO
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
/* compatibility with older versions of OpenAL */
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#include <AL/alext.h>
|
||||
#endif /* ALC_ALL_DEVICES_SPECIFIER */
|
||||
#endif /* __APPLE__ */
|
||||
#endif /* AUDIO */
|
||||
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#define SDBAR_OFST 2 /* Offset for the peer number box at the top of the statusbar */
|
||||
#define MAX_GROUPCHAT_NUM MAX_WINDOWS_NUM - 2
|
||||
#define GROUP_EVENT_WAIT 3
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio {
|
||||
ALCdevice *dvhandle; /* Handle of device selected/opened */
|
||||
ALCcontext *dvctx;
|
||||
ALuint source;
|
||||
ALuint buffers[OPENAL_BUFS];
|
||||
};
|
||||
#endif /* AUDIO */
|
||||
typedef struct GroupPeer {
|
||||
bool active;
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
uint32_t peernumber;
|
||||
} GroupPeer;
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int num_peers;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
time_t start_time;
|
||||
uint8_t *peer_names;
|
||||
uint8_t *oldpeer_names;
|
||||
uint16_t *peer_name_lengths;
|
||||
uint16_t *oldpeer_name_lengths;
|
||||
|
||||
#ifdef AUDIO
|
||||
struct GAudio audio;
|
||||
#endif
|
||||
GroupPeer *peer_list;
|
||||
size_t max_idx;
|
||||
|
||||
char *name_list;
|
||||
size_t num_peers;
|
||||
|
||||
} GroupChat;
|
||||
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int groupnum, uint8_t type);
|
||||
/* Frees all Toxic associated data structures for a groupchat (does not call tox_conference_delete() ) */
|
||||
void free_groupchat(ToxWindow *self, Tox *m, uint32_t groupnum);
|
||||
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, uint32_t groupnum, uint8_t type);
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self);
|
||||
|
||||
ToxWindow new_group_chat(Tox *m, int groupnum);
|
||||
ToxWindow new_group_chat(Tox *m, uint32_t groupnum);
|
||||
|
||||
#endif /* #define GROUPCHAT_H */
|
||||
|
90
src/help.c
90
src/help.c
@ -27,7 +27,15 @@
|
||||
#include "help.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#ifdef PYTHON
|
||||
#include "api.h"
|
||||
#endif /* PYTHON */
|
||||
|
||||
#ifdef PYTHON
|
||||
#define HELP_MENU_HEIGHT 10
|
||||
#else
|
||||
#define HELP_MENU_HEIGHT 9
|
||||
#endif /* PYTHON */
|
||||
#define HELP_MENU_WIDTH 26
|
||||
|
||||
void help_init_menu(ToxWindow *self)
|
||||
@ -95,6 +103,13 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "oup commands\n");
|
||||
|
||||
#ifdef PYTHON
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " p");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "lugin commands\n");
|
||||
#endif /* PYTHON */
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, " f");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
@ -112,7 +127,7 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wprintw(win, "it menu\n");
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_bottom_menu(WINDOW *win)
|
||||
@ -185,10 +200,18 @@ static void help_draw_global(ToxWindow *self)
|
||||
wprintw(win, " /svdev <type> <id> : Set active video device\n");
|
||||
#endif /* VIDEO */
|
||||
|
||||
#ifdef PYTHON
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Scripting:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
|
||||
wprintw(win, " /run <path> : Load and run the script at path\n");
|
||||
#endif /* PYTHON */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_chat(ToxWindow *self)
|
||||
@ -219,6 +242,7 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wprintw(win, " /sdev <type> <id> : Change active device\n");
|
||||
wprintw(win, " /mute <type> : Mute active device if in call\n");
|
||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n");
|
||||
wprintw(win, " /bitrate <n> : Set the audio encoding bitrate\n");
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef VIDEO
|
||||
@ -231,7 +255,7 @@ static void help_draw_chat(ToxWindow *self)
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_keys(ToxWindow *self)
|
||||
@ -257,7 +281,7 @@ static void help_draw_keys(ToxWindow *self)
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_group(ToxWindow *self)
|
||||
@ -275,9 +299,29 @@ static void help_draw_group(ToxWindow *self)
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
static void help_draw_plugin(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Plugin commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
draw_handler_help(win);
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void help_draw_contacts(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
@ -297,11 +341,13 @@ static void help_draw_contacts(ToxWindow *self)
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
void help_onKey(ToxWindow *self, wint_t key)
|
||||
{
|
||||
int height;
|
||||
|
||||
switch (key) {
|
||||
case 'x':
|
||||
case T_KEY_ESC:
|
||||
@ -310,9 +356,9 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
|
||||
case 'c':
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 22, 80);
|
||||
help_init_window(self, 23, 80);
|
||||
#elif AUDIO
|
||||
help_init_window(self, 19, 80);
|
||||
help_init_window(self, 20, 80);
|
||||
#else
|
||||
help_init_window(self, 10, 80);
|
||||
#endif
|
||||
@ -320,13 +366,16 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
height = 22;
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 30, 80);
|
||||
height += 8;
|
||||
#elif AUDIO
|
||||
help_init_window(self, 26, 80);
|
||||
#else
|
||||
help_init_window(self, 22, 80);
|
||||
height += 4;
|
||||
#endif
|
||||
#ifdef PYTHON
|
||||
height += 2;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
||||
@ -335,6 +384,14 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
self->help->type = HELP_GROUP;
|
||||
break;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
case 'p':
|
||||
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
|
||||
self->help->type = HELP_PLUGIN;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case 'f':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_CONTACTS;
|
||||
@ -354,8 +411,6 @@ void help_onKey(ToxWindow *self, wint_t key)
|
||||
|
||||
void help_onDraw(ToxWindow *self)
|
||||
{
|
||||
curs_set(0);
|
||||
|
||||
switch (self->help->type) {
|
||||
case HELP_MENU:
|
||||
help_draw_menu(self);
|
||||
@ -380,5 +435,12 @@ void help_onDraw(ToxWindow *self)
|
||||
case HELP_GROUP:
|
||||
help_draw_group(self);
|
||||
break;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
case HELP_PLUGIN:
|
||||
help_draw_plugin(self);
|
||||
break;
|
||||
#endif /* PYTHON */
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,9 @@ typedef enum {
|
||||
HELP_GROUP,
|
||||
HELP_KEYS,
|
||||
HELP_CONTACTS,
|
||||
#ifdef PYTHON
|
||||
HELP_PLUGIN,
|
||||
#endif
|
||||
} HELP_TYPES;
|
||||
|
||||
void help_onDraw(ToxWindow *self);
|
||||
|
@ -129,17 +129,21 @@ static struct line_info *line_info_ret_queue(struct history *hst)
|
||||
return line;
|
||||
}
|
||||
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
*
|
||||
* Returns the id of the new line.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...)
|
||||
{
|
||||
if (!self)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
struct history *hst = self->chatwin->hst;
|
||||
|
||||
if (hst->queue_sz >= MAX_LINE_INFO_QUEUE)
|
||||
return;
|
||||
return -1;
|
||||
|
||||
struct line_info *new_line = calloc(1, sizeof(struct line_info));
|
||||
|
||||
@ -222,6 +226,7 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
|
||||
len += strlen(new_line->name2);
|
||||
}
|
||||
|
||||
new_line->id = (hst->line_end->id + 1 + hst->queue_sz) % INT_MAX;
|
||||
new_line->len = len;
|
||||
new_line->type = type;
|
||||
new_line->bold = bold;
|
||||
@ -230,6 +235,8 @@ void line_info_add(ToxWindow *self, const char *timestr, const char *name1, cons
|
||||
new_line->timestamp = get_unix_time();
|
||||
|
||||
hst->queue[hst->queue_sz++] = new_line;
|
||||
|
||||
return new_line->id;
|
||||
}
|
||||
|
||||
/* adds a single queue item to hst if possible. only called once per call to line_info_print() */
|
||||
@ -244,10 +251,10 @@ static void line_info_check_queue(ToxWindow *self)
|
||||
if (hst->start_id > user_settings->history_size)
|
||||
line_info_root_fwd(hst);
|
||||
|
||||
line->id = hst->line_end->id + 1;
|
||||
line->prev = hst->line_end;
|
||||
hst->line_end->next = line;
|
||||
hst->line_end = line;
|
||||
hst->line_end->id = line->id;
|
||||
|
||||
int y, y2, x, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
@ -31,7 +31,7 @@
|
||||
#define MAX_LINE_INFO_QUEUE 1024
|
||||
#define MAX_LINE_INFO_MSG_SIZE MAX_STR_SIZE + TOXIC_MAX_NAME_LENGTH + 32 /* needs extra room for log loading */
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
SYS_MSG,
|
||||
IN_MSG,
|
||||
OUT_MSG,
|
||||
@ -74,9 +74,13 @@ struct history {
|
||||
int queue_sz;
|
||||
};
|
||||
|
||||
/* creates new line_info line and puts it in the queue. */
|
||||
void line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||
/* creates new line_info line and puts it in the queue.
|
||||
*
|
||||
* Returns the id of the new line.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int line_info_add(ToxWindow *self, const char *timestr, const char *name1, const char *name2, uint8_t type,
|
||||
uint8_t bold, uint8_t colour, const char *msg, ...);
|
||||
|
||||
/* Prints a section of history starting at line_start */
|
||||
void line_info_print(ToxWindow *self);
|
||||
|
@ -30,7 +30,7 @@ struct chatlog {
|
||||
bool log_on; /* specific to current chat window */
|
||||
};
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
LOG_GROUP,
|
||||
LOG_PROMPT,
|
||||
LOG_CHAT,
|
||||
|
@ -42,8 +42,12 @@ void cqueue_cleanup(struct chat_queue *q)
|
||||
free(q);
|
||||
}
|
||||
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id)
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id)
|
||||
{
|
||||
if (line_id < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct cqueue_msg *new_m = malloc(sizeof(struct cqueue_msg));
|
||||
|
||||
if (new_m == NULL)
|
||||
|
@ -40,7 +40,7 @@ struct chat_queue {
|
||||
};
|
||||
|
||||
void cqueue_cleanup(struct chat_queue *q);
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, uint32_t line_id);
|
||||
void cqueue_add(struct chat_queue *q, const char *msg, size_t len, uint8_t type, int line_id);
|
||||
|
||||
/* Tries to send the oldest unsent message in queue. */
|
||||
void cqueue_try_send(ToxWindow *self, Tox *m);
|
||||
|
@ -26,12 +26,9 @@
|
||||
#include <time.h>
|
||||
#include <limits.h>
|
||||
#include <dirent.h>
|
||||
#if defined(__FreeBSD__)
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "toxic.h"
|
||||
@ -323,45 +320,71 @@ void str_to_lower(char *str)
|
||||
Returns nick len */
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum)
|
||||
{
|
||||
size_t len = tox_friend_get_name_size(m, friendnum, NULL);
|
||||
TOX_ERR_FRIEND_QUERY err;
|
||||
size_t len = tox_friend_get_name_size(m, friendnum, &err);
|
||||
|
||||
if (len == 0) {
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
if (err != TOX_ERR_FRIEND_QUERY_OK) {
|
||||
goto on_error;
|
||||
} else {
|
||||
tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL);
|
||||
if (!tox_friend_get_name(m, friendnum, (uint8_t *) buf, NULL)) {
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
buf[len] = '\0';
|
||||
filter_str(buf, len);
|
||||
return len;
|
||||
|
||||
on_error:
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
buf[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum)
|
||||
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum)
|
||||
{
|
||||
int len = tox_group_peername(m, groupnum, peernum, (uint8_t *) buf);
|
||||
TOX_ERR_CONFERENCE_PEER_QUERY err;
|
||||
size_t len = tox_conference_peer_get_name_size(m, groupnum, peernum, &err);
|
||||
|
||||
if (len == -1) {
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) {
|
||||
goto on_error;
|
||||
} else {
|
||||
if (!tox_conference_peer_get_name(m, groupnum, peernum, (uint8_t *) buf, NULL)) {
|
||||
goto on_error;
|
||||
}
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
buf[len] = '\0';
|
||||
filter_str(buf, len);
|
||||
return len;
|
||||
|
||||
on_error:
|
||||
strcpy(buf, UNKNOWN_NAME);
|
||||
len = strlen(UNKNOWN_NAME);
|
||||
buf[len] = '\0';
|
||||
return len;
|
||||
}
|
||||
|
||||
/* copies data to msg buffer.
|
||||
/* copies data to msg buffer, removing return characters.
|
||||
returns length of msg, which will be no larger than size-1 */
|
||||
size_t copy_tox_str(char *msg, size_t size, const char *data, size_t length)
|
||||
{
|
||||
size_t len = MIN(length, size - 1);
|
||||
memcpy(msg, data, len);
|
||||
msg[len] = '\0';
|
||||
return len;
|
||||
size_t i;
|
||||
size_t j = 0;
|
||||
|
||||
for (i = 0; (i < length) && (j < size - 1); ++i) {
|
||||
if (data[i] != '\r') {
|
||||
msg[j++] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
msg[j] = '\0';
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
/* returns index of the first instance of ch in s starting at idx.
|
||||
|
@ -126,7 +126,7 @@ void str_to_lower(char *str);
|
||||
size_t get_nick_truncate(Tox *m, char *buf, uint32_t friendnum);
|
||||
|
||||
/* same as get_nick_truncate but for groupchats */
|
||||
int get_group_nick_truncate(Tox *m, char *buf, int peernum, int groupnum);
|
||||
int get_group_nick_truncate(Tox *m, char *buf, uint32_t peernum, uint32_t groupnum);
|
||||
|
||||
/* copies data to msg buffer.
|
||||
returns length of msg, which will be no larger than size-1 */
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
|
@ -208,10 +208,10 @@ void bgrxtoyuv420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v, uint8_t
|
||||
// TODO possibly get a better pixel format
|
||||
if (_shouldMangleDimensions) {
|
||||
[_linkerVideo setVideoSettings: @ {
|
||||
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
|
||||
(id)kCVPixelBufferWidthKey: @640,
|
||||
(id)kCVPixelBufferHeightKey: @480
|
||||
}];
|
||||
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
|
||||
(id)kCVPixelBufferWidthKey: @640,
|
||||
(id)kCVPixelBufferHeightKey: @480
|
||||
}];
|
||||
} else {
|
||||
[_linkerVideo setVideoSettings: @ {(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA)}];
|
||||
}
|
||||
|
30
src/prompt.c
30
src/prompt.c
@ -49,10 +49,16 @@ extern struct Winthread Winthread;
|
||||
|
||||
extern FriendsList Friends;
|
||||
FriendRequests FrndRequests;
|
||||
#ifdef VIDEO
|
||||
#if defined(PYTHON) && defined(VIDEO)
|
||||
#define AC_NUM_GLOB_COMMANDS 23
|
||||
#elif defined(PYTHON) && defined(AUDIO)
|
||||
#define AC_NUM_GLOB_COMMANDS 21
|
||||
#elif VIDEO
|
||||
#define AC_NUM_GLOB_COMMANDS 22
|
||||
#elif AUDIO
|
||||
#define AC_NUM_GLOB_COMMANDS 20
|
||||
#elif PYTHON
|
||||
#define AC_NUM_GLOB_COMMANDS 19
|
||||
#else
|
||||
#define AC_NUM_GLOB_COMMANDS 18
|
||||
#endif
|
||||
@ -92,6 +98,12 @@ static const char glob_cmd_list[AC_NUM_GLOB_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
{ "/run" },
|
||||
|
||||
#endif /* PYTHON */
|
||||
|
||||
};
|
||||
|
||||
void kill_prompt_window(ToxWindow *self)
|
||||
@ -151,6 +163,13 @@ void prompt_update_status(ToxWindow *prompt, TOX_USER_STATUS status)
|
||||
statusbar->status = status;
|
||||
}
|
||||
|
||||
/* Returns our own connection status */
|
||||
TOX_CONNECTION prompt_selfConnectionStatus(void)
|
||||
{
|
||||
StatusBar *statusbar = prompt->stb;
|
||||
return statusbar->connection;
|
||||
}
|
||||
|
||||
/* Adds friend request to pending friend requests.
|
||||
Returns request number on success, -1 if queue is full. */
|
||||
static int add_friend_request(const char *public_key, const char *data)
|
||||
@ -214,6 +233,13 @@ static void prompt_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
|
||||
if (wcsncmp(ctx->line, L"/avatar \"", wcslen(L"/avatar \"")) == 0)
|
||||
diff = dir_match(self, m, ctx->line, L"/avatar");
|
||||
|
||||
#ifdef PYTHON
|
||||
else if (wcsncmp(ctx->line, L"/run \"", wcslen(L"/run \"")) == 0)
|
||||
diff = dir_match(self, m, ctx->line, L"/run");
|
||||
|
||||
#endif
|
||||
|
||||
else if (wcsncmp(ctx->line, L"/status ", wcslen(L"/status ")) == 0) {
|
||||
const char status_cmd_list[3][8] = {
|
||||
{"online"},
|
||||
@ -376,7 +402,7 @@ static void prompt_onDraw(ToxWindow *self, Tox *m)
|
||||
help_onDraw(self);
|
||||
}
|
||||
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum , TOX_CONNECTION connection_status)
|
||||
static void prompt_onConnectionChange(ToxWindow *self, Tox *m, uint32_t friendnum, TOX_CONNECTION connection_status)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
|
@ -53,4 +53,7 @@ void kill_prompt_window(ToxWindow *self);
|
||||
/* callback: Updates own connection status in prompt statusbar */
|
||||
void prompt_onSelfConnectionChange(Tox *m, TOX_CONNECTION connection_status, void *userdata);
|
||||
|
||||
/* Returns our own connection status */
|
||||
TOX_CONNECTION prompt_selfConnectionStatus(void);
|
||||
|
||||
#endif /* end of include guard: PROMPT_H */
|
||||
|
347
src/python_api.c
Normal file
347
src/python_api.c
Normal file
@ -0,0 +1,347 @@
|
||||
/* python_api.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef PYTHON
|
||||
#include <Python.h>
|
||||
#include "api.h"
|
||||
|
||||
#include "execute.h"
|
||||
|
||||
extern Tox *user_tox;
|
||||
|
||||
static struct python_registered_func {
|
||||
char *name;
|
||||
char *help;
|
||||
PyObject *callback;
|
||||
struct python_registered_func *next;
|
||||
} python_commands = {0};
|
||||
|
||||
static PyObject *python_api_display(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &msg))
|
||||
return NULL;
|
||||
|
||||
api_display(msg);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *python_api_get_nick(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *name;
|
||||
PyObject *ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
name = api_get_nick();
|
||||
|
||||
if (name == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = Py_BuildValue("s", name);
|
||||
free(name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *python_api_get_status(PyObject *self, PyObject *args)
|
||||
{
|
||||
PyObject *ret = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
switch (api_get_status()) {
|
||||
case TOX_USER_STATUS_NONE:
|
||||
ret = Py_BuildValue("s", "online");
|
||||
break;
|
||||
|
||||
case TOX_USER_STATUS_AWAY:
|
||||
ret = Py_BuildValue("s", "away");
|
||||
break;
|
||||
|
||||
case TOX_USER_STATUS_BUSY:
|
||||
ret = Py_BuildValue("s", "busy");
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *python_api_get_status_message(PyObject *self, PyObject *args)
|
||||
{
|
||||
char *status;
|
||||
PyObject *ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
status = api_get_status_message();
|
||||
|
||||
if (status == NULL)
|
||||
return NULL;
|
||||
|
||||
ret = Py_BuildValue("s", status);
|
||||
free(status);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *python_api_get_all_friends(PyObject *self, PyObject *args)
|
||||
{
|
||||
size_t i, ii;
|
||||
FriendsList friends;
|
||||
PyObject *cur, *ret;
|
||||
char pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
|
||||
if (!PyArg_ParseTuple(args, ""))
|
||||
return NULL;
|
||||
|
||||
friends = api_get_friendslist();
|
||||
ret = PyList_New(0);
|
||||
|
||||
for (i = 0; i < friends.num_friends; i++) {
|
||||
for (ii = 0; ii < TOX_PUBLIC_KEY_SIZE; ii++)
|
||||
snprintf(pubkey_buf + ii * 2, 3, "%02X", friends.list[i].pub_key[ii] & 0xff);
|
||||
|
||||
pubkey_buf[TOX_PUBLIC_KEY_SIZE * 2] = '\0';
|
||||
cur = Py_BuildValue("(s,s)", friends.list[i].name, pubkey_buf);
|
||||
PyList_Append(ret, cur);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *python_api_send(PyObject *self, PyObject *args)
|
||||
{
|
||||
const char *msg;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "s", &msg))
|
||||
return NULL;
|
||||
|
||||
api_send(msg);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *python_api_execute(PyObject *self, PyObject *args)
|
||||
{
|
||||
int mode;
|
||||
const char *command;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "si", &command, &mode))
|
||||
return NULL;
|
||||
|
||||
api_execute(command, mode);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyObject *python_api_register(PyObject *self, PyObject *args)
|
||||
{
|
||||
struct python_registered_func *cur;
|
||||
size_t command_len, help_len;
|
||||
const char *command, *help;
|
||||
PyObject *callback;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ssO:register_command", &command, &help, &callback))
|
||||
return NULL;
|
||||
|
||||
if (!PyCallable_Check(callback)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Calback parameter must be callable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (command[0] != '/') {
|
||||
PyErr_SetString(PyExc_TypeError, "Command must be prefixed with a '/'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (cur = &python_commands; ; cur = cur->next) {
|
||||
if (cur->name != NULL && !strcmp(command, cur->name)) {
|
||||
Py_XDECREF(cur->callback);
|
||||
Py_XINCREF(callback);
|
||||
cur->callback = callback;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cur->next == NULL) {
|
||||
Py_XINCREF(callback);
|
||||
cur->next = malloc(sizeof(struct python_registered_func));
|
||||
|
||||
if (cur->next == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
command_len = strlen(command);
|
||||
cur->next->name = malloc(command_len + 1);
|
||||
|
||||
if (cur->next->name == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
strncpy(cur->next->name, command, command_len + 1);
|
||||
help_len = strlen(help);
|
||||
cur->next->help = malloc(help_len + 1);
|
||||
|
||||
if (cur->next->help == NULL)
|
||||
return PyErr_NoMemory();
|
||||
|
||||
strncpy(cur->next->help, help, help_len + 1);
|
||||
cur->next->callback = callback;
|
||||
cur->next->next = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
static PyMethodDef ToxicApiMethods[] = {
|
||||
{"display", python_api_display, METH_VARARGS, "Display a message to the current prompt"},
|
||||
{"get_nick", python_api_get_nick, METH_VARARGS, "Return the user's current nickname"},
|
||||
{"get_status", python_api_get_status, METH_VARARGS, "Returns the user's current status"},
|
||||
{"get_status_message", python_api_get_status_message, METH_VARARGS, "Return the user's current status message"},
|
||||
{"get_all_friends", python_api_get_all_friends, METH_VARARGS, "Return all of the user's friends"},
|
||||
{"send", python_api_send, METH_VARARGS, "Send the message to the current user"},
|
||||
{"execute", python_api_execute, METH_VARARGS, "Execute a command like `/nick`"},
|
||||
{"register", python_api_register, METH_VARARGS, "Register a command like `/nick` to a Python function"},
|
||||
{NULL, NULL, 0, NULL},
|
||||
};
|
||||
|
||||
static struct PyModuleDef toxic_api_module = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"toxic_api",
|
||||
NULL,
|
||||
-1,
|
||||
ToxicApiMethods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC PyInit_toxic_api(void)
|
||||
{
|
||||
PyObject *m = PyModule_Create(&toxic_api_module);
|
||||
PyObject *global_command_const = Py_BuildValue("i", GLOBAL_COMMAND_MODE);
|
||||
PyObject *chat_command_const = Py_BuildValue("i", CHAT_COMMAND_MODE);
|
||||
PyObject *groupchat_command_const = Py_BuildValue("i", GROUPCHAT_COMMAND_MODE);
|
||||
PyObject_SetAttrString(m, "GLOBAL_COMMAND", global_command_const);
|
||||
PyObject_SetAttrString(m, "CHAT_COMMAND", chat_command_const);
|
||||
PyObject_SetAttrString(m, "GROUPCHAT_COMMAND", groupchat_command_const);
|
||||
Py_DECREF(global_command_const);
|
||||
Py_DECREF(chat_command_const);
|
||||
Py_DECREF(groupchat_command_const);
|
||||
return m;
|
||||
}
|
||||
|
||||
void terminate_python(void)
|
||||
{
|
||||
struct python_registered_func *cur, *old;
|
||||
|
||||
if (python_commands.name != NULL)
|
||||
free(python_commands.name);
|
||||
|
||||
for (cur = python_commands.next; cur != NULL;) {
|
||||
old = cur;
|
||||
cur = cur->next;
|
||||
free(old->name);
|
||||
free(old);
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
void init_python(Tox *m)
|
||||
{
|
||||
user_tox = m;
|
||||
PyImport_AppendInittab("toxic_api", PyInit_toxic_api);
|
||||
Py_Initialize();
|
||||
}
|
||||
|
||||
void run_python(FILE *fp, char *path)
|
||||
{
|
||||
PyRun_SimpleFile(fp, path);
|
||||
}
|
||||
|
||||
int do_python_command(int num_args, char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
int i;
|
||||
PyObject *callback_args, *args_strings;
|
||||
struct python_registered_func *cur;
|
||||
|
||||
for (cur = &python_commands; cur != NULL; cur = cur->next) {
|
||||
if (cur->name == NULL)
|
||||
continue;
|
||||
|
||||
if (!strcmp(args[0], cur->name)) {
|
||||
args_strings = PyList_New(0);
|
||||
|
||||
for (i = 1; i < num_args; i++)
|
||||
PyList_Append(args_strings, Py_BuildValue("s", args[i]));
|
||||
|
||||
callback_args = PyTuple_Pack(1, args_strings);
|
||||
|
||||
if (PyObject_CallObject(cur->callback, callback_args) == NULL)
|
||||
api_display("Exception raised in callback function");
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int python_num_registered_handlers(void)
|
||||
{
|
||||
int n = 0;
|
||||
struct python_registered_func *cur;
|
||||
|
||||
for (cur = &python_commands; cur != NULL; cur = cur->next) {
|
||||
if (cur->name != NULL)
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
int python_help_max_width(void)
|
||||
{
|
||||
size_t tmp;
|
||||
int max = 0;
|
||||
struct python_registered_func *cur;
|
||||
|
||||
for (cur = &python_commands; cur != NULL; cur = cur->next) {
|
||||
if (cur->name != NULL) {
|
||||
tmp = strlen(cur->help);
|
||||
max = tmp > max ? tmp : max;
|
||||
}
|
||||
}
|
||||
|
||||
max = max > 50 ? 50 : max;
|
||||
return 37 + max;
|
||||
}
|
||||
|
||||
void python_draw_handler_help(WINDOW *win)
|
||||
{
|
||||
struct python_registered_func *cur;
|
||||
|
||||
for (cur = &python_commands; cur != NULL; cur = cur->next) {
|
||||
if (cur->name != NULL)
|
||||
wprintw(win, " %-29s: %.50s\n", cur->name, cur->help);
|
||||
}
|
||||
}
|
||||
#endif /* PYTHON */
|
39
src/python_api.h
Normal file
39
src/python_api.h
Normal 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 */
|
@ -23,6 +23,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <qrencode.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
@ -179,12 +179,14 @@ static const struct tox_strings {
|
||||
const char *download_path;
|
||||
const char *chatlogs_path;
|
||||
const char *avatar_path;
|
||||
const char *autorun_path;
|
||||
const char *password_eval;
|
||||
} tox_strings = {
|
||||
"tox",
|
||||
"download_path",
|
||||
"chatlogs_path",
|
||||
"avatar_path",
|
||||
"autorun_path",
|
||||
"password_eval",
|
||||
};
|
||||
|
||||
@ -193,6 +195,7 @@ static void tox_defaults(struct user_settings *settings)
|
||||
strcpy(settings->download_path, "");
|
||||
strcpy(settings->chatlogs_path, "");
|
||||
strcpy(settings->avatar_path, "");
|
||||
strcpy(settings->autorun_path, "");
|
||||
strcpy(settings->password_eval, "");
|
||||
}
|
||||
|
||||
@ -418,6 +421,20 @@ int settings_load(struct user_settings *s, const char *patharg)
|
||||
s->avatar_path[0] = '\0';
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.autorun_path, &str) ) {
|
||||
snprintf(s->autorun_path, sizeof(s->autorun_path), "%s", str);
|
||||
int len = strlen(str);
|
||||
|
||||
if (len >= sizeof(s->autorun_path) - 2)
|
||||
s->autorun_path[0] = '\0';
|
||||
else if (s->autorun_path[len - 1] != '/')
|
||||
strcat(&s->autorun_path[len - 1], "/");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if ( config_setting_lookup_string(setting, tox_strings.password_eval, &str) ) {
|
||||
snprintf(s->password_eval, sizeof(s->password_eval), "%s", str);
|
||||
int len = strlen(str);
|
||||
|
@ -63,6 +63,7 @@ struct user_settings {
|
||||
char download_path[PATH_MAX];
|
||||
char chatlogs_path[PATH_MAX];
|
||||
char avatar_path[PATH_MAX];
|
||||
char autorun_path[PATH_MAX];
|
||||
char password_eval[PASSWORD_EVAL_MAX];
|
||||
|
||||
int key_next_tab;
|
||||
@ -87,7 +88,7 @@ struct user_settings {
|
||||
#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
enum settings_values {
|
||||
AUTOLOG_OFF = 0,
|
||||
AUTOLOG_ON = 1,
|
||||
|
||||
@ -113,7 +114,7 @@ enum {
|
||||
|
||||
MPLEX_OFF = 0,
|
||||
MPLEX_ON = 1,
|
||||
} settings_values;
|
||||
};
|
||||
|
||||
#define LINE_JOIN "-->"
|
||||
#define LINE_QUIT "<--"
|
||||
|
@ -214,8 +214,8 @@ static int detect_tmux ()
|
||||
if (!pos)
|
||||
return 0;
|
||||
|
||||
/* store the session number string for later use */
|
||||
snprintf (mplex_data, sizeof(mplex_data), "%s", pos + 1);
|
||||
/* store the session id for later use */
|
||||
snprintf (mplex_data, sizeof(mplex_data), "$%s", pos + 1);
|
||||
mplex = MPLEX_TMUX;
|
||||
return 1;
|
||||
}
|
||||
@ -252,14 +252,8 @@ static int gnu_screen_is_detached ()
|
||||
}
|
||||
|
||||
/* Detects tmux attached/detached by getting session data and finding the
|
||||
current session's entry. An attached entry ends with "(attached)". Example:
|
||||
|
||||
$ tmux list-sessions
|
||||
0: 1 windows (created Mon Mar 2 21:48:29 2015) [80x23] (attached)
|
||||
1: 2 windows (created Mon Mar 2 21:48:43 2015) [80x23]
|
||||
|
||||
In this example, session 0 is attached and session 1 is detached.
|
||||
*/
|
||||
current session's entry.
|
||||
*/
|
||||
static int tmux_is_detached ()
|
||||
{
|
||||
if (mplex != MPLEX_TMUX)
|
||||
@ -267,10 +261,12 @@ static int tmux_is_detached ()
|
||||
|
||||
FILE *session_info_stream = NULL;
|
||||
char *dyn_buffer = NULL, *search_str = NULL;
|
||||
char *entry_pos, *nl_pos, *attached_pos;
|
||||
char *entry_pos;
|
||||
int detached;
|
||||
const int numstr_len = strlen (mplex_data);
|
||||
|
||||
session_info_stream = popen ("env LC_ALL=C tmux list-sessions", "r");
|
||||
/* get the number of attached clients for each session */
|
||||
session_info_stream = popen ("tmux list-sessions -F \"#{session_id} #{session_attached}\"", "r");
|
||||
|
||||
if (!session_info_stream)
|
||||
goto fail;
|
||||
@ -284,13 +280,12 @@ static int tmux_is_detached ()
|
||||
session_info_stream = NULL;
|
||||
|
||||
/* prepare search string, for finding the current session's entry */
|
||||
search_str = (char *) malloc (numstr_len + 4);
|
||||
search_str = (char *) malloc (numstr_len + 2);
|
||||
search_str[0] = '\n';
|
||||
strcpy (search_str + 1, mplex_data);
|
||||
strcat (search_str, ": ");
|
||||
|
||||
/* do the search */
|
||||
if (strncmp (dyn_buffer, search_str + 1, numstr_len + 2) == 0)
|
||||
if (strncmp (dyn_buffer, search_str + 1, numstr_len) == 0)
|
||||
entry_pos = dyn_buffer;
|
||||
else
|
||||
entry_pos = strstr (dyn_buffer, search_str);
|
||||
@ -298,9 +293,8 @@ static int tmux_is_detached ()
|
||||
if (! entry_pos)
|
||||
goto fail;
|
||||
|
||||
/* find the next \n and look for the "(attached)" before it */
|
||||
nl_pos = strchr (entry_pos + 1, '\n');
|
||||
attached_pos = strstr (entry_pos + 1, "(attached)\n");
|
||||
entry_pos = strchr (entry_pos, ' ') + 1;
|
||||
detached = strncmp (entry_pos, "0\n", 2) == 0;
|
||||
|
||||
free (search_str);
|
||||
search_str = NULL;
|
||||
@ -308,7 +302,7 @@ static int tmux_is_detached ()
|
||||
free (dyn_buffer);
|
||||
dyn_buffer = NULL;
|
||||
|
||||
return attached_pos == NULL || attached_pos > nl_pos;
|
||||
return detached;
|
||||
|
||||
fail:
|
||||
|
||||
|
181
src/toxic.c
181
src/toxic.c
@ -26,6 +26,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
@ -51,6 +52,7 @@
|
||||
#include "friendlist.h"
|
||||
#include "prompt.h"
|
||||
#include "misc_tools.h"
|
||||
#include "groupchat.h"
|
||||
#include "file_transfers.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
@ -75,6 +77,11 @@
|
||||
ToxAV *av;
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef PYTHON
|
||||
#include "api.h"
|
||||
#include "python_api.h"
|
||||
#endif
|
||||
|
||||
#ifndef PACKAGE_DATADIR
|
||||
#define PACKAGE_DATADIR "."
|
||||
#endif
|
||||
@ -104,9 +111,17 @@ static struct user_password {
|
||||
int len;
|
||||
} user_password;
|
||||
|
||||
static time_t last_signal_time;
|
||||
|
||||
static void catch_SIGINT(int sig)
|
||||
{
|
||||
Winthread.sig_exit_toxic = 1;
|
||||
time_t cur_time = get_unix_time();
|
||||
|
||||
if (difftime(cur_time, last_signal_time) <= 1) {
|
||||
Winthread.sig_exit_toxic = 1;
|
||||
} else {
|
||||
last_signal_time = cur_time;
|
||||
}
|
||||
}
|
||||
|
||||
static void catch_SIGSEGV(int sig)
|
||||
@ -162,6 +177,10 @@ void exit_toxic_success(Tox *m)
|
||||
terminate_audio();
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef PYTHON
|
||||
terminate_python();
|
||||
#endif /* PYTHON */
|
||||
|
||||
free_global_data();
|
||||
tox_kill(m);
|
||||
endwin();
|
||||
@ -287,12 +306,46 @@ static void load_friendlist(Tox *m)
|
||||
size_t i;
|
||||
size_t numfriends = tox_self_get_friend_list_size(m);
|
||||
|
||||
for (i = 0; i < numfriends; ++i)
|
||||
for (i = 0; i < numfriends; ++i) {
|
||||
friendlist_onFriendAdded(NULL, m, i, false);
|
||||
}
|
||||
|
||||
sort_friendlist_index();
|
||||
}
|
||||
|
||||
static void load_groups(ToxWindow *prompt, Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
size_t num_chats = tox_conference_get_chatlist_size(m);
|
||||
uint32_t chatlist[num_chats];
|
||||
|
||||
if (num_chats) {
|
||||
tox_conference_get_chatlist(m, chatlist);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_chats; ++i) {
|
||||
uint32_t groupnum = chatlist[i];
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
TOX_ERR_CONFERENCE_GET_TYPE err;
|
||||
TOX_CONFERENCE_TYPE type = tox_conference_get_type(m, groupnum, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_GET_TYPE_OK) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (init_groupchat_win(prompt, m, groupnum, type) == -1) {
|
||||
tox_conference_delete(m, groupnum, NULL);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return length of password on success, 0 on failure */
|
||||
static int password_prompt(char *buf, int size)
|
||||
{
|
||||
@ -382,6 +435,7 @@ static void first_time_encrypt(const char *msg)
|
||||
do {
|
||||
system("clear");
|
||||
printf("%s ", msg);
|
||||
fflush(stdout);
|
||||
|
||||
if (!strcasecmp(ch, "y\n") || !strcasecmp(ch, "n\n") || !strcasecmp(ch, "yes\n")
|
||||
|| !strcasecmp(ch, "no\n") || !strcasecmp(ch, "q\n"))
|
||||
@ -402,6 +456,7 @@ static void first_time_encrypt(const char *msg)
|
||||
printf("Enter a new password (must be at least %d characters) ", MIN_PASSWORD_LEN);
|
||||
|
||||
while (valid_password == false) {
|
||||
fflush(stdout); // Flush all before user input
|
||||
len = password_prompt(user_password.pass, sizeof(user_password.pass));
|
||||
user_password.len = len;
|
||||
|
||||
@ -520,54 +575,54 @@ int store_data(Tox *m, const char *path)
|
||||
|
||||
static void init_tox_callbacks(Tox *m)
|
||||
{
|
||||
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange, NULL);
|
||||
tox_callback_friend_connection_status(m, on_connectionchange, NULL);
|
||||
tox_callback_friend_typing(m, on_typing_change, NULL);
|
||||
tox_callback_friend_request(m, on_request, NULL);
|
||||
tox_callback_friend_message(m, on_message, NULL);
|
||||
tox_callback_friend_name(m, on_nickchange, NULL);
|
||||
tox_callback_friend_status(m, on_statuschange, NULL);
|
||||
tox_callback_friend_status_message(m, on_statusmessagechange, NULL);
|
||||
tox_callback_friend_read_receipt(m, on_read_receipt, NULL);
|
||||
tox_callback_group_invite(m, on_groupinvite, NULL);
|
||||
tox_callback_group_message(m, on_groupmessage, NULL);
|
||||
tox_callback_group_action(m, on_groupaction, NULL);
|
||||
tox_callback_group_namelist_change(m, on_group_namelistchange, NULL);
|
||||
tox_callback_group_title(m, on_group_titlechange, NULL);
|
||||
tox_callback_file_recv(m, on_file_recv, NULL);
|
||||
tox_callback_file_chunk_request(m, on_file_chunk_request, NULL);
|
||||
tox_callback_file_recv_control(m, on_file_control, NULL);
|
||||
tox_callback_file_recv_chunk(m, on_file_recv_chunk, NULL);
|
||||
tox_callback_self_connection_status(m, prompt_onSelfConnectionChange);
|
||||
tox_callback_friend_connection_status(m, on_connectionchange);
|
||||
tox_callback_friend_typing(m, on_typing_change);
|
||||
tox_callback_friend_request(m, on_request);
|
||||
tox_callback_friend_message(m, on_message);
|
||||
tox_callback_friend_name(m, on_nickchange);
|
||||
tox_callback_friend_status(m, on_statuschange);
|
||||
tox_callback_friend_status_message(m, on_statusmessagechange);
|
||||
tox_callback_friend_read_receipt(m, on_read_receipt);
|
||||
tox_callback_conference_invite(m, on_groupinvite);
|
||||
tox_callback_conference_message(m, on_groupmessage);
|
||||
tox_callback_conference_peer_list_changed(m, on_group_namelistchange);
|
||||
tox_callback_conference_peer_name(m, on_group_peernamechange);
|
||||
tox_callback_conference_title(m, on_group_titlechange);
|
||||
tox_callback_file_recv(m, on_file_recv);
|
||||
tox_callback_file_chunk_request(m, on_file_chunk_request);
|
||||
tox_callback_file_recv_control(m, on_file_control);
|
||||
tox_callback_file_recv_chunk(m, on_file_recv_chunk);
|
||||
}
|
||||
|
||||
static void init_tox_options(struct Tox_Options *tox_opts)
|
||||
{
|
||||
tox_options_default(tox_opts);
|
||||
|
||||
tox_opts->ipv6_enabled = !arg_opts.use_ipv4;
|
||||
tox_opts->udp_enabled = !arg_opts.force_tcp;
|
||||
tox_opts->proxy_type = arg_opts.proxy_type;
|
||||
tox_opts->tcp_port = arg_opts.tcp_port;
|
||||
tox_options_set_ipv6_enabled(tox_opts, !arg_opts.use_ipv4);
|
||||
tox_options_set_udp_enabled(tox_opts, !arg_opts.force_tcp);
|
||||
tox_options_set_proxy_type(tox_opts, arg_opts.proxy_type);
|
||||
tox_options_set_tcp_port(tox_opts, arg_opts.tcp_port);
|
||||
|
||||
if (!tox_opts->ipv6_enabled)
|
||||
if (!tox_options_get_ipv6_enabled(tox_opts))
|
||||
queue_init_message("Forcing IPv4 connection");
|
||||
|
||||
if (tox_opts->tcp_port)
|
||||
queue_init_message("TCP relaying enabled on port %d", tox_opts->tcp_port);
|
||||
if (tox_options_get_tcp_port(tox_opts))
|
||||
queue_init_message("TCP relaying enabled on port %d", tox_options_get_tcp_port(tox_opts));
|
||||
|
||||
if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) {
|
||||
tox_opts->proxy_port = arg_opts.proxy_port;
|
||||
tox_opts->proxy_host = arg_opts.proxy_address;
|
||||
const char *ps = tox_opts->proxy_type == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
|
||||
if (tox_options_get_proxy_type(tox_opts) != TOX_PROXY_TYPE_NONE) {
|
||||
tox_options_set_proxy_port(tox_opts, arg_opts.proxy_port);
|
||||
tox_options_set_proxy_host(tox_opts, arg_opts.proxy_address);
|
||||
const char *ps = tox_options_get_proxy_type(tox_opts) == TOX_PROXY_TYPE_SOCKS5 ? "SOCKS5" : "HTTP";
|
||||
|
||||
char tmp[48];
|
||||
char tmp[sizeof(arg_opts.proxy_address) + MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "Using %s proxy %s : %d", ps, arg_opts.proxy_address, arg_opts.proxy_port);
|
||||
queue_init_message("%s", tmp);
|
||||
}
|
||||
|
||||
if (!tox_opts->udp_enabled) {
|
||||
if (!tox_options_get_udp_enabled(tox_opts)) {
|
||||
queue_init_message("UDP disabled");
|
||||
} else if (tox_opts->proxy_type != TOX_PROXY_TYPE_NONE) {
|
||||
} else if (tox_options_get_proxy_type(tox_opts) != TOX_PROXY_TYPE_NONE) {
|
||||
const char *msg = "WARNING: Using a proxy without disabling UDP may leak your real IP address.";
|
||||
queue_init_message("%s", msg);
|
||||
msg = "Use the -t option to disable UDP.";
|
||||
@ -628,6 +683,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
|
||||
char plain[plain_len];
|
||||
|
||||
while (true) {
|
||||
fflush(stdout); // Flush before prompts so the user sees the question/message
|
||||
|
||||
if (pweval) {
|
||||
pwlen = password_eval(user_password.pass, sizeof(user_password.pass));
|
||||
} else {
|
||||
@ -654,9 +711,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
|
||||
(uint8_t *) plain, &pwerr);
|
||||
|
||||
if (pwerr == TOX_ERR_DECRYPTION_OK) {
|
||||
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
|
||||
tox_opts->savedata_data = (uint8_t *) plain;
|
||||
tox_opts->savedata_length = plain_len;
|
||||
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
|
||||
tox_options_set_savedata_data(tox_opts, (uint8_t *) plain, plain_len);
|
||||
|
||||
m = tox_new(tox_opts, new_err);
|
||||
|
||||
@ -677,9 +733,8 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
|
||||
}
|
||||
}
|
||||
} else { /* data is not encrypted */
|
||||
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_TOX_SAVE;
|
||||
tox_opts->savedata_data = (uint8_t *) data;
|
||||
tox_opts->savedata_length = len;
|
||||
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_TOX_SAVE);
|
||||
tox_options_set_savedata_data(tox_opts, (uint8_t *) data, len);
|
||||
|
||||
m = tox_new(tox_opts, new_err);
|
||||
|
||||
@ -694,7 +749,7 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
|
||||
if (file_exists(data_path))
|
||||
exit_toxic_err("failed in load_tox", FATALERR_FILEOP);
|
||||
|
||||
tox_opts->savedata_type = TOX_SAVEDATA_TYPE_NONE;
|
||||
tox_options_set_savedata_type(tox_opts, TOX_SAVEDATA_TYPE_NONE);
|
||||
|
||||
m = tox_new(tox_opts, new_err);
|
||||
|
||||
@ -710,16 +765,19 @@ static Tox *load_tox(char *data_path, struct Tox_Options *tox_opts, TOX_ERR_NEW
|
||||
|
||||
static Tox *load_toxic(char *data_path)
|
||||
{
|
||||
struct Tox_Options tox_opts;
|
||||
init_tox_options(&tox_opts);
|
||||
TOX_ERR_OPTIONS_NEW options_new_err;
|
||||
struct Tox_Options *tox_opts = tox_options_new(&options_new_err);
|
||||
if (!tox_opts)
|
||||
exit_toxic_err("tox_options_new returned fatal error", options_new_err);
|
||||
init_tox_options(tox_opts);
|
||||
|
||||
TOX_ERR_NEW new_err;
|
||||
Tox *m = load_tox(data_path, &tox_opts, &new_err);
|
||||
Tox *m = load_tox(data_path, tox_opts, &new_err);
|
||||
|
||||
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_opts.ipv6_enabled) {
|
||||
if (new_err == TOX_ERR_NEW_PORT_ALLOC && tox_options_get_ipv6_enabled(tox_opts)) {
|
||||
queue_init_message("Falling back to ipv4");
|
||||
tox_opts.ipv6_enabled = false;
|
||||
m = load_tox(data_path, &tox_opts, &new_err);
|
||||
tox_options_set_ipv6_enabled(tox_opts, false);
|
||||
m = load_tox(data_path, tox_opts, &new_err);
|
||||
}
|
||||
|
||||
if (!m)
|
||||
@ -735,6 +793,7 @@ static Tox *load_toxic(char *data_path)
|
||||
if (tox_self_get_name_size(m) == 0)
|
||||
tox_self_set_name(m, (uint8_t *) "Toxic User", strlen("Toxic User"), NULL);
|
||||
|
||||
tox_options_free(tox_opts);
|
||||
return m;
|
||||
}
|
||||
|
||||
@ -747,7 +806,7 @@ static void do_toxic(Tox *m)
|
||||
return;
|
||||
}
|
||||
|
||||
tox_iterate(m);
|
||||
tox_iterate(m, NULL);
|
||||
do_tox_connection(m);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
}
|
||||
@ -793,7 +852,7 @@ void *thread_cqueue(void *data)
|
||||
ToxWindow *toxwin = get_window_ptr(i);
|
||||
|
||||
if (toxwin != NULL && toxwin->is_chat
|
||||
&& tox_friend_get_connection_status(m, toxwin->num, NULL) != TOX_CONNECTION_NONE)
|
||||
&& get_friend_connection_status(toxwin->num) != TOX_CONNECTION_NONE)
|
||||
cqueue_try_send(toxwin, m);
|
||||
}
|
||||
|
||||
@ -836,6 +895,13 @@ static void print_usage(void)
|
||||
fprintf(stderr, " -t, --force-tcp Force toxic to use a TCP connection (use with proxies)\n");
|
||||
fprintf(stderr, " -T, --tcp-server Act as a TCP relay server: Requires [port]\n");
|
||||
fprintf(stderr, " -u, --unencrypt-data Unencrypt an encrypted data file\n");
|
||||
fprintf(stderr, " -v, --version Print the version\n");
|
||||
}
|
||||
|
||||
static void print_version(void)
|
||||
{
|
||||
fprintf(stderr, "Toxic version %s\n", TOXICVER);
|
||||
fprintf(stderr, "Toxcore version %d.%d.%d\n", tox_version_major(), tox_version_minor(), tox_version_patch());
|
||||
}
|
||||
|
||||
static void set_default_opts(void)
|
||||
@ -866,10 +932,11 @@ static void parse_args(int argc, char *argv[])
|
||||
{"SOCKS5-proxy", required_argument, 0, 'p'},
|
||||
{"HTTP-proxy", required_argument, 0, 'P'},
|
||||
{"unencrypt-data", no_argument, 0, 'u'},
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{NULL, no_argument, NULL, 0},
|
||||
};
|
||||
|
||||
const char *opts_str = "4bdehotuxc:f:n:r:p:P:T:";
|
||||
const char *opts_str = "4bdehotuxvc:f:n:r:p:P:T:";
|
||||
int opt, indexptr;
|
||||
long int port = 0;
|
||||
|
||||
@ -992,6 +1059,10 @@ static void parse_args(int argc, char *argv[])
|
||||
arg_opts.unencrypt_data = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
print_version();
|
||||
exit(EXIT_SUCCESS);
|
||||
|
||||
case 'h':
|
||||
default:
|
||||
print_usage();
|
||||
@ -1163,6 +1234,7 @@ int main(int argc, char **argv)
|
||||
|
||||
prompt = init_windows(m);
|
||||
prompt_init_statusbar(prompt, m);
|
||||
load_groups(prompt, m);
|
||||
|
||||
/* thread for ncurses stuff */
|
||||
if (pthread_mutex_init(&Winthread.lock, NULL) != 0)
|
||||
@ -1198,6 +1270,13 @@ int main(int argc, char **argv)
|
||||
|
||||
#endif /* AUDIO */
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
init_python(m);
|
||||
invoke_autoruns(prompt->chatwin->history, prompt);
|
||||
|
||||
#endif /* PYTHON */
|
||||
|
||||
init_notify(60, 3000);
|
||||
|
||||
/* screen/tmux auto-away timer */
|
||||
@ -1217,7 +1296,7 @@ int main(int argc, char **argv)
|
||||
cleanup_init_messages();
|
||||
|
||||
/* set user avatar from config file. if no path is supplied tox_unset_avatar is called */
|
||||
char avatarstr[MAX_STR_SIZE];
|
||||
char avatarstr[PATH_MAX + 11];
|
||||
snprintf(avatarstr, sizeof(avatarstr), "/avatar \"%s\"", user_settings->avatar_path);
|
||||
execute(prompt->chatwin->history, prompt, m, avatarstr, GLOBAL_COMMAND_MODE);
|
||||
|
||||
|
14
src/toxic.h
14
src/toxic.h
@ -114,12 +114,14 @@ void on_nickchange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t
|
||||
void on_statuschange(Tox *m, uint32_t friendnumber, TOX_USER_STATUS status, void *userdata);
|
||||
void on_statusmessagechange(Tox *m, uint32_t friendnumber, const uint8_t *string, size_t length, void *userdata);
|
||||
void on_friendadded(Tox *m, uint32_t friendnumber, bool sort);
|
||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length, void *userdata);
|
||||
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length, void *userdata);
|
||||
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length,
|
||||
void *userdata);
|
||||
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata);
|
||||
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
|
||||
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
|
||||
const uint8_t *message, size_t length, void *userdata);
|
||||
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
|
||||
size_t length, void *userdata);
|
||||
void on_group_namelistchange(Tox *m, uint32_t groupnumber, void *userdata);
|
||||
void on_group_peernamechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
|
||||
size_t length, void *userdata);
|
||||
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void *userdata);
|
||||
void on_file_chunk_request(Tox *m, uint32_t friendnumber, uint32_t filenumber, uint64_t position, size_t length,
|
||||
void *userdata);
|
||||
|
@ -45,8 +45,7 @@ void receive_video_frame_cb( ToxAV *av, uint32_t friend_number,
|
||||
int32_t ystride, int32_t ustride, int32_t vstride,
|
||||
void *user_data );
|
||||
|
||||
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||
uint32_t video_bit_rate, void *user_data);
|
||||
void video_bit_rate_status_cb( ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data);
|
||||
|
||||
static void print_err (ToxWindow *self, const char *error_str)
|
||||
{
|
||||
@ -74,7 +73,7 @@ ToxAV *init_video(ToxWindow *self, Tox *tox)
|
||||
}
|
||||
|
||||
toxav_callback_video_receive_frame(CallControl.av, receive_video_frame_cb, &CallControl);
|
||||
toxav_callback_bit_rate_status(CallControl.av, video_bit_rate_status_cb, &CallControl);
|
||||
toxav_callback_video_bit_rate(CallControl.av, video_bit_rate_status_cb, &CallControl);
|
||||
|
||||
return CallControl.av;
|
||||
}
|
||||
@ -83,7 +82,7 @@ void terminate_video()
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_CALLS; ++i) {
|
||||
for (i = 0; i < CallControl.max_calls; ++i) {
|
||||
Call *this_call = &CallControl.calls[i];
|
||||
|
||||
stop_video_transmission(this_call, i);
|
||||
@ -135,7 +134,7 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
|
||||
|
||||
CallControl.video_bit_rate = default_video_bit_rate;
|
||||
|
||||
if ( toxav_bit_rate_set(CallControl.av, self->num, -1, CallControl.video_bit_rate, NULL) == false ) {
|
||||
if ( toxav_video_set_bit_rate(CallControl.av, self->num, CallControl.video_bit_rate, NULL) == false ) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Failed to set video bit rate");
|
||||
return -1;
|
||||
}
|
||||
@ -156,7 +155,7 @@ int start_video_transmission(ToxWindow *self, ToxAV *av, Call *call)
|
||||
int stop_video_transmission(Call *call, int friend_number)
|
||||
{
|
||||
CallControl.video_bit_rate = 0;
|
||||
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
|
||||
|
||||
if ( call->vin_idx != -1 ) {
|
||||
close_video_device(vdt_input, call->vin_idx);
|
||||
@ -185,11 +184,10 @@ void receive_video_frame_cb(ToxAV *av, uint32_t friend_number,
|
||||
write_video_device_callback(friend_number, width, height, y, u, v, ystride, ustride, vstride, user_data);
|
||||
}
|
||||
|
||||
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t audio_bit_rate,
|
||||
uint32_t video_bit_rate, void *user_data)
|
||||
void video_bit_rate_status_cb(ToxAV *av, uint32_t friend_number, uint32_t video_bit_rate, void *user_data)
|
||||
{
|
||||
CallControl.video_bit_rate = video_bit_rate;
|
||||
toxav_bit_rate_set(CallControl.av, friend_number, -1, CallControl.video_bit_rate, NULL);
|
||||
toxav_video_set_bit_rate(CallControl.av, friend_number, CallControl.video_bit_rate, NULL);
|
||||
}
|
||||
|
||||
void callback_recv_video_starting(uint32_t friend_number)
|
||||
|
@ -30,14 +30,18 @@
|
||||
|
||||
#include <vpx/vpx_image.h>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
#import "osx_video.h"
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#if defined(__linux__)
|
||||
#include <linux/videodev2.h>
|
||||
#else /* __OSX__ */
|
||||
#import "osx_video.h"
|
||||
#else
|
||||
#include <sys/videoio.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "line_info.h"
|
||||
@ -65,7 +69,7 @@ typedef struct VideoDevice {
|
||||
void *cb_data; /* Data to be passed to callback */
|
||||
int32_t friend_number; /* ToxAV friend number */
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
int fd; /* File descriptor of video device selected/opened */
|
||||
struct v4l2_format fmt;
|
||||
struct VideoBuffer *buffers;
|
||||
@ -132,7 +136,7 @@ static void yuv420tobgr(uint16_t width, uint16_t height, const uint8_t *y,
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
static void yuv422to420(uint8_t *plane_y, uint8_t *plane_u, uint8_t *plane_v,
|
||||
uint8_t *input, uint16_t width, uint16_t height)
|
||||
{
|
||||
@ -170,7 +174,7 @@ static int xioctl(int fh, unsigned long request, void *arg)
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
||||
#endif
|
||||
|
||||
/* Meet devices */
|
||||
#ifdef VIDEO
|
||||
@ -181,7 +185,10 @@ VideoDeviceError init_video_devices()
|
||||
{
|
||||
size[vdt_input] = 0;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
if( osx_video_init((char**)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
|
||||
return vde_InternalError;
|
||||
#else /* not __OSX__*/
|
||||
|
||||
for (; size[vdt_input] <= MAX_DEVICES; ++size[vdt_input]) {
|
||||
int fd;
|
||||
@ -216,11 +223,6 @@ VideoDeviceError init_video_devices()
|
||||
}
|
||||
}
|
||||
|
||||
#else /* __OSX__ */
|
||||
|
||||
if ( osx_video_init((char **)video_devices_names[vdt_input], &size[vdt_input]) != 0 )
|
||||
return vde_InternalError;
|
||||
|
||||
#endif
|
||||
|
||||
size[vdt_output] = 1;
|
||||
@ -271,17 +273,17 @@ VideoDeviceError terminate_video_devices()
|
||||
VideoDeviceError register_video_device_callback(int32_t friend_number, uint32_t device_idx,
|
||||
VideoDataHandleCallback callback, void *data)
|
||||
{
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
|
||||
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
|
||||
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;
|
||||
|
||||
#else /* __OSX__ */
|
||||
|
||||
if ( size[vdt_input] <= device_idx || !video_devices_running[vdt_input][device_idx] )
|
||||
return vde_InvalidSelection;
|
||||
|
||||
#endif
|
||||
|
||||
lock;
|
||||
@ -355,10 +357,16 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
if ( type == vdt_input ) {
|
||||
video_thread_paused = true;
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
|
||||
free(device);
|
||||
unlock;
|
||||
return vde_FailedStart;
|
||||
}
|
||||
#else /* not __OSX__*/
|
||||
/* Open selected device */
|
||||
char device_address[] = "/dev/videoXX";
|
||||
snprintf(device_address + 10 , sizeof(device_address) - 10, "%i", selection);
|
||||
snprintf(device_address + 10, sizeof(device_address) - 10, "%i", selection);
|
||||
|
||||
device->fd = open(device_address, O_RDWR);
|
||||
|
||||
@ -482,14 +490,6 @@ VideoDeviceError open_video_device(VideoDeviceType type, int32_t selection, uint
|
||||
return vde_FailedStart;
|
||||
}
|
||||
|
||||
#else /* __OSX__ */
|
||||
|
||||
if ( osx_video_open_device(selection, &device->video_width, &device->video_height) != 0 ) {
|
||||
free(device);
|
||||
unlock;
|
||||
return vde_FailedStart;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Create X11 window associated to device */
|
||||
@ -666,7 +666,12 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
|
||||
uint8_t *u = device->input.planes[1];
|
||||
uint8_t *v = device->input.planes[2];
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
|
||||
unlock;
|
||||
continue;
|
||||
}
|
||||
#else /* not __OSX__*/
|
||||
struct v4l2_buffer buf;
|
||||
memset(&(buf), 0, sizeof(buf));
|
||||
|
||||
@ -683,13 +688,6 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
|
||||
/* Convert frame image data to YUV420 for ToxAV */
|
||||
yuv422to420(y, u, v, data, video_width, video_height);
|
||||
|
||||
#else /* __OSX__*/
|
||||
|
||||
if ( osx_video_read_device(y, u, v, &video_width, &video_height) != 0 ) {
|
||||
unlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Send frame data to friend through ToxAV */
|
||||
@ -726,14 +724,14 @@ void *video_thread_poll (void *arg) // TODO: maybe use thread for every input so
|
||||
XFlush(device->x_display);
|
||||
free(img_data);
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
|
||||
if ( -1 == xioctl(device->fd, VIDIOC_QBUF, &buf) ) {
|
||||
unlock;
|
||||
continue;
|
||||
}
|
||||
|
||||
#endif /* __linux__ */
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@ -765,7 +763,10 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
|
||||
if ( !device->ref_count ) {
|
||||
|
||||
if ( type == vdt_input ) {
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__OSX__)
|
||||
|
||||
osx_video_close_device(device_idx);
|
||||
#else /* not __OSX__ */
|
||||
enum v4l2_buf_type buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
|
||||
if ( -1 == xioctl(device->fd, VIDIOC_STREAMOFF, &buf_type) ) {}
|
||||
@ -779,8 +780,6 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
|
||||
|
||||
close(device->fd);
|
||||
|
||||
#else /* __OSX__ */
|
||||
osx_video_close_device(device_idx);
|
||||
#endif
|
||||
vpx_img_free(&device->input);
|
||||
XDestroyWindow(device->x_display, device->x_window);
|
||||
@ -788,9 +787,9 @@ VideoDeviceError close_video_device(VideoDeviceType type, uint32_t device_idx)
|
||||
XCloseDisplay(device->x_display);
|
||||
pthread_mutex_destroy(device->mutex);
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
||||
free(device->buffers);
|
||||
#endif /* __linux__ */
|
||||
#endif /* __linux__ / BSD */
|
||||
|
||||
free(device);
|
||||
} else {
|
||||
|
@ -151,8 +151,8 @@ void on_friendadded(Tox *m, uint32_t friendnumber, bool sort)
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
|
||||
void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *message, uint16_t length,
|
||||
void *userdata)
|
||||
void on_groupmessage(Tox *m, uint32_t groupnumber, uint32_t peernumber, TOX_MESSAGE_TYPE type,
|
||||
const uint8_t *message, size_t length, void *userdata)
|
||||
{
|
||||
char msg[MAX_STR_SIZE + 1];
|
||||
length = copy_tox_str(msg, sizeof(msg), (const char *) message, length);
|
||||
@ -161,26 +161,12 @@ void on_groupmessage(Tox *m, int groupnumber, int peernumber, const uint8_t *mes
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].onGroupMessage != NULL)
|
||||
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, msg, length);
|
||||
windows[i].onGroupMessage(&windows[i], m, groupnumber, peernumber, type, msg, length);
|
||||
}
|
||||
}
|
||||
|
||||
void on_groupaction(Tox *m, int groupnumber, int peernumber, const uint8_t *action, uint16_t length,
|
||||
void *userdata)
|
||||
{
|
||||
char msg[MAX_STR_SIZE + 1];
|
||||
length = copy_tox_str(msg, sizeof(msg), (const char *) action, length);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].onGroupAction != NULL)
|
||||
windows[i].onGroupAction(&windows[i], m, groupnumber, peernumber, msg, length);
|
||||
}
|
||||
}
|
||||
|
||||
void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *group_pub_key, uint16_t length,
|
||||
void *userdata)
|
||||
void on_groupinvite(Tox *m, uint32_t friendnumber, TOX_CONFERENCE_TYPE type, const uint8_t *group_pub_key,
|
||||
size_t length, void *userdata)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
@ -190,17 +176,32 @@ void on_groupinvite(Tox *m, int32_t friendnumber, uint8_t type, const uint8_t *g
|
||||
}
|
||||
}
|
||||
|
||||
void on_group_namelistchange(Tox *m, int groupnumber, int peernumber, uint8_t change, void *userdata)
|
||||
void on_group_namelistchange(Tox *m, uint32_t groupnumber, void *userdata)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].onGroupNamelistChange != NULL)
|
||||
windows[i].onGroupNamelistChange(&windows[i], m, groupnumber, peernumber, change);
|
||||
if (windows[i].onGroupNameListChange != NULL)
|
||||
windows[i].onGroupNameListChange(&windows[i], m, groupnumber);
|
||||
}
|
||||
}
|
||||
|
||||
void on_group_titlechange(Tox *m, int groupnumber, int peernumber, const uint8_t *title, uint8_t length,
|
||||
void on_group_peernamechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *name,
|
||||
size_t length, void *userdata)
|
||||
{
|
||||
char nick[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
length = copy_tox_str(nick, sizeof(nick), (const char *) name, length);
|
||||
filter_str(nick, length);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].onGroupPeerNameChange != NULL)
|
||||
windows[i].onGroupPeerNameChange(&windows[i], m, groupnumber, peernumber, nick, length);
|
||||
}
|
||||
}
|
||||
|
||||
void on_group_titlechange(Tox *m, uint32_t groupnumber, uint32_t peernumber, const uint8_t *title, size_t length,
|
||||
void *userdata)
|
||||
{
|
||||
char data[MAX_STR_SIZE + 1];
|
||||
@ -488,10 +489,6 @@ static void draw_bar(void)
|
||||
|
||||
move(LINES - 1, 0);
|
||||
|
||||
attron(COLOR_PAIR(BLUE) | A_BOLD);
|
||||
printw(" TOXIC " TOXICVER " |");
|
||||
attroff(COLOR_PAIR(BLUE) | A_BOLD);
|
||||
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
@ -601,6 +598,12 @@ ToxWindow *get_window_ptr(int i)
|
||||
return toxwin;
|
||||
}
|
||||
|
||||
/* returns a pointer to the currently open ToxWindow. */
|
||||
ToxWindow *get_active_window(void)
|
||||
{
|
||||
return active_window;
|
||||
}
|
||||
|
||||
void force_refresh(WINDOW *w)
|
||||
{
|
||||
wclear(w);
|
||||
@ -619,10 +622,11 @@ void kill_all_windows(Tox *m)
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_WINDOWS_NUM; ++i) {
|
||||
if (windows[i].is_chat)
|
||||
if (windows[i].is_chat) {
|
||||
kill_chat_window(&windows[i], m);
|
||||
else if (windows[i].is_groupchat)
|
||||
close_groupchat(&windows[i], m, windows[i].num);
|
||||
} else if (windows[i].is_groupchat) {
|
||||
free_groupchat(&windows[i], m, windows[i].num);
|
||||
}
|
||||
}
|
||||
|
||||
kill_prompt_window(prompt);
|
||||
|
@ -42,7 +42,7 @@
|
||||
#define CHATBOX_HEIGHT 2
|
||||
|
||||
/* Curses foreground colours (background is black) */
|
||||
enum {
|
||||
typedef enum {
|
||||
WHITE,
|
||||
GREEN,
|
||||
CYAN,
|
||||
@ -121,11 +121,11 @@ struct ToxWindow {
|
||||
void(*onNickChange)(ToxWindow *, Tox *, uint32_t, const char *, size_t);
|
||||
void(*onStatusChange)(ToxWindow *, Tox *, uint32_t, TOX_USER_STATUS);
|
||||
void(*onStatusMessageChange)(ToxWindow *, uint32_t, const char *, size_t);
|
||||
void(*onGroupMessage)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
|
||||
void(*onGroupAction)(ToxWindow *, Tox *, int, int, const char *, uint16_t);
|
||||
void(*onGroupMessage)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_MESSAGE_TYPE, const char *, size_t);
|
||||
void(*onGroupInvite)(ToxWindow *, Tox *, int32_t, uint8_t, const char *, uint16_t);
|
||||
void(*onGroupNamelistChange)(ToxWindow *, Tox *, int, int, uint8_t);
|
||||
void(*onGroupTitleChange)(ToxWindow *, Tox *, int, int, const char *, uint8_t);
|
||||
void(*onGroupNameListChange)(ToxWindow *, Tox *, uint32_t);
|
||||
void(*onGroupPeerNameChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onGroupTitleChange)(ToxWindow *, Tox *, uint32_t, uint32_t, const char *, size_t);
|
||||
void(*onFileChunkRequest)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, size_t);
|
||||
void(*onFileRecvChunk)(ToxWindow *, Tox *, uint32_t, uint32_t, uint64_t, const char *, size_t);
|
||||
void(*onFileControl)(ToxWindow *, Tox *, uint32_t, uint32_t, TOX_FILE_CONTROL);
|
||||
@ -260,6 +260,7 @@ void kill_all_windows(Tox *m); /* should only be called on shutdown */
|
||||
void on_window_resize(void);
|
||||
void force_refresh(WINDOW *w);
|
||||
ToxWindow *get_window_ptr(int i);
|
||||
ToxWindow *get_active_window(void);
|
||||
|
||||
/* refresh inactive windows to prevent scrolling bugs.
|
||||
call at least once per second */
|
||||
|
Reference in New Issue
Block a user