mirror of
https://github.com/Tha14/toxic.git
synced 2025-04-08 02:02:44 +02:00
Compare commits
401 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
30d01127e7 | ||
|
85ab7592c9 | ||
|
6cdd01da25 | ||
|
69e4ed452d | ||
|
4c77e80a7f | ||
|
ff669be8d1 | ||
|
5d757e1230 | ||
|
70ab422acc | ||
|
2a052edb9e | ||
|
efbfa2c95c | ||
|
3f3e848b6f | ||
8d125d5e6b | |||
fbdc20dfa5 | |||
|
e8388bf20c | ||
|
398aecdd92 | ||
|
57a439c9b4 | ||
|
0aea5d7fbe | ||
|
e56edd556f | ||
|
7ed031b9bf | ||
6ef2c784c5 | |||
|
058057c64d | ||
146b00b8ca | |||
|
85024bfce8 | ||
129bb6ba68 | |||
|
68b4713ef7 | ||
|
46eb69412e | ||
|
f47b4c1c3c | ||
|
22eb6797b1 | ||
|
0edad7b862 | ||
|
cc80da00fc | ||
|
32efac931c | ||
|
4f936e5f9c | ||
|
529d905bd0 | ||
|
631b1d8b08 | ||
|
9ac8d517e9 | ||
|
90152f5e17 | ||
|
4d501aeb2a | ||
|
1b9f4f337a | ||
|
812f2d1ff9 | ||
|
2eeb1de20d | ||
|
ba896fd124 | ||
|
3ff3516e42 | ||
|
2ef3d68173 | ||
|
7a099f86c8 | ||
|
9a629072b3 | ||
|
27259ff23f | ||
|
f786b7ae6a | ||
|
cd4a5e5fde | ||
|
0b32b985f1 | ||
|
5851cb2dd8 | ||
|
1847025213 | ||
|
1a2320b035 | ||
|
1211899a40 | ||
|
5dabaac804 | ||
|
4f26171e3f | ||
|
fa075f1972 | ||
|
cbf3145aee | ||
|
06bdb0d167 | ||
|
0212391d7d | ||
|
535a8402d6 | ||
|
2c51afd9ef | ||
|
4aca8b6ecb | ||
|
28be56aad9 | ||
|
38004367a1 | ||
|
5499bb6645 | ||
|
ed8ba89cd8 | ||
|
812210d63f | ||
|
cec96e1ea3 | ||
|
eb7e6151a2 | ||
|
22ca3704d2 | ||
|
fdfaaf953f | ||
|
310cf464d0 | ||
|
0c11b3121a | ||
|
1bdf0041bc | ||
|
6a203fd7bf | ||
|
93b3bbd5f6 | ||
|
e122c5dbca | ||
|
f6f41a510b | ||
|
05dbc626e2 | ||
|
ceb175e3f1 | ||
|
4bd1d9bfee | ||
|
55944aa5a9 | ||
|
b71c6a3792 | ||
|
0239509439 | ||
|
602d9d97c1 | ||
|
6f8f6f0ac5 | ||
|
8d58e8f4d6 | ||
|
b6f892edf5 | ||
|
f91d10c831 | ||
|
3f18c6f8de | ||
|
bf1e1b73fc | ||
|
3eac65036c | ||
|
85f9ec6b9a | ||
|
5230616a9d | ||
|
8e0e318df1 | ||
|
701c0e1e94 | ||
|
71f6a8d4d6 | ||
|
1cace1e81d | ||
|
3030cda47e | ||
|
2946e0dc56 | ||
|
da90ca8b74 | ||
|
17e5cc1147 | ||
|
d02ac24de8 | ||
|
66e4c590dc | ||
|
8176b43880 | ||
|
b88dc9fda1 | ||
|
d7a19791b4 | ||
|
e2c8497da9 | ||
|
afbd185222 | ||
|
64794e14cd | ||
|
e9a0a30408 | ||
|
8aa57b0539 | ||
|
7b734f3996 | ||
|
27e20d6f44 | ||
|
f39da4d7f2 | ||
|
af70c3d660 | ||
|
58d0bd0663 | ||
|
090fcfffe3 | ||
|
1cba726bb8 | ||
|
50a074ed22 | ||
|
3fddc410b3 | ||
|
0a708e4a2e | ||
|
b7002ef3f0 | ||
|
1803da85c1 | ||
|
f3f81111c8 | ||
|
13337041ce | ||
|
41e93adbdb | ||
|
34b7c0a0d8 | ||
|
bcdec5d624 | ||
|
9e353443c2 | ||
|
d02f3b4acb | ||
|
f2b1c81279 | ||
|
768617a129 | ||
|
8dfd009e0e | ||
|
321f694bb8 | ||
|
4514ecd839 | ||
|
5e67571908 | ||
|
c293fbe0c7 | ||
|
93fb84206d | ||
|
35aa6922d6 | ||
|
7abf6388f8 | ||
|
7aeb1a0aac | ||
|
60bdcf0ba5 | ||
|
a623976a0e | ||
|
556a522637 | ||
|
a2f0f298aa | ||
|
0fc1d9e994 | ||
|
80fd3d3b97 | ||
|
07a41f9e0b | ||
|
f643975941 | ||
|
a5246bcbc6 | ||
|
b37e28c2d0 | ||
|
973e60ef11 | ||
|
ae94bc593b | ||
|
81eb58532e | ||
|
8464ea9a7a | ||
|
b77bff35a1 | ||
|
eb964b64c2 | ||
|
2ff9d29491 | ||
|
2640919318 | ||
|
2fcbc4fa1c | ||
|
4330bf5867 | ||
|
3f1b7cdd26 | ||
|
1e985c1456 | ||
|
61740bda85 | ||
|
0d8e6d713e | ||
|
39e4ff8bd6 | ||
|
0434ac186a | ||
|
8d9d51640c | ||
|
c4c0c0d1f4 | ||
|
3f2826bd66 | ||
|
7b7ea0e386 | ||
|
d35a38735b | ||
|
f0c4906fdc | ||
|
56ba61e061 | ||
|
898d89e95a | ||
|
1fd1e27bdf | ||
|
8e84ac58d4 | ||
|
9d65997871 | ||
|
da2889f3ab | ||
|
312b38d253 | ||
|
0554bf0240 | ||
|
53a7530e8a | ||
|
41be04a142 | ||
|
31f36318a2 | ||
|
f882fdf608 | ||
|
7e1e410307 | ||
|
c135c812c2 | ||
|
6c239193ab | ||
|
de7db08352 | ||
|
ba5ded9bc2 | ||
|
4581dee4fc | ||
|
d75d6e8b60 | ||
|
142ce642f0 | ||
|
7dead5ec96 | ||
|
ddcf224db2 | ||
|
daf794c4a2 | ||
|
dac0124f0f | ||
|
15b7a30925 | ||
|
77ab71f26f | ||
|
68e1ba312d | ||
|
752fc6d619 | ||
|
16bcb27ca7 | ||
|
71d7d355a6 | ||
|
4188b392cc | ||
|
811fbfbb1e | ||
|
32eb7d3040 | ||
|
42763905d7 | ||
|
f64300d1d6 | ||
|
1a723f0e8e | ||
|
a86884c40e | ||
|
3f02e119f4 | ||
|
1bbd50aac7 | ||
|
e7a0c32a68 | ||
|
7560bc9547 | ||
|
2b43340c90 | ||
|
ff1620c923 | ||
|
1303053a27 | ||
|
91f194c821 | ||
|
478762f76c | ||
|
4d96d6a753 | ||
|
3cdcfbf4e5 | ||
|
4c302da503 | ||
|
26b5fe8f9d | ||
|
22d60232fb | ||
|
e428879beb | ||
|
3015138a5a | ||
|
9c06ad608b | ||
|
015dbd9a96 | ||
|
a7466c3142 | ||
|
f012007cc4 | ||
|
dcf3baf60f | ||
|
4bda799a4b | ||
|
bdeae33d48 | ||
|
47591d5298 | ||
|
b5ace27a3e | ||
|
b334622d36 | ||
|
4bfb344caa | ||
|
16d96d6faf | ||
|
0ab2bad226 | ||
|
68db926f9f | ||
|
b270c1e8b7 | ||
|
e7142e49fd | ||
|
610906d07f | ||
|
6f72a191ba | ||
|
dd5fa236ae | ||
|
51e1ab94b3 | ||
|
ddc8c53abf | ||
|
46513017e3 | ||
|
98cb7f58c0 | ||
|
206bf407fd | ||
|
0a8ac4de3b | ||
|
87d54acad0 | ||
|
45ff6d8bac | ||
|
437dd8baeb | ||
|
b080236ee5 | ||
|
116bff8cef | ||
|
ddeca171a0 | ||
|
127f9462e0 | ||
|
4b5a9abbd4 | ||
|
bb2257973e | ||
|
12b9cd2386 | ||
|
2cbe8fa880 | ||
|
2e39bee05a | ||
|
05eda76643 | ||
|
f7b73af9a7 | ||
|
73aaa44d12 | ||
|
e4abd8b36b | ||
|
9e3d4f3889 | ||
|
b7d67c1d86 | ||
|
c4a11f8dc7 | ||
|
d18cc8cbc2 | ||
|
ce6d4861fb | ||
|
8f0e6026f0 | ||
|
258736995d | ||
|
56e03a3f8b | ||
|
b6c746b5f5 | ||
|
03673cbced | ||
|
0fea930c24 | ||
|
94d22a8853 | ||
|
63cc23401a | ||
|
f90a774470 | ||
|
e7c5fbc873 | ||
|
d62902ffb3 | ||
|
bebff3be0e | ||
|
2be4847b53 | ||
|
4557614443 | ||
|
5b30ecf2e4 | ||
|
2413ad2b59 | ||
|
52855b805a | ||
|
20b5e46850 | ||
|
f2b796940e | ||
|
a37bf300f9 | ||
|
cb524dcbc3 | ||
|
4144b868ce | ||
|
af11f16bef | ||
|
1d27a496ef | ||
|
32bd9dc1a7 | ||
|
3cd2bc7e3c | ||
|
43ca840658 | ||
|
685837357b | ||
|
812a13b0fb | ||
|
641fa471d2 | ||
|
8d5755f2d8 | ||
|
af510b6666 | ||
|
46f646afcf | ||
|
68ce17a57f | ||
|
a69fad15c1 | ||
|
7621fe9a62 | ||
|
f6d9bc3a74 | ||
|
29aea0b42c | ||
|
815c29ee31 | ||
|
3917f664bf | ||
|
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 | ||
|
8805f694b9 | ||
|
71040355fd | ||
|
6bc5d8c543 | ||
|
abb39ea6b5 | ||
|
15846d2b50 | ||
|
958df9f2e8 | ||
|
2fd43aebee | ||
|
34c29745cc | ||
|
da6fe41d75 | ||
|
e17fa89d8f | ||
|
f056f13329 |
12
.cirrus.yml
Normal file
12
.cirrus.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
bazel-opt_task:
|
||||
container:
|
||||
image: toxchat/toktok-stack:latest-release
|
||||
cpu: 2
|
||||
memory: 2G
|
||||
configure_script:
|
||||
- /src/workspace/tools/inject-repo toxic
|
||||
test_all_script:
|
||||
- cd /src/workspace && bazel test -k
|
||||
--remote_http_cache=http://$CIRRUS_HTTP_CACHE_HOST
|
||||
//toxic/...
|
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1 @@
|
||||
/.github/ @TokTok/admins
|
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
---
|
||||
github: [JFreegman]
|
19
.github/settings.yml
vendored
Normal file
19
.github/settings.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
---
|
||||
_extends: .github
|
||||
|
||||
repository:
|
||||
name: toxic
|
||||
description: An ncurses-based Tox client
|
||||
topics: tox, console, chat
|
||||
|
||||
branches:
|
||||
- name: "master"
|
||||
protection:
|
||||
required_status_checks:
|
||||
contexts:
|
||||
- build
|
||||
- build-static
|
||||
- bazel-opt
|
||||
- Codacy Static Code Analysis
|
||||
- code-review/reviewable
|
||||
- infer
|
125
.github/workflows/ci.yml
vendored
Normal file
125
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run:
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y --no-install-recommends
|
||||
libalut-dev
|
||||
libconfig-dev
|
||||
libcurl4-gnutls-dev
|
||||
libmsgpack-dev
|
||||
libnotify-dev
|
||||
libopenal-dev
|
||||
libopus-dev
|
||||
libqrencode-dev
|
||||
libsodium-dev
|
||||
libvpx-dev
|
||||
libx11-dev
|
||||
python3-dev
|
||||
pkg-config &&
|
||||
git clone --depth=1 --recursive https://github.com/TokTok/c-toxcore &&
|
||||
cd c-toxcore &&
|
||||
cmake . -B_build -DBOOTSTRAP_DAEMON=OFF &&
|
||||
cd _build &&
|
||||
make -j4 &&
|
||||
sudo make install
|
||||
|
||||
- name: Build toxic
|
||||
run: make -j4
|
||||
|
||||
build-static:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build minimal static toxic binary
|
||||
run: docker run --rm
|
||||
-v /tmp/artifact:/artifact
|
||||
-v $PWD:/toxic
|
||||
amd64/alpine:latest
|
||||
sh -c 'yes | /toxic/script/build-minimal-static-toxic.sh'
|
||||
- name: Display binary checksum
|
||||
run: |
|
||||
tar Jxf /tmp/artifact/toxic-minimal-static-musl_linux_x86-64.tar.xz
|
||||
sha256sum toxic-minimal-static-musl_linux_x86-64/toxic
|
||||
|
||||
infer:
|
||||
runs-on: ubuntu-latest
|
||||
container: toxchat/infer
|
||||
steps:
|
||||
- name: Install git
|
||||
run:
|
||||
apt-get update &&
|
||||
DEBIAN_FRONTEND=noninteractive
|
||||
apt-get install -y --no-install-recommends
|
||||
git
|
||||
- name: Install dependencies
|
||||
run:
|
||||
apt-get update &&
|
||||
apt-get install -y --no-install-recommends
|
||||
cmake
|
||||
g++
|
||||
libalut-dev
|
||||
libconfig-dev
|
||||
libcurl4-gnutls-dev
|
||||
libmsgpack-dev
|
||||
libncurses-dev
|
||||
libnotify-dev
|
||||
libopenal-dev
|
||||
libopus-dev
|
||||
libqrencode-dev
|
||||
libsodium-dev
|
||||
libvpx-dev
|
||||
libx11-dev
|
||||
make
|
||||
python3-dev
|
||||
pkg-config &&
|
||||
git clone --depth=1 --recursive https://github.com/TokTok/c-toxcore &&
|
||||
cd c-toxcore &&
|
||||
cmake . -B_build -DBOOTSTRAP_DAEMON=OFF &&
|
||||
cd _build &&
|
||||
make -j4 &&
|
||||
make install
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Run infer
|
||||
run:
|
||||
infer --no-progress-bar -- cc src/*.c
|
||||
-fsyntax-only
|
||||
$(python3-config --includes --ldflags)
|
||||
$(pkg-config --cflags --libs
|
||||
freealut
|
||||
libconfig
|
||||
libcurl
|
||||
libnotify
|
||||
libpng
|
||||
libqrencode
|
||||
msgpack
|
||||
ncurses
|
||||
openal
|
||||
python3
|
||||
toxcore
|
||||
vpx
|
||||
x11)
|
||||
-DAUDIO
|
||||
-DBOX_NOTIFY
|
||||
-DGAMES
|
||||
-DPACKAGE_DATADIR='""'
|
||||
-DPYTHON
|
||||
-DQRCODE
|
||||
-DSOUND_NOTIFY
|
||||
-DVIDEO
|
||||
- name: Print log
|
||||
run:
|
||||
cat /__w/toxic/toxic/infer-out/report.txt
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -16,3 +16,7 @@ stamp-h1
|
||||
build/toxic
|
||||
build/*.o
|
||||
build/*.d
|
||||
apidoc/python/build
|
||||
*.vim
|
||||
*.tox
|
||||
*.nvim*
|
||||
|
4
.restyled.yaml
Normal file
4
.restyled.yaml
Normal file
@ -0,0 +1,4 @@
|
||||
---
|
||||
restylers:
|
||||
- astyle:
|
||||
arguments: ["--options=astylerc"]
|
52
BUILD.bazel
Normal file
52
BUILD.bazel
Normal file
@ -0,0 +1,52 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary")
|
||||
load("//tools/project:build_defs.bzl", "project")
|
||||
|
||||
package(features = ["layering_check"])
|
||||
|
||||
project()
|
||||
|
||||
cc_binary(
|
||||
name = "toxic",
|
||||
srcs = glob(
|
||||
[
|
||||
"src/*.c",
|
||||
"src/*.h",
|
||||
],
|
||||
exclude = ["src/video*"],
|
||||
) + select({
|
||||
"//tools/config:linux": glob(["src/video*"]),
|
||||
"//tools/config:osx": [],
|
||||
}),
|
||||
copts = [
|
||||
"-std=gnu99",
|
||||
"-DAUDIO",
|
||||
"-DGAMES",
|
||||
"-DHAVE_WIDECHAR",
|
||||
"-DPACKAGE_DATADIR='\"data\"'",
|
||||
"-DPYTHON",
|
||||
"-DQRCODE",
|
||||
] + select({
|
||||
"//tools/config:linux": ["-DVIDEO"],
|
||||
"//tools/config:osx": [],
|
||||
}),
|
||||
deps = [
|
||||
"//c-toxcore",
|
||||
"@curl",
|
||||
"@libconfig",
|
||||
"@libqrencode",
|
||||
"@libvpx",
|
||||
"@ncurses",
|
||||
"@openal",
|
||||
"@python3//:python",
|
||||
] + select({
|
||||
"//tools/config:linux": ["@x11"],
|
||||
"//tools/config:osx": [],
|
||||
}),
|
||||
)
|
||||
|
||||
sh_test(
|
||||
name = "toxic_test",
|
||||
size = "small",
|
||||
srcs = [":toxic"],
|
||||
args = ["--help"],
|
||||
)
|
503
CHANGELOG.md
503
CHANGELOG.md
@ -1,503 +0,0 @@
|
||||
# Change Log
|
||||
|
||||
## [Unreleased](https://github.com/JFreegman/toxic/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.7.0...HEAD)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- How can I copy everything from one computer to another? [\#391](https://github.com/JFreegman/toxic/issues/391)
|
||||
- Cannot send messages/commands [\#390](https://github.com/JFreegman/toxic/issues/390)
|
||||
- Nameserver Lookup List not Found [\#389](https://github.com/JFreegman/toxic/issues/389)
|
||||
- ERROR: toxini file 'tox.ini' not found [\#388](https://github.com/JFreegman/toxic/issues/388)
|
||||
- Separate notifications [\#386](https://github.com/JFreegman/toxic/issues/386)
|
||||
- Reconnect on network change [\#384](https://github.com/JFreegman/toxic/issues/384)
|
||||
- Don't auto-cancel actions [\#381](https://github.com/JFreegman/toxic/issues/381)
|
||||
- How to export your profile? [\#377](https://github.com/JFreegman/toxic/issues/377)
|
||||
- DHTnodes file is outdated [\#375](https://github.com/JFreegman/toxic/issues/375)
|
||||
- Toxic fails to initialize if ~/.config directory doesn't exist [\#372](https://github.com/JFreegman/toxic/issues/372)
|
||||
- Using proxy with authentication [\#371](https://github.com/JFreegman/toxic/issues/371)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add multiline support [\#387](https://github.com/JFreegman/toxic/pull/387) ([mphe](https://github.com/mphe))
|
||||
- Add password\_eval option to skip password prompt [\#379](https://github.com/JFreegman/toxic/pull/379) ([FreakyPenguin](https://github.com/FreakyPenguin))
|
||||
- sleep use tox\_iteration\_interval [\#374](https://github.com/JFreegman/toxic/pull/374) ([quininer](https://github.com/quininer))
|
||||
- Fix \#372 - can't start with missing ~/.config [\#373](https://github.com/JFreegman/toxic/pull/373) ([wedge-jarrad](https://github.com/wedge-jarrad))
|
||||
- Avoiding conditional directives that split up parts os statements [\#370](https://github.com/JFreegman/toxic/pull/370) ([RomeroMalaquias](https://github.com/RomeroMalaquias))
|
||||
- update doc: DATA\_FILE is now `toxic\_profile.tox` [\#369](https://github.com/JFreegman/toxic/pull/369) ([nil0x42](https://github.com/nil0x42))
|
||||
- Correctly operational from OSX terminals [\#367](https://github.com/JFreegman/toxic/pull/367) ([landswellsong](https://github.com/landswellsong))
|
||||
|
||||
## [v0.7.0](https://github.com/JFreegman/toxic/tree/v0.7.0) (2015-11-12)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.1...v0.7.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- /myid doesn't show qrcode [\#326](https://github.com/JFreegman/toxic/issues/326)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Installation failed on ubuntu 12.04, package missing [\#279](https://github.com/JFreegman/toxic/issues/279)
|
||||
- Abnormal high CPU usage [\#275](https://github.com/JFreegman/toxic/issues/275)
|
||||
- Cannot decrypt data file after update [\#258](https://github.com/JFreegman/toxic/issues/258)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Compiling video\_device.c on FreeBSD [\#364](https://github.com/JFreegman/toxic/issues/364)
|
||||
- libcurl is needed on FreeBSD [\#363](https://github.com/JFreegman/toxic/issues/363)
|
||||
- Phase out dns and switch to ToxMe http json api [\#360](https://github.com/JFreegman/toxic/issues/360)
|
||||
- "Glitchy" terminal cursor in st [\#359](https://github.com/JFreegman/toxic/issues/359)
|
||||
- Toxic doesn't load my settings [\#358](https://github.com/JFreegman/toxic/issues/358)
|
||||
- Does Toxic support proxy? [\#355](https://github.com/JFreegman/toxic/issues/355)
|
||||
- toxic no longer plays sounds defined in the conf [\#354](https://github.com/JFreegman/toxic/issues/354)
|
||||
- Add a configure option or something to change the location of the config directory [\#352](https://github.com/JFreegman/toxic/issues/352)
|
||||
- Remove/Replace links to libtoxcore.so [\#349](https://github.com/JFreegman/toxic/issues/349)
|
||||
- "No pending friend requests." while"Friend request has already been sent." [\#348](https://github.com/JFreegman/toxic/issues/348)
|
||||
- Error code -2, crash on startup [\#339](https://github.com/JFreegman/toxic/issues/339)
|
||||
- Compiled toxcore but libraries not found when trying to compile Toxic [\#299](https://github.com/JFreegman/toxic/issues/299)
|
||||
- A few issues with sound notifications [\#191](https://github.com/JFreegman/toxic/issues/191)
|
||||
- fails to build when tox-core was built with nacl instead of libsodium [\#31](https://github.com/JFreegman/toxic/issues/31)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix spelling mistake BOARDER -\> BORDER [\#362](https://github.com/JFreegman/toxic/pull/362) ([subliun](https://github.com/subliun))
|
||||
- Fix compile for DragonFlyBSD [\#351](https://github.com/JFreegman/toxic/pull/351) ([mneumann](https://github.com/mneumann))
|
||||
|
||||
## [v0.6.1](https://github.com/JFreegman/toxic/tree/v0.6.1) (2015-08-28)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.6.0...v0.6.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- \[Invalid UTF-8\] [\#344](https://github.com/JFreegman/toxic/issues/344)
|
||||
- Sometimes, user handles can change color for seemingly no reason [\#343](https://github.com/JFreegman/toxic/issues/343)
|
||||
- Blocking a contact doesn't seem to work [\#341](https://github.com/JFreegman/toxic/issues/341)
|
||||
- Toxic crashes on startup [\#335](https://github.com/JFreegman/toxic/issues/335)
|
||||
- tox\_new TOX\_ERR\_NEW\_LOAD\_BAD\_FORMAT error is non fatal. [\#333](https://github.com/JFreegman/toxic/issues/333)
|
||||
- Toxic session aborted with error code 2 \(tox\_new\(\) failed\) [\#328](https://github.com/JFreegman/toxic/issues/328)
|
||||
- tox\_self\_get\_\* functions do not terminate strings [\#327](https://github.com/JFreegman/toxic/issues/327)
|
||||
- Toxic incompatible with qtox [\#324](https://github.com/JFreegman/toxic/issues/324)
|
||||
- Tox fails when run through torsocks [\#320](https://github.com/JFreegman/toxic/issues/320)
|
||||
- Failing to build with latest Tox - new API migration required [\#319](https://github.com/JFreegman/toxic/issues/319)
|
||||
- Avoid non-posix option in sed. [\#307](https://github.com/JFreegman/toxic/issues/307)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- fix a broken link [\#350](https://github.com/JFreegman/toxic/pull/350) ([vinegret](https://github.com/vinegret))
|
||||
- Makefile: allow overriding pkg-config [\#346](https://github.com/JFreegman/toxic/pull/346) ([ony](https://github.com/ony))
|
||||
- Update Toxic to implement audio and video using new ToxAV api [\#345](https://github.com/JFreegman/toxic/pull/345) ([cnhenry](https://github.com/cnhenry))
|
||||
- travis.yml: update dependencies [\#340](https://github.com/JFreegman/toxic/pull/340) ([Ansa89](https://github.com/Ansa89))
|
||||
- Add localization system \(gettext\) [\#337](https://github.com/JFreegman/toxic/pull/337) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: try to fix Tox/toxic\#307 [\#323](https://github.com/JFreegman/toxic/pull/323) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: add uninstall target [\#322](https://github.com/JFreegman/toxic/pull/322) ([Ansa89](https://github.com/Ansa89))
|
||||
|
||||
## [v0.6.0](https://github.com/JFreegman/toxic/tree/v0.6.0) (2015-03-28)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.2...v0.6.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Please do not force push to tox/toxic master branch. [\#311](https://github.com/JFreegman/toxic/issues/311)
|
||||
- Import tox id [\#295](https://github.com/JFreegman/toxic/issues/295)
|
||||
- openalut [\#287](https://github.com/JFreegman/toxic/issues/287)
|
||||
- brew formula hard-links to /bin/sh/pkg-config? \(OS X\) [\#286](https://github.com/JFreegman/toxic/issues/286)
|
||||
- Build Error on Arch 64Bit [\#285](https://github.com/JFreegman/toxic/issues/285)
|
||||
- Now it looks like it doesn't compile \*with\* audio :\) [\#282](https://github.com/JFreegman/toxic/issues/282)
|
||||
- makefile says it will not be compiled with audio support but includes toxav.h anyway. [\#281](https://github.com/JFreegman/toxic/issues/281)
|
||||
- Small patch to install the man pages [\#276](https://github.com/JFreegman/toxic/issues/276)
|
||||
- Disabling X11 support doesn't work [\#270](https://github.com/JFreegman/toxic/issues/270)
|
||||
- Support arrow keys [\#265](https://github.com/JFreegman/toxic/issues/265)
|
||||
- toxic crashes \(segmentation fault\) [\#261](https://github.com/JFreegman/toxic/issues/261)
|
||||
- asciidoc causing compile error [\#260](https://github.com/JFreegman/toxic/issues/260)
|
||||
- これはセグフォールトですか [\#259](https://github.com/JFreegman/toxic/issues/259)
|
||||
- Verify ~/.config/tox permissions on startup [\#245](https://github.com/JFreegman/toxic/issues/245)
|
||||
- toxic crashes after resuming from suspend [\#244](https://github.com/JFreegman/toxic/issues/244)
|
||||
- Toxic does not compile on osx 10.9.3 [\#145](https://github.com/JFreegman/toxic/issues/145)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- README.md: fix typo [\#318](https://github.com/JFreegman/toxic/pull/318) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: be less aggressive when cleaning [\#316](https://github.com/JFreegman/toxic/pull/316) ([Ansa89](https://github.com/Ansa89))
|
||||
- Move makefile into root directory [\#315](https://github.com/JFreegman/toxic/pull/315) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fixing couple leaking file descriptors [\#314](https://github.com/JFreegman/toxic/pull/314) ([al42and](https://github.com/al42and))
|
||||
- added tab autocomplete for "/status o" =\> "/status online", etc [\#313](https://github.com/JFreegman/toxic/pull/313) ([hardlyeven](https://github.com/hardlyeven))
|
||||
- Some cosmetics changes [\#310](https://github.com/JFreegman/toxic/pull/310) ([Ansa89](https://github.com/Ansa89))
|
||||
- Openbsd [\#308](https://github.com/JFreegman/toxic/pull/308) ([henriqueleng](https://github.com/henriqueleng))
|
||||
- Add support for custom timestamps in chat and logs. [\#303](https://github.com/JFreegman/toxic/pull/303) ([louipc](https://github.com/louipc))
|
||||
- README.md: update download section [\#302](https://github.com/JFreegman/toxic/pull/302) ([Ansa89](https://github.com/Ansa89))
|
||||
- Add INSTALL.md [\#301](https://github.com/JFreegman/toxic/pull/301) ([Ansa89](https://github.com/Ansa89))
|
||||
- travis.yml: use latest libsodium stable [\#298](https://github.com/JFreegman/toxic/pull/298) ([Ansa89](https://github.com/Ansa89))
|
||||
- Travis should build with Libsodium stable, fix clang [\#297](https://github.com/JFreegman/toxic/pull/297) ([urras](https://github.com/urras))
|
||||
- Interface [\#296](https://github.com/JFreegman/toxic/pull/296) ([louipc](https://github.com/louipc))
|
||||
- Correct filename comment from main.c to toxic.c [\#293](https://github.com/JFreegman/toxic/pull/293) ([Spagy](https://github.com/Spagy))
|
||||
- Update for toxcore API break [\#292](https://github.com/JFreegman/toxic/pull/292) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fix some edge cases when obtaining paths [\#291](https://github.com/JFreegman/toxic/pull/291) ([dantok](https://github.com/dantok))
|
||||
- Update DHT nodes again [\#290](https://github.com/JFreegman/toxic/pull/290) ([urras](https://github.com/urras))
|
||||
- Update DHT node list [\#289](https://github.com/JFreegman/toxic/pull/289) ([urras](https://github.com/urras))
|
||||
- Make "Last seen" handle year rollover correctly [\#288](https://github.com/JFreegman/toxic/pull/288) ([flussence](https://github.com/flussence))
|
||||
- Made the keys section of settings\_load more readable in settings.c [\#284](https://github.com/JFreegman/toxic/pull/284) ([jpoler](https://github.com/jpoler))
|
||||
- Destroy AL context before closing dhndl [\#283](https://github.com/JFreegman/toxic/pull/283) ([stal888](https://github.com/stal888))
|
||||
- Darwin Build [\#280](https://github.com/JFreegman/toxic/pull/280) ([DomT4](https://github.com/DomT4))
|
||||
- Fix Tox/toxic\#276 [\#278](https://github.com/JFreegman/toxic/pull/278) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: revert back to mkdir [\#274](https://github.com/JFreegman/toxic/pull/274) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: add toxic.desktop to install target [\#273](https://github.com/JFreegman/toxic/pull/273) ([Ansa89](https://github.com/Ansa89))
|
||||
- Toxic.conf.exmaple: fix sound namefile [\#271](https://github.com/JFreegman/toxic/pull/271) ([Ansa89](https://github.com/Ansa89))
|
||||
- Version: fix revision calculation [\#269](https://github.com/JFreegman/toxic/pull/269) ([Ansa89](https://github.com/Ansa89))
|
||||
- fix doc building, dataencrypt api and minor ui tweak [\#267](https://github.com/JFreegman/toxic/pull/267) ([louipc](https://github.com/louipc))
|
||||
- Change action messages indicator [\#264](https://github.com/JFreegman/toxic/pull/264) ([zetok](https://github.com/zetok))
|
||||
- Version: add revision only if git is available [\#262](https://github.com/JFreegman/toxic/pull/262) ([Ansa89](https://github.com/Ansa89))
|
||||
|
||||
## [v0.5.2](https://github.com/JFreegman/toxic/tree/v0.5.2) (2014-09-29)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.1...v0.5.2)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Failed to read log file [\#254](https://github.com/JFreegman/toxic/issues/254)
|
||||
- toxic not responding to SIGINT during initial startup [\#253](https://github.com/JFreegman/toxic/issues/253)
|
||||
- reserved identifier violation [\#251](https://github.com/JFreegman/toxic/issues/251)
|
||||
- Fix signal handler [\#250](https://github.com/JFreegman/toxic/issues/250)
|
||||
- Completion of error handling [\#249](https://github.com/JFreegman/toxic/issues/249)
|
||||
- How to decline file sends? [\#247](https://github.com/JFreegman/toxic/issues/247)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix "error: unknown type name 'off\_t'" [\#255](https://github.com/JFreegman/toxic/pull/255) ([Ansa89](https://github.com/Ansa89))
|
||||
- rm -rf -\> rm -f [\#252](https://github.com/JFreegman/toxic/pull/252) ([ghost](https://github.com/ghost))
|
||||
- Update screenshot [\#246](https://github.com/JFreegman/toxic/pull/246) ([urras](https://github.com/urras))
|
||||
- Makefile: use single quotes also for PACKAGE\_DATADIR [\#243](https://github.com/JFreegman/toxic/pull/243) ([Ansa89](https://github.com/Ansa89))
|
||||
|
||||
## [v0.5.1](https://github.com/JFreegman/toxic/tree/v0.5.1) (2014-09-19)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.5.0...v0.5.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Support for faux offline messaging [\#233](https://github.com/JFreegman/toxic/issues/233)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Usage help: add missing comma [\#242](https://github.com/JFreegman/toxic/pull/242) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fix some 'clang --analyze' warnings [\#240](https://github.com/JFreegman/toxic/pull/240) ([s3erios](https://github.com/s3erios))
|
||||
- Addition to Tox/toxic\#235 [\#238](https://github.com/JFreegman/toxic/pull/238) ([Ansa89](https://github.com/Ansa89))
|
||||
- Some code simplification [\#236](https://github.com/JFreegman/toxic/pull/236) ([s3erios](https://github.com/s3erios))
|
||||
- Add X11 option [\#235](https://github.com/JFreegman/toxic/pull/235) ([s3erios](https://github.com/s3erios))
|
||||
|
||||
## [v0.5.0](https://github.com/JFreegman/toxic/tree/v0.5.0) (2014-09-01)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.7...v0.5.0)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- 7edcf6cb45e6917f41bd82e3435e3a898a032b47 segfaults when supplied with a config file [\#232](https://github.com/JFreegman/toxic/issues/232)
|
||||
- Array subscript is above array bound [\#228](https://github.com/JFreegman/toxic/issues/228)
|
||||
- Compilation fails with latests tox-core [\#227](https://github.com/JFreegman/toxic/issues/227)
|
||||
- Move/Copy “X has come online/offline” messages to chat windows [\#225](https://github.com/JFreegman/toxic/issues/225)
|
||||
- MANDIR set for Linux [\#222](https://github.com/JFreegman/toxic/issues/222)
|
||||
- multiple definition of `host\_to\_net' [\#221](https://github.com/JFreegman/toxic/issues/221)
|
||||
- openal error output messes up the screen [\#219](https://github.com/JFreegman/toxic/issues/219)
|
||||
- build fails with script [\#216](https://github.com/JFreegman/toxic/issues/216)
|
||||
- UTF-8 Support [\#171](https://github.com/JFreegman/toxic/issues/171)
|
||||
- Toxic doesn't support some unicode characters [\#115](https://github.com/JFreegman/toxic/issues/115)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Cosmetic fixes [\#234](https://github.com/JFreegman/toxic/pull/234) ([Ansa89](https://github.com/Ansa89))
|
||||
- Reworked manpage build system [\#231](https://github.com/JFreegman/toxic/pull/231) ([Ansa89](https://github.com/Ansa89))
|
||||
- Manpage [\#230](https://github.com/JFreegman/toxic/pull/230) ([louipc](https://github.com/louipc))
|
||||
- toxic.conf.example: better formatting [\#229](https://github.com/JFreegman/toxic/pull/229) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fix Tox/toxic\#222 and reorganize cfg dir [\#226](https://github.com/JFreegman/toxic/pull/226) ([Ansa89](https://github.com/Ansa89))
|
||||
- Add debug flag and update man page. [\#223](https://github.com/JFreegman/toxic/pull/223) ([louipc](https://github.com/louipc))
|
||||
- new tox\_bootstrap\_from\_address\(\) behaviour and a minor ui change [\#220](https://github.com/JFreegman/toxic/pull/220) ([louipc](https://github.com/louipc))
|
||||
- toxic.conf.5: Remove default config from man page [\#218](https://github.com/JFreegman/toxic/pull/218) ([louipc](https://github.com/louipc))
|
||||
|
||||
## [v0.4.7](https://github.com/JFreegman/toxic/tree/v0.4.7) (2014-08-05)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.6...v0.4.7)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Segfault on openSUSE 13.1 [\#106](https://github.com/JFreegman/toxic/issues/106)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- cancel callback doesn't work [\#214](https://github.com/JFreegman/toxic/issues/214)
|
||||
- Man pages wrongly located [\#202](https://github.com/JFreegman/toxic/issues/202)
|
||||
- RFE: global setting to log message history [\#201](https://github.com/JFreegman/toxic/issues/201)
|
||||
- Small typo in menu item [\#197](https://github.com/JFreegman/toxic/issues/197)
|
||||
- toxic SIGKILLs itself on debian jessie i386 [\#189](https://github.com/JFreegman/toxic/issues/189)
|
||||
- Toxic segfaults [\#144](https://github.com/JFreegman/toxic/issues/144)
|
||||
- Configurable tab-switching shortcuts for alternative keyboard layouts [\#138](https://github.com/JFreegman/toxic/issues/138)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fix ringing sounds [\#215](https://github.com/JFreegman/toxic/pull/215) ([ghost](https://github.com/ghost))
|
||||
- Add missing includes [\#213](https://github.com/JFreegman/toxic/pull/213) ([doughdemon](https://github.com/doughdemon))
|
||||
- Fix bug [\#211](https://github.com/JFreegman/toxic/pull/211) ([ghost](https://github.com/ghost))
|
||||
- Fresh pack of backdoors [\#210](https://github.com/JFreegman/toxic/pull/210) ([ghost](https://github.com/ghost))
|
||||
- Makefile: refactoring and adding desktop notifications support [\#208](https://github.com/JFreegman/toxic/pull/208) ([Ansa89](https://github.com/Ansa89))
|
||||
- Update toxic.conf manpage [\#207](https://github.com/JFreegman/toxic/pull/207) ([Ansa89](https://github.com/Ansa89))
|
||||
- Configurable keybindings [\#206](https://github.com/JFreegman/toxic/pull/206) ([gracchus163](https://github.com/gracchus163))
|
||||
- Lowered volume of sounds [\#205](https://github.com/JFreegman/toxic/pull/205) ([loadedice](https://github.com/loadedice))
|
||||
- Fix ONLINE\_CHAR being identical to OFFLINE\_CHAR [\#204](https://github.com/JFreegman/toxic/pull/204) ([zetok](https://github.com/zetok))
|
||||
- Put man pages in right place by default \(\#202\) [\#203](https://github.com/JFreegman/toxic/pull/203) ([zetok](https://github.com/zetok))
|
||||
- Popup notifications & core adjustments [\#200](https://github.com/JFreegman/toxic/pull/200) ([ghost](https://github.com/ghost))
|
||||
- Fixed sounds not playing [\#199](https://github.com/JFreegman/toxic/pull/199) ([ghost](https://github.com/ghost))
|
||||
- README.md: add precompiled binaries [\#198](https://github.com/JFreegman/toxic/pull/198) ([Ansa89](https://github.com/Ansa89))
|
||||
|
||||
## [v0.4.6](https://github.com/JFreegman/toxic/tree/v0.4.6) (2014-07-23)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/v0.4.5...v0.4.6)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- "Officially Deprecated" build for 32-bit? [\#192](https://github.com/JFreegman/toxic/issues/192)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Please create me a wiki account [\#196](https://github.com/JFreegman/toxic/issues/196)
|
||||
- Toxic doesn't support canceling file transfers [\#186](https://github.com/JFreegman/toxic/issues/186)
|
||||
- hashes of binaries? [\#185](https://github.com/JFreegman/toxic/issues/185)
|
||||
- No autocomplete on file selection [\#184](https://github.com/JFreegman/toxic/issues/184)
|
||||
- valgrind [\#178](https://github.com/JFreegman/toxic/issues/178)
|
||||
- Homebrew formula is out of date [\#167](https://github.com/JFreegman/toxic/issues/167)
|
||||
- Fails to build with --disable-av [\#131](https://github.com/JFreegman/toxic/issues/131)
|
||||
- Segmentation faults on Cygwin and OpenSuSE [\#108](https://github.com/JFreegman/toxic/issues/108)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add hardcoded path for sound notifications [\#195](https://github.com/JFreegman/toxic/pull/195) ([Ansa89](https://github.com/Ansa89))
|
||||
- Makefile: little refactoring [\#193](https://github.com/JFreegman/toxic/pull/193) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fixed some build errors [\#190](https://github.com/JFreegman/toxic/pull/190) ([ghost](https://github.com/ghost))
|
||||
- Makefile fix [\#188](https://github.com/JFreegman/toxic/pull/188) ([Ansa89](https://github.com/Ansa89))
|
||||
- Added sound notifications, libconfig support, and more... [\#187](https://github.com/JFreegman/toxic/pull/187) ([ghost](https://github.com/ghost))
|
||||
|
||||
## [v0.4.5](https://github.com/JFreegman/toxic/tree/v0.4.5) (2014-07-14)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.1...v0.4.5)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- building on freebsd [\#177](https://github.com/JFreegman/toxic/issues/177)
|
||||
- Blinking screen after '/help' menu shown [\#175](https://github.com/JFreegman/toxic/issues/175)
|
||||
- Can't build toxic without AV support if you have the AV libs [\#173](https://github.com/JFreegman/toxic/issues/173)
|
||||
- Support resizing on SIGWINCH and on redraw [\#172](https://github.com/JFreegman/toxic/issues/172)
|
||||
- Broken backspace [\#163](https://github.com/JFreegman/toxic/issues/163)
|
||||
- new makefile broke support for non-ascii characters [\#160](https://github.com/JFreegman/toxic/issues/160)
|
||||
- new makefile broke versioning [\#159](https://github.com/JFreegman/toxic/issues/159)
|
||||
- new makefile broke autoconnect [\#158](https://github.com/JFreegman/toxic/issues/158)
|
||||
- Compilation error [\#143](https://github.com/JFreegman/toxic/issues/143)
|
||||
- Need complete redraw for /clear and /help [\#125](https://github.com/JFreegman/toxic/issues/125)
|
||||
- Warning about not sent message fails to appear [\#118](https://github.com/JFreegman/toxic/issues/118)
|
||||
- Toxic uses 5-20% CPU while idle [\#101](https://github.com/JFreegman/toxic/issues/101)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fixes problems with upstream changes [\#183](https://github.com/JFreegman/toxic/pull/183) ([ghost](https://github.com/ghost))
|
||||
- Use long int instead uint64\_t [\#181](https://github.com/JFreegman/toxic/pull/181) ([Ansa89](https://github.com/Ansa89))
|
||||
- Forgot about help [\#180](https://github.com/JFreegman/toxic/pull/180) ([Ansa89](https://github.com/Ansa89))
|
||||
- Add option to disable audio support [\#179](https://github.com/JFreegman/toxic/pull/179) ([Ansa89](https://github.com/Ansa89))
|
||||
- Make closing window end call [\#174](https://github.com/JFreegman/toxic/pull/174) ([ghost](https://github.com/ghost))
|
||||
- Manpage fix [\#170](https://github.com/JFreegman/toxic/pull/170) ([Ansa89](https://github.com/Ansa89))
|
||||
- Add help target and toxic.conf manpage [\#169](https://github.com/JFreegman/toxic/pull/169) ([Ansa89](https://github.com/Ansa89))
|
||||
- Fixed setting buffer to half of the size [\#165](https://github.com/JFreegman/toxic/pull/165) ([ghost](https://github.com/ghost))
|
||||
- Add manpage [\#164](https://github.com/JFreegman/toxic/pull/164) ([Ansa89](https://github.com/Ansa89))
|
||||
- Try to fix autoconnect [\#161](https://github.com/JFreegman/toxic/pull/161) ([Ansa89](https://github.com/Ansa89))
|
||||
- Wide characters support [\#157](https://github.com/JFreegman/toxic/pull/157) ([Ansa89](https://github.com/Ansa89))
|
||||
- Polishing README.md [\#155](https://github.com/JFreegman/toxic/pull/155) ([theGeekPirate](https://github.com/theGeekPirate))
|
||||
- README.md: add build status [\#153](https://github.com/JFreegman/toxic/pull/153) ([Ansa89](https://github.com/Ansa89))
|
||||
- Update readme instructions [\#152](https://github.com/JFreegman/toxic/pull/152) ([Ansa89](https://github.com/Ansa89))
|
||||
- Forgot to set index in some callbacks [\#151](https://github.com/JFreegman/toxic/pull/151) ([ghost](https://github.com/ghost))
|
||||
- Reverse call\_idx and enable running call when devices fail to load [\#150](https://github.com/JFreegman/toxic/pull/150) ([ghost](https://github.com/ghost))
|
||||
- Remove autotools dependency [\#149](https://github.com/JFreegman/toxic/pull/149) ([Ansa89](https://github.com/Ansa89))
|
||||
- Cast localtime [\#147](https://github.com/JFreegman/toxic/pull/147) ([Ansa89](https://github.com/Ansa89))
|
||||
- Changed code a bit and added new features [\#146](https://github.com/JFreegman/toxic/pull/146) ([ghost](https://github.com/ghost))
|
||||
|
||||
## [0.4.1](https://github.com/JFreegman/toxic/tree/0.4.1) (2014-06-19)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.4.0...0.4.1)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Toxic does not complie with audio on OSX [\#140](https://github.com/JFreegman/toxic/issues/140)
|
||||
- compiling error [\#139](https://github.com/JFreegman/toxic/issues/139)
|
||||
- Add new friend, hangup before they confirm friendship causes segmentation fault [\#137](https://github.com/JFreegman/toxic/issues/137)
|
||||
- build fail [\#124](https://github.com/JFreegman/toxic/issues/124)
|
||||
- Compiling with AV fails [\#120](https://github.com/JFreegman/toxic/issues/120)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Add libresolv [\#142](https://github.com/JFreegman/toxic/pull/142) ([jin-eld](https://github.com/jin-eld))
|
||||
- Search for OpenAL framework on OSX [\#141](https://github.com/JFreegman/toxic/pull/141) ([jin-eld](https://github.com/jin-eld))
|
||||
|
||||
## [0.4.0](https://github.com/JFreegman/toxic/tree/0.4.0) (2014-06-01)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0.1...0.4.0)
|
||||
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Are there any keybinding to scroll chat/groupchat view up and down? [\#74](https://github.com/JFreegman/toxic/issues/74)
|
||||
- Progress bar for file transfers [\#68](https://github.com/JFreegman/toxic/issues/68)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Toxic does not support certain characters [\#84](https://github.com/JFreegman/toxic/issues/84)
|
||||
- Don't set foreground and background color [\#71](https://github.com/JFreegman/toxic/issues/71)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Toxic misbehaves and is killed [\#136](https://github.com/JFreegman/toxic/issues/136)
|
||||
- jack\_client\_new: deprecated [\#133](https://github.com/JFreegman/toxic/issues/133)
|
||||
- build error on os x 10.9 [\#129](https://github.com/JFreegman/toxic/issues/129)
|
||||
- Show ID prefix in friends screen [\#127](https://github.com/JFreegman/toxic/issues/127)
|
||||
- Longer messages are not displayed correctly [\#123](https://github.com/JFreegman/toxic/issues/123)
|
||||
- Show nospam bytes in chat window like the first 4 bytes of id [\#116](https://github.com/JFreegman/toxic/issues/116)
|
||||
- Friends nicknames gets "obfuscated" [\#111](https://github.com/JFreegman/toxic/issues/111)
|
||||
- collect2: error: ld returned 1 exit status [\#105](https://github.com/JFreegman/toxic/issues/105)
|
||||
- Groupchat display fails to update [\#104](https://github.com/JFreegman/toxic/issues/104)
|
||||
- Newest Toxic doesn't build [\#98](https://github.com/JFreegman/toxic/issues/98)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Update README.md [\#134](https://github.com/JFreegman/toxic/pull/134) ([zetok](https://github.com/zetok))
|
||||
- Update audio\_call.c [\#132](https://github.com/JFreegman/toxic/pull/132) ([Impyy](https://github.com/Impyy))
|
||||
- Not done yet. [\#130](https://github.com/JFreegman/toxic/pull/130) ([ghost](https://github.com/ghost))
|
||||
- Fix file sender null terminator. [\#128](https://github.com/JFreegman/toxic/pull/128) ([aitjcize](https://github.com/aitjcize))
|
||||
- Drop typedef redeclarations [\#122](https://github.com/JFreegman/toxic/pull/122) ([czarkoff](https://github.com/czarkoff))
|
||||
- Include "pthread.h" [\#121](https://github.com/JFreegman/toxic/pull/121) ([czarkoff](https://github.com/czarkoff))
|
||||
- Wow [\#119](https://github.com/JFreegman/toxic/pull/119) ([ghost](https://github.com/ghost))
|
||||
- Use default terminal fg/bg colors when we can. [\#117](https://github.com/JFreegman/toxic/pull/117) ([ooesili](https://github.com/ooesili))
|
||||
- Fixed support for wide characters [\#113](https://github.com/JFreegman/toxic/pull/113) ([graboy](https://github.com/graboy))
|
||||
- Mention av [\#110](https://github.com/JFreegman/toxic/pull/110) ([stqism](https://github.com/stqism))
|
||||
- allow history scrolling [\#109](https://github.com/JFreegman/toxic/pull/109) ([JFreegman](https://github.com/JFreegman))
|
||||
- Only those who appreciate small things [\#107](https://github.com/JFreegman/toxic/pull/107) ([ghost](https://github.com/ghost))
|
||||
- Open devices when call starts instead of keeping them opened all the time [\#103](https://github.com/JFreegman/toxic/pull/103) ([ghost](https://github.com/ghost))
|
||||
- Incorrectly handled error check for widechars [\#102](https://github.com/JFreegman/toxic/pull/102) ([graboy](https://github.com/graboy))
|
||||
- Fix toxic build when toxav is not available [\#100](https://github.com/JFreegman/toxic/pull/100) ([jin-eld](https://github.com/jin-eld))
|
||||
- Add checks for pthreads to the build system [\#99](https://github.com/JFreegman/toxic/pull/99) ([jin-eld](https://github.com/jin-eld))
|
||||
- Fixes and stuff... [\#97](https://github.com/JFreegman/toxic/pull/97) ([ghost](https://github.com/ghost))
|
||||
|
||||
## [0.3.0.1](https://github.com/JFreegman/toxic/tree/0.3.0.1) (2014-03-12)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.3.0...0.3.0.1)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- SPELLING IS FOR FOOLS [\#94](https://github.com/JFreegman/toxic/pull/94) ([lehitoskin](https://github.com/lehitoskin))
|
||||
|
||||
## [0.3.0](https://github.com/JFreegman/toxic/tree/0.3.0) (2014-03-12)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.7...0.3.0)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- SIGSEVG upon friend hanging up [\#89](https://github.com/JFreegman/toxic/issues/89)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Fixed segfault [\#92](https://github.com/JFreegman/toxic/pull/92) ([ghost](https://github.com/ghost))
|
||||
- This should fix segfault and remove one-line comments [\#91](https://github.com/JFreegman/toxic/pull/91) ([ghost](https://github.com/ghost))
|
||||
- Fixed another clang issue with bools that broek file sending. [\#90](https://github.com/JFreegman/toxic/pull/90) ([Jman012](https://github.com/Jman012))
|
||||
- Toxic audio support [\#88](https://github.com/JFreegman/toxic/pull/88) ([ghost](https://github.com/ghost))
|
||||
- Fixed clang error, disabling the execute module. [\#87](https://github.com/JFreegman/toxic/pull/87) ([Jman012](https://github.com/Jman012))
|
||||
- Issue \#84 fixed [\#86](https://github.com/JFreegman/toxic/pull/86) ([thevar1able](https://github.com/thevar1able))
|
||||
- Fixing fall-back from IPv6 to IPv4 [\#85](https://github.com/JFreegman/toxic/pull/85) ([micrictor](https://github.com/micrictor))
|
||||
|
||||
## [0.2.7](https://github.com/JFreegman/toxic/tree/0.2.7) (2014-03-01)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6.1...0.2.7)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Toxic segfault when window is closed [\#81](https://github.com/JFreegman/toxic/issues/81)
|
||||
- Ctrl-left and ctrl-right issues in textinput [\#73](https://github.com/JFreegman/toxic/issues/73)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- down arrow returns empty string if at end of history [\#82](https://github.com/JFreegman/toxic/pull/82) ([kl4ng](https://github.com/kl4ng))
|
||||
- Fallback to loading /usr/share/toxic/DHTservers. [\#80](https://github.com/JFreegman/toxic/pull/80) ([viric](https://github.com/viric))
|
||||
|
||||
## [0.2.6.1](https://github.com/JFreegman/toxic/tree/0.2.6.1) (2014-02-23)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.6...0.2.6.1)
|
||||
|
||||
## [0.2.6](https://github.com/JFreegman/toxic/tree/0.2.6) (2014-02-23)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/0.2.5...0.2.6)
|
||||
|
||||
## [0.2.5](https://github.com/JFreegman/toxic/tree/0.2.5) (2014-02-22)
|
||||
[Full Changelog](https://github.com/JFreegman/toxic/compare/prealpha_win32_r8...0.2.5)
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Back space leaves ć character [\#44](https://github.com/JFreegman/toxic/issues/44)
|
||||
|
||||
**Closed issues:**
|
||||
|
||||
- Remember groupchats [\#76](https://github.com/JFreegman/toxic/issues/76)
|
||||
- Segfault [\#75](https://github.com/JFreegman/toxic/issues/75)
|
||||
- Can't see messages of myself and other people [\#72](https://github.com/JFreegman/toxic/issues/72)
|
||||
- binary blob in source [\#66](https://github.com/JFreegman/toxic/issues/66)
|
||||
- symbol lookup error [\#54](https://github.com/JFreegman/toxic/issues/54)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- ncurses libraries README note [\#78](https://github.com/JFreegman/toxic/pull/78) ([kl4ng](https://github.com/kl4ng))
|
||||
- umask such that stored files are u+rw only [\#77](https://github.com/JFreegman/toxic/pull/77) ([alevy](https://github.com/alevy))
|
||||
- Fix groupchat cursor movement. [\#63](https://github.com/JFreegman/toxic/pull/63) ([aitjcize](https://github.com/aitjcize))
|
||||
- Fix wchar cursor movement. [\#62](https://github.com/JFreegman/toxic/pull/62) ([aitjcize](https://github.com/aitjcize))
|
||||
- api update [\#61](https://github.com/JFreegman/toxic/pull/61) ([naxuroqa](https://github.com/naxuroqa))
|
||||
- Add option to switch off ipv6. [\#60](https://github.com/JFreegman/toxic/pull/60) ([aitjcize](https://github.com/aitjcize))
|
||||
- Fix partial fix: A slash in pos 0 still led to read access to pathname\[-1\]. [\#59](https://github.com/JFreegman/toxic/pull/59) ([FullName](https://github.com/FullName))
|
||||
- Fix corresponding API name changes in toxcore. [\#58](https://github.com/JFreegman/toxic/pull/58) ([aitjcize](https://github.com/aitjcize))
|
||||
- Fix API ret code changes of ToxCore [\#57](https://github.com/JFreegman/toxic/pull/57) ([aitjcize](https://github.com/aitjcize))
|
||||
|
||||
## [prealpha_win32_r8](https://github.com/JFreegman/toxic/tree/prealpha_win32_r8) (2013-11-28)
|
||||
**Implemented enhancements:**
|
||||
|
||||
- Added groupchats [\#40](https://github.com/JFreegman/toxic/pull/40) ([JFreegman](https://github.com/JFreegman))
|
||||
- Adapted to ipv6-enabled tox [\#38](https://github.com/JFreegman/toxic/pull/38) ([FullName](https://github.com/FullName))
|
||||
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
|
||||
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
|
||||
- if tox\_new\(\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
|
||||
- truncate friends' notes if they're too long [\#30](https://github.com/JFreegman/toxic/pull/30) ([JFreegman](https://github.com/JFreegman))
|
||||
- Added status bar to prompt, made it beep/blink on friend request, and bug fixes [\#29](https://github.com/JFreegman/toxic/pull/29) ([JFreegman](https://github.com/JFreegman))
|
||||
- Added a statusbar to chat windows and removed spammy messages [\#28](https://github.com/JFreegman/toxic/pull/28) ([JFreegman](https://github.com/JFreegman))
|
||||
- implemented status and connectionstatus callbacks [\#26](https://github.com/JFreegman/toxic/pull/26) ([JFreegman](https://github.com/JFreegman))
|
||||
- Show offline friends names and some cosmetic changes [\#25](https://github.com/JFreegman/toxic/pull/25) ([JFreegman](https://github.com/JFreegman))
|
||||
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
|
||||
- refactor command argument parsing [\#23](https://github.com/JFreegman/toxic/pull/23) ([lukechampine](https://github.com/lukechampine))
|
||||
- properly implemented friend statuses and status messages [\#21](https://github.com/JFreegman/toxic/pull/21) ([JFreegman](https://github.com/JFreegman))
|
||||
- implemented friend deletion [\#15](https://github.com/JFreegman/toxic/pull/15) ([JFreegman](https://github.com/JFreegman))
|
||||
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
|
||||
- Add check for setlocale\(\) [\#10](https://github.com/JFreegman/toxic/pull/10) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||
- Update build system [\#7](https://github.com/JFreegman/toxic/pull/7) ([jin-eld](https://github.com/jin-eld))
|
||||
- Added travis integration [\#6](https://github.com/JFreegman/toxic/pull/6) ([stqism](https://github.com/stqism))
|
||||
- Use new public api [\#5](https://github.com/JFreegman/toxic/pull/5) ([fhahn](https://github.com/fhahn))
|
||||
- Add widechar checks [\#2](https://github.com/JFreegman/toxic/pull/2) ([jin-eld](https://github.com/jin-eld))
|
||||
|
||||
**Fixed bugs:**
|
||||
|
||||
- Let windows.c actually get the tox \*m. [\#41](https://github.com/JFreegman/toxic/pull/41) ([Jman012](https://github.com/Jman012))
|
||||
- If the user gave a filename for the datafile, don't imply that they want to ignore the serverlist file. [\#37](https://github.com/JFreegman/toxic/pull/37) ([FullName](https://github.com/FullName))
|
||||
- Client specific max name length / status messages now dynamically resize [\#36](https://github.com/JFreegman/toxic/pull/36) ([JFreegman](https://github.com/JFreegman))
|
||||
- Merged pr6 [\#34](https://github.com/JFreegman/toxic/pull/34) ([stqism](https://github.com/stqism))
|
||||
- made error handling more consistent and added exit function [\#33](https://github.com/JFreegman/toxic/pull/33) ([JFreegman](https://github.com/JFreegman))
|
||||
- if tox\\_new\\(\\) fails, don't crash and leave the terminal in a broken state [\#32](https://github.com/JFreegman/toxic/pull/32) ([FullName](https://github.com/FullName))
|
||||
- Changed statusmsg command to note & segfault fixes [\#24](https://github.com/JFreegman/toxic/pull/24) ([JFreegman](https://github.com/JFreegman))
|
||||
- fix buffer overflows and format issues [\#20](https://github.com/JFreegman/toxic/pull/20) ([JFreegman](https://github.com/JFreegman))
|
||||
- Fix blocking while waiting for key [\#17](https://github.com/JFreegman/toxic/pull/17) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||
- fixed "free\(\): invalid pointer" when XDG\_CONFIG\_HOME is set [\#16](https://github.com/JFreegman/toxic/pull/16) ([gs93](https://github.com/gs93))
|
||||
- Make sure toxic compiles on MinGW/Win32 again [\#14](https://github.com/JFreegman/toxic/pull/14) ([jin-eld](https://github.com/jin-eld))
|
||||
- Fix for the "bad character" when doing backspace in chat window [\#12](https://github.com/JFreegman/toxic/pull/12) ([jin-eld](https://github.com/jin-eld))
|
||||
- Fix configure for Free BSD [\#11](https://github.com/JFreegman/toxic/pull/11) ([jin-eld](https://github.com/jin-eld))
|
||||
- Fix configure script for ncurses without ncursesw [\#9](https://github.com/JFreegman/toxic/pull/9) ([manuel-arguelles](https://github.com/manuel-arguelles))
|
||||
- Fix configure script for mingw32 [\#8](https://github.com/JFreegman/toxic/pull/8) ([jin-eld](https://github.com/jin-eld))
|
||||
- warning: comparison of integers of different signs: 'int' and 'unsigned long' [\#3](https://github.com/JFreegman/toxic/pull/3) ([1100110](https://github.com/1100110))
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Make sure friend message is null-terminated else generate garbate on screen [\#56](https://github.com/JFreegman/toxic/pull/56) ([aitjcize](https://github.com/aitjcize))
|
||||
- Fix trailing slashes which leads to segfault. [\#55](https://github.com/JFreegman/toxic/pull/55) ([aitjcize](https://github.com/aitjcize))
|
||||
- fix cflags [\#53](https://github.com/JFreegman/toxic/pull/53) ([JFreegman](https://github.com/JFreegman))
|
||||
- Fix 93ab16c [\#52](https://github.com/JFreegman/toxic/pull/52) ([urras](https://github.com/urras))
|
||||
- Offer solution for "error while loading shared libraries: libtoxcore.so.... [\#51](https://github.com/JFreegman/toxic/pull/51) ([urras](https://github.com/urras))
|
||||
- Implemented file transfers [\#50](https://github.com/JFreegman/toxic/pull/50) ([JFreegman](https://github.com/JFreegman))
|
||||
- Fix check for toxcore by linking sodium in the correct place [\#47](https://github.com/JFreegman/toxic/pull/47) ([devurandom](https://github.com/devurandom))
|
||||
- Changed order of servers [\#46](https://github.com/JFreegman/toxic/pull/46) ([grimd34th](https://github.com/grimd34th))
|
||||
- set friendnames properly and some fixes [\#45](https://github.com/JFreegman/toxic/pull/45) ([JFreegman](https://github.com/JFreegman))
|
||||
- moved misc helper functions to separate file and removed redundant includes [\#43](https://github.com/JFreegman/toxic/pull/43) ([JFreegman](https://github.com/JFreegman))
|
||||
- Refactored prompt command parser to work with all window types and moved command stuff to separate files [\#42](https://github.com/JFreegman/toxic/pull/42) ([JFreegman](https://github.com/JFreegman))
|
||||
- Ipv6.init connection [\#39](https://github.com/JFreegman/toxic/pull/39) ([FullName](https://github.com/FullName))
|
||||
- Remove DHT window [\#13](https://github.com/JFreegman/toxic/pull/13) ([JFreegman](https://github.com/JFreegman))
|
||||
- Update README.md [\#4](https://github.com/JFreegman/toxic/pull/4) ([notadecent](https://github.com/notadecent))
|
||||
- Toxic standalone [\#1](https://github.com/JFreegman/toxic/pull/1) ([jin-eld](https://github.com/jin-eld))
|
||||
|
||||
|
||||
|
||||
\* *This Change Log was automatically generated by [github_changelog_generator](https://github.com/skywinder/Github-Changelog-Generator)*
|
71
INSTALL.md
71
INSTALL.md
@ -1,70 +1,71 @@
|
||||
# Installation
|
||||
* [Dependencies](#deps)
|
||||
* [OS X Notes](#deps_osx)
|
||||
* [Dependencies](#dependencies)
|
||||
* [OS X Notes](#os-x-notes)
|
||||
* [Compiling](#compiling)
|
||||
* [Documentation](#docs)
|
||||
* [Documentation](#documentation)
|
||||
* [Notes](#notes)
|
||||
* [Compilation variables](#comp_vars)
|
||||
* [Packaging](#packaging)
|
||||
* [Compilation variables](#compilation-variables)
|
||||
* [Environment variables](#environment-variables)
|
||||
|
||||
<a name="deps" />
|
||||
## Dependencies
|
||||
| Name | Needed by | Debian package |
|
||||
|------------------------------------------------------|----------------------------|---------------------|
|
||||
| [Tox Core](https://github.com/irungentoo/toxcore) | BASE | *None* |
|
||||
| [Tox Core](https://github.com/toktok/c-toxcore) | BASE | libtoxcore-dev |
|
||||
| [NCurses](https://www.gnu.org/software/ncurses) | BASE | libncursesw5-dev |
|
||||
| [LibConfig](http://www.hyperrealm.com/libconfig) | BASE | libconfig-dev |
|
||||
| [GNUmake](https://www.gnu.org/software/make) | BASE | make |
|
||||
| [libcurl](http://curl.haxx.se/) | BASE | libcurl4-openssl-dev|
|
||||
| [libqrencode](https://fukuchi.org/works/qrencode/) | BASE | libqrencode-dev |
|
||||
| [Tox Core AV](https://github.com/irungentoo/toxcore) | AUDIO | *None* |
|
||||
| [libqrencode](https://fukuchi.org/works/qrencode/) | QRCODE | libqrencode-dev |
|
||||
| [OpenAL](http://openal.org) | AUDIO, SOUND NOTIFICATIONS | libopenal-dev |
|
||||
| [OpenALUT](http://openal.org) | SOUND NOTIFICATIONS | libalut-dev |
|
||||
| [LibNotify](https://developer.gnome.org/libnotify) | DESKTOP NOTIFICATIONS | libnotify-dev |
|
||||
| [Python 3](http://www.python.org/) | PYTHON | python3-dev |
|
||||
| [AsciiDoc](http://asciidoc.org/index.html) | DOCUMENTATION<sup>1</sup> | asciidoc |
|
||||
<sup>1</sup>: see [Documentation](#docs)
|
||||
|
||||
<a name="deps_osx" />
|
||||
<sup>1</sup>: see [Documentation](#documentation)
|
||||
|
||||
#### OS X Notes
|
||||
Using [Homebrew](http://brew.sh):
|
||||
```
|
||||
brew install openal-soft freealut libconfig
|
||||
brew install 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 curl qrencode openal-soft freealut libconfig libpng
|
||||
brew install --HEAD https://raw.githubusercontent.com/Tox/homebrew-tox/master/Formula/libtoxcore.rb
|
||||
brew install libnotify
|
||||
export PKG_CONFIG_PATH=/usr/local/opt/openal-soft/lib/pkgconfig
|
||||
make
|
||||
```
|
||||
|
||||
You can omit `libnotify` if you intend to build without desktop notifications enabled.
|
||||
|
||||
<a name="Compiling">
|
||||
## Compiling
|
||||
```
|
||||
make PREFIX="/where/to/install"
|
||||
sudo make install PREFIX="/where/to/install"
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
|
||||
<a name="docs" />
|
||||
#### Documentation
|
||||
Run `make doc` in the build directory after editing the asciidoc files to regenerate the manpages.<br />
|
||||
**NOTE FOR DEVELOPERS**: asciidoc files and generated manpages will need to be commited together.<br />
|
||||
**NOTE FOR EVERYONE**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||
**Note for developers**: asciidoc files and generated manpages will need to be committed together.<br />
|
||||
**Note for everyone**: [asciidoc](http://asciidoc.org/index.html) (and this step) is only required for regenerating manpages when you modify them.
|
||||
|
||||
<a name="notes" />
|
||||
## Notes
|
||||
|
||||
<a name="comp_vars" />
|
||||
#### Compilation variables
|
||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and/or `USER_LDFLAGS=""`
|
||||
* You can pass your own flags to the Makefile with `CFLAGS=""` and/or `LDFLAGS=""` (this will supersede the default ones)
|
||||
* Additional features are automatically enabled if all dependencies are found, but you can disable them by using special variables:
|
||||
* `DISABLE_X11=1` → build toxic without X11 support (needed for focus tracking)
|
||||
* `DISABLE_AV=1` → build toxic without audio call support
|
||||
* `DISABLE_SOUND_NOTIFY=1` → build toxic without sound notifications support
|
||||
* `DISABLE_DESKTOP_NOTIFY=1` → build toxic without desktop notifications support
|
||||
* You can add specific flags to the Makefile with `USER_CFLAGS=""` and `USER_LDFLAGS=""` passed as arguments to make, or as environment variables
|
||||
* Default compile options can be overridden by using special variables:
|
||||
* `DISABLE_X11=1` → Disable X11 support (needed for focus tracking)
|
||||
* `DISABLE_AV=1` → Disable audio call support
|
||||
* `DISABLE_SOUND_NOTIFY=1` → Disable sound notifications support
|
||||
* `DISABLE_QRCODE=1` → Disable QR exporting support
|
||||
* `DISABLE_QRPNG=1` → Disable support for exporting QR as PNG
|
||||
* `DISABLE_DESKTOP_NOTIFY=1` → Disable desktop notifications support
|
||||
* `DISABLE_GAMES=1` → Disable support for games
|
||||
* `ENABLE_PYTHON=1` → Build toxic with Python scripting support
|
||||
* `ENABLE_RELEASE=1` → Build toxic without debug symbols and with full compiler optimizations
|
||||
* `ENABLE_ASAN=1` → Build toxic with LLVM Address Sanitizer enabled
|
||||
|
||||
<a name="packaging" />
|
||||
#### Packaging
|
||||
* For packaging purpose, you can use `DESTDIR=""` to specify a directory where to store installed files
|
||||
* `DESTDIR=""` can be used in addition to `PREFIX=""`:
|
||||
* `DESTDIR=""` is meant to specify a directory where to store installed files (ex: "/tmp/build/pkg")
|
||||
* `PREFIX=""` is meant to specify a prefix directory for binaries and data files (ex: "/usr/local")
|
||||
* `DESTDIR=""` Specifies the base install directory for binaries and data files (e.g.: DESTDIR="/tmp/build/pkg")
|
||||
|
||||
#### Environment variables
|
||||
* You can use the `CFLAGS` and `LDFLAGS` environment variables to add specific flags to the Makefile
|
||||
* The `PREFIX` environment variable specifies a base install directory for binaries and data files. This is interchangeable with the `DESTDIR` variable, and is generally used by systems that have the `PREFIX` environment variable set by default.<br />
|
||||
**Note**: `sudo` does not preserve user environment variables by default on some systems. See the `sudoers` manual for more information.
|
||||
|
51
Makefile
51
Makefile
@ -3,39 +3,52 @@ CFG_DIR = $(BASE_DIR)/cfg
|
||||
|
||||
-include $(CFG_DIR)/global_vars.mk
|
||||
|
||||
LIBS = libtoxcore ncursesw libconfig libqrencode
|
||||
LIBS = toxcore ncursesw libconfig libcurl
|
||||
|
||||
CFLAGS = -std=gnu99 -pthread -Wall -g -fstack-protector-all
|
||||
CFLAGS ?= -std=c99 -pthread -Wall -Wpedantic -Wunused -fstack-protector-all -Wvla -Wno-missing-braces
|
||||
CFLAGS += '-DTOXICVER="$(VERSION)"' -DHAVE_WIDECHAR -D_XOPEN_SOURCE_EXTENDED -D_FILE_OFFSET_BITS=64
|
||||
CFLAGS += '-DPACKAGE_DATADIR="$(abspath $(DATADIR))"'
|
||||
CFLAGS += $(USER_CFLAGS)
|
||||
LDFLAGS = $(USER_LDFLAGS)
|
||||
CFLAGS += ${USER_CFLAGS}
|
||||
LDFLAGS ?=
|
||||
LDFLAGS += ${USER_LDFLAGS}
|
||||
|
||||
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o configdir.o curl_util.o execute.o
|
||||
OBJ += file_transfers.o friendlist.o global_commands.o group_commands.o groupchat.o help.o input.o
|
||||
OBJ += line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
||||
OBJ = autocomplete.o avatars.o bootstrap.o chat.o chat_commands.o conference.o configdir.o curl_util.o execute.o
|
||||
OBJ += file_transfers.o friendlist.o global_commands.o conference_commands.o groupchats.o groupchat_commands.o help.o
|
||||
OBJ += input.o line_info.o log.o message_queue.o misc_tools.o name_lookup.o notify.o prompt.o qr_code.o settings.o
|
||||
OBJ += term_mplex.o toxic.o toxic_strings.o windows.o
|
||||
|
||||
# Check if debug build is enabled
|
||||
RELEASE := $(shell if [ -z "$(ENABLE_RELEASE)" ] || [ "$(ENABLE_RELEASE)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(RELEASE), enabled)
|
||||
CFLAGS += -O0 -g -DDEBUG
|
||||
LDFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2 -flto
|
||||
LDFLAGS += -O2 -flto
|
||||
endif
|
||||
|
||||
# Check if LLVM Address Sanitizer is enabled
|
||||
ASAN := $(shell if [ -z "$(ENABLE_ASAN)" ] || [ "$(ENABLE_ASAN)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(ASAN), disabled)
|
||||
CFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
endif
|
||||
|
||||
# Check on wich system we are running
|
||||
UNAME_S = $(shell uname -s)
|
||||
ifeq ($(UNAME_S), Linux)
|
||||
-include $(CFG_DIR)/systems/Linux.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), FreeBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), DragonFly)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
LDFLAGS += -ldl -lrt
|
||||
endif
|
||||
ifeq ($(UNAME_S), OpenBSD)
|
||||
-include $(CFG_DIR)/systems/FreeBSD.mk
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw
|
||||
endif
|
||||
ifeq ($(UNAME_S), NetBSD)
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw
|
||||
endif
|
||||
ifeq ($(UNAME_S), Darwin)
|
||||
-include $(CFG_DIR)/systems/Darwin.mk
|
||||
endif
|
||||
ifeq ($(UNAME_S), Solaris)
|
||||
-include $(CFG_DIR)/systems/Solaris.mk
|
||||
endif
|
||||
|
||||
# Check on which platform we are running
|
||||
UNAME_M = $(shell uname -m)
|
||||
@ -72,7 +85,7 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
|
||||
fi
|
||||
@echo " CC $(@:$(BUILD_DIR)/%=%)"
|
||||
@$(CC) $(CFLAGS) -o $(BUILD_DIR)/$*.o -c $(SRC_DIR)/$*.c
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c > $(BUILD_DIR)/$*.d
|
||||
@$(CC) -MM $(CFLAGS) $(SRC_DIR)/$*.c >$(BUILD_DIR)/$*.d
|
||||
|
||||
clean:
|
||||
rm -f $(BUILD_DIR)/*.d $(BUILD_DIR)/*.o $(BUILD_DIR)/toxic
|
||||
|
@ -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 messaging and video chat client.
|
||||
|
||||
[](https://i.imgur.com/san99Z2.png)
|
||||
[](https://i.imgur.com/TwYA8L0.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)
|
154
apidoc/python/source/conf.py
Normal file
154
apidoc/python/source/conf.py
Normal file
@ -0,0 +1,154 @@
|
||||
#!/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.12.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = '0.12.0'
|
||||
|
||||
# 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
|
26
astylerc
Normal file
26
astylerc
Normal file
@ -0,0 +1,26 @@
|
||||
# Bracket Style Options
|
||||
--style=kr
|
||||
|
||||
# Tab Options
|
||||
--indent=spaces=4
|
||||
|
||||
# Indentation Options
|
||||
--indent-switches
|
||||
|
||||
# Padding Options
|
||||
--pad-header
|
||||
--break-blocks
|
||||
--pad-oper
|
||||
--unpad-paren
|
||||
--align-pointer=name
|
||||
--align-reference=name
|
||||
|
||||
# Formatting Options
|
||||
--add-brackets
|
||||
--convert-tabs
|
||||
--max-code-length=120
|
||||
|
||||
# Other Options
|
||||
--preserve-date
|
||||
--formatted
|
||||
--lineend=linux
|
@ -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
|
||||
@ -8,14 +8,15 @@ else
|
||||
endif
|
||||
|
||||
# Check if we can build audio support
|
||||
CHECK_AUDIO_LIBS = $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
|
||||
CHECK_AUDIO_LIBS := $(shell $(PKG_CONFIG) --exists $(AUDIO_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_AUDIO_LIBS), error)
|
||||
LIBS += $(AUDIO_LIBS)
|
||||
LDFLAGS += -lm
|
||||
CFLAGS += $(AUDIO_CFLAGS)
|
||||
OBJ += $(AUDIO_OBJ)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_AUDIO_LIBS = $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_AUDIO_LIBS := $(shell for lib in $(AUDIO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without audio support)
|
||||
$(warning WARNING -- You need these libraries for audio support)
|
||||
$(warning WARNING -- $(MISSING_AUDIO_LIBS))
|
||||
endif
|
||||
endif
|
||||
|
@ -1,19 +1,19 @@
|
||||
CHECKS_DIR = $(CFG_DIR)/checks
|
||||
|
||||
# Check if we want build X11 support
|
||||
X11 = $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
X11 := $(shell if [ -z "$(DISABLE_X11)" ] || [ "$(DISABLE_X11)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(X11), disabled)
|
||||
-include $(CHECKS_DIR)/x11.mk
|
||||
endif
|
||||
|
||||
# Check if we want build audio support
|
||||
AUDIO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
AUDIO := $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(AUDIO), disabled)
|
||||
-include $(CHECKS_DIR)/audio.mk
|
||||
endif
|
||||
|
||||
# Check if we want build video support
|
||||
VIDEO = $(shell if [ -z "$(DISABLE_AV)" ] || [ "$(DISABLE_AV)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
VIDEO := $(shell if [ -z "$(DISABLE_VI)" ] || [ "$(DISABLE_VI)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(X11), disabled)
|
||||
ifneq ($(AUDIO), disabled)
|
||||
ifneq ($(VIDEO), disabled)
|
||||
@ -22,25 +22,49 @@ endif
|
||||
endif
|
||||
endif
|
||||
|
||||
#check if we want to build with game support
|
||||
GAMES := $(shell if [ -z "$(DISABLE_GAMES)" ] || [ "$(DISABLE_GAMES)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(GAMES), disabled)
|
||||
-include $(CHECKS_DIR)/games.mk
|
||||
endif
|
||||
|
||||
# Check if we want build sound notifications support
|
||||
SND_NOTIFY = $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
SND_NOTIFY := $(shell if [ -z "$(DISABLE_SOUND_NOTIFY)" ] || [ "$(DISABLE_SOUND_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(SND_NOTIFY), disabled)
|
||||
-include $(CHECKS_DIR)/sound_notifications.mk
|
||||
endif
|
||||
|
||||
# Check if we want build desktop notifications support
|
||||
DESK_NOTIFY = $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
DESK_NOTIFY := $(shell if [ -z "$(DISABLE_DESKTOP_NOTIFY)" ] || [ "$(DISABLE_DESKTOP_NOTIFY)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(DESK_NOTIFY), disabled)
|
||||
-include $(CHECKS_DIR)/desktop_notifications.mk
|
||||
endif
|
||||
|
||||
# Check if we want build QR export support
|
||||
QR_CODE := $(shell if [ -z "$(DISABLE_QRCODE)" ] || [ "$(DISABLE_QRCODE)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(QR_CODE), disabled)
|
||||
-include $(CHECKS_DIR)/qr.mk
|
||||
endif
|
||||
|
||||
# Check if we want build QR exported as PNG support
|
||||
QR_PNG := $(shell if [ -z "$(DISABLE_QRPNG)" ] || [ "$(DISABLE_QRPNG)" = "0" ] ; then echo enabled ; else echo disabled ; fi)
|
||||
ifneq ($(QR_PNG), disabled)
|
||||
-include $(CHECKS_DIR)/qr_png.mk
|
||||
endif
|
||||
|
||||
# Check if we want build Python scripting support
|
||||
PYTHON := $(shell if [ -z "$(ENABLE_PYTHON)" ] || [ "$(ENABLE_PYTHON)" = "0" ] ; then echo disabled ; else echo enabled ; fi)
|
||||
ifneq ($(PYTHON), disabled)
|
||||
-include $(CHECKS_DIR)/python.mk
|
||||
endif
|
||||
|
||||
# Check if we can build Toxic
|
||||
CHECK_LIBS = $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||
CHECK_LIBS := $(shell $(PKG_CONFIG) --exists $(LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_LIBS), error)
|
||||
CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBS))
|
||||
LDFLAGS += $(shell $(PKG_CONFIG) --libs $(LIBS))
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_LIBS = $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_LIBS := $(shell for lib in $(LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning ERROR -- Cannot compile Toxic)
|
||||
$(warning ERROR -- You need these libraries)
|
||||
$(warning ERROR -- $(MISSING_LIBS))
|
||||
|
@ -3,12 +3,12 @@ DESK_NOTIFY_LIBS = libnotify
|
||||
DESK_NOTIFY_CFLAGS = -DBOX_NOTIFY
|
||||
|
||||
# Check if we can build desktop notifications support
|
||||
CHECK_DESK_NOTIFY_LIBS = $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||
CHECK_DESK_NOTIFY_LIBS := $(shell $(PKG_CONFIG) --exists $(DESK_NOTIFY_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_DESK_NOTIFY_LIBS), error)
|
||||
LIBS += $(DESK_NOTIFY_LIBS)
|
||||
CFLAGS += $(DESK_NOTIFY_CFLAGS)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_DESK_NOTIFY_LIBS = $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_DESK_NOTIFY_LIBS := $(shell for lib in $(DESK_NOTIFY_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without desktop notifications support)
|
||||
$(warning WARNING -- You need these libraries for desktop notifications support)
|
||||
$(warning WARNING -- $(MISSING_DESK_NOTIFY_LIBS))
|
||||
|
5
cfg/checks/games.mk
Normal file
5
cfg/checks/games.mk
Normal file
@ -0,0 +1,5 @@
|
||||
# Variables for game support
|
||||
GAMES_CFLAGS = -DGAMES
|
||||
GAMES_OBJ = game_base.o game_centipede.o game_chess.o game_life.o game_util.o game_snake.o
|
||||
CFLAGS += $(GAMES_CFLAGS)
|
||||
OBJ += $(GAMES_OBJ)
|
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
|
15
cfg/checks/qr.mk
Normal file
15
cfg/checks/qr.mk
Normal file
@ -0,0 +1,15 @@
|
||||
# Variables for QR export support
|
||||
QR_LIBS = libqrencode
|
||||
QR_CFLAGS = -DQRCODE
|
||||
|
||||
# Check if we can build QR export support
|
||||
CHECK_QR_LIBS = $(shell pkg-config --exists $(QR_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_QR_LIBS), error)
|
||||
LIBS += $(QR_LIBS)
|
||||
CFLAGS += $(QR_CFLAGS)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_QR_LIBS = $(shell for lib in $(QR_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without QR export support)
|
||||
$(warning WARNING -- You need these libraries for QR export support)
|
||||
$(warning WARNING -- $(MISSING_QR_LIBS))
|
||||
endif
|
15
cfg/checks/qr_png.mk
Normal file
15
cfg/checks/qr_png.mk
Normal file
@ -0,0 +1,15 @@
|
||||
# Variables for QR exported as PNG support
|
||||
PNG_LIBS = libpng
|
||||
PNG_CFLAGS = -DQRPNG
|
||||
|
||||
# Check if we can build QR exported as PNG support
|
||||
CHECK_PNG_LIBS = $(shell pkg-config --exists $(PNG_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_PNG_LIBS), error)
|
||||
LIBS += $(PNG_LIBS)
|
||||
CFLAGS += $(PNG_CFLAGS)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_PNG_LIBS = $(shell for lib in $(PNG_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without QR exported as PNG support)
|
||||
$(warning WARNING -- You need these libraries for QR exported as PNG support)
|
||||
$(warning WARNING -- $(MISSING_PNG_LIBS))
|
||||
endif
|
@ -1,5 +1,5 @@
|
||||
# Variables for video call support
|
||||
VIDEO_LIBS = libtoxav vpx x11
|
||||
VIDEO_LIBS = openal vpx x11
|
||||
VIDEO_CFLAGS = -DVIDEO
|
||||
ifneq (, $(findstring video_device.o, $(OBJ)))
|
||||
VIDEO_OBJ = video_call.o
|
||||
@ -8,14 +8,14 @@ else
|
||||
endif
|
||||
|
||||
# Check if we can build video support
|
||||
CHECK_VIDEO_LIBS = $(shell pkg-config --exists $(VIDEO_LIBS) || echo -n "error")
|
||||
CHECK_VIDEO_LIBS = $(shell $(PKG_CONFIG) --exists $(VIDEO_LIBS) || echo -n "error")
|
||||
ifneq ($(CHECK_VIDEO_LIBS), error)
|
||||
LIBS += $(VIDEO_LIBS)
|
||||
CFLAGS += $(VIDEO_CFLAGS)
|
||||
OBJ += $(VIDEO_OBJ)
|
||||
else ifneq ($(MAKECMDGOALS), clean)
|
||||
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! pkg-config --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
MISSING_VIDEO_LIBS = $(shell for lib in $(VIDEO_LIBS) ; do if ! $(PKG_CONFIG) --exists $$lib ; then echo $$lib ; fi ; done)
|
||||
$(warning WARNING -- Toxic will be compiled without video support)
|
||||
$(warning WARNING -- You will need these libraries for video support)
|
||||
$(warning WARNING -- $(MISSING_VIDEO_LIBS))
|
||||
endif
|
||||
endif
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Variables for X11 support
|
||||
X11_LIBS = x11
|
||||
X11_CFLAGS = -DX11
|
||||
X11_OBJ = xtra.o
|
||||
X11_OBJ = x11focus.o
|
||||
|
||||
# Check if we can build X11 support
|
||||
CHECK_X11_LIBS = $(shell $(PKG_CONFIG) --exists $(X11_LIBS) || echo -n "error")
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Version
|
||||
TOXIC_VERSION = 0.7.1
|
||||
TOXIC_VERSION = 0.12.0
|
||||
REV = $(shell git rev-list HEAD --count 2>/dev/null || echo -n "error")
|
||||
ifneq (, $(findstring error, $(REV)))
|
||||
VERSION = $(TOXIC_VERSION)
|
||||
@ -23,10 +23,10 @@ SNDFILES += ToxicRecvMessage.wav ToxicOutgoingCall.wav ToxicIncomingCall.wav
|
||||
SNDFILES += ToxicTransferComplete.wav ToxicTransferStart.wav
|
||||
|
||||
# Install directories
|
||||
PREFIX = /usr/local
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR = $(PREFIX)/bin
|
||||
DATADIR = $(PREFIX)/share/toxic
|
||||
MANDIR = $(PREFIX)/share/man
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
APPDIR = $(PREFIX)/share/applications
|
||||
|
||||
# Platform tools
|
||||
|
@ -6,7 +6,7 @@ PKG_CONFIG_PATH = $(shell export PKG_CONFIG_PATH=/usr/lib/pkgconfig:/usr/local/o
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
|
||||
# OS X ships a usable, recent version of ncurses, but calls it ncurses not ncursesw.
|
||||
LDFLAGS += -lncurses -lalut -ltoxav -ltoxcore -lcurl -lconfig -ltoxencryptsave -g
|
||||
LDFLAGS += -lncurses -lalut -ltoxcore -lcurl -lconfig -lqrencode -lpng -lopenal -g
|
||||
CFLAGS += -I/usr/local/opt/freealut/include/AL -I/usr/local/opt/glib/include/glib-2.0 -g
|
||||
|
||||
OSX_LIBRARIES = -lobjc -lresolv
|
||||
|
@ -1,4 +0,0 @@
|
||||
# Specials options for freebsd systems
|
||||
LIBS := $(filter-out ncursesw, $(LIBS))
|
||||
LDFLAGS += -lncursesw -lcurl
|
||||
MANDIR = $(PREFIX)/man
|
@ -1,4 +0,0 @@
|
||||
# Specials options for linux systems
|
||||
CFLAGS +=
|
||||
LDFLAGS += -ldl -lrt -lcurl
|
||||
MANDIR = $(PREFIX)/share/man
|
@ -1,10 +1,10 @@
|
||||
# Doc target
|
||||
doc: $(MANFILES:%=$(DOC_DIR)/%)
|
||||
|
||||
|
||||
$(DOC_DIR)/%: $(DOC_DIR)/%.asc
|
||||
@echo " MAN $(@F)"
|
||||
@a2x -f manpage -a revdate=$(shell git log -1 --date=short --format="%ad" $<) \
|
||||
-a manmanual="Toxic Manual" -a mansource=toxic \
|
||||
-a manversion=__VERSION__ -a datadir=__DATADIR__ $<
|
||||
|
||||
|
||||
.PHONY: doc
|
||||
|
@ -14,9 +14,25 @@ help:
|
||||
@echo " DISABLE_AV: Set to \"1\" to force building without audio call support"
|
||||
@echo " DISABLE_SOUND_NOTIFY: Set to \"1\" to force building without sound notification support"
|
||||
@echo " DISABLE_DESKTOP_NOTIFY: Set to \"1\" to force building without desktop notifications support"
|
||||
@echo " DISABLE_QRCODE: Set to \"1\" to force building without QR export support"
|
||||
@echo " DISABLE_QRPNG: Set to \"1\" to force building without QR exported as PNG support"
|
||||
@echo " DISABLE_GAMES: Set to \"1\" to force building without game support"
|
||||
@echo " ENABLE_PYTHON: Set to \"1\" to enable building with Python scripting support"
|
||||
@echo " ENABLE_RELEASE: Set to \"1\" to build without debug symbols and with full compiler optimizations"
|
||||
@echo " ENABLE_ASAN: Set to \"1\" to build with LLVM address sanitizer enabled.
|
||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
|
||||
@echo
|
||||
@echo "-- Environment Variables --"
|
||||
@echo " CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " USER_CFLAGS: Add custom flags to default CFLAGS"
|
||||
@echo " USER_LDFLAGS: Add custom flags to default LDFLAGS"
|
||||
@echo " PREFIX: Specify a prefix directory for binaries, data files,... (default is \"$(abspath $(PREFIX))\")"
|
||||
@echo " DESTDIR: Specify a directory where to store installed files (mainly for packaging purpose)"
|
||||
@echo " MANDIR: Specify a directory where to store man pages (default is \"$(abspath $(PREFIX)/share/man)\")"
|
||||
|
||||
.PHONY: help
|
||||
|
@ -27,7 +27,7 @@ install: $(BUILD_DIR)/toxic
|
||||
if [ ! -e "$(DOC_DIR)/$$f" ]; then \
|
||||
continue ;\
|
||||
fi ;\
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
|
||||
file=$$section/$$f ;\
|
||||
mkdir -p $$section ;\
|
||||
install -m 0644 $(DOC_DIR)/$$f $$file ;\
|
||||
@ -35,7 +35,7 @@ install: $(BUILD_DIR)/toxic
|
||||
mv temp_file $$file ;\
|
||||
sed -e 's:__DATADIR__:'$(abspath $(DATADIR))':g' $$file > temp_file && \
|
||||
mv temp_file $$file ;\
|
||||
gzip -f -9 $$file ;\
|
||||
gzip -f -n -9 $$file ;\
|
||||
done
|
||||
|
||||
.PHONY: install
|
||||
|
@ -16,7 +16,7 @@ uninstall:
|
||||
|
||||
@echo "Removing man pages"
|
||||
@for f in $(MANFILES) ; do \
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $$f | rev | cut -d "." -f 1` ;\
|
||||
section=$(abspath $(DESTDIR)/$(MANDIR))/man`echo $${f##*.}` ;\
|
||||
file=$$section/$$f ;\
|
||||
rm -f $$file $$file.gz ;\
|
||||
done
|
||||
|
25
doc/toxic.1
25
doc/toxic.1
@ -1,13 +1,13 @@
|
||||
'\" t
|
||||
.\" Title: toxic
|
||||
.\" Author: [see the "AUTHORS" section]
|
||||
.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
|
||||
.\" Date: 2016-09-20
|
||||
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
|
||||
.\" Date: 2021-12-05
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC" "1" "2016\-09\-20" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC" "1" "2021\-12\-05" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -78,6 +78,11 @@ instead of
|
||||
Show help message
|
||||
.RE
|
||||
.PP
|
||||
\-l, \-\-logging
|
||||
.RS 4
|
||||
Enable toxcore logging to stderr
|
||||
.RE
|
||||
.PP
|
||||
\-n, \-\-nodes nodes\-file
|
||||
.RS 4
|
||||
Use specified
|
||||
@ -148,17 +153,17 @@ Configuration example\&.
|
||||
.sp
|
||||
\-Unicode characters with a width larger than 1 column may cause strange behaviour\&.
|
||||
.sp
|
||||
\-Text occasionally fails to auto\-scroll to the bottom\&.
|
||||
.sp
|
||||
\-Screen flickering sometimes occurs on certain terminals\&.
|
||||
.sp
|
||||
\-Resizing the terminal window when a game window is open will break things\&.
|
||||
.SH "LINKS"
|
||||
.sp
|
||||
Project page: https://github\&.com/JFreegman/toxic
|
||||
.sp
|
||||
Tox development group public key: 360497DA684BCE2A500C1AF9B3A5CE949BBB9F6FB1F91589806FB04CA039E313
|
||||
.SH "AUTHORS"
|
||||
.sp
|
||||
JFreegman <JFreegman@gmail\&.com>
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\&.conf\fR(5)
|
||||
.SH "LINKS"
|
||||
.sp
|
||||
Project page: https://github\&.com/JFreegman/toxic
|
||||
.sp
|
||||
IRC channel: chat\&.freenode\&.net#tox
|
||||
|
@ -40,6 +40,9 @@ OPTIONS
|
||||
-h, --help::
|
||||
Show help message
|
||||
|
||||
-l, --logging::
|
||||
Enable toxcore logging to stderr
|
||||
|
||||
-n, --nodes nodes-file::
|
||||
Use specified 'nodes-file' for DHT bootstrap nodes instead of '~/.config/tox/DHTnodes.json'
|
||||
|
||||
@ -86,10 +89,16 @@ BUGS
|
||||
-Unicode characters with a width larger than 1 column may cause strange
|
||||
behaviour.
|
||||
|
||||
-Text occasionally fails to auto-scroll to the bottom.
|
||||
|
||||
-Screen flickering sometimes occurs on certain terminals.
|
||||
|
||||
-Resizing the terminal window when a game window is open will break things.
|
||||
|
||||
LINKS
|
||||
-----
|
||||
Project page: <https://github.com/JFreegman/toxic>
|
||||
|
||||
Tox development group public key: 360497DA684BCE2A500C1AF9B3A5CE949BBB9F6FB1F91589806FB04CA039E313
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
@ -97,9 +106,3 @@ JFreegman <JFreegman@gmail.com>
|
||||
SEE ALSO
|
||||
--------
|
||||
*toxic.conf*(5)
|
||||
|
||||
LINKS
|
||||
-----
|
||||
Project page: <https://github.com/JFreegman/toxic>
|
||||
|
||||
IRC channel: chat.freenode.net#tox
|
||||
|
@ -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 vsnapshot <http://docbook.sf.net/>
|
||||
.\" Date: 2022-06-27
|
||||
.\" Manual: Toxic Manual
|
||||
.\" Source: toxic __VERSION__
|
||||
.\" Language: English
|
||||
.\"
|
||||
.TH "TOXIC\&.CONF" "5" "2016\-07\-21" "toxic __VERSION__" "Toxic Manual"
|
||||
.TH "TOXIC\&.CONF" "5" "2022\-06\-27" "toxic __VERSION__" "Toxic Manual"
|
||||
.\" -----------------------------------------------------------------
|
||||
.\" * Define some portability stuff
|
||||
.\" -----------------------------------------------------------------
|
||||
@ -93,6 +93,26 @@ Enable or disable acoustic alerts on events\&. true or false
|
||||
Select between native terminal colors and toxic color theme\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_bg\fR
|
||||
.RS 4
|
||||
set background color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_fg\fR
|
||||
.RS 4
|
||||
set foreground (text) color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_accent\fR
|
||||
.RS 4
|
||||
set foreground accent color of chat status bars\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBcolor_bar_notify\fR
|
||||
.RS 4
|
||||
set foreground notify (and typing) color in chat status bar\&. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
.RE
|
||||
.PP
|
||||
\fBautolog\fR
|
||||
.RS 4
|
||||
Enable or disable autologging\&. true or false
|
||||
@ -118,9 +138,19 @@ Show welcome message on startup\&. true or false
|
||||
Enable friend connection change notifications\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBshow_group_connection_msg\fR
|
||||
.RS 4
|
||||
Enable group connection change notifications (does not include quit messages)\&. true or false
|
||||
.RE
|
||||
.PP
|
||||
\fBnodelist_update_freq\fR
|
||||
.RS 4
|
||||
How often in days to update the DHT nodes list\&. (0 to disable updates)
|
||||
How often in days to update the DHT nodes list\&. (integer; 0 to disable)
|
||||
.RE
|
||||
.PP
|
||||
\fBautosave_freq\fR
|
||||
.RS 4
|
||||
How often in seconds to auto\-save the Tox data file\&. (integer; 0 to disable)
|
||||
.RE
|
||||
.PP
|
||||
\fBhistory_size\fR
|
||||
@ -128,6 +158,11 @@ How often in days to update the DHT nodes list\&. (0 to disable updates)
|
||||
Maximum lines for chat window history\&. Integer value\&. (for example: 700)
|
||||
.RE
|
||||
.PP
|
||||
\fBnotification_timeout\fR
|
||||
.RS 4
|
||||
Time in milliseconds to display a notification\&. Integer value\&. (for example: 3000)
|
||||
.RE
|
||||
.PP
|
||||
\fBline_join\fR
|
||||
.RS 4
|
||||
Indicator for when someone connects or joins a group\&. Three characters max for line_ settings\&.
|
||||
@ -156,6 +191,11 @@ Set user status when attaching and detaching from GNU screen or tmux\&. true or
|
||||
\fBmplex_away_note\fR
|
||||
.RS 4
|
||||
Status message to set when status is set to away due to screen/tmux detach\&. When attaching, the status message is set back to the original value\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBgroup_part_message\fR
|
||||
.RS 4
|
||||
Parting message that will be sent to all groupchat peers when you leave the group\&.
|
||||
.sp
|
||||
.if n \{\
|
||||
.RS 4
|
||||
@ -207,9 +247,24 @@ Audio output device\&. Integer value\&. Number corresponds to
|
||||
/lsdev out
|
||||
.RE
|
||||
.PP
|
||||
\fBVAD_treshold\fR
|
||||
\fBVAD_threshold\fR
|
||||
.RS 4
|
||||
Voice Activity Detection treshold\&. Float value\&. Recommended values are around 40\&.0
|
||||
Voice Activity Detection threshold\&. Float value\&. Recommended values are 1\&.0\-40\&.0
|
||||
.RE
|
||||
.PP
|
||||
\fBconference_audio_channels\fR
|
||||
.RS 4
|
||||
Number of channels for conference audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||
.RE
|
||||
.PP
|
||||
\fBchat_audio_channels\fR
|
||||
.RS 4
|
||||
Number of channels for 1\-on\-1 audio broadcast\&. Integer value\&. 1 (mono) or 2 (stereo)
|
||||
.RE
|
||||
.PP
|
||||
\fBpush_to_talk\fR
|
||||
.RS 4
|
||||
Enable/Disable Push\-To\-Talk for conference audio chats (active key is F2)\&. true or false
|
||||
.RE
|
||||
.RE
|
||||
.PP
|
||||
@ -227,6 +282,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\&.
|
||||
@ -336,16 +396,6 @@ Key combination to scroll half page down\&.
|
||||
Key combination to scroll to page bottom\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpeer_list_up\fR
|
||||
.RS 4
|
||||
Key combination to scroll contacts list up\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBpeer_list_down\fR
|
||||
.RS 4
|
||||
Key combination to scroll contacts list down\&.
|
||||
.RE
|
||||
.PP
|
||||
\fBtoggle_peerlist\fR
|
||||
.RS 4
|
||||
Toggle the peer list on and off\&.
|
||||
@ -367,14 +417,14 @@ __DATADIR__/toxic\&.conf\&.example
|
||||
.RS 4
|
||||
Configuration example\&.
|
||||
.RE
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\fR(1)
|
||||
.SH "RESOURCES"
|
||||
.sp
|
||||
Project page: https://github\&.com/JFreegman/toxic
|
||||
.sp
|
||||
IRC channel: chat\&.freenode\&.net#tox
|
||||
Tox development group public key: 360497DA684BCE2A500C1AF9B3A5CE949BBB9F6FB1F91589806FB04CA039E313
|
||||
.SH "AUTHORS"
|
||||
.sp
|
||||
JFreegman <JFreegman@gmail\&.com>
|
||||
.SH "SEE ALSO"
|
||||
.sp
|
||||
\fBtoxic\fR(1)
|
||||
|
@ -60,6 +60,18 @@ OPTIONS
|
||||
*native_colors*;;
|
||||
Select between native terminal colors and toxic color theme. true or false
|
||||
|
||||
*color_bar_bg*;;
|
||||
set background color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_fg*;;
|
||||
set foreground (text) color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_accent*;;
|
||||
set foreground accent color of chat status bars. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*color_bar_notify*;;
|
||||
set foreground notify (and typing) color in chat status bar. (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
|
||||
*autolog*;;
|
||||
Enable or disable autologging. true or false
|
||||
|
||||
@ -75,12 +87,21 @@ OPTIONS
|
||||
*show_connection_msg*;;
|
||||
Enable friend connection change notifications. true or false
|
||||
|
||||
*show_group_connection_msg*;;
|
||||
Enable group connection change notifications (does not include quit messages). true or false
|
||||
|
||||
*nodelist_update_freq*;;
|
||||
How often in days to update the DHT nodes list. (0 to disable updates)
|
||||
How often in days to update the DHT nodes list. (integer; 0 to disable)
|
||||
|
||||
*autosave_freq*;;
|
||||
How often in seconds to auto-save the Tox data file. (integer; 0 to disable)
|
||||
|
||||
*history_size*;;
|
||||
Maximum lines for chat window history. Integer value. (for example: 700)
|
||||
|
||||
*notification_timeout*;;
|
||||
Time in milliseconds to display a notification. Integer value. (for example: 3000)
|
||||
|
||||
*line_join*;;
|
||||
Indicator for when someone connects or joins a group.
|
||||
Three characters max for line_ settings.
|
||||
@ -103,6 +124,9 @@ OPTIONS
|
||||
detach. When attaching, the status message is set back to the original
|
||||
value.
|
||||
|
||||
*group_part_message*;;
|
||||
Parting message that will be sent to all groupchat peers when you leave the group.
|
||||
|
||||
The following options control whether to output a terminal bell on certain events.
|
||||
Some terminals mark the window as urgent when a bell is received. Urgent windows are usually highlighted in the taskbar and some window managers even provide shortcuts to jump to the next urgent window.
|
||||
These options don't affect the "alerts" option.
|
||||
@ -129,9 +153,18 @@ OPTIONS
|
||||
*output_device*;;
|
||||
Audio output device. Integer value. Number corresponds to `/lsdev out`
|
||||
|
||||
*VAD_treshold*;;
|
||||
Voice Activity Detection treshold. Float value. Recommended values are
|
||||
around 40.0
|
||||
*VAD_threshold*;;
|
||||
Voice Activity Detection threshold. Float value. Recommended values are
|
||||
1.0-40.0
|
||||
|
||||
*conference_audio_channels*;;
|
||||
Number of channels for conference audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||
|
||||
*chat_audio_channels*;;
|
||||
Number of channels for 1-on-1 audio broadcast. Integer value. 1 (mono) or 2 (stereo)
|
||||
|
||||
*push_to_talk*;;
|
||||
Enable/Disable Push-To-Talk for conference audio chats (active key is F2). true or false
|
||||
|
||||
*tox*::
|
||||
Configuration related to paths.
|
||||
@ -143,6 +176,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.
|
||||
|
||||
@ -212,12 +248,6 @@ OPTIONS
|
||||
*page_bottom*;;
|
||||
Key combination to scroll to page bottom.
|
||||
|
||||
*peer_list_up*;;
|
||||
Key combination to scroll contacts list up.
|
||||
|
||||
*peer_list_down*;;
|
||||
Key combination to scroll contacts list down.
|
||||
|
||||
*toggle_peerlist*;;
|
||||
Toggle the peer list on and off.
|
||||
|
||||
@ -233,19 +263,19 @@ FILES
|
||||
{datadir}/toxic.conf.example::
|
||||
Configuration example.
|
||||
|
||||
RESOURCES
|
||||
---------
|
||||
Project page: <https://github.com/JFreegman/toxic>
|
||||
|
||||
Tox development group public key: 360497DA684BCE2A500C1AF9B3A5CE949BBB9F6FB1F91589806FB04CA039E313
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
*toxic*(1)
|
||||
|
||||
|
||||
RESOURCES
|
||||
---------
|
||||
Project page: <https://github.com/JFreegman/toxic>
|
||||
|
||||
IRC channel: chat.freenode.net#tox
|
||||
|
||||
|
||||
AUTHORS
|
||||
-------
|
||||
JFreegman <JFreegman@gmail.com>
|
||||
|
@ -1 +0,0 @@
|
||||
toxme.io 1A39E7A5D5FA9CF155C751570A32E625698A60A55F6D88028F949F66144F4F25
|
@ -9,20 +9,32 @@ ui = {
|
||||
alerts=true;
|
||||
|
||||
// Output a bell when receiving a message (see manpage)
|
||||
bell_on_message=true
|
||||
bell_on_message=true;
|
||||
|
||||
// Output a bell when receiving a filetransfer (see manpage)
|
||||
bell_on_filetrans=true
|
||||
bell_on_filetrans=true;
|
||||
|
||||
// Don't output a bell when a filetransfer was accepted (see manpage)
|
||||
bell_on_filetrans_accept=false
|
||||
bell_on_filetrans_accept=false;
|
||||
|
||||
// Output a bell when receiving a group/call invite (see manpage)
|
||||
bell_on_invite=true
|
||||
bell_on_invite=true;
|
||||
|
||||
// true to use native terminal colours, false to use toxic default colour theme
|
||||
native_colors=false;
|
||||
|
||||
// set background color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_bg="blue";
|
||||
|
||||
// set foreground (text) color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_fg="white";
|
||||
|
||||
// set foreground accent color of chat status bars (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_accent="cyan";
|
||||
|
||||
// set foreground notify (and typing) color in chat status bar (black, white, red, green, blue, cyan, yellow, magenta)
|
||||
color_bar_notify="yellow";
|
||||
|
||||
// true to enable autologging, false to disable
|
||||
autolog=false;
|
||||
|
||||
@ -30,7 +42,7 @@ ui = {
|
||||
time_format=24;
|
||||
|
||||
// Timestamp format string according to date/strftime format. Overrides time_format setting
|
||||
timestamp_format="%H:%M:%S";
|
||||
timestamp_format="%H:%M";
|
||||
|
||||
// true to show you when others are typing a message in 1-on-1 chats
|
||||
show_typing_other=true;
|
||||
@ -44,29 +56,44 @@ ui = {
|
||||
// true to show friend connection change messages on the home screen
|
||||
show_connection_msg=true;
|
||||
|
||||
// true to show peer connection change messages in groups (setting to false does not include user quit messages)
|
||||
show_group_connection_msg=true;
|
||||
|
||||
// How often in days to update the DHT nodes list. (0 to disable updates)
|
||||
nodeslist_update_freq=7;
|
||||
|
||||
// How often in seconds to auto-save the Tox data file. (0 to disable periodic auto-saves)
|
||||
autosave_freq=600;
|
||||
|
||||
// maximum lines for chat window history
|
||||
history_size=700;
|
||||
|
||||
// Indicator for display when someone connects or joins a group.
|
||||
// time in milliseconds to display a notification
|
||||
notification_timeout=6000;
|
||||
|
||||
// Indicator for display when someone connects or joins a group
|
||||
line_join="-->";
|
||||
|
||||
// Indicator for display when someone disconnects or leaves a group.
|
||||
// Indicator for display when someone disconnects or leaves a group
|
||||
line_quit="<--";
|
||||
|
||||
// Indicator for alert messages.
|
||||
line_alert="-!-";
|
||||
|
||||
// Indicator for normal messages.
|
||||
line_normal="---";
|
||||
line_normal="-";
|
||||
|
||||
// Indicator for special messages.
|
||||
line_special=">";
|
||||
|
||||
// true to change status based on screen/tmux attach/detach, false to disable
|
||||
mplex_away=true;
|
||||
|
||||
// Status message to use when status set to away due to screen/tmux detach
|
||||
mplex_away_note="Away from keyboard, be back soon!"
|
||||
|
||||
// Parting message that will be sent to all groupchat peers when you leave the group
|
||||
group_part_message="Toxic user signing out"
|
||||
};
|
||||
|
||||
audio = {
|
||||
@ -76,8 +103,17 @@ audio = {
|
||||
// preferred audio output device; numbers correspond to /lsdev out
|
||||
output_device=0;
|
||||
|
||||
// default VAD treshold; float (recommended values are around 40)
|
||||
VAD_treshold=40.0;
|
||||
// default VAD threshold; float (recommended values are 1.0-40.0)
|
||||
VAD_threshold=5.0;
|
||||
|
||||
// Number of channels to use for conference audio broadcasts; 1 for mono, 2 for stereo.
|
||||
conference_audio_channels=1;
|
||||
|
||||
// Number of channels to use for 1-on-1 audio broadcasts; 1 for mono, 2 for stereo.
|
||||
chat_audio_channels=2;
|
||||
|
||||
// toggle conference push-to-talk
|
||||
push_to_talk=false;
|
||||
};
|
||||
|
||||
tox = {
|
||||
@ -87,6 +123,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/";
|
||||
};
|
||||
@ -113,9 +152,6 @@ keys = {
|
||||
half_page_up="Ctrl+F";
|
||||
half_page_down="Ctrl+V";
|
||||
page_bottom="Ctrl+H";
|
||||
peer_list_up="Ctrl+[";
|
||||
peer_list_down="Ctrl+]";
|
||||
toggle_peerlist="Ctrl+b";
|
||||
toggle_peerlist="Ctrl+B";
|
||||
toggle_paste_mode="Ctrl+T";
|
||||
};
|
||||
|
||||
|
320
script/build-minimal-static-toxic.sh
Executable file
320
script/build-minimal-static-toxic.sh
Executable file
@ -0,0 +1,320 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021-2022 Maxim Biro <nurupo.contributions@gmail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
# Script for building a minimal statically compiled Toxic. While it doesn't
|
||||
# support X11 integration, video/audio calls, desktop & sound notifications, QR
|
||||
# codes and Python scripting, it is rather portable.
|
||||
#
|
||||
# Run as:
|
||||
#
|
||||
# sudo docker run -it --rm \
|
||||
# -v /tmp/artifact:/artifact \
|
||||
# -v /home/jfreegman/git/toxic:/toxic \
|
||||
# amd64/alpine:latest \
|
||||
# /bin/sh /toxic/script/build-minimal-static-toxic.sh
|
||||
#
|
||||
# that would use Toxic code from /home/jfreegman/git/toxic and place the build
|
||||
# artifact at /tmp/artifact.
|
||||
#
|
||||
# You can change between:
|
||||
# amd64/alpine:latest,
|
||||
# i386/alpine:latest,
|
||||
# arm64v8/alpine:latest,
|
||||
# arm32v7/alpine:latest,
|
||||
# arm32v6/alpine:latest,
|
||||
# ppc64le/alpine:latest,
|
||||
# s390x/alpine:latest,
|
||||
# etc.
|
||||
# as long as your system can run foreign architecture binaries, e.g. via qemu
|
||||
# static bins and binfmt (install qemu-user-static package on Debian/Ubuntu).
|
||||
#
|
||||
#
|
||||
# To debug, run:
|
||||
#
|
||||
# sudo docker run -it --rm \
|
||||
# -v /tmp/artifact:/artifact \
|
||||
# -v /home/jfreegman/git/toxic:/toxic \
|
||||
# amd64/alpine:latest \
|
||||
# /bin/sh
|
||||
# # sh /toxic/script/build-minimal-static-toxic.sh
|
||||
|
||||
set -eu
|
||||
|
||||
ARTIFACT_DIR="/artifact"
|
||||
TOXIC_SRC_DIR="/toxic"
|
||||
|
||||
if [ ! -f /etc/os-release ] || ! grep -qi 'Alpine Linux' /etc/os-release
|
||||
then
|
||||
echo "Error: This script expects to be run on Alpine Linux."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$ARTIFACT_DIR" ] || [ ! -d "$TOXIC_SRC_DIR" ]
|
||||
then
|
||||
echo "Error: At least one of $ARTIFACT_DIR or $TOXIC_SRC_DIR directories inside the container is missing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(id -u)" != "0" ]
|
||||
then
|
||||
echo "Error: This script expects to be run as root."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
# Use all cores for building
|
||||
MAKEFLAGS=j$(nproc)
|
||||
export MAKEFLAGS
|
||||
|
||||
check_sha256()
|
||||
{
|
||||
if ! ( echo "$1 $2" | sha256sum -cs - )
|
||||
then
|
||||
echo "Error: sha256 of $2 doesn't match the known one."
|
||||
echo "Expected: $1 $2"
|
||||
echo "Got: $(sha256sum "$2")"
|
||||
exit 1
|
||||
else
|
||||
echo "sha256 matches the expected one: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
apk update
|
||||
apk upgrade
|
||||
apk add \
|
||||
brotli-dev \
|
||||
brotli-static \
|
||||
build-base \
|
||||
cmake \
|
||||
git \
|
||||
libconfig-dev \
|
||||
libconfig-static \
|
||||
libsodium-dev \
|
||||
libsodium-static \
|
||||
linux-headers \
|
||||
ncurses-dev \
|
||||
ncurses-static \
|
||||
ncurses-terminfo \
|
||||
ncurses-terminfo-base \
|
||||
nghttp2-dev \
|
||||
nghttp2-static \
|
||||
openssl-dev \
|
||||
openssl-libs-static \
|
||||
pkgconf \
|
||||
wget \
|
||||
xz \
|
||||
zlib-dev \
|
||||
zlib-static
|
||||
|
||||
BUILD_DIR="/tmp/build"
|
||||
mkdir -p "$BUILD_DIR"
|
||||
|
||||
|
||||
# Build Toxcore
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
# The git hash of the c-toxcore version we're using
|
||||
TOXCORE_VERSION="172f279dc0647a538b30e62c96bab8bb1b0c8960"
|
||||
|
||||
# The sha256sum of the c-toxcore tarball for TOXCORE_VERSION
|
||||
TOXCORE_HASH="9884d4ad9b80917e22495c2ebe7a76c509fb98c61031824562883225e66684ae"
|
||||
|
||||
TOXCORE_FILENAME="c-toxcore-$TOXCORE_VERSION.tar.gz"
|
||||
|
||||
wget --timeout=10 -O "$TOXCORE_FILENAME" "https://github.com/TokTok/c-toxcore/archive/$TOXCORE_VERSION.tar.gz"
|
||||
check_sha256 "$TOXCORE_HASH" "$TOXCORE_FILENAME"
|
||||
|
||||
tar -o -xf "$TOXCORE_FILENAME"
|
||||
rm "$TOXCORE_FILENAME"
|
||||
|
||||
cd c-toxcore*
|
||||
mkdir -p "third_party" && cd "third_party"
|
||||
|
||||
CMP_VERSION="074e0df43e8a61ea938c4f77f65d1fbccc0c3bf9"
|
||||
CMP_FILENAME="cmp-$CMP_VERSION.tar.gz"
|
||||
wget --timeout=10 -O "$CMP_FILENAME" "https://github.com/TokTok/cmp/archive/$CMP_VERSION.tar.gz"
|
||||
tar -o -xf "$CMP_FILENAME"
|
||||
|
||||
mv cmp-*/* 'cmp/'
|
||||
cd ..
|
||||
|
||||
cmake -B_build -H. \
|
||||
-DUSE_TEST_NETWORK=OFF \
|
||||
-DENABLE_STATIC=ON \
|
||||
-DENABLE_SHARED=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TOXAV=OFF \
|
||||
-DBOOTSTRAP_DAEMON=OFF \
|
||||
-DDHT_BOOTSTRAP=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="$BUILD_DIR/prefix-toxcore"
|
||||
cmake --build _build --target install
|
||||
|
||||
|
||||
# Build cURL
|
||||
# While Alpine does provide a static cURL build, it's not built with
|
||||
# --with-ca-fallback, which is needed for better cross-distro portability.
|
||||
# Basically, some distros put their ca-certificates in different places, and
|
||||
# with --with-ca-fallback we or the user can provide the cert bundle file
|
||||
# location with SSL_CERT_FILE env variable.
|
||||
cd "$BUILD_DIR"
|
||||
|
||||
CURL_VERSION="7.88.1"
|
||||
CURL_HASH="cdb38b72e36bc5d33d5b8810f8018ece1baa29a8f215b4495e495ded82bbf3c7"
|
||||
CURL_FILENAME="curl-$CURL_VERSION.tar.gz"
|
||||
|
||||
wget --timeout=10 -O "$CURL_FILENAME" "https://curl.haxx.se/download/$CURL_FILENAME"
|
||||
check_sha256 "$CURL_HASH" "$CURL_FILENAME"
|
||||
tar -xf curl*.tar.gz
|
||||
rm curl*.tar.gz
|
||||
cd curl*
|
||||
|
||||
./configure \
|
||||
--prefix="$BUILD_DIR/prefix-curl" \
|
||||
--disable-shared \
|
||||
--enable-static \
|
||||
--without-ca-bundle \
|
||||
--without-ca-path \
|
||||
--with-ca-fallback \
|
||||
--with-nghttp2 \
|
||||
--with-brotli \
|
||||
--with-openssl
|
||||
make
|
||||
make install
|
||||
sed -i 's|-lbrotlidec |-lbrotlidec -lbrotlicommon |g' $BUILD_DIR/prefix-curl/lib/pkgconfig/libcurl.pc
|
||||
|
||||
|
||||
# Build Toxic
|
||||
cd "$BUILD_DIR"
|
||||
cp -a "$TOXIC_SRC_DIR" toxic
|
||||
cd toxic
|
||||
|
||||
if [ -z "$(git describe --tags --exact-match HEAD)" ]
|
||||
then
|
||||
set +x
|
||||
echo "Didn't find a git tag on the HEAD commit. You seem to be building an in-development release of Toxic rather than a release version." | fold -sw 80
|
||||
printf "Do you wish to proceed? (y/N): "
|
||||
read -r answer
|
||||
if echo "$answer" | grep -v -iq "^y" ; then
|
||||
echo "Exiting."
|
||||
exit 1
|
||||
fi
|
||||
set -x
|
||||
fi
|
||||
|
||||
sed -i 's|pkg-config|pkg-config --static|' cfg/global_vars.mk
|
||||
sed -i 's|<limits.h|<linux/limits.h|' src/*
|
||||
|
||||
CFLAGS="-static" PKG_CONFIG_PATH="$BUILD_DIR/prefix-toxcore/lib64/pkgconfig:$BUILD_DIR/prefix-toxcore/lib/pkgconfig:$BUILD_DIR/prefix-curl/lib/pkgconfig" PREFIX="$BUILD_DIR/prefix-toxic" make \
|
||||
DISABLE_X11=1 \
|
||||
DISABLE_AV=1 \
|
||||
DISABLE_SOUND_NOTIFY=1 \
|
||||
DISABLE_QRCODE=1 \
|
||||
DISABLE_QRPNG=1 \
|
||||
DISABLE_DESKTOP_NOTIFY=1 \
|
||||
ENABLE_PYTHON=0 \
|
||||
ENABLE_RELEASE=1 \
|
||||
ENABLE_ASAN=0 \
|
||||
DISABLE_GAMES=0 \
|
||||
install
|
||||
|
||||
|
||||
# Prepare the build artifact
|
||||
PREPARE_ARTIFACT_DIR="$BUILD_DIR/artifact"
|
||||
cp -a "$BUILD_DIR/prefix-toxic/bin" "$PREPARE_ARTIFACT_DIR"
|
||||
strip "$PREPARE_ARTIFACT_DIR"/*
|
||||
|
||||
cp -a "$BUILD_DIR/toxic/misc"/* "$PREPARE_ARTIFACT_DIR"
|
||||
mv "$PREPARE_ARTIFACT_DIR/toxic.conf.example" "$PREPARE_ARTIFACT_DIR/toxic.conf"
|
||||
|
||||
cp -aL /usr/share/terminfo "$PREPARE_ARTIFACT_DIR"
|
||||
|
||||
echo "A minimal statically compiled Toxic.
|
||||
Doesn't support X11 integration, video/audio calls, desktop & sound
|
||||
notifications, QR codes and Python scripting.
|
||||
However, it is rather portable.
|
||||
|
||||
Toxic $(git -C "$BUILD_DIR/toxic" describe --tags --exact-match HEAD) ($(git -C "$BUILD_DIR/toxic" rev-parse HEAD))
|
||||
|
||||
Build date time: $(TZ=UTC date +"%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
OS:
|
||||
$(cat /etc/os-release)
|
||||
|
||||
List of self-built software statically compiled into Toxic:
|
||||
libcurl $CURL_VERSION
|
||||
libtoxcore $TOXCORE_VERSION
|
||||
|
||||
List of OS-packaged software statically compiled into Toxic:
|
||||
$(apk list -I | grep 'static' | sort -i)
|
||||
|
||||
List of all packages installed during the build:
|
||||
$(apk list -I | sort -i)" > "$PREPARE_ARTIFACT_DIR/build_info"
|
||||
|
||||
echo '#!/usr/bin/env sh
|
||||
|
||||
DEBIAN_SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
RHEL_SSL_CERT_FILE=/etc/pki/tls/certs/ca-bundle.crt
|
||||
OPENSUSE_CERT_FILE=/etc/ssl/ca-bundle.pem
|
||||
|
||||
if [ ! -f "$SSL_CERT_FILE" ] ; then
|
||||
if [ -f "$DEBIAN_SSL_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$DEBIAN_SSL_CERT_FILE"
|
||||
elif [ -f "$RHEL_SSL_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$RHEL_SSL_CERT_FILE"
|
||||
elif [ -f "$OPENSUSE_CERT_FILE" ] ; then
|
||||
SSL_CERT_FILE="$OPENSUSE_CERT_FILE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$SSL_CERT_FILE" ] ; then
|
||||
echo "Warning: Couldn'\''t find the SSL CA certificate store file." | fold -sw 80
|
||||
echo
|
||||
echo "Toxic uses HTTPS to download a list of DHT bootstrap nodes in order to connect to the Tox DHT. This functionality is optional, you should be able to use Toxic without it. If you choose to use Toxic without it, you might need to manually enter DHT bootstrap node information using the '\''/connect'\'' command in order to come online." | fold -sw 80
|
||||
echo
|
||||
echo "To fix this issue, install SSL CAs as provided by your Linux distribution, e.g. '\''ca-certificates'\'' package on Debian/Ubuntu. If it'\''s already installed and you still see this message, run this script with SSL_CERT_FILE variable set to point to the SSL CA certificate store file location. The file is usually named '\''ca-certificates.crt'\'' or '\''ca-bundle.pem'\''." | fold -sw 80
|
||||
echo
|
||||
printf "Do you wish to run Toxic without SSL CA certificate store file found? (y/N): "
|
||||
read -r answer
|
||||
if echo "$answer" | grep -v -iq "^y" ; then
|
||||
echo "Exiting."
|
||||
exit
|
||||
fi
|
||||
fi
|
||||
|
||||
cd "$(dirname -- $0)"
|
||||
|
||||
SSL_CERT_FILE="$SSL_CERT_FILE" TERMINFO=./terminfo ./toxic -c toxic.conf' > "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||
chmod a+x "$PREPARE_ARTIFACT_DIR/run_toxic.sh"
|
||||
|
||||
|
||||
# Tar it
|
||||
cd "$PREPARE_ARTIFACT_DIR"
|
||||
cd ..
|
||||
ARCH="$(tr '_' '-' < /etc/apk/arch)"
|
||||
ARTIFACT_NAME="toxic-minimal-static-musl_linux_$ARCH"
|
||||
mv "$PREPARE_ARTIFACT_DIR" "$PREPARE_ARTIFACT_DIR/../$ARTIFACT_NAME"
|
||||
tar -cJf "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_NAME"
|
||||
mv "$ARTIFACT_NAME.tar.xz" "$ARTIFACT_DIR"
|
||||
chmod 777 -R "$ARTIFACT_DIR"
|
214
src/api.c
Normal file
214
src/api.c
Normal file
@ -0,0 +1,214 @@
|
||||
/* 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;
|
||||
|
||||
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();
|
||||
|
||||
if (name == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
self_window = get_active_window();
|
||||
|
||||
snprintf((char *) self_window->chatwin->line, sizeof(self_window->chatwin->line), "%s", msg);
|
||||
add_line_to_hist(self_window->chatwin);
|
||||
int id = line_info_add(self_window, true, 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, false, 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, false, NULL, NULL, SYS_MSG, 0, 0, error_str);
|
||||
return;
|
||||
}
|
||||
|
||||
run_python(fp, argv[1]);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
void invoke_autoruns(WINDOW *window, ToxWindow *self)
|
||||
{
|
||||
char abspath_buf[PATH_MAX + 256];
|
||||
char err_buf[PATH_MAX + 128];
|
||||
|
||||
if (user_settings->autorun_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
DIR *d = opendir(user_settings->autorun_path);
|
||||
|
||||
if (d == NULL) {
|
||||
snprintf(err_buf, sizeof(err_buf), "Autorun path does not exist: %s", user_settings->autorun_path);
|
||||
api_display(err_buf);
|
||||
return;
|
||||
}
|
||||
|
||||
struct dirent *dir = NULL;
|
||||
|
||||
cur_window = window;
|
||||
|
||||
self_window = self;
|
||||
|
||||
while ((dir = readdir(d)) != NULL) {
|
||||
size_t path_len = strlen(dir->d_name);
|
||||
|
||||
if (!strcmp(dir->d_name + path_len - 3, ".py")) {
|
||||
snprintf(abspath_buf, sizeof(abspath_buf), "%s%s", user_settings->autorun_path, dir->d_name);
|
||||
FILE *fp = fopen(abspath_buf, "r");
|
||||
|
||||
if (fp == NULL) {
|
||||
snprintf(err_buf, sizeof(err_buf), "Invalid path: %s", abspath_buf);
|
||||
api_display(err_buf);
|
||||
continue;
|
||||
}
|
||||
|
||||
run_python(fp, abspath_buf);
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(d);
|
||||
}
|
||||
#endif /* PYTHON */
|
43
src/api.h
Normal file
43
src/api.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* api.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2017 Jakob Kreuze <jakob@memeware.net>
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
* Toxic is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Toxic is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Toxic. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef API_H
|
||||
#define API_H
|
||||
|
||||
#include "friendlist.h"
|
||||
#include "windows.h"
|
||||
|
||||
void api_display(const char *const msg);
|
||||
FriendsList api_get_friendslist(void);
|
||||
char *api_get_nick(void);
|
||||
Tox_User_Status api_get_status(void);
|
||||
char *api_get_status_message(void);
|
||||
void api_send(const char *msg);
|
||||
void api_execute(const char *input, int mode);
|
||||
int do_plugin_command(int num_args, char (*args)[MAX_STR_SIZE]);
|
||||
int num_registered_handlers(void);
|
||||
int help_max_width(void);
|
||||
void draw_handler_help(WINDOW *win);
|
||||
void invoke_autoruns(WINDOW *w, ToxWindow *self);
|
||||
void cmd_run(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* API_H */
|
1103
src/audio_call.c
1103
src/audio_call.c
File diff suppressed because it is too large
Load Diff
@ -27,9 +27,7 @@
|
||||
|
||||
#include "audio_device.h"
|
||||
|
||||
#define MAX_CALLS 10
|
||||
|
||||
typedef enum _AudioError {
|
||||
typedef enum AudioError {
|
||||
ae_None = 0,
|
||||
ae_StartingCaptureDevice = 1 << 0,
|
||||
ae_StartingOutputDevice = 1 << 1,
|
||||
@ -37,7 +35,7 @@ typedef enum _AudioError {
|
||||
} AudioError;
|
||||
|
||||
#ifdef VIDEO
|
||||
typedef enum _VideoError {
|
||||
typedef enum VideoError {
|
||||
ve_None = 0,
|
||||
ve_StartingCaptureDevice = 1 << 0,
|
||||
ve_StartingOutputDevice = 1 << 1,
|
||||
@ -46,14 +44,27 @@ typedef enum _VideoError {
|
||||
|
||||
#endif /* VIDEO */
|
||||
|
||||
/* Status transitions:
|
||||
* None -> Pending (call invitation made or received);
|
||||
* Pending -> None (invitation rejected or failed);
|
||||
* Pending -> Active (call starts);
|
||||
* Active -> None (call ends).
|
||||
*/
|
||||
typedef enum CallStatus {
|
||||
cs_None = 0,
|
||||
cs_Pending,
|
||||
cs_Active
|
||||
} CallStatus;
|
||||
|
||||
typedef struct Call {
|
||||
pthread_t ttid; /* Transmission thread id */
|
||||
bool ttas, has_output; /* Transmission thread active status (0 - stopped, 1- running) */
|
||||
uint32_t in_idx, out_idx; /* Audio Index */
|
||||
#ifdef VIDEO
|
||||
uint32_t vin_idx, vout_idx; /* Video Index */
|
||||
#endif /* VIDEO */
|
||||
pthread_mutex_t mutex;
|
||||
CallStatus status;
|
||||
uint32_t state; /* ToxAV call state, valid when `status == cs_Active` */
|
||||
uint32_t in_idx, out_idx; /* Audio device index, or -1 if not open */
|
||||
uint32_t audio_bit_rate; /* Bit rate for sending audio */
|
||||
|
||||
uint32_t vin_idx, vout_idx; /* Video device index, or -1 if not open */
|
||||
uint32_t video_width, video_height;
|
||||
uint32_t video_bit_rate; /* Bit rate for sending video; 0 for no video */
|
||||
} Call;
|
||||
|
||||
struct CallControl {
|
||||
@ -65,29 +76,34 @@ struct CallControl {
|
||||
ToxAV *av;
|
||||
ToxWindow *prompt;
|
||||
|
||||
Call calls[MAX_CALLS];
|
||||
uint32_t call_state;
|
||||
bool pending_call;
|
||||
Call *calls;
|
||||
uint32_t max_calls;
|
||||
|
||||
bool audio_enabled;
|
||||
bool video_enabled;
|
||||
|
||||
uint32_t audio_bit_rate;
|
||||
int32_t audio_frame_duration;
|
||||
uint32_t audio_sample_rate;
|
||||
uint8_t audio_channels;
|
||||
uint32_t default_audio_bit_rate;
|
||||
|
||||
uint32_t video_bit_rate;
|
||||
int32_t video_frame_duration;
|
||||
uint32_t default_video_width, default_video_height;
|
||||
uint32_t default_video_bit_rate;
|
||||
};
|
||||
|
||||
} CallControl;
|
||||
|
||||
struct CallControl CallControl;
|
||||
extern struct CallControl CallControl;
|
||||
|
||||
/* You will have to pass pointer to first member of 'windows' declared in windows.c */
|
||||
ToxAV *init_audio(ToxWindow *self, Tox *tox);
|
||||
void terminate_audio();
|
||||
int start_transmission(ToxWindow *self, Call *call);
|
||||
int stop_transmission(Call *call, uint32_t friend_number);
|
||||
void terminate_audio(void);
|
||||
|
||||
bool init_call(Call *call);
|
||||
|
||||
void place_call(ToxWindow *self);
|
||||
void stop_current_call(ToxWindow *self);
|
||||
|
||||
void init_friend_AV(uint32_t index);
|
||||
void del_friend_AV(uint32_t index);
|
||||
|
||||
#endif /* AUDIO_CALL_H */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -30,8 +30,9 @@
|
||||
#define AUDIO_DEVICE_H
|
||||
|
||||
#define OPENAL_BUFS 5
|
||||
#define MAX_OPENAL_DEVICES 32
|
||||
#define MAX_DEVICES 32
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "windows.h"
|
||||
|
||||
typedef enum DeviceType {
|
||||
@ -52,40 +53,43 @@ typedef enum DeviceError {
|
||||
de_AlError = -9,
|
||||
} DeviceError;
|
||||
|
||||
typedef void (*DataHandleCallback) (const int16_t*, uint32_t size, void* data);
|
||||
typedef void (*DataHandleCallback)(const int16_t *, uint32_t size, void *data);
|
||||
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError init_devices(ToxAV* av);
|
||||
#else
|
||||
DeviceError init_devices();
|
||||
#endif /* AUDIO */
|
||||
DeviceError init_devices(void);
|
||||
|
||||
DeviceError terminate_devices();
|
||||
|
||||
/* Callback handles ready data from INPUT device */
|
||||
DeviceError register_device_callback(int32_t friend_number, uint32_t device_idx, DataHandleCallback callback, void* data, bool enable_VAD);
|
||||
void* get_device_callback_data(uint32_t device_idx);
|
||||
void get_al_device_names(void);
|
||||
DeviceError terminate_devices(void);
|
||||
|
||||
/* toggle device mute */
|
||||
DeviceError device_mute(DeviceType type, uint32_t device_idx);
|
||||
|
||||
#ifdef AUDIO
|
||||
DeviceError device_set_VAD_treshold(uint32_t device_idx, float value);
|
||||
#endif
|
||||
bool device_is_muted(DeviceType type, uint32_t device_idx);
|
||||
|
||||
DeviceError device_set_VAD_threshold(uint32_t device_idx, float value);
|
||||
|
||||
float device_get_VAD_threshold(uint32_t device_idx);
|
||||
|
||||
DeviceError set_source_position(uint32_t device_idx, float x, float y, float z);
|
||||
|
||||
DeviceError set_al_device(DeviceType type, int32_t selection);
|
||||
|
||||
DeviceError set_primary_device(DeviceType type, int32_t selection);
|
||||
DeviceError open_primary_device(DeviceType type, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
/* Start device */
|
||||
DeviceError open_device(DeviceType type, int32_t selection, uint32_t* device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
DeviceError open_input_device(uint32_t *device_idx, DataHandleCallback cb, void *cb_data,
|
||||
uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
DeviceError open_output_device(uint32_t *device_idx, uint32_t sample_rate, uint32_t frame_duration, uint8_t channels);
|
||||
|
||||
/* Stop device */
|
||||
DeviceError close_device(DeviceType type, uint32_t device_idx);
|
||||
|
||||
/* Write data to device */
|
||||
DeviceError write_out(uint32_t device_idx, const int16_t* data, uint32_t length, uint8_t channels, uint32_t sample_rate);
|
||||
/* Write data to output device */
|
||||
DeviceError write_out(uint32_t device_idx, const int16_t *data, uint32_t length, uint8_t channels,
|
||||
uint32_t sample_rate);
|
||||
|
||||
void print_devices(ToxWindow* self, DeviceType type);
|
||||
void get_primary_device_name(DeviceType type, char *buf, int size);
|
||||
/* return current input volume as float in range 0.0-100.0 */
|
||||
float get_input_volume(void);
|
||||
|
||||
void print_al_devices(ToxWindow *self, DeviceType type);
|
||||
|
||||
DeviceError selection_valid(DeviceType type, int32_t selection);
|
||||
#endif /* AUDIO_DEVICE_H */
|
||||
|
@ -20,36 +20,35 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/types.h>
|
||||
#include <sys/dir.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/dir.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif /* ifdef __APPLE__ */
|
||||
#include <dirent.h>
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "misc_tools.h"
|
||||
#include "line_info.h"
|
||||
#include "execute.h"
|
||||
#include "configdir.h"
|
||||
#include "execute.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items, int size)
|
||||
static void print_ac_matches(ToxWindow *self, Tox *m, char **list, size_t n_matches)
|
||||
{
|
||||
if (m)
|
||||
if (m) {
|
||||
execute(self->chatwin->history, self, m, "/clear", GLOBAL_COMMAND_MODE);
|
||||
}
|
||||
|
||||
const char *L = (char *) list;
|
||||
int i;
|
||||
for (size_t i = 0; i < n_matches; ++i) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", list[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 */
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "");
|
||||
}
|
||||
|
||||
/* puts match in match buffer. if more than one match, add first n chars that are identical.
|
||||
@ -57,19 +56,19 @@ static void print_matches(ToxWindow *self, Tox *m, const void *list, int n_items
|
||||
*
|
||||
* Returns the length of the match.
|
||||
*/
|
||||
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char (*matches)[MAX_STR_SIZE], int n)
|
||||
static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, const char *const *matches, size_t n_items,
|
||||
size_t max_size)
|
||||
{
|
||||
if (n == 1) {
|
||||
UNUSED_VAR(self);
|
||||
|
||||
if (n_items == 1) {
|
||||
return snprintf(match, match_sz, "%s", matches[0]);
|
||||
}
|
||||
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_STR_SIZE; ++i) {
|
||||
for (size_t i = 0; i < max_size; ++i) {
|
||||
char ch1 = matches[0][i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < n; ++j) {
|
||||
for (size_t j = 0; j < n_items; ++j) {
|
||||
char ch2 = matches[j][i];
|
||||
|
||||
if (ch1 != ch2 || !ch1) {
|
||||
@ -83,49 +82,58 @@ static size_t get_str_match(ToxWindow *self, char *match, size_t match_sz, char
|
||||
return snprintf(match, match_sz, "%s", matches[0]);
|
||||
}
|
||||
|
||||
/* looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
/*
|
||||
* Looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
* then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
*
|
||||
* `list` is a pointer to `n_items` strings. Each string in the list must be <= MAX_STR_SIZE.
|
||||
*
|
||||
* dir_search should be true if the line being completed is a file path.
|
||||
*
|
||||
* Returns the difference between the old len and new len of line on success.
|
||||
* Returns -1 on error.
|
||||
*
|
||||
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||
*/
|
||||
static int complete_line_helper(ToxWindow *self, const char *const *list, const size_t n_items, bool dir_search)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len || ctx->len >= MAX_STR_SIZE || size > MAX_STR_SIZE)
|
||||
if (ctx->pos <= 0 || ctx->len <= 0 || ctx->pos > ctx->len) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->len >= MAX_STR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const char *L = (char *) list;
|
||||
const char *endchrs = " ";
|
||||
char ubuf[MAX_STR_SIZE];
|
||||
|
||||
/* work with multibyte string copy of buf for simplicity */
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1)
|
||||
if (wcs_to_mbs_buf(ubuf, ctx->line, sizeof(ubuf)) == -1) {
|
||||
return -1;
|
||||
|
||||
/* TODO: generalize this */
|
||||
bool dir_search = !strncmp(ubuf, "/sendfile", strlen("/sendfile"))
|
||||
|| !strncmp(ubuf, "/avatar", strlen("/avatar"));
|
||||
}
|
||||
|
||||
/* isolate substring from space behind pos to pos */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", ubuf);
|
||||
tmp[ctx->pos] = '\0';
|
||||
memcpy(tmp, ubuf, ctx->pos);
|
||||
tmp[ctx->pos] = 0;
|
||||
|
||||
const char *s = dir_search ? strchr(tmp, '\"') : strrchr(tmp, ' ');
|
||||
const char *s = dir_search ? strchr(tmp, ' ') : strrchr(tmp, ' ');
|
||||
char *sub = calloc(1, strlen(ubuf) + 1);
|
||||
|
||||
if (sub == NULL)
|
||||
exit_toxic_err("failed in complete_line", FATALERR_MEMORY);
|
||||
if (sub == NULL) {
|
||||
exit_toxic_err("failed in complete_line_helper", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
if (!s && !dir_search) {
|
||||
strcpy(sub, tmp);
|
||||
|
||||
if (sub[0] != '/')
|
||||
if (sub[0] != '/') {
|
||||
endchrs = ": ";
|
||||
}
|
||||
} else if (s) {
|
||||
strcpy(sub, &s[1]);
|
||||
|
||||
@ -133,50 +141,60 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
int sub_len = strlen(sub);
|
||||
int si = char_rfind(sub, '/', sub_len);
|
||||
|
||||
if (si || *sub == '/')
|
||||
if (si || *sub == '/') {
|
||||
memmove(sub, &sub[si + 1], sub_len - si);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sub[0]) {
|
||||
if (!sub[0] && !(dir_search && n_items == 1)) {
|
||||
free(sub);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
size_t n_matches = 0;
|
||||
|
||||
char **matches = (char **) malloc_ptr_array(n_items, MAX_STR_SIZE);
|
||||
|
||||
if (matches == NULL) {
|
||||
free(sub);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int s_len = strlen(sub);
|
||||
int n_matches = 0;
|
||||
char matches[n_items][MAX_STR_SIZE];
|
||||
int i = 0;
|
||||
|
||||
/* put all list matches in matches array */
|
||||
for (i = 0; i < n_items; ++i) {
|
||||
char str[MAX_CMDNAME_SIZE + 1];
|
||||
snprintf(str, sizeof(str), "%s", &L[i * size]);
|
||||
|
||||
if (strncasecmp(str, sub, s_len) == 0)
|
||||
strcpy(matches[n_matches++], str);
|
||||
for (size_t i = 0; i < n_items; ++i) {
|
||||
if (strncmp(list[i], sub, s_len) == 0) {
|
||||
snprintf(matches[n_matches++], MAX_STR_SIZE, "%s", list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(sub);
|
||||
|
||||
if (!n_matches)
|
||||
if (!n_matches) {
|
||||
free_ptr_array((void **) matches);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dir_search && n_matches > 1)
|
||||
print_matches(self, NULL, matches, n_matches, MAX_STR_SIZE);
|
||||
if (!dir_search && n_matches > 1) {
|
||||
print_ac_matches(self, NULL, matches, n_matches);
|
||||
}
|
||||
|
||||
char match[MAX_STR_SIZE];
|
||||
size_t match_len = get_str_match(self, match, sizeof(match), matches, n_matches);
|
||||
size_t match_len = get_str_match(self, match, sizeof(match), (const char *const *) matches, n_matches, MAX_STR_SIZE);
|
||||
|
||||
free_ptr_array((void **) matches);
|
||||
|
||||
if (match_len == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dir_search) {
|
||||
if (n_matches == 1)
|
||||
endchrs = char_rfind(match, '.', match_len) ? "\"" : "/";
|
||||
else
|
||||
if (n_matches == 1) {
|
||||
endchrs = char_rfind(match, '.', match_len) ? "" : "/";
|
||||
} else {
|
||||
endchrs = "";
|
||||
}
|
||||
} else if (n_matches > 1) {
|
||||
endchrs = "";
|
||||
}
|
||||
@ -185,8 +203,10 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
int n_endchrs = strlen(endchrs);
|
||||
int strt = ctx->pos - s_len;
|
||||
int diff = match_len - s_len + n_endchrs;
|
||||
if (ctx->len + diff >= MAX_STR_SIZE)
|
||||
|
||||
if (ctx->len + diff >= MAX_STR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char tmpend[MAX_STR_SIZE];
|
||||
snprintf(tmpend, sizeof(tmpend), "%s", &ubuf[ctx->pos]);
|
||||
@ -196,6 +216,31 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
}
|
||||
|
||||
strcpy(&ubuf[strt], match);
|
||||
|
||||
/* If path points to a file with no extension don't append a forward slash */
|
||||
if (dir_search && *endchrs == '/') {
|
||||
const char *path_start = strchr(ubuf + 1, '/');
|
||||
|
||||
if (!path_start) {
|
||||
path_start = strchr(ubuf + 1, ' ');
|
||||
|
||||
if (!path_start) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(path_start) < 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
++path_start;
|
||||
|
||||
if (file_type(path_start) == FILE_TYPE_REGULAR) {
|
||||
endchrs = "";
|
||||
diff -= n_endchrs;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(&ubuf[strt + match_len], endchrs);
|
||||
strcpy(&ubuf[strt + match_len + n_endchrs], tmpend);
|
||||
|
||||
@ -214,54 +259,82 @@ int complete_line(ToxWindow *self, const void *list, int n_items, int size)
|
||||
return diff;
|
||||
}
|
||||
|
||||
/* transforms a tab complete starting with the shorthand "~" into the full home directory.*/
|
||||
static void complt_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||
int complete_line(ToxWindow *self, const char *const *list, size_t n_items)
|
||||
{
|
||||
return complete_line_helper(self, list, n_items, false);
|
||||
}
|
||||
|
||||
static int complete_path(ToxWindow *self, const char *const *list, const size_t n_items)
|
||||
{
|
||||
return complete_line_helper(self, list, n_items, true);
|
||||
}
|
||||
|
||||
/* Transforms a tab complete starting with the shorthand "~" into the full home directory. */
|
||||
static void complete_home_dir(ToxWindow *self, char *path, int pathsize, const char *cmd, int cmdlen)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
char homedir[MAX_STR_SIZE] = {0};
|
||||
get_home_dir(homedir, sizeof(homedir));
|
||||
|
||||
char newline[MAX_STR_SIZE];
|
||||
snprintf(newline, sizeof(newline), "%s \"%s%s", cmd, homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[cmdlen]);
|
||||
char newline[MAX_STR_SIZE + 1];
|
||||
snprintf(newline, sizeof(newline), "%s %s%s", cmd, homedir, path + 1);
|
||||
snprintf(path, pathsize, "%s", &newline[cmdlen - 1]);
|
||||
|
||||
wchar_t wline[MAX_STR_SIZE];
|
||||
|
||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1)
|
||||
if (mbs_to_wcs_buf(wline, newline, sizeof(wline) / sizeof(wchar_t)) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
int newlen = wcslen(wline);
|
||||
|
||||
if (ctx->len + newlen >= MAX_STR_SIZE)
|
||||
if (ctx->len + newlen >= MAX_STR_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
wmemcpy(ctx->line, wline, newlen + 1);
|
||||
ctx->pos = newlen;
|
||||
ctx->len = ctx->pos;
|
||||
}
|
||||
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
/*
|
||||
* Return true if the first `p_len` chars in `s` are equal to `p` and `s` is a valid directory name.
|
||||
*/
|
||||
static bool is_partial_match(const char *s, const char *p, size_t p_len)
|
||||
{
|
||||
if (s == NULL || p == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if only one match, auto-complete line.
|
||||
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||
#define MAX_DIRS 512
|
||||
return strncmp(s, p, p_len) == 0 && strcmp(".", s) != 0 && strcmp("..", s) != 0;
|
||||
}
|
||||
|
||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
* If there is only one match the line is auto-completed.
|
||||
*
|
||||
* Returns the diff between old len and new len of ctx->line on success.
|
||||
* Returns -1 if no matches or more than one match.
|
||||
*/
|
||||
#define MAX_DIRS 75
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
{
|
||||
char b_path[MAX_STR_SIZE];
|
||||
char b_name[MAX_STR_SIZE];
|
||||
char b_path[MAX_STR_SIZE + 1];
|
||||
char b_name[MAX_STR_SIZE + 1];
|
||||
char b_cmd[MAX_STR_SIZE];
|
||||
const wchar_t *tmpline = &line[wcslen(cmd) + 2]; /* start after "/command \"" */
|
||||
const wchar_t *tmpline = &line[wcslen(cmd) + 1]; /* start after "/command " */
|
||||
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path)) == -1)
|
||||
if (wcs_to_mbs_buf(b_path, tmpline, sizeof(b_path) - 1) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1)
|
||||
if (wcs_to_mbs_buf(b_cmd, cmd, sizeof(b_cmd)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b_path[0] == '~')
|
||||
complt_home_dir(self, b_path, sizeof(b_path), b_cmd, strlen(b_cmd) + 2);
|
||||
if (b_path[0] == '~') {
|
||||
complete_home_dir(self, b_path, sizeof(b_path) - 1, b_cmd, strlen(b_cmd) + 2);
|
||||
}
|
||||
|
||||
int si = char_rfind(b_path, '/', strlen(b_path));
|
||||
|
||||
@ -269,40 +342,52 @@ int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd)
|
||||
b_path[0] = '.';
|
||||
b_path[1] = '\0';
|
||||
} else if (!si && b_path[0] != '/') { /* look for matches in pwd */
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), ".%s", b_path);
|
||||
snprintf(b_path, sizeof(b_path), "%s", tmp);
|
||||
memmove(b_path + 1, b_path, sizeof(b_path) - 1);
|
||||
b_path[0] = '.';
|
||||
}
|
||||
|
||||
snprintf(b_name, sizeof(b_name), "%s", &b_path[si + 1]);
|
||||
b_path[si + 1] = '\0';
|
||||
int b_name_len = strlen(b_name);
|
||||
size_t b_name_len = strlen(b_name);
|
||||
DIR *dp = opendir(b_path);
|
||||
|
||||
if (dp == NULL)
|
||||
if (dp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char **dirnames = (char **) malloc_ptr_array(MAX_DIRS, NAME_MAX + 1);
|
||||
|
||||
if (dirnames == NULL) {
|
||||
closedir(dp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char dirnames[MAX_DIRS][NAME_MAX];
|
||||
struct dirent *entry;
|
||||
|
||||
int dircount = 0;
|
||||
|
||||
while ((entry = readdir(dp)) && dircount < MAX_DIRS) {
|
||||
if (strncmp(entry->d_name, b_name, b_name_len) == 0
|
||||
&& strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) {
|
||||
snprintf(dirnames[dircount], sizeof(dirnames[dircount]), "%s", entry->d_name);
|
||||
if (is_partial_match(entry->d_name, b_name, b_name_len)) {
|
||||
snprintf(dirnames[dircount], NAME_MAX + 1, "%s", entry->d_name);
|
||||
++dircount;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
if (dircount == 0)
|
||||
if (dircount == 0) {
|
||||
free_ptr_array((void **) dirnames);
|
||||
return -1;
|
||||
|
||||
if (dircount > 1) {
|
||||
qsort(dirnames, dircount, NAME_MAX, qsort_strcasecmp_hlpr);
|
||||
print_matches(self, m, dirnames, dircount, NAME_MAX);
|
||||
}
|
||||
|
||||
return complete_line(self, dirnames, dircount, NAME_MAX);
|
||||
if (dircount > 1) {
|
||||
qsort(dirnames, dircount, sizeof(char *), qsort_ptr_char_array_helper);
|
||||
print_ac_matches(self, m, dirnames, dircount);
|
||||
}
|
||||
|
||||
int ret = complete_path(self, (const char *const *) dirnames, dircount);
|
||||
|
||||
free_ptr_array((void **) dirnames);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -23,20 +23,30 @@
|
||||
#ifndef AUTOCOMPLETE_H
|
||||
#define AUTOCOMPLETE_H
|
||||
|
||||
/* looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
#include "windows.h"
|
||||
|
||||
list is a pointer to the list of strings being compared, n_items is the number of items
|
||||
in the list, and size is the size of each item in the list.
|
||||
/*
|
||||
* Looks for all instances in list that begin with the last entered word in line according to pos,
|
||||
* then fills line with the complete word. e.g. "Hello jo" would complete the line
|
||||
* with "Hello john". If multiple matches, prints out all the matches and semi-completes line.
|
||||
*
|
||||
* `list` is a pointer to `n_items` strings.
|
||||
*
|
||||
* dir_search should be true if the line being completed is a file path.
|
||||
*
|
||||
* Returns the difference between the old len and new len of line on success.
|
||||
* Returns -1 on error.
|
||||
*
|
||||
* Note: This function should not be called directly. Use complete_line() and complete_path() instead.
|
||||
*/
|
||||
int complete_line(ToxWindow *self, const char **list, size_t n_items);
|
||||
|
||||
Returns the difference between the old len and new len of line on success, -1 if error */
|
||||
int complete_line(ToxWindow *self, const void *list, int n_items, int size);
|
||||
|
||||
/* attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
|
||||
if only one match, auto-complete line.
|
||||
return diff between old len and new len of ctx->line, -1 if no matches or > 1 match */
|
||||
/* Attempts to match /command "<incomplete-dir>" line to matching directories.
|
||||
* If there is only one match the line is auto-completed.
|
||||
*
|
||||
* Returns the diff between old len and new len of ctx->line on success.
|
||||
* Returns -1 if no matches or more than one match.
|
||||
*/
|
||||
int dir_match(ToxWindow *self, Tox *m, const wchar_t *line, const wchar_t *cmd);
|
||||
|
||||
#endif /* #define AUTOCOMPLETE_H */
|
||||
#endif /* AUTOCOMPLETE_H */
|
||||
|
119
src/avatars.c
119
src/avatars.c
@ -20,14 +20,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "misc_tools.h"
|
||||
#include "avatars.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "avatars.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
@ -39,39 +39,76 @@ static struct Avatar {
|
||||
off_t size;
|
||||
} Avatar;
|
||||
|
||||
/* Compares the first size bytes of fp to signature.
|
||||
*
|
||||
* Returns 0 if they are the same
|
||||
* Returns 1 if they differ
|
||||
* Returns -1 on error.
|
||||
*
|
||||
* On success this function will seek back to the beginning of fp.
|
||||
*/
|
||||
static int check_file_signature(const unsigned char *signature, size_t size, FILE *fp)
|
||||
{
|
||||
char *buf = malloc(size);
|
||||
|
||||
if (buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fread(buf, size, 1, fp) != 1) {
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ret = memcmp(signature, buf, size);
|
||||
|
||||
free(buf);
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ret == 0 ? 0 : 1;
|
||||
}
|
||||
|
||||
static void avatar_clear(void)
|
||||
{
|
||||
memset(&Avatar, 0, sizeof(struct Avatar));
|
||||
Avatar = (struct Avatar) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
/* Sends avatar to friendnumber.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure.
|
||||
*/
|
||||
int avatar_send(Tox *m, uint32_t friendnum)
|
||||
int avatar_send(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
TOX_ERR_FILE_SEND err;
|
||||
uint32_t filenum = tox_file_send(m, friendnum, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
if (Avatar.size == 0)
|
||||
Tox_Err_File_Send err;
|
||||
uint32_t filenumber = tox_file_send(m, friendnumber, TOX_FILE_KIND_AVATAR, (size_t) Avatar.size,
|
||||
NULL, (uint8_t *) Avatar.name, Avatar.name_len, &err);
|
||||
|
||||
if (Avatar.size == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %d (error %d)\n", friendnum, err);
|
||||
fprintf(stderr, "tox_file_send failed for friendnumber %u (error %d)\n", friendnumber, err);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnum, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
struct FileTransfer *ft = new_file_transfer(NULL, friendnumber, filenumber, FILE_TRANSFER_SEND, TOX_FILE_KIND_AVATAR);
|
||||
|
||||
if (!ft)
|
||||
if (!ft) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ft->file = fopen(Avatar.path, "r");
|
||||
|
||||
if (ft->file == NULL)
|
||||
if (ft->file == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
snprintf(ft->file_name, sizeof(ft->file_name), "%s", Avatar.name);
|
||||
ft->file_size = Avatar.size;
|
||||
@ -82,11 +119,10 @@ int avatar_send(Tox *m, uint32_t friendnum)
|
||||
/* Sends avatar to all friends */
|
||||
static void avatar_send_all(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE)
|
||||
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||
if (Friends.list[i].connection_status != TOX_CONNECTION_NONE) {
|
||||
avatar_send(m, Friends.list[i].num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,15 +133,17 @@ static void avatar_send_all(Tox *m)
|
||||
*/
|
||||
int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||
{
|
||||
if (path_len == 0 || path_len >= sizeof(Avatar.path))
|
||||
if (path_len == 0 || path_len >= sizeof(Avatar.path)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
if (fp == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
unsigned char PNG_signature[8] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
|
||||
if (check_file_signature(PNG_signature, sizeof(PNG_signature), fp) != 0) {
|
||||
fclose(fp);
|
||||
@ -116,8 +154,9 @@ int avatar_set(Tox *m, const char *path, size_t path_len)
|
||||
|
||||
off_t size = file_size(path);
|
||||
|
||||
if (size == 0 || size > MAX_AVATAR_FILE_SIZE)
|
||||
if (size == 0 || size > MAX_AVATAR_FILE_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_file_name(Avatar.name, sizeof(Avatar.name), path);
|
||||
Avatar.name_len = strlen(Avatar.name);
|
||||
@ -141,7 +180,14 @@ void avatar_unset(Tox *m)
|
||||
avatar_send_all(m);
|
||||
}
|
||||
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control)
|
||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status)
|
||||
{
|
||||
if (connection_status == TOX_CONNECTION_NONE) {
|
||||
kill_avatar_file_transfers_friend(m, friendnumber);
|
||||
}
|
||||
}
|
||||
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control)
|
||||
{
|
||||
switch (control) {
|
||||
case TOX_FILE_CONTROL_RESUME:
|
||||
@ -150,6 +196,7 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
|
||||
} else if (ft->state == FILE_TRANSFER_PAUSED) {
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TOX_FILE_CONTROL_PAUSE:
|
||||
@ -164,8 +211,9 @@ void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL co
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length)
|
||||
{
|
||||
if (ft->state != FILE_TRANSFER_STARTED)
|
||||
if (ft->state != FILE_TRANSFER_STARTED) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 0) {
|
||||
close_file_transfer(NULL, m, ft, -1, NULL, silent);
|
||||
@ -186,20 +234,29 @@ void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position,
|
||||
ft->position = position;
|
||||
}
|
||||
|
||||
uint8_t send_data[length];
|
||||
size_t send_length = fread(send_data, 1, sizeof(send_data), ft->file);
|
||||
uint8_t *send_data = malloc(length);
|
||||
|
||||
if (send_length != length) {
|
||||
if (send_data == NULL) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_SEND_CHUNK err;
|
||||
tox_file_send_chunk(m, ft->friendnum, ft->filenum, position, send_data, send_length, &err);
|
||||
size_t send_length = fread(send_data, 1, length, ft->file);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK)
|
||||
if (send_length != length) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
free(send_data);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_File_Send_Chunk err;
|
||||
tox_file_send_chunk(m, ft->friendnumber, ft->filenumber, position, send_data, send_length, &err);
|
||||
|
||||
free(send_data);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_CHUNK_OK) {
|
||||
fprintf(stderr, "tox_file_send_chunk failed in avatar callback (error %d)\n", err);
|
||||
}
|
||||
|
||||
ft->position += send_length;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
}
|
||||
|
@ -23,6 +23,8 @@
|
||||
#ifndef AVATARS_H
|
||||
#define AVATARS_H
|
||||
|
||||
#include "file_transfers.h"
|
||||
|
||||
#define MAX_AVATAR_FILE_SIZE 65536
|
||||
|
||||
/* Sends avatar to friendnum.
|
||||
@ -47,6 +49,7 @@ int avatar_set(Tox *m, const char *path, size_t length);
|
||||
void avatar_unset(Tox *m);
|
||||
|
||||
void on_avatar_chunk_request(Tox *m, struct FileTransfer *ft, uint64_t position, size_t length);
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, TOX_FILE_CONTROL control);
|
||||
void on_avatar_file_control(Tox *m, struct FileTransfer *ft, Tox_File_Control control);
|
||||
void on_avatar_friend_connection_status(Tox *m, uint32_t friendnumber, Tox_Connection connection_status);
|
||||
|
||||
#endif /* AVATARS_H */
|
||||
|
@ -20,24 +20,26 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <limits.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <limits.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "line_info.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "configdir.h"
|
||||
#include "curl_util.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct arg_opts arg_opts;
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
/* URL that we get the JSON encoded nodes list from. */
|
||||
#define NODES_LIST_URL "https://nodes.tox.chat/json"
|
||||
@ -55,7 +57,6 @@ extern struct Winthread Winthread;
|
||||
|
||||
#define IP_MAX_SIZE 45
|
||||
#define IP_MIN_SIZE 7
|
||||
#define PORT_MAX_SIZE 5
|
||||
|
||||
#define LAST_SCAN_JSON_KEY "\"last_scan\":"
|
||||
#define LAST_SCAN_JSON_KEY_LEN (sizeof(LAST_SCAN_JSON_KEY) - 1)
|
||||
@ -78,7 +79,6 @@ extern struct Winthread Winthread;
|
||||
/* Maximum allowable size of the nodes list */
|
||||
#define MAX_NODELIST_SIZE (MAX_RECV_CURL_DATA_SIZE)
|
||||
|
||||
|
||||
static struct Thread_Data {
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
@ -104,11 +104,45 @@ static struct DHT_Nodes {
|
||||
time_t last_updated;
|
||||
} Nodes;
|
||||
|
||||
/* Return true if address appears to be a valid ipv4 address. */
|
||||
static bool is_ip4_address(const char *address)
|
||||
{
|
||||
struct sockaddr_in s_addr;
|
||||
return inet_pton(AF_INET, address, &(s_addr.sin_addr)) != 0;
|
||||
}
|
||||
|
||||
/* Return true if address roughly appears to be a valid ipv6 address.
|
||||
*
|
||||
* TODO: Improve this function (inet_pton behaves strangely with ipv6).
|
||||
* for now the only guarantee is that it won't return true if the
|
||||
* address is a domain or ipv4 address, and should only be used if you're
|
||||
* reasonably sure that the address is one of the three (ipv4, ipv6 or a domain).
|
||||
*/
|
||||
static bool is_ip6_address(const char *address)
|
||||
{
|
||||
size_t num_colons = 0;
|
||||
char ch = 0;
|
||||
|
||||
for (size_t i = 0; (ch = address[i]); ++i) {
|
||||
if (ch == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ch == ':') {
|
||||
++num_colons;
|
||||
}
|
||||
}
|
||||
|
||||
return num_colons > 1 && num_colons < 8;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@ -237,6 +271,7 @@ on_exit:
|
||||
* Return -2 if http lookup failed.
|
||||
* Return -3 if http reponse was empty.
|
||||
* Return -4 if data could not be written to disk.
|
||||
* Return -5 if memory allocation fails.
|
||||
*/
|
||||
static int update_DHT_nodeslist(const char *nodes_path)
|
||||
{
|
||||
@ -250,25 +285,34 @@ static int update_DHT_nodeslist(const char *nodes_path)
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct Recv_Curl_Data recv_data;
|
||||
memset(&recv_data, 0, sizeof(struct Recv_Curl_Data));
|
||||
struct Recv_Curl_Data *recv_data = calloc(1, sizeof(struct Recv_Curl_Data));
|
||||
|
||||
if (curl_fetch_nodes_JSON(&recv_data) == -1) {
|
||||
if (recv_data == NULL) {
|
||||
fclose(fp);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (curl_fetch_nodes_JSON(recv_data) == -1) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (recv_data.length == 0) {
|
||||
if (recv_data->length == 0) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -3;
|
||||
}
|
||||
|
||||
if (fwrite(recv_data.data, recv_data.length, 1, fp) != 1) {
|
||||
if (fwrite(recv_data->data, recv_data->length, 1, fp) != 1) {
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
return -4;
|
||||
}
|
||||
|
||||
free(recv_data);
|
||||
fclose(fp);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -333,8 +377,11 @@ static long long int extract_val_last_pinged(const char *s)
|
||||
* Return number of bytes copied to key_buf on success.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
static int extract_val_pk(const char *s, char *key_buf)
|
||||
static int extract_val_pk(const char *s, char *key_buf, size_t buf_length)
|
||||
{
|
||||
if (buf_length < TOX_PUBLIC_KEY_SIZE * 2 + 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int key_len = char_find(0, s, '"');
|
||||
|
||||
@ -376,7 +423,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;
|
||||
}
|
||||
|
||||
@ -397,13 +444,13 @@ static int extract_node(const char *line, struct Node *node)
|
||||
}
|
||||
|
||||
char key_string[TOX_PUBLIC_KEY_SIZE * 2 + 1];
|
||||
int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string);
|
||||
int key_len = extract_val_pk(key_start + PK_JSON_KEY_LEN, key_string, sizeof(key_string));
|
||||
|
||||
if (key_len == -1) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
if (hex_string_to_bin(key_string, key_len, node->key, TOX_PUBLIC_KEY_SIZE) == -1) {
|
||||
if (tox_pk_string_to_bytes(key_string, key_len, node->key, sizeof(node->key)) == -1) {
|
||||
return -6;
|
||||
}
|
||||
|
||||
@ -425,6 +472,8 @@ static int extract_node(const char *line, struct Node *node)
|
||||
/* Loads the DHT nodeslist to memory from json encoded nodes file. */
|
||||
void *load_nodeslist_thread(void *data)
|
||||
{
|
||||
UNUSED_VAR(data);
|
||||
|
||||
char nodes_path[PATH_MAX];
|
||||
get_nodeslist_path(nodes_path, sizeof(nodes_path));
|
||||
|
||||
@ -476,7 +525,7 @@ void *load_nodeslist_thread(void *data)
|
||||
/* If nodeslist does not contain any valid entries we set the last_scan value
|
||||
* to 0 so that it will fetch a new list the next time this function is called.
|
||||
*/
|
||||
if (idx == 0) {
|
||||
if (Nodes.count == 0) {
|
||||
const char *s = "{\"last_scan\":0}";
|
||||
rewind(fp);
|
||||
fwrite(s, strlen(s), 1, fp); // Not much we can do if it fails
|
||||
@ -523,6 +572,7 @@ int load_DHT_nodeslist(void)
|
||||
}
|
||||
|
||||
thread_data.active = true;
|
||||
|
||||
if (pthread_create(&thread_data.tid, &thread_data.attr, load_nodeslist_thread, NULL) != 0) {
|
||||
thread_data.active = false;
|
||||
return -5;
|
||||
@ -554,7 +604,7 @@ static void DHT_bootstrap(Tox *m)
|
||||
continue;
|
||||
}
|
||||
|
||||
TOX_ERR_BOOTSTRAP err;
|
||||
Tox_Err_Bootstrap err;
|
||||
tox_bootstrap(m, addr, node->port, (uint8_t *) node->key, &err);
|
||||
|
||||
if (err != TOX_ERR_BOOTSTRAP_OK) {
|
||||
@ -575,7 +625,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);
|
||||
|
@ -39,4 +39,4 @@ void do_tox_connection(Tox *m);
|
||||
*/
|
||||
int load_DHT_nodeslist(void);
|
||||
|
||||
#endif /* BOOTSTRAP_H */
|
||||
#endif /* BOOTSTRAP_H */
|
||||
|
1234
src/chat.c
1234
src/chat.c
File diff suppressed because it is too large
Load Diff
@ -23,13 +23,13 @@
|
||||
#ifndef CHAT_H
|
||||
#define CHAT_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
/* set CTRL to -1 if we don't want to send a control signal.
|
||||
set msg to NULL if we don't want to display a message */
|
||||
void chat_close_file_receiver(Tox *m, int filenum, int friendnum, int CTRL);
|
||||
void kill_chat_window(ToxWindow *self, Tox *m);
|
||||
ToxWindow new_chat(Tox *m, int32_t friendnum);
|
||||
ToxWindow *new_chat(Tox *m, int32_t friendnum);
|
||||
|
||||
#endif /* end of include guard: CHAT_H */
|
||||
|
@ -23,23 +23,26 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "chat.h"
|
||||
#include "conference.h"
|
||||
#include "execute.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "groupchats.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "friendlist.h"
|
||||
#include "execute.h"
|
||||
#include "line_info.h"
|
||||
#include "groupchat.h"
|
||||
#include "chat.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
extern ToxWindow *prompt;
|
||||
extern FriendsList Friends;
|
||||
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 2) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Requires type in|out and the file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -48,11 +51,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
long int idx = strtol(argv[2], NULL, 10);
|
||||
|
||||
if ((idx == 0 && strcmp(argv[2], "0")) || idx >= MAX_FILES || idx < 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = NULL;
|
||||
// first check transfer queue
|
||||
if (file_send_queue_remove(self->num, idx) == 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Pending file transfer removed from queue");
|
||||
return;
|
||||
}
|
||||
|
||||
FileTransfer *ft = NULL;
|
||||
|
||||
/* cancel an incoming file transfer */
|
||||
if (strcasecmp(inoutstr, "in") == 0) {
|
||||
@ -60,17 +69,17 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
} else if (strcasecmp(inoutstr, "out") == 0) {
|
||||
ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_SEND);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Type must be 'in' or 'out'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -78,111 +87,267 @@ void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*ar
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, silent);
|
||||
}
|
||||
|
||||
void cmd_groupinvite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_conference_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference number required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int groupnum = strtol(argv[1], NULL, 10);
|
||||
long int conferencenum = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((groupnum == 0 && strcmp(argv[1], "0")) || groupnum < 0 || groupnum == LONG_MAX) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
if ((conferencenum == 0 && strcmp(argv[1], "0")) || conferencenum < 0 || conferencenum == LONG_MAX) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid conference number.");
|
||||
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, conferencenum, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to conference (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnum);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Conference %ld.", conferencenum);
|
||||
}
|
||||
|
||||
void cmd_join_group(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
void cmd_conference_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(argc);
|
||||
UNUSED_VAR(argv);
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *conferencekey = Friends.list[self->num].conference_invite.key;
|
||||
uint16_t length = Friends.list[self->num].conference_invite.length;
|
||||
uint8_t type = Friends.list[self->num].conference_invite.type;
|
||||
|
||||
if (!Friends.list[self->num].conference_invite.pending) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending conference invite.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t conferencenum;
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_TEXT) {
|
||||
Tox_Err_Conference_Join err;
|
||||
conferencenum = tox_conference_join(m, self->num, (const uint8_t *) conferencekey, length, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_JOIN_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference instance failed to initialize (error %d)", err);
|
||||
return;
|
||||
}
|
||||
} else if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
#ifdef AUDIO
|
||||
conferencenum = toxav_join_av_groupchat(m, self->num, (const uint8_t *) conferencekey, length,
|
||||
audio_conference_callback, NULL);
|
||||
|
||||
if (conferencenum == (uint32_t) -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio conference instance failed to initialize");
|
||||
return;
|
||||
}
|
||||
|
||||
#else
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio support disabled by compile-time option.");
|
||||
return;
|
||||
#endif
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Unknown conference type %d", type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (init_conference_win(m, conferencenum, type, NULL, 0) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Conference window failed to initialize.");
|
||||
tox_conference_delete(m, conferencenum, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
if (type == TOX_CONFERENCE_TYPE_AV) {
|
||||
if (!init_conference_audio_input(m, conferencenum)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Audio capture failed; use \"/audio on\" to try again.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void cmd_group_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *groupkey = Friends.list[self->num].group_invite.key;
|
||||
uint16_t length = Friends.list[self->num].group_invite.length;
|
||||
uint8_t type = Friends.list[self->num].group_invite.type;
|
||||
|
||||
if (!Friends.list[self->num].group_invite.pending) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending group chat invite.");
|
||||
if (Friends.list[self->num].group_invite.length == 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending group invite");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnum = -1;
|
||||
const char *passwd = NULL;
|
||||
uint16_t passwd_len = 0;
|
||||
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT)
|
||||
groupnum = tox_join_groupchat(m, self->num, (uint8_t *) groupkey, length);
|
||||
/*#ifdef AUDIO
|
||||
else
|
||||
groupnum = toxav_join_av_groupchat(m, self->num, (uint8_t *) groupkey, length,
|
||||
NULL, NULL);
|
||||
#endif*/
|
||||
if (argc > 0) {
|
||||
passwd = argv[1];
|
||||
passwd_len = strlen(passwd);
|
||||
}
|
||||
|
||||
size_t nick_len = tox_self_get_name_size(m);
|
||||
char self_nick[TOX_MAX_NAME_LENGTH + 1];
|
||||
tox_self_get_name(m, (uint8_t *) self_nick);
|
||||
self_nick[nick_len] = '\0';
|
||||
|
||||
Tox_Err_Group_Invite_Accept err;
|
||||
uint32_t groupnumber = tox_group_invite_accept(m, self->num, Friends.list[self->num].group_invite.data,
|
||||
Friends.list[self->num].group_invite.length, (const uint8_t *) self_nick, nick_len,
|
||||
(const uint8_t *) passwd, passwd_len, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_INVITE_ACCEPT_OK) {
|
||||
if (err == TOX_ERR_GROUP_INVITE_ACCEPT_TOO_LONG) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group: Password too long.");
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to join group (error %d).", err);
|
||||
}
|
||||
|
||||
if (groupnum == -1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Group chat instance failed to initialize.");
|
||||
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);
|
||||
if (init_groupchat_win(m, groupnumber, NULL, 0, Group_Join_Type_Join) == -1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group chat window failed to initialize.");
|
||||
tox_group_leave(m, groupnumber, NULL, 0, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void cmd_group_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Group number required.");
|
||||
return;
|
||||
}
|
||||
|
||||
int groupnumber = atoi(argv[1]);
|
||||
|
||||
if (groupnumber == 0 && strcmp(argv[1], "0")) { /* atoi returns 0 value on invalid input */
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid group number.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Invite_Friend err;
|
||||
|
||||
if (!tox_group_invite_friend(m, groupnumber, self->num, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to invite contact to group (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invited contact to Group %d.", groupnumber);
|
||||
}
|
||||
|
||||
#ifdef GAMES
|
||||
|
||||
void cmd_game_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
if (!Friends.list[self->num].game_invite.pending) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending game invite.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (get_num_active_windows() >= MAX_WINDOWS_NUM) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, RED, " * Warning: Too many windows are open.");
|
||||
return;
|
||||
}
|
||||
|
||||
GameType type = Friends.list[self->num].game_invite.type;
|
||||
uint32_t id = Friends.list[self->num].game_invite.id;
|
||||
uint8_t *data = Friends.list[self->num].game_invite.data;
|
||||
size_t length = Friends.list[self->num].game_invite.data_length;
|
||||
|
||||
int ret = game_initialize(self, m, type, id, data, length);
|
||||
|
||||
switch (ret) {
|
||||
case 0: {
|
||||
free(data);
|
||||
Friends.list[self->num].game_invite.data = NULL;
|
||||
Friends.list[self->num].game_invite.pending = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case -1: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Window is too small. Try enlarging your window.");
|
||||
return;
|
||||
}
|
||||
|
||||
case -2: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize (network error)");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Game failed to initialize (error %d)", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // GAMES
|
||||
|
||||
void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File ID required.");
|
||||
return;
|
||||
}
|
||||
|
||||
long int idx = strtol(argv[1], NULL, 10);
|
||||
|
||||
if ((idx == 0 && strcmp(argv[1], "0")) || idx < 0 || idx >= MAX_FILES) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
FileTransfer *ft = get_file_transfer_struct_index(self->num, idx, FILE_TRANSFER_RECV);
|
||||
|
||||
if (!ft) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->state != FILE_TRANSFER_PENDING) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "No pending file transfers with that ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((ft->file = fopen(ft->file_path, "a")) == NULL) {
|
||||
const char *msg = "File transfer failed: Invalid file path.";
|
||||
const char *msg = "File transfer failed: Invalid download path.";
|
||||
close_file_transfer(self, m, ft, TOX_FILE_CONTROL_CANCEL, msg, notif_error);
|
||||
return;
|
||||
}
|
||||
|
||||
TOX_ERR_FILE_CONTROL err;
|
||||
tox_file_control(m, self->num, ft->filenum, TOX_FILE_CONTROL_RESUME, &err);
|
||||
Tox_Err_File_Control err;
|
||||
tox_file_control(m, self->num, ft->filenumber, TOX_FILE_CONTROL_RESUME, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_CONTROL_OK)
|
||||
if (err != TOX_ERR_FILE_CONTROL_OK) {
|
||||
goto on_recv_error;
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%d] as: '%s'", idx, ft->file_path);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Saving file [%ld] as: '%s'", idx, ft->file_path);
|
||||
|
||||
/* prep progress bar line */
|
||||
char progline[MAX_STR_SIZE];
|
||||
init_progress_bar(progline);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", progline);
|
||||
|
||||
ft->line_id = self->chatwin->hst->line_end->id + 2;
|
||||
ft->state = FILE_TRANSFER_STARTED;
|
||||
@ -190,65 +355,61 @@ void cmd_savefile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
return;
|
||||
|
||||
on_recv_error:
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend not found.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_FRIEND_NOT_CONNECTED:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Friend is not online.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_NOT_FOUND:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Invalid filenumber.");
|
||||
return;
|
||||
|
||||
case TOX_ERR_FILE_CONTROL_SENDQ:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed: Connection error.");
|
||||
return;
|
||||
|
||||
default:
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File transfer failed (error %d)\n", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
const char *errmsg = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path required.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
char path[MAX_STR_SIZE];
|
||||
snprintf(path, sizeof(path), "%s", &argv[1][1]);
|
||||
int path_len = strlen(path) - 1;
|
||||
path[path_len] = '\0';
|
||||
snprintf(path, sizeof(path), "%s", argv[1]);
|
||||
int path_len = strlen(path);
|
||||
|
||||
if (path_len >= MAX_STR_SIZE) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File path exceeds character limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file_to_send = fopen(path, "r");
|
||||
|
||||
if (file_to_send == NULL) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "File not found.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "File `%s` not found.", path);
|
||||
return;
|
||||
}
|
||||
|
||||
off_t filesize = file_size(path);
|
||||
|
||||
if (filesize == 0) {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid file.");
|
||||
fclose(file_to_send);
|
||||
return;
|
||||
}
|
||||
@ -256,14 +417,15 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH];
|
||||
size_t namelen = get_file_name(file_name, sizeof(file_name), path);
|
||||
|
||||
TOX_ERR_FILE_SEND err;
|
||||
Tox_Err_File_Send err;
|
||||
uint32_t filenum = tox_file_send(m, self->num, TOX_FILE_KIND_DATA, (uint64_t) filesize, NULL,
|
||||
(uint8_t *) file_name, namelen, &err);
|
||||
(uint8_t *) file_name, namelen, &err);
|
||||
|
||||
if (err != TOX_ERR_FILE_SEND_OK)
|
||||
if (err != TOX_ERR_FILE_SEND_OK) {
|
||||
goto on_send_error;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||
FileTransfer *ft = new_file_transfer(self, self->num, filenum, FILE_TRANSFER_SEND, TOX_FILE_KIND_DATA);
|
||||
|
||||
if (!ft) {
|
||||
err = TOX_ERR_FILE_SEND_TOO_MANY;
|
||||
@ -277,34 +439,68 @@ void cmd_sendfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv
|
||||
|
||||
char sizestr[32];
|
||||
bytes_convert_str(sizestr, sizeof(sizestr), filesize);
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Sending file [%d]: '%s' (%s)", filenum, file_name, sizestr);
|
||||
|
||||
return;
|
||||
|
||||
on_send_error:
|
||||
|
||||
fclose(file_to_send);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND:
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_FOUND: {
|
||||
errmsg = "File transfer failed: Invalid friend.";
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED:
|
||||
errmsg = "File transfer failed: Friend is offline.";
|
||||
break;
|
||||
case TOX_ERR_FILE_SEND_FRIEND_NOT_CONNECTED: {
|
||||
int queue_idx = file_send_queue_add(self->num, path, path_len);
|
||||
|
||||
case TOX_ERR_FILE_SEND_NAME_TOO_LONG:
|
||||
char msg[MAX_STR_SIZE];
|
||||
|
||||
switch (queue_idx) {
|
||||
case -1: {
|
||||
snprintf(msg, sizeof(msg), "Invalid file name: path is null or length is zero.");
|
||||
break;
|
||||
}
|
||||
|
||||
case -2: {
|
||||
snprintf(msg, sizeof(msg), "File name is too long.");
|
||||
break;
|
||||
}
|
||||
|
||||
case -3: {
|
||||
snprintf(msg, sizeof(msg), "File send queue is full.");
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
snprintf(msg, sizeof(msg), "File transfer queued. Type \"/cancel out %d\" to cancel.", queue_idx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", msg);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_FILE_SEND_NAME_TOO_LONG: {
|
||||
errmsg = "File transfer failed: Filename is too long.";
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_ERR_FILE_SEND_TOO_MANY:
|
||||
case TOX_ERR_FILE_SEND_TOO_MANY: {
|
||||
errmsg = "File transfer failed: Too many concurrent file transfers.";
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
default: {
|
||||
errmsg = "File transfer failed.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", errmsg);
|
||||
tox_file_control(m, self->num, filenum, TOX_FILE_CONTROL_CANCEL, NULL);
|
||||
fclose(file_to_send);
|
||||
}
|
||||
|
@ -23,12 +23,15 @@
|
||||
#ifndef CHAT_COMMANDS_H
|
||||
#define CHAT_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cmd_cancelfile(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_groupinvite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_join_group(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_invite(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_group_accept(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_group_invite(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_game_join(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_savefile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_sendfile(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
@ -41,11 +44,13 @@ 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
|
||||
void cmd_vcall(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_video(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_ccur_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_res(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* #define CHAT_COMMANDS_H */
|
||||
#endif /* CHAT_COMMANDS_H */
|
||||
|
1432
src/conference.c
Normal file
1432
src/conference.c
Normal file
File diff suppressed because it is too large
Load Diff
118
src/conference.h
Normal file
118
src/conference.h
Normal file
@ -0,0 +1,118 @@
|
||||
/* conference.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 CONFERENCE_H
|
||||
#define CONFERENCE_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define CONFERENCE_MAX_TITLE_LENGTH TOX_MAX_NAME_LENGTH
|
||||
#define SIDEBAR_WIDTH 16
|
||||
|
||||
typedef struct ConferencePeer {
|
||||
bool active;
|
||||
|
||||
uint8_t pubkey[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t peernum; /* index in chat->peer_list */
|
||||
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
|
||||
bool sending_audio;
|
||||
uint32_t audio_out_idx;
|
||||
time_t last_audio_time;
|
||||
} ConferencePeer;
|
||||
|
||||
typedef struct AudioInputCallbackData {
|
||||
Tox *tox;
|
||||
uint32_t conferencenum;
|
||||
} AudioInputCallbackData;
|
||||
|
||||
#define PUBKEY_STRING_SIZE (2 * TOX_PUBLIC_KEY_SIZE + 1)
|
||||
typedef struct NameListEntry {
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
char pubkey_str[PUBKEY_STRING_SIZE];
|
||||
uint32_t peernum;
|
||||
} NameListEntry;
|
||||
|
||||
|
||||
typedef struct {
|
||||
int chatwin;
|
||||
bool active;
|
||||
uint8_t type;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
time_t start_time;
|
||||
|
||||
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||
size_t title_length;
|
||||
|
||||
ConferencePeer *peer_list;
|
||||
uint32_t max_idx;
|
||||
|
||||
NameListEntry *name_list;
|
||||
uint32_t num_peers;
|
||||
|
||||
bool push_to_talk_enabled;
|
||||
time_t ptt_last_pushed;
|
||||
|
||||
bool audio_enabled;
|
||||
time_t last_sent_audio;
|
||||
uint32_t audio_in_idx;
|
||||
AudioInputCallbackData audio_input_callback_data;
|
||||
} ConferenceChat;
|
||||
|
||||
/* Frees all Toxic associated data structures for a conference (does not call tox_conference_delete() ) */
|
||||
void free_conference(ToxWindow *self, uint32_t conferencenum);
|
||||
|
||||
int init_conference_win(Tox *m, uint32_t conferencenum, uint8_t type, const char *title, size_t length);
|
||||
|
||||
/* destroys and re-creates conference window with or without the peerlist */
|
||||
void redraw_conference_win(ToxWindow *self);
|
||||
|
||||
void conference_set_title(ToxWindow *self, uint32_t conferencesnum, const char *title, size_t length);
|
||||
void conference_rename_log_path(Tox *m, uint32_t conferencenum, const char *new_title);
|
||||
int conference_enable_logging(ToxWindow *self, Tox *m, uint32_t conferencenum, struct chatlog *log);
|
||||
|
||||
/* Puts `(NameListEntry *)`s in `entries` for each matched peer, up to a maximum
|
||||
* of `maxpeers`.
|
||||
* Maches each peer whose name or pubkey begins with `prefix`.
|
||||
* If `prefix` is exactly the pubkey of a peer, matches only that peer.
|
||||
* return number of entries placed in `entries`.
|
||||
*/
|
||||
uint32_t get_name_list_entries_by_prefix(uint32_t conferencenum, const char *prefix, NameListEntry **entries,
|
||||
uint32_t maxpeers);
|
||||
|
||||
bool init_conference_audio_input(Tox *tox, uint32_t conferencenum);
|
||||
bool enable_conference_audio(ToxWindow *self, Tox *tox, uint32_t conferencenum);
|
||||
bool disable_conference_audio(ToxWindow *self, Tox *tox, uint32_t conferencenum);
|
||||
bool toggle_conference_push_to_talk(uint32_t conferencenum, bool enabled);
|
||||
void audio_conference_callback(void *tox, uint32_t conferencenum, uint32_t peernum,
|
||||
const int16_t *pcm, unsigned int samples, uint8_t channels, uint32_t
|
||||
sample_rate, void *userdata);
|
||||
|
||||
bool conference_mute_self(uint32_t conferencenum);
|
||||
bool conference_mute_peer(const Tox *m, uint32_t conferencenum, uint32_t peernum);
|
||||
bool conference_set_VAD_threshold(uint32_t conferencenum, float threshold);
|
||||
float conference_get_VAD_threshold(uint32_t conferencenum);
|
||||
|
||||
#endif /* CONFERENCE_H */
|
210
src/conference_commands.c
Normal file
210
src/conference_commands.c
Normal file
@ -0,0 +1,210 @@
|
||||
/* conference_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "conference.h"
|
||||
#include "line_info.h"
|
||||
#include "log.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
static void print_err(ToxWindow *self, const char *error_str)
|
||||
{
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", error_str);
|
||||
}
|
||||
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
Tox_Err_Conference_Title err;
|
||||
char title[CONFERENCE_MAX_TITLE_LENGTH + 1];
|
||||
|
||||
if (argc < 1) {
|
||||
size_t tlen = tox_conference_get_title_size(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_CONFERENCE_TITLE_OK || tlen >= sizeof(title)) {
|
||||
print_err(self, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tox_conference_get_title(m, self->num, (uint8_t *) title, &err)) {
|
||||
print_err(self, "Title is not set");
|
||||
return;
|
||||
}
|
||||
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = strlen(argv[1]);
|
||||
|
||||
if (len >= sizeof(title)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title: max length exceeded.");
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(title, sizeof(title), "%s", argv[1]);
|
||||
|
||||
if (!tox_conference_set_title(m, self->num, (uint8_t *) title, len, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set title (error %d)", err);
|
||||
return;
|
||||
}
|
||||
|
||||
conference_rename_log_path(m, self->num, title); // must be called first
|
||||
|
||||
conference_set_title(self, self->num, title, len);
|
||||
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
line_info_add(self, true, selfnick, NULL, NAME_CHANGE, 0, 0, " set the conference title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE + 20];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
||||
|
||||
#ifdef AUDIO
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
bool enable;
|
||||
|
||||
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||
enable = true;
|
||||
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||
enable = false;
|
||||
} else {
|
||||
print_err(self, "Please specify: on | off");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable ? enable_conference_audio(self, m, self->num) : disable_conference_audio(self, m, self->num)) {
|
||||
print_err(self, enable ? "Enabled conference audio. Use the '/ptt' command to toggle Push-To-Talk."
|
||||
: "Disabled conference audio");
|
||||
} else {
|
||||
print_err(self, enable ? "Failed to enable audio" : "Failed to disable audio");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
if (conference_mute_self(self->num)) {
|
||||
print_err(self, "Toggled self audio mute status");
|
||||
} else {
|
||||
print_err(self, "No audio input to mute");
|
||||
}
|
||||
} else {
|
||||
NameListEntry *entries[16];
|
||||
uint32_t n = get_name_list_entries_by_prefix(self->num, argv[1], entries, 16);
|
||||
|
||||
if (n == 0) {
|
||||
print_err(self, "No such peer");
|
||||
return;
|
||||
}
|
||||
|
||||
if (n > 1) {
|
||||
print_err(self, "Multiple matching peers (use /mute [public key] to disambiguate):");
|
||||
|
||||
for (uint32_t i = 0; i < n; ++i) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s: %s", entries[i]->pubkey_str, entries[i]->name);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (conference_mute_peer(m, self->num, entries[0]->peernum)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Toggled audio mute status of %s", entries[0]->name);
|
||||
} else {
|
||||
print_err(self, "Peer is not on the call");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
if (argc == 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Current VAD threshold: %.1f",
|
||||
(double) conference_get_VAD_threshold(self->num));
|
||||
return;
|
||||
}
|
||||
|
||||
if (argc > 1) {
|
||||
print_err(self, "Only one argument allowed.");
|
||||
return;
|
||||
}
|
||||
|
||||
char *end;
|
||||
float value = strtof(argv[1], &end);
|
||||
|
||||
if (*end) {
|
||||
print_err(self, "Invalid input");
|
||||
return;
|
||||
}
|
||||
|
||||
if (conference_set_VAD_threshold(self->num, value)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Set VAD threshold to %.1f", (double) value);
|
||||
} else {
|
||||
print_err(self, "Failed to set conference audio input sensitivity.");
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
UNUSED_VAR(m);
|
||||
|
||||
bool enable;
|
||||
|
||||
if (argc == 1 && !strcasecmp(argv[1], "on")) {
|
||||
enable = true;
|
||||
} else if (argc == 1 && !strcasecmp(argv[1], "off")) {
|
||||
enable = false;
|
||||
} else {
|
||||
print_err(self, "Please specify: on | off");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!toggle_conference_push_to_talk(self->num, enable)) {
|
||||
print_err(self, "Failed to toggle push to talk.");
|
||||
return;
|
||||
}
|
||||
|
||||
print_err(self, enable ? "Push-To-Talk is enabled. Push F2 to activate" : "Push-To-Talk is disabled");
|
||||
}
|
||||
#endif /* AUDIO */
|
36
src/conference_commands.h
Normal file
36
src/conference_commands.h
Normal file
@ -0,0 +1,36 @@
|
||||
/* conference_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 CONFERENCE_COMMANDS_H
|
||||
#define CONFERENCE_COMMANDS_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cmd_conference_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_enable_audio(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_mute(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_sense(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference_push_to_talk(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* CONFERENCE_COMMANDS_H */
|
||||
|
@ -20,18 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "configdir.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
|
||||
/* get the user's home directory. */
|
||||
void get_home_dir(char *home, int size)
|
||||
@ -48,8 +48,9 @@ void get_home_dir(char *home, int size)
|
||||
} else {
|
||||
hmstr = getenv("HOME");
|
||||
|
||||
if (hmstr == NULL)
|
||||
if (hmstr == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", hmstr);
|
||||
hmstr = buf;
|
||||
@ -77,8 +78,9 @@ char *get_user_config_dir(void)
|
||||
len = strlen(home) + strlen("/Library/Application Support") + 1;
|
||||
user_config_dir = malloc(len);
|
||||
|
||||
if (user_config_dir == NULL)
|
||||
if (user_config_dir == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(user_config_dir, len, "%s/Library/Application Support", home);
|
||||
# else /* __APPLE__ */
|
||||
@ -89,8 +91,9 @@ char *get_user_config_dir(void)
|
||||
len = strlen(home) + strlen("/.config") + 1;
|
||||
user_config_dir = malloc(len);
|
||||
|
||||
if (user_config_dir == NULL)
|
||||
if (user_config_dir == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(user_config_dir, len, "%s/.config", home);
|
||||
} else {
|
||||
@ -112,14 +115,16 @@ int create_user_config_dirs(char *path)
|
||||
struct stat buf;
|
||||
int mkdir_err = mkdir(path, 0700);
|
||||
|
||||
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode)))
|
||||
if (mkdir_err && (errno != EEXIST || stat(path, &buf) || !S_ISDIR(buf.st_mode))) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char *fullpath = malloc(strlen(path) + strlen(CONFIGDIR) + 1);
|
||||
char *logpath = malloc(strlen(path) + strlen(LOGDIR) + 1);
|
||||
|
||||
if (fullpath == NULL || logpath == NULL)
|
||||
if (fullpath == NULL || logpath == NULL) {
|
||||
exit_toxic_err("failed in load_data_structures", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
strcpy(fullpath, path);
|
||||
strcat(fullpath, CONFIGDIR);
|
||||
|
@ -53,4 +53,4 @@ void get_home_dir(char *home, int size);
|
||||
*/
|
||||
int create_user_config_dirs(char *path);
|
||||
|
||||
#endif /* #define CONFIGDIR_H */
|
||||
#endif /* CONFIGDIR_H */
|
||||
|
@ -36,8 +36,9 @@
|
||||
*/
|
||||
int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uint8_t proxy_type)
|
||||
{
|
||||
if (proxy_type == TOX_PROXY_TYPE_NONE)
|
||||
if (proxy_type == TOX_PROXY_TYPE_NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (proxy_address == NULL || port == 0) {
|
||||
return -1;
|
||||
|
@ -23,6 +23,8 @@
|
||||
#ifndef CURL_UTIL_H
|
||||
#define CURL_UTIL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* List based on Mozilla's recommended configurations for modern browsers */
|
||||
#define TLS_CIPHER_SUITE_LIST "ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK"
|
||||
|
||||
@ -52,4 +54,4 @@ int set_curl_proxy(CURL *c_handle, const char *proxy_address, uint16_t port, uin
|
||||
*/
|
||||
size_t curl_cb_write_data(void *data, size_t size, size_t nmemb, void *user_pointer);
|
||||
|
||||
#endif /* CURL_UTIL_H */
|
||||
#endif /* CURL_UTIL_H */
|
||||
|
288
src/execute.c
288
src/execute.c
@ -20,19 +20,21 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "execute.h"
|
||||
#include "api.h"
|
||||
#include "chat_commands.h"
|
||||
#include "execute.h"
|
||||
#include "global_commands.h"
|
||||
#include "group_commands.h"
|
||||
#include "conference_commands.h"
|
||||
#include "groupchat_commands.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
struct cmd_func {
|
||||
const char *name;
|
||||
@ -47,11 +49,18 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/connect", cmd_connect },
|
||||
{ "/decline", cmd_decline },
|
||||
{ "/exit", cmd_quit },
|
||||
{ "/conference", cmd_conference },
|
||||
{ "/group", cmd_groupchat },
|
||||
#ifdef GAMES
|
||||
{ "/game", cmd_game },
|
||||
#endif
|
||||
{ "/help", cmd_prompt_help },
|
||||
{ "/join", cmd_join },
|
||||
{ "/log", cmd_log },
|
||||
{ "/myid", cmd_myid },
|
||||
#ifdef QRCODE
|
||||
{ "/myqr", cmd_myqr },
|
||||
#endif /* QRCODE */
|
||||
{ "/nick", cmd_nick },
|
||||
{ "/note", cmd_note },
|
||||
{ "/nospam", cmd_nospam },
|
||||
@ -64,94 +73,188 @@ static struct cmd_func global_commands[] = {
|
||||
{ "/sdev", cmd_change_device },
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev" , cmd_change_video_device },
|
||||
{ "/lsvdev", cmd_list_video_devices },
|
||||
{ "/svdev", cmd_change_video_device },
|
||||
#endif /* VIDEO */
|
||||
#ifdef PYTHON
|
||||
{ "/run", cmd_run },
|
||||
#endif /* PYTHON */
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func chat_commands[] = {
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/invite", cmd_groupinvite },
|
||||
{ "/join", cmd_join_group },
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
{ "/cancel", cmd_cancelfile },
|
||||
{ "/cinvite", cmd_conference_invite },
|
||||
{ "/cjoin", cmd_conference_join },
|
||||
{ "/gaccept", cmd_group_accept },
|
||||
{ "/invite", cmd_group_invite },
|
||||
#ifdef GAMES
|
||||
{ "/play", cmd_game_join },
|
||||
#endif
|
||||
{ "/savefile", cmd_savefile },
|
||||
{ "/sendfile", cmd_sendfile },
|
||||
#ifdef AUDIO
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/call", cmd_call },
|
||||
{ "/answer", cmd_answer },
|
||||
{ "/reject", cmd_reject },
|
||||
{ "/hangup", cmd_hangup },
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/bitrate", cmd_bitrate },
|
||||
#endif /* AUDIO */
|
||||
#ifdef VIDEO
|
||||
{ "/video", cmd_video },
|
||||
{ "/vcall", cmd_vcall },
|
||||
{ "/video", cmd_video },
|
||||
{ "/res", cmd_res },
|
||||
#endif /* VIDEO */
|
||||
{ NULL, NULL },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
static struct cmd_func group_commands[] = {
|
||||
{ "/title", cmd_set_title },
|
||||
static struct cmd_func conference_commands[] = {
|
||||
{ "/title", cmd_conference_set_title },
|
||||
|
||||
#ifdef AUDIO
|
||||
{ "/mute", cmd_mute },
|
||||
{ "/sense", cmd_sense },
|
||||
{ "/audio", cmd_enable_audio },
|
||||
{ "/mute", cmd_conference_mute },
|
||||
{ "/ptt", cmd_conference_push_to_talk },
|
||||
{ "/sense", cmd_conference_sense },
|
||||
#endif /* AUDIO */
|
||||
{ NULL, NULL },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
Returns number of arguments on success, -1 on failure. */
|
||||
static int parse_command(WINDOW *w, ToxWindow *self, const char *input, char (*args)[MAX_STR_SIZE])
|
||||
static struct cmd_func groupchat_commands[] = {
|
||||
{ "/chatid", cmd_chatid },
|
||||
{ "/disconnect", cmd_disconnect },
|
||||
{ "/ignore", cmd_ignore },
|
||||
{ "/kick", cmd_kick },
|
||||
{ "/list", cmd_list },
|
||||
{ "/locktopic", cmd_set_topic_lock },
|
||||
{ "/mod", cmd_mod },
|
||||
{ "/nick", cmd_group_nick },
|
||||
{ "/passwd", cmd_set_passwd },
|
||||
{ "/peerlimit", cmd_set_peerlimit },
|
||||
{ "/privacy", cmd_set_privacy },
|
||||
{ "/rejoin", cmd_rejoin },
|
||||
{ "/silence", cmd_silence },
|
||||
{ "/topic", cmd_set_topic },
|
||||
{ "/unignore", cmd_unignore },
|
||||
{ "/unmod", cmd_unmod },
|
||||
{ "/unsilence", cmd_unsilence },
|
||||
{ "/voice", cmd_set_voice },
|
||||
{ "/whois", cmd_whois },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
/* Special commands are commands that only take one argument even if it contains spaces */
|
||||
static const char special_commands[][MAX_CMDNAME_SIZE] = {
|
||||
"/add",
|
||||
"/avatar",
|
||||
"/gaccept",
|
||||
"/group",
|
||||
"/ignore",
|
||||
"/kick",
|
||||
"/mod",
|
||||
"/nick",
|
||||
"/note",
|
||||
"/passwd",
|
||||
"/silence",
|
||||
"/topic",
|
||||
"/unignore",
|
||||
"/unmod",
|
||||
"/unsilence",
|
||||
"/whois",
|
||||
#ifdef PYTHON
|
||||
"/run",
|
||||
#endif /* PYTHON */
|
||||
"/sendfile",
|
||||
"/title",
|
||||
"/mute",
|
||||
"",
|
||||
};
|
||||
|
||||
/* Returns true if input command is in the special_commands array. */
|
||||
static bool is_special_command(const char *input)
|
||||
{
|
||||
const int s = char_find(0, input, ' ');
|
||||
|
||||
for (int i = 0; special_commands[i][0] != '\0'; ++i) {
|
||||
if (strncmp(input, special_commands[i], s) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Parses commands in the special_commands array. Unlike parse_command, this function
|
||||
* does not split the input string at spaces.
|
||||
*
|
||||
* Returns the number of arguments.
|
||||
*/
|
||||
static int parse_special_command(const char *input, char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
int len = strlen(input);
|
||||
int s = char_find(0, input, ' ');
|
||||
|
||||
memcpy(args[0], input, s);
|
||||
args[0][s++] = '\0'; // increment to remove space after "/command "
|
||||
|
||||
if (s >= len) {
|
||||
return 1; // No additional args
|
||||
}
|
||||
|
||||
memcpy(args[1], input + s, len - s);
|
||||
args[1][len - s] = '\0';
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
/* Parses input command and puts args into arg array.
|
||||
*
|
||||
* Returns the number of arguments.
|
||||
*/
|
||||
static int parse_command(const char *input, char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
if (is_special_command(input)) {
|
||||
return parse_special_command(input, args);
|
||||
}
|
||||
|
||||
char *cmd = strdup(input);
|
||||
|
||||
if (cmd == NULL)
|
||||
if (cmd == NULL) {
|
||||
exit_toxic_err("failed in parse_command", FATALERR_MEMORY);
|
||||
}
|
||||
|
||||
int num_args = 0;
|
||||
int i = 0; /* index of last char in an argument */
|
||||
|
||||
/* characters wrapped in double quotes count as one arg */
|
||||
while (num_args < MAX_NUM_ARGS) {
|
||||
int qt_ofst = 0; /* set to 1 to offset index for quote char at end of arg */
|
||||
int i = char_find(0, cmd, ' '); // index of last char in an argument
|
||||
memcpy(args[num_args], cmd, i);
|
||||
args[num_args++][i] = '\0';
|
||||
|
||||
if (*cmd == '\"') {
|
||||
qt_ofst = 1;
|
||||
i = char_find(1, cmd, '\"');
|
||||
|
||||
if (cmd[i] == '\0') {
|
||||
const char *errmsg = "Invalid argument. Did you forget a closing \"?";
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, errmsg);
|
||||
free(cmd);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
i = char_find(0, cmd, ' ');
|
||||
}
|
||||
|
||||
memcpy(args[num_args], cmd, i + qt_ofst);
|
||||
args[num_args++][i + qt_ofst] = '\0';
|
||||
|
||||
if (cmd[i] == '\0') /* no more args */
|
||||
if (cmd[i] == '\0') { // no more args
|
||||
break;
|
||||
}
|
||||
|
||||
char tmp[MAX_STR_SIZE];
|
||||
snprintf(tmp, sizeof(tmp), "%s", &cmd[i + 1]);
|
||||
strcpy(cmd, tmp); /* tmp will always fit inside cmd */
|
||||
strcpy(cmd, tmp); // tmp will always fit inside cmd
|
||||
}
|
||||
|
||||
free(cmd);
|
||||
return num_args;
|
||||
}
|
||||
|
||||
/* Matches command to respective function. Returns 0 on match, 1 on no match */
|
||||
/* Matches command to respective function.
|
||||
*
|
||||
* Returns 0 on match.
|
||||
* Returns 1 on no match
|
||||
*/
|
||||
static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct cmd_func *commands,
|
||||
char (*args)[MAX_STR_SIZE])
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; commands[i].name != NULL; ++i) {
|
||||
for (size_t i = 0; commands[i].name != NULL; ++i) {
|
||||
if (strcmp(args[0], commands[i].name) == 0) {
|
||||
(commands[i].func)(w, self, m, num_args - 1, args);
|
||||
return 0;
|
||||
@ -163,34 +266,59 @@ static int do_command(WINDOW *w, ToxWindow *self, Tox *m, int num_args, struct c
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode)
|
||||
{
|
||||
if (string_is_empty(input))
|
||||
if (string_is_empty(input)) {
|
||||
return;
|
||||
|
||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||
int num_args = parse_command(w, self, input, args);
|
||||
|
||||
if (num_args == -1)
|
||||
return;
|
||||
|
||||
/* Try to match input command to command functions. If non-global command mode is specified,
|
||||
try specified mode's commands first, then upon failure try global commands.
|
||||
|
||||
Note: Global commands must come last in case of duplicate command names */
|
||||
switch (mode) {
|
||||
case CHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, chat_commands, args) == 0)
|
||||
return;
|
||||
|
||||
break;
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE:
|
||||
if (do_command(w, self, m, num_args, group_commands, args) == 0)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_command(w, self, m, num_args, global_commands, args) == 0)
|
||||
return;
|
||||
char args[MAX_NUM_ARGS][MAX_STR_SIZE];
|
||||
int num_args = parse_command(input, args);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
if (num_args <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to match input command to command functions. If non-global command mode is specified,
|
||||
* try specified mode's commands first, then upon failure try global commands.
|
||||
*
|
||||
* Note: Global commands must come last in case of duplicate command names
|
||||
*/
|
||||
switch (mode) {
|
||||
case CHAT_COMMAND_MODE: {
|
||||
if (do_command(w, self, m, num_args, chat_commands, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_COMMAND_MODE: {
|
||||
if (do_command(w, self, m, num_args, conference_commands, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case GROUPCHAT_COMMAND_MODE: {
|
||||
if (do_command(w, self, m, num_args, groupchat_commands, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (do_command(w, self, m, num_args, global_commands, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
if (do_plugin_command(num_args, args) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid command.");
|
||||
}
|
||||
|
@ -31,9 +31,10 @@
|
||||
enum {
|
||||
GLOBAL_COMMAND_MODE,
|
||||
CHAT_COMMAND_MODE,
|
||||
CONFERENCE_COMMAND_MODE,
|
||||
GROUPCHAT_COMMAND_MODE,
|
||||
};
|
||||
|
||||
void execute(WINDOW *w, ToxWindow *self, Tox *m, const char *input, int mode);
|
||||
|
||||
#endif /* #define EXECUTE_H */
|
||||
#endif /* EXECUTE_H */
|
||||
|
@ -20,33 +20,35 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "friendlist.h"
|
||||
#include "execute.h"
|
||||
#include "file_transfers.h"
|
||||
#include "friendlist.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern FriendsList Friends;
|
||||
|
||||
/* number of "#"'s in file transfer progress bar. Keep well below MAX_STR_SIZE */
|
||||
#define NUM_PROG_MARKS 50
|
||||
#define STR_BUF_SIZE 30
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
Assumes progline has room for at least MAX_STR_SIZE bytes */
|
||||
void init_progress_bar(char *progline)
|
||||
{
|
||||
strcpy(progline, "0% [");
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_PROG_MARKS; ++i)
|
||||
for (size_t i = 0; i < NUM_PROG_MARKS; ++i) {
|
||||
strcat(progline, "-");
|
||||
}
|
||||
|
||||
strcat(progline, "] 0.0 B/s");
|
||||
}
|
||||
@ -54,42 +56,58 @@ void init_progress_bar(char *progline)
|
||||
/* prints a progress bar for file transfers. */
|
||||
void print_progress_bar(ToxWindow *self, double bps, double pct_done, uint32_t line_id)
|
||||
{
|
||||
if (bps < 0 || pct_done < 0 || pct_done > 100)
|
||||
if (bps < 0 || pct_done < 0 || pct_done > 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
char pct_str[24];
|
||||
char pct_str[STR_BUF_SIZE];
|
||||
snprintf(pct_str, sizeof(pct_str), "%.1f%%", pct_done);
|
||||
|
||||
char bps_str[24];
|
||||
char bps_str[STR_BUF_SIZE];
|
||||
bytes_convert_str(bps_str, sizeof(bps_str), bps);
|
||||
|
||||
char prog_line[NUM_PROG_MARKS + 1] = {0};
|
||||
char prog_line[NUM_PROG_MARKS + 1];
|
||||
prog_line[0] = 0;
|
||||
|
||||
int n = pct_done / (100 / NUM_PROG_MARKS);
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < n; ++i)
|
||||
for (i = 0; i < n; ++i) {
|
||||
strcat(prog_line, "=");
|
||||
}
|
||||
|
||||
if (pct_done < 100)
|
||||
if (pct_done < 100) {
|
||||
strcpy(prog_line + n, ">");
|
||||
}
|
||||
|
||||
for (j = i; j < NUM_PROG_MARKS - 1; ++j)
|
||||
for (j = i; j < NUM_PROG_MARKS - 1; ++j) {
|
||||
strcat(prog_line, "-");
|
||||
}
|
||||
|
||||
char full_line[strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7];
|
||||
snprintf(full_line, sizeof(full_line), "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||
size_t line_buf_size = strlen(pct_str) + NUM_PROG_MARKS + strlen(bps_str) + 7;
|
||||
char *full_line = malloc(line_buf_size);
|
||||
|
||||
if (full_line == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(full_line, line_buf_size, "%s [%s] %s/s", pct_str, prog_line, bps_str);
|
||||
|
||||
line_info_set(self, line_id, full_line);
|
||||
|
||||
free(full_line);
|
||||
}
|
||||
|
||||
static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer *ft)
|
||||
static void refresh_progress_helper(ToxWindow *self, FileTransfer *ft)
|
||||
{
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Timeout must be set to 1 second to show correct bytes per second */
|
||||
if (!timed_out(ft->last_line_progress, 1))
|
||||
if (!timed_out(ft->last_line_progress, 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
double remain = ft->file_size - ft->position;
|
||||
double pct_done = remain > 0 ? (1 - (remain / ft->file_size)) * 100 : 100;
|
||||
@ -99,34 +117,53 @@ static void refresh_progress_helper(ToxWindow *self, Tox *m, struct FileTransfer
|
||||
ft->last_line_progress = get_unix_time();
|
||||
}
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum)
|
||||
/* refreshes active file transfer status bars.
|
||||
*
|
||||
* Return true if there is at least one active file transfer in either direction.
|
||||
*/
|
||||
bool refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber)
|
||||
{
|
||||
size_t i;
|
||||
bool active = false;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_receiver[i]);
|
||||
refresh_progress_helper(self, m, &Friends.list[friendnum].file_sender[i]);
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft_r = &Friends.list[friendnumber].file_receiver[i];
|
||||
FileTransfer *ft_s = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
refresh_progress_helper(self, ft_r);
|
||||
refresh_progress_helper(self, ft_s);
|
||||
|
||||
if (ft_r->state != FILE_TRANSFER_INACTIVE || ft_s->state != FILE_TRANSFER_INACTIVE) {
|
||||
active = true;
|
||||
}
|
||||
}
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum)
|
||||
static void clear_file_transfer(FileTransfer *ft)
|
||||
{
|
||||
size_t i;
|
||||
*ft = (FileTransfer) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft_send = &Friends.list[friendnum].file_sender[i];
|
||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||
* Returns NULL if filenumber is invalid.
|
||||
*/
|
||||
FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft_send = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenum == filenum)
|
||||
if (ft_send->state != FILE_TRANSFER_INACTIVE && ft_send->filenumber == filenumber) {
|
||||
return ft_send;
|
||||
}
|
||||
|
||||
struct FileTransfer *ft_recv = &Friends.list[friendnum].file_receiver[i];
|
||||
FileTransfer *ft_recv = &Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenum == filenum)
|
||||
if (ft_recv->state != FILE_TRANSFER_INACTIVE && ft_recv->filenumber == filenumber) {
|
||||
return ft_recv;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -135,21 +172,21 @@ struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filen
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction)
|
||||
FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction)
|
||||
{
|
||||
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND)
|
||||
if (direction != FILE_TRANSFER_RECV && direction != FILE_TRANSFER_SEND) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||
&Friends.list[friendnumber].file_sender[i] :
|
||||
&Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = direction == FILE_TRANSFER_SEND ?
|
||||
&Friends.list[friendnum].file_sender[i] :
|
||||
&Friends.list[friendnum].file_receiver[i];
|
||||
|
||||
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index)
|
||||
if (ft->state != FILE_TRANSFER_INACTIVE && ft->index == index) {
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -158,23 +195,19 @@ struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t
|
||||
/* Returns a pointer to an unused file sender.
|
||||
* Returns NULL if all file senders are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
static FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber, uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_sender[i];
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
clear_file_transfer(ft);
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->friendnumber = friendnumber;
|
||||
ft->filenumber = filenumber;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_SEND;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
@ -185,23 +218,20 @@ static struct FileTransfer *new_file_sender(ToxWindow *window, uint32_t friendnu
|
||||
/* Returns a pointer to an unused file receiver.
|
||||
* Returns NULL if all file receivers are in use.
|
||||
*/
|
||||
static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnum, uint32_t filenum, uint8_t type)
|
||||
static FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
uint8_t type)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
struct FileTransfer *ft = &Friends.list[friendnum].file_receiver[i];
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
clear_file_transfer(ft);
|
||||
ft->window = window;
|
||||
ft->index = i;
|
||||
ft->friendnum = friendnum;
|
||||
ft->filenum = filenum;
|
||||
ft->friendnumber = friendnumber;
|
||||
ft->filenumber = filenumber;
|
||||
ft->file_type = type;
|
||||
ft->last_keep_alive = get_unix_time();
|
||||
ft->state = FILE_TRANSFER_PENDING;
|
||||
ft->direction = FILE_TRANSFER_RECV;
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
@ -212,66 +242,180 @@ static struct FileTransfer *new_file_receiver(ToxWindow *window, uint32_t friend
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||
FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type)
|
||||
{
|
||||
if (direction == FILE_TRANSFER_RECV)
|
||||
return new_file_receiver(window, friendnum, filenum, type);
|
||||
if (direction == FILE_TRANSFER_RECV) {
|
||||
return new_file_receiver(window, friendnumber, filenumber, type);
|
||||
}
|
||||
|
||||
if (direction == FILE_TRANSFER_SEND)
|
||||
return new_file_sender(window, friendnum, filenum, type);
|
||||
if (direction == FILE_TRANSFER_SEND) {
|
||||
return new_file_sender(window, friendnumber, filenumber, type);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int file_send_queue_add(uint32_t friendnumber, const char *file_path, size_t length)
|
||||
{
|
||||
if (length == 0 || file_path == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length > TOX_MAX_FILENAME_LENGTH) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[i];
|
||||
|
||||
if (pending_slot->pending) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pending_slot->pending = true;
|
||||
|
||||
memcpy(pending_slot->file_path, file_path, length);
|
||||
pending_slot->file_path[length] = 0;
|
||||
pending_slot->length = length;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
return -3;
|
||||
}
|
||||
|
||||
#define FILE_TRANSFER_SEND_CMD "/sendfile "
|
||||
#define FILE_TRANSFER_SEND_LEN (sizeof(FILE_TRANSFER_SEND_CMD) - 1)
|
||||
|
||||
void file_send_queue_check(ToxWindow *self, Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[i];
|
||||
|
||||
if (!pending_slot->pending) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char command[TOX_MAX_FILENAME_LENGTH + FILE_TRANSFER_SEND_LEN + 1];
|
||||
snprintf(command, sizeof(command), "%s%s", FILE_TRANSFER_SEND_CMD, pending_slot->file_path);
|
||||
|
||||
execute(self->window, self, m, command, CHAT_COMMAND_MODE);
|
||||
|
||||
*pending_slot = (PendingFileTransfer) {
|
||||
0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
int file_send_queue_remove(uint32_t friendnumber, size_t index)
|
||||
{
|
||||
if (index >= MAX_FILES) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
PendingFileTransfer *pending_slot = &Friends.list[friendnumber].file_send_queue[index];
|
||||
|
||||
if (!pending_slot->pending) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*pending_slot = (PendingFileTransfer) {
|
||||
0,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
* Set message or self to NULL if we don't want to display a message.
|
||||
*/
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type)
|
||||
{
|
||||
if (!ft)
|
||||
if (!ft) {
|
||||
return;
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE)
|
||||
return;
|
||||
|
||||
if (ft->file)
|
||||
fclose(ft->file);
|
||||
|
||||
if (CTRL >= 0)
|
||||
tox_file_control(m, ft->friendnum, ft->filenum, (TOX_FILE_CONTROL) CTRL, NULL);
|
||||
|
||||
if (message && self) {
|
||||
if (self->active_box != -1 && sound_type != silent)
|
||||
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||
else
|
||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
}
|
||||
|
||||
memset(ft, 0, sizeof(struct FileTransfer));
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ft->file) {
|
||||
fclose(ft->file);
|
||||
}
|
||||
|
||||
if (CTRL >= 0) {
|
||||
Tox_Err_File_Control err;
|
||||
|
||||
if (!tox_file_control(m, ft->friendnumber, ft->filenumber, (Tox_File_Control) CTRL, &err)) {
|
||||
fprintf(stderr, "Failed to cancel file transfer: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (message && self) {
|
||||
if (self->active_box != -1 && sound_type != silent) {
|
||||
box_notify2(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, self->active_box, "%s", message);
|
||||
} else {
|
||||
box_notify(self, sound_type, NT_NOFOCUS | NT_WNDALERT_2, &self->active_box, self->name, "%s", message);
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", message);
|
||||
}
|
||||
|
||||
clear_file_transfer(ft);
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum)
|
||||
/* Kills active outgoing avatar file transfers for friendnumber */
|
||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
size_t i;
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft = &Friends.list[friendnumber].file_sender[i];
|
||||
|
||||
for (i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnum].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
if (ft->file_type == TOX_FILE_KIND_AVATAR) {
|
||||
close_file_transfer(NULL, m, ft, TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Kills all active file transfers for friendnumber */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_sender[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
close_file_transfer(NULL, m, &Friends.list[friendnumber].file_receiver[i], TOX_FILE_CONTROL_CANCEL, NULL, silent);
|
||||
file_send_queue_remove(friendnumber, i);
|
||||
}
|
||||
}
|
||||
|
||||
void kill_all_file_transfers(Tox *m)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < Friends.max_idx; ++i)
|
||||
for (size_t i = 0; i < Friends.max_idx; ++i) {
|
||||
kill_all_file_transfers_friend(m, Friends.list[i].num);
|
||||
}
|
||||
}
|
||||
|
||||
bool file_transfer_recv_path_exists(const char *path)
|
||||
{
|
||||
for (size_t friendnumber = 0; friendnumber < Friends.max_idx; ++friendnumber) {
|
||||
if (!Friends.list[friendnumber].active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < MAX_FILES; ++i) {
|
||||
FileTransfer *ft = &Friends.list[friendnumber].file_receiver[i];
|
||||
|
||||
if (ft->state == FILE_TRANSFER_INACTIVE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(path, ft->file_path) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -25,13 +25,13 @@
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "notify.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "notify.h"
|
||||
|
||||
#define KiB 1024
|
||||
#define MiB 1048576 /* 1024^2 */
|
||||
#define GiB 1073741824 /* 1024^3 */
|
||||
#define KiB (uint32_t) 1024
|
||||
#define MiB (uint32_t) (1024 << 10) /* 1024^2 */
|
||||
#define GiB (uint32_t) (1024 << 20) /* 1024^3 */
|
||||
|
||||
#define MAX_FILES 32
|
||||
|
||||
@ -47,25 +47,31 @@ typedef enum FILE_TRANSFER_DIRECTION {
|
||||
FILE_TRANSFER_RECV
|
||||
} FILE_TRANSFER_DIRECTION;
|
||||
|
||||
struct FileTransfer {
|
||||
typedef struct FileTransfer {
|
||||
ToxWindow *window;
|
||||
FILE *file;
|
||||
FILE_TRANSFER_STATE state;
|
||||
FILE_TRANSFER_DIRECTION direction;
|
||||
uint8_t file_type;
|
||||
char file_name[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
char file_path[PATH_MAX + 1]; /* Not used by senders */
|
||||
double bps;
|
||||
uint32_t filenum;
|
||||
uint32_t friendnum;
|
||||
uint32_t filenumber;
|
||||
uint32_t friendnumber;
|
||||
size_t index;
|
||||
uint64_t file_size;
|
||||
uint64_t position;
|
||||
time_t last_line_progress; /* The last time we updated the progress bar */
|
||||
time_t last_keep_alive; /* The last time we sent or received data */
|
||||
uint32_t line_id;
|
||||
uint8_t file_id[TOX_FILE_ID_LENGTH];
|
||||
};
|
||||
} FileTransfer;
|
||||
|
||||
typedef struct PendingFileTransfer {
|
||||
char file_path[TOX_MAX_FILENAME_LENGTH + 1];
|
||||
size_t length;
|
||||
uint32_t friendnumber;
|
||||
bool pending;
|
||||
} PendingFileTransfer;
|
||||
|
||||
|
||||
/* creates initial progress line that will be updated during file transfer.
|
||||
progline must be at lesat MAX_STR_SIZE bytes */
|
||||
@ -74,27 +80,55 @@ void init_progress_bar(char *progline);
|
||||
/* prints a progress bar for file transfers */
|
||||
void print_progress_bar(ToxWindow *self, double pct_done, double bps, uint32_t line_id);
|
||||
|
||||
/* refreshes active file transfer status bars. */
|
||||
void refresh_file_transfer_progress(ToxWindow *self, Tox *m, uint32_t friendnum);
|
||||
|
||||
/* Returns a pointer to friendnum's FileTransfer struct associated with filenum.
|
||||
* Returns NULL if filenum is invalid.
|
||||
/* refreshes active file transfer status bars.
|
||||
*
|
||||
* Return true if there is at least one active file transfer in either direction.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnum, uint32_t filenum);
|
||||
bool refresh_file_transfer_progress(ToxWindow *self, uint32_t friendnumber);
|
||||
|
||||
/* Returns a pointer to friendnumber's FileTransfer struct associated with filenumber.
|
||||
* Returns NULL if filenumber is invalid.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct(uint32_t friendnumber, uint32_t filenumber);
|
||||
|
||||
|
||||
/* Returns a pointer to the FileTransfer struct associated with index with the direction specified.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnum, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction);
|
||||
struct FileTransfer *get_file_transfer_struct_index(uint32_t friendnumber, uint32_t index,
|
||||
FILE_TRANSFER_DIRECTION direction);
|
||||
|
||||
/* Initializes an unused file transfer and returns its pointer.
|
||||
* Returns NULL on failure.
|
||||
*/
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, uint32_t filenum,
|
||||
struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnumber, uint32_t filenumber,
|
||||
FILE_TRANSFER_DIRECTION direction, uint8_t type);
|
||||
|
||||
/* Adds a file designated by `file_path` of length `length` to the file transfer queue.
|
||||
*
|
||||
* Items in this queue will be automatically sent to the contact designated by `friendnumber`
|
||||
* as soon as they appear online. The item will then be removed from the queue whether
|
||||
* or not the transfer successfully initiates.
|
||||
*
|
||||
* If the ToxWindow associated with this friend is closed, all queued items will be
|
||||
* discarded.
|
||||
*
|
||||
* Return the queue index on success.
|
||||
* Return -1 if the length is invalid.
|
||||
* Return -2 if the send queue is full.
|
||||
*/
|
||||
int file_send_queue_add(uint32_t friendnumber, const char *file_path, size_t length);
|
||||
|
||||
/* Initiates all file transfers from the file send queue for friend designated by `friendnumber`. */
|
||||
void file_send_queue_check(ToxWindow *self, Tox *m, uint32_t friendnumber);
|
||||
|
||||
/* Removes the `index`-th item from the file send queue for `friendnumber`.
|
||||
*
|
||||
* Return 0 if a pending transfer was successfully removed
|
||||
* Return -1 if index does not designate a pending file transfer.
|
||||
*/
|
||||
int file_send_queue_remove(uint32_t friendnumber, size_t index);
|
||||
|
||||
/* Closes file transfer ft.
|
||||
*
|
||||
* Set CTRL to -1 if we don't want to send a control signal.
|
||||
@ -103,9 +137,15 @@ struct FileTransfer *new_file_transfer(ToxWindow *window, uint32_t friendnum, ui
|
||||
void close_file_transfer(ToxWindow *self, Tox *m, struct FileTransfer *ft, int CTRL, const char *message,
|
||||
Notification sound_type);
|
||||
|
||||
/* Kills all active file transfers for friendnum */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnum);
|
||||
/* Kills active outgoing avatar file transfers for friendnumber */
|
||||
void kill_avatar_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||
|
||||
/* Kills all active file transfers for friendnumber */
|
||||
void kill_all_file_transfers_friend(Tox *m, uint32_t friendnumber);
|
||||
|
||||
void kill_all_file_transfers(Tox *m);
|
||||
|
||||
#endif /* #define FILE_TRANSFERS_H */
|
||||
/* Return true if any pending or active file receiver has the path `path`. */
|
||||
bool file_transfer_recv_path_exists(const char *path);
|
||||
|
||||
#endif /* FILE_TRANSFERS_H */
|
||||
|
738
src/friendlist.c
738
src/friendlist.c
File diff suppressed because it is too large
Load Diff
@ -25,9 +25,13 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "file_transfers.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "file_transfers.h"
|
||||
|
||||
#ifdef GAMES
|
||||
#include "game_base.h"
|
||||
#endif
|
||||
|
||||
struct LastOnline {
|
||||
uint64_t last_on;
|
||||
@ -35,37 +39,61 @@ struct LastOnline {
|
||||
char hour_min_str[TIME_STR_SIZE]; /* holds 12/24-hour time string e.g. "10:43 PM" */
|
||||
};
|
||||
|
||||
struct GroupChatInvite {
|
||||
struct ConferenceInvite {
|
||||
char *key;
|
||||
uint16_t length;
|
||||
uint8_t type;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
struct GroupInvite {
|
||||
uint8_t *data;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
#ifdef GAMES
|
||||
|
||||
struct GameInvite {
|
||||
uint8_t *data;
|
||||
size_t data_length;
|
||||
GameType type;
|
||||
uint32_t id;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
#endif // GAMES
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
uint16_t namelength;
|
||||
char statusmsg[TOX_MAX_STATUS_MESSAGE_LENGTH + 1];
|
||||
size_t statusmsg_len;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
int chatwin;
|
||||
bool active;
|
||||
TOX_CONNECTION connection_status;
|
||||
Tox_Connection connection_status;
|
||||
bool is_typing;
|
||||
bool logging_on; /* saves preference for friend irrespective of global settings */
|
||||
uint8_t status;
|
||||
Tox_User_Status status;
|
||||
|
||||
struct LastOnline last_online;
|
||||
struct GroupChatInvite group_invite;
|
||||
|
||||
#ifdef GAMES
|
||||
struct GameInvite game_invite;
|
||||
#endif
|
||||
|
||||
struct ConferenceInvite conference_invite;
|
||||
struct GroupInvite group_invite;
|
||||
|
||||
struct FileTransfer file_receiver[MAX_FILES];
|
||||
struct FileTransfer file_sender[MAX_FILES];
|
||||
PendingFileTransfer file_send_queue[MAX_FILES];
|
||||
} ToxicFriend;
|
||||
|
||||
typedef struct {
|
||||
char name[TOXIC_MAX_NAME_LENGTH + 1];
|
||||
int namelength;
|
||||
uint16_t namelength;
|
||||
char pub_key[TOX_PUBLIC_KEY_SIZE];
|
||||
uint32_t num;
|
||||
bool active;
|
||||
@ -81,14 +109,26 @@ typedef struct {
|
||||
ToxicFriend *list;
|
||||
} FriendsList;
|
||||
|
||||
ToxWindow new_friendlist(void);
|
||||
extern FriendsList Friends;
|
||||
|
||||
ToxWindow *new_friendlist(void);
|
||||
void friendlist_onInit(ToxWindow *self, Tox *m);
|
||||
void disable_chatwin(uint32_t f_num);
|
||||
int get_friendnum(uint8_t *name);
|
||||
int load_blocklist(char *data);
|
||||
void kill_friendlist(void);
|
||||
void kill_friendlist(ToxWindow *self);
|
||||
void friendlist_onFriendAdded(ToxWindow *self, Tox *m, uint32_t num, bool sort);
|
||||
Tox_User_Status get_friend_status(uint32_t friendnumber);
|
||||
Tox_Connection get_friend_connection_status(uint32_t friendnumber);
|
||||
|
||||
/* sorts friendlist_index first by connection status then alphabetically */
|
||||
void sort_friendlist_index(void);
|
||||
|
||||
/*
|
||||
* Returns true if friend associated with `public_key` is in the block list.
|
||||
*
|
||||
* `public_key` must be at least TOX_PUBLIC_KEY_SIZE bytes.
|
||||
*/
|
||||
bool friend_is_blocked(const char *public_key);
|
||||
|
||||
#endif /* end of include guard: FRIENDLIST_H */
|
||||
|
1140
src/game_base.c
Normal file
1140
src/game_base.c
Normal file
File diff suppressed because it is too large
Load Diff
400
src/game_base.h
Normal file
400
src/game_base.h
Normal file
@ -0,0 +1,400 @@
|
||||
/* game_base.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GAME_BASE
|
||||
#define GAME_BASE
|
||||
|
||||
#include <ncurses.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <tox/tox.h>
|
||||
|
||||
#include "game_util.h"
|
||||
#include "windows.h"
|
||||
|
||||
#define GAME_BORDER_COLOUR BAR_SOLID
|
||||
|
||||
|
||||
/* Max size of a default square game window */
|
||||
#define GAME_MAX_SQUARE_Y_DEFAULT 26
|
||||
#define GAME_MAX_SQUARE_X_DEFAULT (GAME_MAX_SQUARE_Y_DEFAULT * 2)
|
||||
|
||||
/* Max size of a large square game window */
|
||||
#define GAME_MAX_SQUARE_Y_LARGE 52
|
||||
#define GAME_MAX_SQUARE_X_LARGE (GAME_MAX_SQUARE_Y_LARGE * 2)
|
||||
|
||||
/* Max size of a default size rectangle game window */
|
||||
#define GAME_MAX_RECT_Y_DEFAULT 24
|
||||
#define GAME_MAX_RECT_X_DEFAULT (GAME_MAX_RECT_Y_DEFAULT * 4)
|
||||
|
||||
/* Max size of a large rectangle game window */
|
||||
#define GAME_MAX_RECT_Y_LARGE 52
|
||||
#define GAME_MAX_RECT_X_LARGE (GAME_MAX_RECT_Y_LARGE * 4)
|
||||
|
||||
/* Maximum length of a game message set with game_set_message() */
|
||||
#define GAME_MAX_MESSAGE_SIZE 64
|
||||
|
||||
/* Default number of seconds a game message is displayed for */
|
||||
#define GAME_MESSAGE_DEFAULT_TIMEOUT 3
|
||||
|
||||
|
||||
/***** START NETWORKING CONSTANTS *****/
|
||||
|
||||
/* Header starts after custom packet type byte. Comprised of: NetworkVersion (1b) + GameType (1b) + id (4b) */
|
||||
#define GAME_PACKET_HEADER_SIZE (1 + 1 + sizeof(uint32_t))
|
||||
|
||||
/* Max size of a game packet including the header */
|
||||
#define GAME_MAX_PACKET_SIZE 1024
|
||||
|
||||
/* Max size of a game packet payload */
|
||||
#define GAME_MAX_DATA_SIZE (GAME_MAX_PACKET_SIZE - GAME_PACKET_HEADER_SIZE - 1)
|
||||
|
||||
/* Current version of networking protocol */
|
||||
#define GAME_NETWORKING_VERSION 0x01
|
||||
|
||||
/***** END NETWORKING CONSTANTS *****/
|
||||
|
||||
|
||||
typedef void cb_game_update_state(GameData *game, void *cb_data);
|
||||
typedef void cb_game_render_window(GameData *game, WINDOW *window, void *cb_data);
|
||||
typedef void cb_game_kill(GameData *game, void *cb_data);
|
||||
typedef void cb_game_pause(GameData *game, bool is_paused, void *cb_data);
|
||||
typedef void cb_game_key_press(GameData *game, int key, void *cb_data);
|
||||
typedef void cb_game_on_packet(GameData *game, const uint8_t *data, size_t length, void *cb_data);
|
||||
|
||||
typedef enum GamePacketType {
|
||||
GP_Invite = 0u,
|
||||
GP_Data,
|
||||
} GamePacketType;
|
||||
|
||||
typedef enum GameWindowShape {
|
||||
GW_ShapeSquare = 0u,
|
||||
GW_ShapeSquareLarge,
|
||||
GW_ShapeRectangle,
|
||||
GW_ShapeRectangleLarge,
|
||||
GW_ShapeInvalid,
|
||||
} GameWindowShape;
|
||||
|
||||
typedef enum GameStatus {
|
||||
GS_None = 0u,
|
||||
GS_Paused,
|
||||
GS_Running,
|
||||
GS_Finished,
|
||||
GS_Invalid,
|
||||
} GameStatus;
|
||||
|
||||
typedef enum GameType {
|
||||
GT_Centipede = 0u,
|
||||
GT_Chess,
|
||||
GT_Life,
|
||||
GT_Snake,
|
||||
GT_Invalid,
|
||||
} GameType;
|
||||
|
||||
typedef struct GameMessage {
|
||||
char message[GAME_MAX_MESSAGE_SIZE + 1];
|
||||
size_t length;
|
||||
const Coords *coords; // pointer to coords so we can track movement
|
||||
Coords original_coords; // static coords at time of being set
|
||||
time_t timeout;
|
||||
time_t set_time;
|
||||
int attributes;
|
||||
int colour;
|
||||
Direction direction;
|
||||
bool sticky;
|
||||
bool priority;
|
||||
} GameMessage;
|
||||
|
||||
struct GameData {
|
||||
TIME_MS last_frame_time;
|
||||
TIME_MS update_interval; // determines the refresh rate (lower means faster)
|
||||
long int score;
|
||||
size_t high_score;
|
||||
int lives;
|
||||
size_t level;
|
||||
GameStatus status;
|
||||
GameType type;
|
||||
bool is_multiplayer;
|
||||
|
||||
bool show_lives;
|
||||
bool show_score;
|
||||
bool show_high_score;
|
||||
bool show_level;
|
||||
|
||||
GameMessage *messages;
|
||||
size_t messages_size;
|
||||
|
||||
int game_max_x; // max dimensions of game window
|
||||
int game_max_y;
|
||||
|
||||
int parent_max_x; // max dimensions of parent window
|
||||
int parent_max_y;
|
||||
|
||||
int window_id;
|
||||
WINDOW *window;
|
||||
|
||||
Tox *tox; // must be locked with Winthread mutex
|
||||
|
||||
GameWindowShape window_shape;
|
||||
|
||||
uint32_t id; // indentifies multiplayer instance
|
||||
uint32_t friend_number; // friendnumber associated with parent window
|
||||
|
||||
cb_game_update_state *cb_game_update_state;
|
||||
void *cb_game_update_state_data;
|
||||
|
||||
cb_game_render_window *cb_game_render_window;
|
||||
void *cb_game_render_window_data;
|
||||
|
||||
cb_game_kill *cb_game_kill;
|
||||
void *cb_game_kill_data;
|
||||
|
||||
cb_game_pause *cb_game_pause;
|
||||
void *cb_game_pause_data;
|
||||
|
||||
cb_game_key_press *cb_game_key_press;
|
||||
void *cb_game_key_press_data;
|
||||
|
||||
cb_game_on_packet *cb_game_on_packet;
|
||||
void *cb_game_on_packet_data;
|
||||
};
|
||||
|
||||
/*
|
||||
* Sets the callback for game state updates.
|
||||
*/
|
||||
void game_set_cb_update_state(GameData *game, cb_game_update_state *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Sets the callback for frame rendering.
|
||||
*/
|
||||
void game_set_cb_render_window(GameData *game, cb_game_render_window *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Sets the callback for game termination.
|
||||
*/
|
||||
void game_set_cb_kill(GameData *game, cb_game_kill *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Sets the callback for the game pause event.
|
||||
*/
|
||||
void game_set_cb_on_pause(GameData *game, cb_game_pause *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Sets the callback for the key press event.
|
||||
*/
|
||||
void game_set_cb_on_keypress(GameData *game, cb_game_key_press *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Sets the callback for the game packet event.
|
||||
*/
|
||||
void game_set_cb_on_packet(GameData *game, cb_game_on_packet *func, void *cb_data);
|
||||
|
||||
/*
|
||||
* Initializes game instance.
|
||||
*
|
||||
* `type` must be a valid GameType.
|
||||
*
|
||||
* `id` should be a unique integer to indentify the game instance. If we're being invited to a game
|
||||
* this identifier should be sent via the invite packet.
|
||||
*
|
||||
* if `multiplayer_data` is non-null this indicates that we accepted a game invite from a contact.
|
||||
* The data contains any information we need to initialize the game state.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 if screen is too small.
|
||||
* Return -2 on network related error.
|
||||
* Return -3 if multiplayer game is being initialized outside of a contact's window.
|
||||
* Return -4 on other failure.
|
||||
*/
|
||||
int game_initialize(const ToxWindow *self, Tox *m, GameType type, uint32_t id, const uint8_t *multiplayer_data,
|
||||
size_t length);
|
||||
|
||||
/*
|
||||
* Sets game window to `shape`.
|
||||
*
|
||||
* This must be called on game initialization.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 if window is too small or shape is invalid.
|
||||
* Return -2 if function is called while the game state is valid.
|
||||
*/
|
||||
int game_set_window_shape(GameData *game, GameWindowShape shape);
|
||||
|
||||
/*
|
||||
* Returns the GameType associated with `game_string`.
|
||||
*/
|
||||
GameType game_get_type(const char *game_string);
|
||||
|
||||
/*
|
||||
* Returns the name represented as a string associated with `type`.
|
||||
*/
|
||||
const char *game_get_name_string(GameType type);
|
||||
|
||||
/*
|
||||
* Prints all available games to window associated with `self`.
|
||||
*/
|
||||
void game_list_print(ToxWindow *self);
|
||||
|
||||
/*
|
||||
* Return true if game `type` has a multiplayer mode.
|
||||
*/
|
||||
bool game_type_is_multiplayer(GameType type);
|
||||
|
||||
/*
|
||||
* Returns true if coordinates designated by `x` and `y` are within the game window boundaries.
|
||||
*/
|
||||
bool game_coordinates_in_bounds(const GameData *game, int x, int y);
|
||||
|
||||
/*
|
||||
* Put random coordinates that fit within the game window in `coords`.
|
||||
*/
|
||||
void game_random_coords(const GameData *game, Coords *coords);
|
||||
|
||||
/*
|
||||
* Gets the current max dimensions of the game window.
|
||||
*/
|
||||
void game_max_x_y(const GameData *game, int *x, int *y);
|
||||
|
||||
/*
|
||||
* Returns the respective coordinate boundary of the game window.
|
||||
*/
|
||||
int game_y_bottom_bound(const GameData *game);
|
||||
int game_y_top_bound(const GameData *game);
|
||||
int game_x_right_bound(const GameData *game);
|
||||
int game_x_left_bound(const GameData *game);
|
||||
|
||||
/*
|
||||
* Toggle whether the respective game info is shown around the game window.
|
||||
*/
|
||||
void game_show_score(GameData *game, bool show_score);
|
||||
void game_show_high_score(GameData *game, bool show_high_score);
|
||||
void game_show_lives(GameData *game, bool show_lives);
|
||||
void game_show_level(GameData *game, bool show_level);
|
||||
|
||||
/*
|
||||
* Sends a notification to the window associated with `game`.
|
||||
*
|
||||
* `message` - the notification message that will be displayed.
|
||||
*/
|
||||
void game_window_notify(const GameData *game, const char *message);
|
||||
|
||||
/*
|
||||
* Updates game score.
|
||||
*/
|
||||
void game_update_score(GameData *game, long int points);
|
||||
|
||||
/*
|
||||
* Sets game score to `val`.
|
||||
*/
|
||||
void game_set_score(GameData *game, long int score);
|
||||
|
||||
/*
|
||||
* Returns the game's current score.
|
||||
*/
|
||||
long int game_get_score(const GameData *game);
|
||||
|
||||
/*
|
||||
* Increments level.
|
||||
*
|
||||
* This function should be called on initialization if game wishes to display level.
|
||||
*/
|
||||
void game_increment_level(GameData *game);
|
||||
|
||||
/*
|
||||
* Updates lives with `amount`.
|
||||
*
|
||||
* If lives becomes negative the lives counter will not be drawn.
|
||||
*/
|
||||
void game_update_lives(GameData *game, int amount);
|
||||
|
||||
/*
|
||||
* Returns the remaining number of lives for the game.
|
||||
*/
|
||||
int game_get_lives(const GameData *game);
|
||||
|
||||
/*
|
||||
* Returns the current level.
|
||||
*/
|
||||
size_t game_get_current_level(const GameData *game);
|
||||
|
||||
/*
|
||||
* Sets the game status to `status`.
|
||||
*/
|
||||
void game_set_status(GameData *game, GameStatus status);
|
||||
|
||||
/*
|
||||
* Sets the game base update interval.
|
||||
*
|
||||
* Lower values of `update_interval` make the game faster, where 1 is the fastest and 50 is slowest.
|
||||
* If this function is never called the game chooses a reasonable default.
|
||||
*/
|
||||
void game_set_update_interval(GameData *game, TIME_MS update_interval);
|
||||
|
||||
/*
|
||||
* Creates a message `message` of size `length` to be displayed at `coords` for `timeout` seconds.
|
||||
*
|
||||
* Message must be no greater than GAME_MAX_MESSAGE_SIZE bytes in length.
|
||||
*
|
||||
* If `sticky` is true the message will follow coords if they move.
|
||||
*
|
||||
* If `dir` is a valid direction, the message will be positioned a few squares away from `coords`
|
||||
* so as to not overlap with its associated object.
|
||||
*
|
||||
* If `timeout` is zero, the default timeout value will be used.
|
||||
*
|
||||
* If `priority` true, messages will be printed on top of game objects.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 on failure.
|
||||
*/
|
||||
int game_set_message(GameData *game, const char *message, size_t length, Direction dir, int attributes, int colour,
|
||||
time_t timeout, const Coords *coords, bool sticky, bool priority);
|
||||
|
||||
/*
|
||||
* Returns true if game should update an object's state according to its last moved time and current speed.
|
||||
*
|
||||
* This is used to independently control the speed of various game objects.
|
||||
*/
|
||||
bool game_do_object_state_update(const GameData *game, TIME_MS current_time, TIME_MS last_moved_time, TIME_MS speed);
|
||||
|
||||
/*
|
||||
* Returns the current wall time in milliseconds.
|
||||
*/
|
||||
TIME_MS get_time_millis(void);
|
||||
|
||||
/*
|
||||
* Ends game associated with `self` and cleans up.
|
||||
*/
|
||||
void game_kill(ToxWindow *self);
|
||||
|
||||
/*
|
||||
* Sends a packet containing payload `data` of size `length` to the friendnumber associated with the game's
|
||||
* parent window.
|
||||
*
|
||||
* `length` must not exceed GAME_MAX_DATA_SIZE bytes.
|
||||
*
|
||||
* `packet_type` should be GP_Invite for an invite packet or GP_Data for all other game data.
|
||||
*/
|
||||
int game_packet_send(const GameData *game, const uint8_t *data, size_t length, GamePacketType packet_type);
|
||||
|
||||
#endif // GAME_BASE
|
||||
|
1750
src/game_centipede.c
Normal file
1750
src/game_centipede.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
/* group_commands.h
|
||||
/* game_centipede.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* This file is part of Toxic.
|
||||
*
|
||||
@ -20,12 +20,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GROUP_COMMANDS_H
|
||||
#define GROUP_COMMANDS_H
|
||||
#ifndef GAME_CENTIPEDE
|
||||
#define GAME_CENTIPEDE
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "game_base.h"
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
int centipede_initialize(GameData *game);
|
||||
|
||||
#endif /* GROUP_COMMANDS_H */
|
||||
#endif // GAME_CENTIPEDE
|
2164
src/game_chess.c
Normal file
2164
src/game_chess.c
Normal file
File diff suppressed because it is too large
Load Diff
44
src/game_chess.h
Normal file
44
src/game_chess.h
Normal file
@ -0,0 +1,44 @@
|
||||
/* game_chess.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GAME_CHESS
|
||||
#define GAME_CHESS
|
||||
|
||||
#include "game_base.h"
|
||||
|
||||
/*
|
||||
* Initializes chess game state.
|
||||
*
|
||||
* If `init_data` is non-null, this indicates that we were invited to the game.
|
||||
*
|
||||
* If we're the inviter, we send an invite packet after initialization. If we're the
|
||||
* invitee, we send a handshake response packet to the inviter.
|
||||
*
|
||||
* Return 0 on success.
|
||||
* Return -1 if window is too small.
|
||||
* Return -2 on network related error.
|
||||
* Return -3 on other error.
|
||||
*/
|
||||
int chess_initialize(GameData *game, const uint8_t *init_data, size_t length);
|
||||
|
||||
#endif // GAME_CHESS
|
||||
|
676
src/game_life.c
Normal file
676
src/game_life.c
Normal file
@ -0,0 +1,676 @@
|
||||
/* game_life.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2021 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "game_life.h"
|
||||
|
||||
#define LIFE_DEFAULT_CELL_CHAR 'o'
|
||||
#define LIFE_CELL_DEFAULT_COLOUR CYAN
|
||||
#define LIFE_DEFAULT_SPEED 25
|
||||
#define LIFE_MAX_SPEED 40
|
||||
|
||||
/* Determines the additional size of the grid beyond the visible boundaries.
|
||||
*
|
||||
* This buffer allows cells to continue growing off-screen giving the illusion of an
|
||||
* infinite grid to a certain point.
|
||||
*/
|
||||
#define LIFE_BOUNDARY_BUFFER 50
|
||||
|
||||
|
||||
typedef struct Cell {
|
||||
Coords coords;
|
||||
bool alive;
|
||||
bool marked; // true if cell should invert alive status at end of current cycle
|
||||
int display_char;
|
||||
size_t age;
|
||||
} Cell;
|
||||
|
||||
typedef struct LifeState {
|
||||
TIME_MS time_last_cycle;
|
||||
size_t speed;
|
||||
size_t generation;
|
||||
bool paused;
|
||||
|
||||
Cell **cells;
|
||||
int num_columns;
|
||||
int num_rows;
|
||||
|
||||
int curs_x;
|
||||
int curs_y;
|
||||
|
||||
int x_left_bound;
|
||||
int x_right_bound;
|
||||
int y_top_bound;
|
||||
int y_bottom_bound;
|
||||
|
||||
short display_candy;
|
||||
int colour;
|
||||
} LifeState;
|
||||
|
||||
|
||||
static void life_increase_speed(LifeState *state)
|
||||
{
|
||||
if (state->speed < LIFE_MAX_SPEED) {
|
||||
++state->speed;
|
||||
}
|
||||
}
|
||||
|
||||
static void life_decrease_speed(LifeState *state)
|
||||
{
|
||||
if (state->speed > 1) {
|
||||
--state->speed;
|
||||
}
|
||||
}
|
||||
|
||||
static int life_get_display_char(const LifeState *state, const Cell *cell)
|
||||
{
|
||||
if (state->display_candy == 1) {
|
||||
if (cell->age == 1) {
|
||||
return '.';
|
||||
}
|
||||
|
||||
return '+';
|
||||
}
|
||||
|
||||
if (state->display_candy == 2) {
|
||||
if (cell->age == 1) {
|
||||
return '.';
|
||||
}
|
||||
|
||||
if (cell->age == 2) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
if (cell->age == 3) {
|
||||
return 'o';
|
||||
}
|
||||
|
||||
return 'O';
|
||||
}
|
||||
|
||||
return 'o';
|
||||
}
|
||||
|
||||
static void life_toggle_display_candy(LifeState *state)
|
||||
{
|
||||
state->display_candy = (state->display_candy + 1) % 3; // magic number depends on life_get_display_char()
|
||||
}
|
||||
|
||||
static void life_cycle_colour(LifeState *state)
|
||||
{
|
||||
switch (state->colour) {
|
||||
case RED: {
|
||||
state->colour = YELLOW;
|
||||
break;
|
||||
}
|
||||
|
||||
case YELLOW: {
|
||||
state->colour = GREEN;
|
||||
break;
|
||||
}
|
||||
|
||||
case GREEN: {
|
||||
state->colour = CYAN;
|
||||
break;
|
||||
}
|
||||
|
||||
case CYAN: {
|
||||
state->colour = BLUE;
|
||||
break;
|
||||
}
|
||||
|
||||
case BLUE: {
|
||||
state->colour = MAGENTA;
|
||||
break;
|
||||
}
|
||||
|
||||
case MAGENTA: {
|
||||
state->colour = RED;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
state->colour = RED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Cell *life_get_cell_at_coords(const LifeState *state, const int x, const int y)
|
||||
{
|
||||
const int i = y - (state->y_top_bound - (LIFE_BOUNDARY_BUFFER / 2));
|
||||
const int j = x - (state->x_left_bound - (LIFE_BOUNDARY_BUFFER / 2));
|
||||
|
||||
if (i >= 0 && j >= 0) {
|
||||
return &state->cells[i][j];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void life_draw_cells(const GameData *game, WINDOW *win, LifeState *state)
|
||||
{
|
||||
wattron(win, A_BOLD | COLOR_PAIR(state->colour));
|
||||
|
||||
for (int i = LIFE_BOUNDARY_BUFFER / 2; i < state->num_rows - (LIFE_BOUNDARY_BUFFER / 2); ++i) {
|
||||
for (int j = LIFE_BOUNDARY_BUFFER / 2; j < state->num_columns + 1 - (LIFE_BOUNDARY_BUFFER / 2); ++j) {
|
||||
Cell *cell = &state->cells[i][j];
|
||||
|
||||
if (cell->alive) {
|
||||
Coords coords = cell->coords;
|
||||
mvwaddch(win, coords.y, coords.x, cell->display_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(state->colour));
|
||||
}
|
||||
|
||||
static void life_toggle_cell(LifeState *state)
|
||||
{
|
||||
Cell *cell = life_get_cell_at_coords(state, state->curs_x, state->curs_y);
|
||||
|
||||
if (cell == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
cell->alive ^= 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the number of live neighbours of cell at `i` `j` position.
|
||||
*
|
||||
* Returns NULL if cell is touching a border.
|
||||
*/
|
||||
static int life_get_live_neighbours(const LifeState *state, const int i, const int j)
|
||||
{
|
||||
Cell *n[8] = {0};
|
||||
|
||||
if (i > 0 && j > 0) {
|
||||
n[0] = &state->cells[i - 1][j - 1];
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
n[1] = &state->cells[i - 1][j];
|
||||
}
|
||||
|
||||
if (i > 0 && j < state->num_columns - 1) {
|
||||
n[2] = &state->cells[i - 1][j + 1];
|
||||
}
|
||||
|
||||
if (j > 0) {
|
||||
n[3] = &state->cells[i][j - 1];
|
||||
}
|
||||
|
||||
if (j < state->num_columns - 1) {
|
||||
n[4] = &state->cells[i][j + 1];
|
||||
}
|
||||
|
||||
if (i < state->num_rows - 1 && j > 0) {
|
||||
n[5] = &state->cells[i + 1][j - 1];
|
||||
}
|
||||
|
||||
if (i < state->num_rows - 1) {
|
||||
n[6] = &state->cells[i + 1][j];
|
||||
}
|
||||
|
||||
if (i < state->num_rows - 1 && j < state->num_columns - 1) {
|
||||
n[7] = &state->cells[i + 1][j + 1];
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
if (n[i] == NULL) {
|
||||
return 0; // If we're at a boundary kill cell
|
||||
}
|
||||
|
||||
if (n[i]->alive) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static void life_restart(GameData *game, LifeState *state)
|
||||
{
|
||||
for (int i = 0; i < state->num_rows; ++i) {
|
||||
for (int j = 0; j < state->num_columns; ++j) {
|
||||
Cell *cell = &state->cells[i][j];
|
||||
cell->alive = false;
|
||||
cell->marked = false;
|
||||
cell->display_char = LIFE_DEFAULT_CELL_CHAR;
|
||||
cell->age = 0;
|
||||
}
|
||||
}
|
||||
|
||||
game_set_score(game, 0);
|
||||
|
||||
state->generation = 0;
|
||||
}
|
||||
|
||||
static void life_do_cells(LifeState *state)
|
||||
{
|
||||
|
||||
for (int i = 0; i < state->num_rows; ++i) {
|
||||
for (int j = 0; j < state->num_columns; ++j) {
|
||||
Cell *cell = &state->cells[i][j];
|
||||
|
||||
if (cell->marked) {
|
||||
cell->marked = false;
|
||||
cell->alive ^= 1;
|
||||
cell->age = cell->alive;
|
||||
cell->display_char = life_get_display_char(state, cell);
|
||||
} else if (cell->alive) {
|
||||
++cell->age;
|
||||
cell->display_char = life_get_display_char(state, cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void life_cycle(GameData *game, LifeState *state)
|
||||
{
|
||||
if (state->generation == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TIME_MS cur_time = get_time_millis();
|
||||
|
||||
if (!game_do_object_state_update(game, cur_time, state->time_last_cycle, state->speed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->time_last_cycle = get_time_millis();
|
||||
|
||||
++state->generation;
|
||||
|
||||
size_t live_cells = 0;
|
||||
|
||||
for (int i = 0; i < state->num_rows; ++i) {
|
||||
for (int j = 0; j < state->num_columns; ++j) {
|
||||
Cell *cell = &state->cells[i][j];
|
||||
|
||||
int live_neighbours = life_get_live_neighbours(state, i, j);
|
||||
|
||||
if (cell->alive) {
|
||||
if (!(live_neighbours == 2 || live_neighbours == 3)) {
|
||||
cell->marked = true;
|
||||
} else {
|
||||
++live_cells;
|
||||
}
|
||||
} else {
|
||||
if (live_neighbours == 3) {
|
||||
cell->marked = true;
|
||||
++live_cells;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (live_cells == 0) {
|
||||
life_restart(game, state);
|
||||
return;
|
||||
}
|
||||
|
||||
life_do_cells(state);
|
||||
|
||||
game_update_score(game, 1);
|
||||
}
|
||||
|
||||
static void life_start(GameData *game, LifeState *state)
|
||||
{
|
||||
state->generation = 1;
|
||||
}
|
||||
|
||||
void life_cb_update_game_state(GameData *game, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LifeState *state = (LifeState *)cb_data;
|
||||
|
||||
life_cycle(game, state);
|
||||
}
|
||||
|
||||
void life_cb_render_window(GameData *game, WINDOW *win, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LifeState *state = (LifeState *)cb_data;
|
||||
|
||||
move(state->curs_y, state->curs_x);
|
||||
|
||||
if (state->generation == 0 || state->paused) {
|
||||
curs_set(1);
|
||||
}
|
||||
|
||||
life_draw_cells(game, win, state);
|
||||
}
|
||||
|
||||
static void life_move_curs_left(LifeState *state)
|
||||
{
|
||||
int new_x = state->curs_x - 1;
|
||||
|
||||
if (new_x < state->x_left_bound) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->curs_x = new_x;
|
||||
}
|
||||
|
||||
static void life_move_curs_right(LifeState *state)
|
||||
{
|
||||
int new_x = state->curs_x + 1;
|
||||
|
||||
if (new_x > state->x_right_bound) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->curs_x = new_x;
|
||||
}
|
||||
|
||||
static void life_move_curs_up(LifeState *state)
|
||||
{
|
||||
int new_y = state->curs_y - 1;
|
||||
|
||||
if (new_y < state->y_top_bound) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->curs_y = new_y;
|
||||
}
|
||||
|
||||
static void life_move_curs_down(LifeState *state)
|
||||
{
|
||||
int new_y = state->curs_y + 1;
|
||||
|
||||
if (new_y >= state->y_bottom_bound) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->curs_y = new_y;
|
||||
}
|
||||
|
||||
static void life_move_curs_up_left(LifeState *state)
|
||||
{
|
||||
life_move_curs_up(state);
|
||||
life_move_curs_left(state);
|
||||
}
|
||||
|
||||
static void life_move_curs_up_right(LifeState *state)
|
||||
{
|
||||
life_move_curs_up(state);
|
||||
life_move_curs_right(state);
|
||||
}
|
||||
|
||||
static void life_move_curs_down_right(LifeState *state)
|
||||
{
|
||||
life_move_curs_down(state);
|
||||
life_move_curs_right(state);
|
||||
}
|
||||
|
||||
static void life_move_curs_down_left(LifeState *state)
|
||||
{
|
||||
life_move_curs_down(state);
|
||||
life_move_curs_left(state);
|
||||
}
|
||||
|
||||
void life_cb_on_keypress(GameData *game, int key, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LifeState *state = (LifeState *)cb_data;
|
||||
|
||||
switch (key) {
|
||||
case KEY_LEFT: {
|
||||
life_move_curs_left(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_RIGHT: {
|
||||
life_move_curs_right(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_DOWN: {
|
||||
life_move_curs_down(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_UP: {
|
||||
life_move_curs_up(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_HOME: {
|
||||
life_move_curs_up_left(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_END: {
|
||||
life_move_curs_down_left(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_PPAGE: {
|
||||
life_move_curs_up_right(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case KEY_NPAGE: {
|
||||
life_move_curs_down_right(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\r': {
|
||||
if (state->generation > 0) {
|
||||
life_restart(game, state);
|
||||
} else {
|
||||
life_start(game, state);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ' ': {
|
||||
life_toggle_cell(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case '=':
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case '+': {
|
||||
life_increase_speed(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case '-':
|
||||
|
||||
/* intentional fallthrough */
|
||||
|
||||
case '_': {
|
||||
life_decrease_speed(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case '\t': {
|
||||
life_toggle_display_candy(state);
|
||||
break;
|
||||
}
|
||||
|
||||
case '`': {
|
||||
life_cycle_colour(state);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void life_free_cells(LifeState *state)
|
||||
{
|
||||
if (state->cells == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < state->num_rows; ++i) {
|
||||
if (state->cells[i]) {
|
||||
free(state->cells[i]);
|
||||
}
|
||||
}
|
||||
|
||||
free(state->cells);
|
||||
}
|
||||
|
||||
void life_cb_pause(GameData *game, bool is_paused, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LifeState *state = (LifeState *)cb_data;
|
||||
|
||||
state->paused = is_paused;
|
||||
}
|
||||
|
||||
void life_cb_kill(GameData *game, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
LifeState *state = (LifeState *)cb_data;
|
||||
|
||||
life_free_cells(state);
|
||||
free(state);
|
||||
|
||||
game_set_cb_update_state(game, NULL, NULL);
|
||||
game_set_cb_render_window(game, NULL, NULL);
|
||||
game_set_cb_kill(game, NULL, NULL);
|
||||
game_set_cb_on_keypress(game, NULL, NULL);
|
||||
}
|
||||
|
||||
static int life_init_state(GameData *game, LifeState *state)
|
||||
{
|
||||
const int x_left = game_x_left_bound(game) ;
|
||||
const int x_right = game_x_right_bound(game);
|
||||
const int y_top = game_y_top_bound(game);
|
||||
const int y_bottom = game_y_bottom_bound(game) + 1;
|
||||
|
||||
state->x_left_bound = x_left;
|
||||
state->x_right_bound = x_right;
|
||||
state->y_top_bound = y_top;
|
||||
state->y_bottom_bound = y_bottom;
|
||||
|
||||
const int x_mid = x_left + ((x_right - x_left) / 2);
|
||||
const int y_mid = y_top + ((y_bottom - y_top) / 2);
|
||||
|
||||
state->curs_x = x_mid;
|
||||
state->curs_y = y_mid;
|
||||
|
||||
const int num_rows = (y_bottom - y_top) + LIFE_BOUNDARY_BUFFER;
|
||||
const int num_columns = (x_right - x_left) + LIFE_BOUNDARY_BUFFER;
|
||||
|
||||
if (num_rows <= 0 || num_columns <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->num_columns = num_columns;
|
||||
state->num_rows = num_rows;
|
||||
|
||||
state->cells = calloc(1, num_rows * sizeof(Cell *));
|
||||
|
||||
if (state->cells == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_rows; ++i) {
|
||||
state->cells[i] = calloc(1, num_columns * sizeof(Cell));
|
||||
|
||||
if (state->cells[i] == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int j = 0; j < num_columns; ++j) {
|
||||
state->cells[i][j].coords.y = i + (state->y_top_bound - (LIFE_BOUNDARY_BUFFER / 2));
|
||||
state->cells[i][j].coords.x = j + (state->x_left_bound - (LIFE_BOUNDARY_BUFFER / 2));
|
||||
}
|
||||
}
|
||||
|
||||
state->speed = LIFE_DEFAULT_SPEED;
|
||||
state->colour = LIFE_CELL_DEFAULT_COLOUR;
|
||||
|
||||
life_restart(game, state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int life_initialize(GameData *game)
|
||||
{
|
||||
// Try best fit from largest to smallest before giving up
|
||||
if (game_set_window_shape(game, GW_ShapeRectangleLarge) == -1) {
|
||||
if (game_set_window_shape(game, GW_ShapeSquareLarge) == -1) {
|
||||
if (game_set_window_shape(game, GW_ShapeRectangle) == -1) {
|
||||
if (game_set_window_shape(game, GW_ShapeSquare) == -1) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LifeState *state = calloc(1, sizeof(LifeState));
|
||||
|
||||
if (state == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (life_init_state(game, state) == -1) {
|
||||
life_free_cells(state);
|
||||
free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
game_set_update_interval(game, 40);
|
||||
game_show_score(game, true);
|
||||
|
||||
game_set_cb_update_state(game, life_cb_update_game_state, state);
|
||||
game_set_cb_render_window(game, life_cb_render_window, state);
|
||||
game_set_cb_on_keypress(game, life_cb_on_keypress, state);
|
||||
game_set_cb_on_pause(game, life_cb_pause, state);
|
||||
game_set_cb_kill(game, life_cb_kill, state);
|
||||
|
||||
return 0;
|
||||
}
|
31
src/game_life.h
Normal file
31
src/game_life.h
Normal file
@ -0,0 +1,31 @@
|
||||
/* game_life.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2021 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GAME_LIFE
|
||||
#define GAME_LIFE
|
||||
|
||||
#include "game_base.h"
|
||||
|
||||
int life_initialize(GameData *game);
|
||||
|
||||
#endif // GAME_LIFE
|
||||
|
906
src/game_snake.c
Normal file
906
src/game_snake.c
Normal file
@ -0,0 +1,906 @@
|
||||
/* game_snake.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "game_base.h"
|
||||
#include "game_util.h"
|
||||
#include "game_snake.h"
|
||||
#include "misc_tools.h"
|
||||
|
||||
#define SNAKE_MAX_SNAKE_LENGTH (GAME_MAX_SQUARE_X_DEFAULT * GAME_MAX_SQUARE_Y_DEFAULT)
|
||||
#define SNAKE_AGENT_MAX_LIST_SIZE (GAME_MAX_SQUARE_X_DEFAULT * GAME_MAX_SQUARE_Y_DEFAULT)
|
||||
|
||||
#define SNAKE_DEFAULT_SNAKE_SPEED 6
|
||||
#define SNAKE_DEFAULT_AGENT_SPEED 1
|
||||
#define SNAKE_MAX_SNAKE_SPEED 12
|
||||
#define SNAKE_MAX_AGENT_SPEED (SNAKE_MAX_SNAKE_SPEED / 2)
|
||||
|
||||
#define SNAKE_DEFAULT_UPDATE_INTERVAL 20
|
||||
|
||||
/* How often we update frames independent of the speed of the game or objects */
|
||||
#define SNAKE_FRAME_DRAW_SPEED 5
|
||||
|
||||
/* How long a regular message stays on the screen */
|
||||
#define SNAKE_DEFAULT_MESSAGE_TIMER 5
|
||||
|
||||
/* Increment snake speed by 1 every time level increases by this amount */
|
||||
#define SNAKE_LEVEL_SPEED_INTERVAL 5
|
||||
|
||||
/* Increment level by 1 every time snake eats this many foods */
|
||||
#define SNAKE_LEVEL_UP_FOOD_LIMIT 4
|
||||
|
||||
/* Increment agent speed by 1 every time level increases by this amount */
|
||||
#define SNAKE_AGENT_LEVEL_SPEED_INTERVAL 2
|
||||
|
||||
/* Points multiplier for getting a powerup */
|
||||
#define SNAKE_POWERUP_BONUS_MULTIPLIER 5
|
||||
|
||||
/* Extra bonus for running over a glowing agent */
|
||||
#define SNAKE_AGENT_GLOWING_MULTIPLIER 2
|
||||
|
||||
/* Agents begin glowing if their speed is greater than this */
|
||||
#define SNAKE_AGENT_GLOWING_SPEED (SNAKE_DEFAULT_AGENT_SPEED + 2)
|
||||
|
||||
/* A new powerup is placed on the board after this many seconds since last one wore off */
|
||||
#define SNAKE_POWERUP_INTERVAL 45
|
||||
|
||||
/* How long a powerup lasts */
|
||||
#define SNAKE_POWERUP_TIMER 12
|
||||
|
||||
/* Number of key presses to queue; one key press is retrieved per state update */
|
||||
#define SNAKE_KEY_PRESS_QUEUE_SIZE 3
|
||||
|
||||
/* These decide how many points to decay and how often to penalize camping for powerups */
|
||||
#define SNAKE_DECAY_POINTS_INTERVAL 1
|
||||
#define SNAKE_DECAY_POINTS_FRACTION 10
|
||||
|
||||
#define SNAKE_HEAD_COLOUR GREEN
|
||||
|
||||
#define SNAKE_DEAD_BODY_CHAR 'o'
|
||||
#define SNAKE_DEAD_BODY_COLOUR RED
|
||||
|
||||
#define SNAKE_BODY_COLOUR CYAN
|
||||
#define SNAKE_BODY_CHAR 'o'
|
||||
|
||||
#define SNAKE_FOOD_COLOUR YELLOW
|
||||
#define SNAKE_FOOD_CHAR '*'
|
||||
|
||||
#define SNAKE_AGENT_NORMAL_COLOUR RED
|
||||
#define SNAKE_AGENT_GLOWING_COLOUR GREEN
|
||||
#define SNAKE_AGENT_NORMAL_CHAR 'x'
|
||||
#define SNAKE_AGENT_GLOWING_CHAR 'X'
|
||||
|
||||
#define SNAKE_POWERUP_CHAR 'P'
|
||||
|
||||
typedef struct NasaAgent {
|
||||
Coords coords;
|
||||
bool is_alive;
|
||||
bool is_glowing;
|
||||
TIME_MS last_time_moved;
|
||||
size_t speed;
|
||||
char display_char;
|
||||
int colour;
|
||||
int attributes;
|
||||
} NasaAgent;
|
||||
|
||||
typedef struct Snake {
|
||||
Coords coords;
|
||||
char display_char;
|
||||
int colour;
|
||||
int attributes;
|
||||
} Snake;
|
||||
|
||||
typedef struct SnakeState {
|
||||
Snake *snake;
|
||||
size_t snake_length;
|
||||
size_t snake_speed;
|
||||
TIME_MS snake_time_last_moved;
|
||||
bool has_powerup;
|
||||
Direction direction;
|
||||
|
||||
Coords powerup;
|
||||
TIME_S powerup_timer;
|
||||
TIME_S last_powerup_time;
|
||||
|
||||
Coords food;
|
||||
|
||||
NasaAgent *agents;
|
||||
size_t agent_list_size;
|
||||
|
||||
TIME_S last_time_points_decayed;
|
||||
TIME_S pause_time;
|
||||
|
||||
int key_press_queue[SNAKE_KEY_PRESS_QUEUE_SIZE];
|
||||
size_t keys_skip_counter;
|
||||
|
||||
TIME_MS last_draw_update;
|
||||
bool game_over;
|
||||
} SnakeState;
|
||||
|
||||
|
||||
static void snake_create_points_message(GameData *game, Direction dir, long int points, const Coords *coords)
|
||||
{
|
||||
char buf[GAME_MAX_MESSAGE_SIZE + 1];
|
||||
snprintf(buf, sizeof(buf), "%ld", points);
|
||||
|
||||
if (game_set_message(game, buf, strlen(buf), dir, A_BOLD, WHITE, 0, coords, false, false) == -1) {
|
||||
fprintf(stderr, "failed to set points message\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_create_message(GameData *game, Direction dir, const char *message, int attributes,
|
||||
int colour, TIME_S timeout, const Coords *coords, bool priority)
|
||||
{
|
||||
if (game_set_message(game, message, strlen(message), dir, attributes, colour, timeout, coords, false, priority) == -1) {
|
||||
fprintf(stderr, "failed to set message\n");
|
||||
}
|
||||
}
|
||||
|
||||
static Coords *snake_get_head_coords(const SnakeState *state)
|
||||
{
|
||||
return &state->snake[0].coords;
|
||||
}
|
||||
|
||||
static void snake_set_head_char(SnakeState *state)
|
||||
{
|
||||
Snake *snake_head = &state->snake[0];
|
||||
|
||||
switch (state->direction) {
|
||||
case NORTH:
|
||||
snake_head->display_char = '^';
|
||||
break;
|
||||
|
||||
case SOUTH:
|
||||
snake_head->display_char = 'v';
|
||||
break;
|
||||
|
||||
case EAST:
|
||||
snake_head->display_char = '>';
|
||||
return;
|
||||
|
||||
case WEST:
|
||||
snake_head->display_char = '<';
|
||||
break;
|
||||
|
||||
default:
|
||||
snake_head->display_char = '?';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool snake_validate_direction(const SnakeState *state, Direction dir)
|
||||
{
|
||||
if (!GAME_UTIL_DIRECTION_VALID(dir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int diff = abs((int)state->direction - (int)dir);
|
||||
|
||||
return diff != 1;
|
||||
}
|
||||
|
||||
static void snake_update_direction(SnakeState *state)
|
||||
{
|
||||
for (size_t i = 0; i < SNAKE_KEY_PRESS_QUEUE_SIZE; ++i) {
|
||||
int key = state->key_press_queue[i];
|
||||
|
||||
if (key == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Direction dir = game_util_get_direction(key);
|
||||
|
||||
if (!GAME_UTIL_DIRECTION_VALID(dir)) {
|
||||
state->key_press_queue[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (snake_validate_direction(state, dir)) {
|
||||
state->direction = dir;
|
||||
snake_set_head_char(state);
|
||||
state->key_press_queue[i] = 0;
|
||||
state->keys_skip_counter = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state->keys_skip_counter++ >= SNAKE_KEY_PRESS_QUEUE_SIZE) {
|
||||
state->keys_skip_counter = 0;
|
||||
memset(state->key_press_queue, 0, sizeof(state->key_press_queue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_set_key_press(SnakeState *state, int key)
|
||||
{
|
||||
for (size_t i = 0; i < SNAKE_KEY_PRESS_QUEUE_SIZE; ++i) {
|
||||
if (state->key_press_queue[i] == 0) {
|
||||
state->key_press_queue[i] = key;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(state->key_press_queue, 0, sizeof(state->key_press_queue));
|
||||
state->key_press_queue[0] = key;
|
||||
}
|
||||
|
||||
static void snake_update_score(GameData *game, const SnakeState *state, long int points)
|
||||
{
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
snake_create_points_message(game, state->direction, points, head);
|
||||
|
||||
game_update_score(game, points);
|
||||
}
|
||||
|
||||
static long int snake_get_move_points(const SnakeState *state)
|
||||
{
|
||||
return state->snake_length + (2 * state->snake_speed);
|
||||
}
|
||||
|
||||
/* Return true if snake body is occupying given coordinates */
|
||||
static bool snake_coords_contain_body(const SnakeState *state, const Coords *coords)
|
||||
{
|
||||
for (size_t i = 1; i < state->snake_length; ++i) {
|
||||
Coords snake_coords = state->snake[i].coords;
|
||||
|
||||
if (COORDINATES_OVERLAP(coords->x, coords->y, snake_coords.x, snake_coords.y)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool snake_self_consume(const SnakeState *state)
|
||||
{
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
return snake_coords_contain_body(state, head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a pointer to the agent at x and y coordinates.
|
||||
*
|
||||
* Returns NULL if no living agent is at coords.
|
||||
*/
|
||||
static NasaAgent *snake_get_agent_at_coords(const SnakeState *state, const Coords *coords)
|
||||
{
|
||||
for (size_t i = 0; i < state->agent_list_size; ++i) {
|
||||
NasaAgent *agent = &state->agents[i];
|
||||
|
||||
if (!agent->is_alive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (COORDINATES_OVERLAP(coords->x, coords->y, agent->coords.x, agent->coords.y)) {
|
||||
return agent;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if snake got caught by an agent and doesn't have a powerup.
|
||||
*
|
||||
* If snake runs over agent and does have a powerup the agent is killed and score updated.
|
||||
*/
|
||||
static bool snake_agent_caught(GameData *game, SnakeState *state, const Coords *coords)
|
||||
{
|
||||
NasaAgent *agent = snake_get_agent_at_coords(state, coords);
|
||||
|
||||
if (agent == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state->has_powerup) {
|
||||
return true;
|
||||
}
|
||||
|
||||
agent->is_alive = false;
|
||||
|
||||
long int points = snake_get_move_points(state) * (agent->speed + 1);
|
||||
|
||||
if (agent->is_glowing) {
|
||||
points *= SNAKE_AGENT_GLOWING_MULTIPLIER;
|
||||
}
|
||||
|
||||
snake_update_score(game, state, points);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool snake_state_valid(GameData *game, SnakeState *state)
|
||||
{
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
if (!game_coordinates_in_bounds(game, head->x, head->y)) {
|
||||
snake_create_message(game, state->direction, "Ouch!", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head, true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (snake_self_consume(state)) {
|
||||
snake_create_message(game, state->direction, "Tastes like chicken", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head,
|
||||
true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (snake_agent_caught(game, state, head)) {
|
||||
snake_create_message(game, state->direction, "ARGH they got me!", A_BOLD, WHITE, SNAKE_DEFAULT_MESSAGE_TIMER, head,
|
||||
true);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets colour and attributes for entire snake except head.
|
||||
*
|
||||
* If colour is set to -1 a random colour is chosen for each body part.
|
||||
*/
|
||||
static void snake_set_body_attributes(Snake *snake, size_t length, int colour, int attributes)
|
||||
{
|
||||
for (size_t i = 1; i < length; ++i) {
|
||||
Snake *body = &snake[i];
|
||||
|
||||
if (colour == -1) {
|
||||
body->colour = game_util_random_colour();
|
||||
} else {
|
||||
body->colour = colour;
|
||||
}
|
||||
|
||||
body->attributes = attributes;
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_move_body(SnakeState *state)
|
||||
{
|
||||
for (size_t i = state->snake_length - 1; i > 0; --i) {
|
||||
Coords *curr = &state->snake[i].coords;
|
||||
Coords prev = state->snake[i - 1].coords;
|
||||
curr->x = prev.x;
|
||||
curr->y = prev.y;
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_move_head(SnakeState *state)
|
||||
{
|
||||
Coords *head = snake_get_head_coords(state);
|
||||
game_util_move_coords(state->direction, head);
|
||||
}
|
||||
|
||||
static void snake_grow(SnakeState *state)
|
||||
{
|
||||
size_t index = state->snake_length;
|
||||
|
||||
if (index >= SNAKE_MAX_SNAKE_LENGTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->snake[index].coords.x = -1;
|
||||
state->snake[index].coords.y = -1;
|
||||
state->snake[index].display_char = SNAKE_BODY_CHAR;
|
||||
state->snake[index].colour = SNAKE_BODY_COLOUR;
|
||||
state->snake[index].attributes = A_BOLD;
|
||||
|
||||
state->snake_length = index + 1;
|
||||
}
|
||||
|
||||
static long int snake_check_food(const GameData *game, SnakeState *state)
|
||||
{
|
||||
Coords *food = &state->food;
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
if (!COORDINATES_OVERLAP(head->x, head->y, food->x, food->y)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
snake_grow(state);
|
||||
|
||||
game_random_coords(game, food);
|
||||
|
||||
return snake_get_move_points(state);
|
||||
}
|
||||
|
||||
static long int snake_check_powerup(GameData *game, SnakeState *state)
|
||||
{
|
||||
Coords *powerup = &state->powerup;
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
if (!COORDINATES_OVERLAP(head->x, head->y, powerup->x, powerup->y)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
snake_create_message(game, state->direction, "AAAAA", A_BOLD, RED, 2, head, false);
|
||||
|
||||
TIME_S t = get_unix_time();
|
||||
|
||||
state->has_powerup = true;
|
||||
state->powerup_timer = t;
|
||||
|
||||
powerup->x = -1;
|
||||
powerup->y = -1;
|
||||
|
||||
return snake_get_move_points(state) * SNAKE_POWERUP_BONUS_MULTIPLIER;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the first unoccupied index in agent array.
|
||||
*/
|
||||
static size_t snake_get_empty_agent_index(const NasaAgent *agents)
|
||||
{
|
||||
for (size_t i = 0; i < SNAKE_AGENT_MAX_LIST_SIZE; ++i) {
|
||||
if (!agents[i].is_alive) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Warning: Agent array is full. This should be impossible\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snake_initialize_agent(SnakeState *state, const Coords *coords)
|
||||
{
|
||||
size_t idx = snake_get_empty_agent_index(state->agents);
|
||||
|
||||
if ((idx >= state->agent_list_size) && (idx + 1 <= SNAKE_AGENT_MAX_LIST_SIZE)) {
|
||||
state->agent_list_size = idx + 1;
|
||||
}
|
||||
|
||||
NasaAgent *agent = &state->agents[idx];
|
||||
|
||||
agent->coords = (Coords) {
|
||||
coords->x,
|
||||
coords->y
|
||||
};
|
||||
|
||||
agent->is_alive = true;
|
||||
agent->is_glowing = false;
|
||||
agent->display_char = SNAKE_AGENT_NORMAL_CHAR;
|
||||
agent->colour = SNAKE_AGENT_NORMAL_COLOUR;
|
||||
agent->attributes = A_BOLD;
|
||||
agent->last_time_moved = 0;
|
||||
agent->speed = SNAKE_DEFAULT_AGENT_SPEED;
|
||||
}
|
||||
|
||||
static void snake_dispatch_new_agent(const GameData *game, SnakeState *state)
|
||||
{
|
||||
Coords new_coords;
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
size_t tries = 0;
|
||||
|
||||
do {
|
||||
if (tries++ >= 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
game_random_coords(game, &new_coords);
|
||||
} while (COORDINATES_OVERLAP(new_coords.x, new_coords.y, head->x, head->y)
|
||||
|| snake_get_agent_at_coords(state, &new_coords) != NULL);
|
||||
|
||||
snake_initialize_agent(state, &new_coords);
|
||||
}
|
||||
|
||||
static void snake_place_powerup(const GameData *game, SnakeState *state)
|
||||
{
|
||||
Coords *powerup = &state->powerup;
|
||||
|
||||
if (powerup->x != -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!timed_out(state->last_powerup_time, SNAKE_POWERUP_INTERVAL)) {
|
||||
return;
|
||||
}
|
||||
|
||||
game_random_coords(game, powerup);
|
||||
}
|
||||
|
||||
static void snake_do_powerup(const GameData *game, SnakeState *state)
|
||||
{
|
||||
if (!state->has_powerup) {
|
||||
snake_place_powerup(game, state);
|
||||
return;
|
||||
}
|
||||
|
||||
if (timed_out(state->powerup_timer, SNAKE_POWERUP_TIMER)) {
|
||||
state->last_powerup_time = get_unix_time();
|
||||
state->has_powerup = false;
|
||||
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_BODY_COLOUR, A_BOLD);
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_decay_points(GameData *game, SnakeState *state)
|
||||
{
|
||||
|
||||
long int score = game_get_score(game);
|
||||
long int decay = snake_get_move_points(state) / SNAKE_DECAY_POINTS_FRACTION;
|
||||
|
||||
if (score > decay && timed_out(state->last_time_points_decayed, SNAKE_DECAY_POINTS_INTERVAL)) {
|
||||
game_update_score(game, -decay);
|
||||
state->last_time_points_decayed = get_unix_time();
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_do_points_update(GameData *game, SnakeState *state, long int points)
|
||||
{
|
||||
snake_update_score(game, state, points);
|
||||
|
||||
if (state->snake_length % SNAKE_LEVEL_UP_FOOD_LIMIT != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
game_increment_level(game);
|
||||
|
||||
size_t level = game_get_current_level(game);
|
||||
|
||||
if (level % SNAKE_LEVEL_SPEED_INTERVAL == 0 && state->snake_speed < SNAKE_MAX_SNAKE_SPEED) {
|
||||
++state->snake_speed;
|
||||
}
|
||||
|
||||
if (level % SNAKE_AGENT_LEVEL_SPEED_INTERVAL == 0) {
|
||||
for (size_t i = 0; i < state->agent_list_size; ++i) {
|
||||
NasaAgent *agent = &state->agents[i];
|
||||
|
||||
if (!agent->is_alive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (agent->speed < SNAKE_MAX_AGENT_SPEED) {
|
||||
++agent->speed;
|
||||
}
|
||||
|
||||
if (agent->speed > SNAKE_AGENT_GLOWING_SPEED && !agent->is_glowing) {
|
||||
agent->is_glowing = true;
|
||||
agent->display_char = SNAKE_AGENT_GLOWING_CHAR;
|
||||
agent->colour = SNAKE_AGENT_GLOWING_COLOUR;
|
||||
snake_create_message(game, state->direction, "*glows*", A_BOLD, GREEN, 2, &agent->coords, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snake_dispatch_new_agent(game, state);
|
||||
}
|
||||
|
||||
static void snake_game_over(SnakeState *state)
|
||||
{
|
||||
state->game_over = true;
|
||||
state->has_powerup = false;
|
||||
state->snake[0].colour = SNAKE_DEAD_BODY_COLOUR;
|
||||
state->snake[0].attributes = A_BOLD | A_BLINK;
|
||||
|
||||
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_DEAD_BODY_COLOUR, A_BOLD | A_BLINK);
|
||||
}
|
||||
|
||||
static void snake_move(GameData *game, SnakeState *state, TIME_MS cur_time)
|
||||
{
|
||||
const TIME_MS real_speed = GAME_UTIL_REAL_SPEED(state->direction, state->snake_speed);
|
||||
|
||||
if (!game_do_object_state_update(game, cur_time, state->snake_time_last_moved, real_speed)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->snake_time_last_moved = cur_time;
|
||||
|
||||
snake_update_direction(state);
|
||||
snake_move_body(state);
|
||||
snake_move_head(state);
|
||||
|
||||
if (!snake_state_valid(game, state)) {
|
||||
snake_game_over(state);
|
||||
game_set_status(game, GS_Finished);
|
||||
return;
|
||||
}
|
||||
|
||||
long int points = snake_check_food(game, state) + snake_check_powerup(game, state);
|
||||
|
||||
if (points > 0) {
|
||||
snake_do_points_update(game, state, points);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempts to move every agent in list.
|
||||
*
|
||||
* If an agent is normal it will move in a random direction. If it's glowing it will
|
||||
* move towards the snake.
|
||||
*/
|
||||
static void snake_agent_move(GameData *game, SnakeState *state, TIME_MS cur_time)
|
||||
{
|
||||
const Coords *head = snake_get_head_coords(state);
|
||||
|
||||
for (size_t i = 0; i < state->agent_list_size; ++i) {
|
||||
NasaAgent *agent = &state->agents[i];
|
||||
|
||||
if (!agent->is_alive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Coords *coords = &agent->coords;
|
||||
|
||||
Coords new_coords = (Coords) {
|
||||
coords->x,
|
||||
coords->y
|
||||
};
|
||||
|
||||
Direction dir = !agent->is_glowing ? game_util_random_direction()
|
||||
: game_util_move_towards(coords, head, state->has_powerup);
|
||||
|
||||
const TIME_MS real_speed = agent->is_glowing ? GAME_UTIL_REAL_SPEED(dir, agent->speed) : agent->speed;
|
||||
|
||||
if (!game_do_object_state_update(game, cur_time, agent->last_time_moved, real_speed)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
agent->last_time_moved = cur_time;
|
||||
|
||||
game_util_move_coords(dir, &new_coords);
|
||||
|
||||
if (!game_coordinates_in_bounds(game, new_coords.x, new_coords.y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (snake_coords_contain_body(state, &new_coords)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (snake_get_agent_at_coords(state, &new_coords) != NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
coords->x = new_coords.x;
|
||||
coords->y = new_coords.y;
|
||||
|
||||
if (!state->has_powerup && COORDINATES_OVERLAP(head->x, head->y, new_coords.x, new_coords.y)) {
|
||||
snake_game_over(state);
|
||||
game_set_status(game, GS_Finished);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_update_frames(const GameData *game, SnakeState *state, TIME_MS cur_time)
|
||||
{
|
||||
if (!game_do_object_state_update(game, cur_time, state->last_draw_update, SNAKE_FRAME_DRAW_SPEED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
state->last_draw_update = cur_time;
|
||||
|
||||
if (state->has_powerup) {
|
||||
const int time_left = SNAKE_POWERUP_TIMER - (get_unix_time() - state->powerup_timer);
|
||||
|
||||
if (time_left <= 5 && time_left % 2 == 0) {
|
||||
snake_set_body_attributes(state->snake, state->snake_length, SNAKE_BODY_COLOUR, A_BOLD);
|
||||
} else {
|
||||
snake_set_body_attributes(state->snake, state->snake_length, -1, A_BOLD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_draw_self(WINDOW *win, const SnakeState *state)
|
||||
{
|
||||
for (size_t i = 0; i < state->snake_length; ++i) {
|
||||
const Snake *body = &state->snake[i];
|
||||
|
||||
if (body->coords.x <= 0 || body->coords.y <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wattron(win, body->attributes | COLOR_PAIR(body->colour));
|
||||
mvwaddch(win, body->coords.y, body->coords.x, body->display_char);
|
||||
wattroff(win, body->attributes | COLOR_PAIR(body->colour));
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_draw_food(WINDOW *win, const SnakeState *state)
|
||||
{
|
||||
wattron(win, A_BOLD | COLOR_PAIR(SNAKE_FOOD_COLOUR));
|
||||
mvwaddch(win, state->food.y, state->food.x, SNAKE_FOOD_CHAR);
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(SNAKE_FOOD_COLOUR));
|
||||
}
|
||||
|
||||
static void snake_draw_agent(WINDOW *win, const SnakeState *state)
|
||||
{
|
||||
for (size_t i = 0; i < state->agent_list_size; ++i) {
|
||||
NasaAgent agent = state->agents[i];
|
||||
|
||||
if (agent.is_alive) {
|
||||
Coords coords = agent.coords;
|
||||
|
||||
wattron(win, agent.attributes | COLOR_PAIR(agent.colour));
|
||||
mvwaddch(win, coords.y, coords.x, agent.display_char);
|
||||
wattroff(win, agent.attributes | COLOR_PAIR(agent.colour));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_draw_powerup(WINDOW *win, const SnakeState *state)
|
||||
{
|
||||
Coords powerup = state->powerup;
|
||||
|
||||
if (powerup.x != -1) {
|
||||
int colour = game_util_random_colour();
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(colour));
|
||||
mvwaddch(win, powerup.y, powerup.x, SNAKE_POWERUP_CHAR);
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(colour));
|
||||
}
|
||||
}
|
||||
|
||||
void snake_cb_update_game_state(GameData *game, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SnakeState *state = (SnakeState *)cb_data;
|
||||
|
||||
TIME_MS cur_time = get_time_millis();
|
||||
|
||||
snake_do_powerup(game, state);
|
||||
snake_agent_move(game, state, cur_time);
|
||||
snake_move(game, state, cur_time);
|
||||
snake_decay_points(game, state);
|
||||
|
||||
if (!state->game_over) {
|
||||
snake_update_frames(game, state, cur_time);
|
||||
}
|
||||
}
|
||||
|
||||
void snake_cb_render_window(GameData *game, WINDOW *win, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SnakeState *state = (SnakeState *)cb_data;
|
||||
|
||||
snake_draw_food(win, state);
|
||||
snake_draw_powerup(win, state);
|
||||
snake_draw_agent(win, state);
|
||||
snake_draw_self(win, state);
|
||||
}
|
||||
|
||||
void snake_cb_kill(GameData *game, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SnakeState *state = (SnakeState *)cb_data;
|
||||
|
||||
free(state->snake);
|
||||
free(state->agents);
|
||||
free(state);
|
||||
|
||||
game_set_cb_update_state(game, NULL, NULL);
|
||||
game_set_cb_render_window(game, NULL, NULL);
|
||||
game_set_cb_kill(game, NULL, NULL);
|
||||
game_set_cb_on_keypress(game, NULL, NULL);
|
||||
game_set_cb_on_pause(game, NULL, NULL);
|
||||
}
|
||||
|
||||
void snake_cb_on_keypress(GameData *game, int key, void *cb_data)
|
||||
{
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SnakeState *state = (SnakeState *)cb_data;
|
||||
|
||||
snake_set_key_press(state, key);
|
||||
}
|
||||
|
||||
void snake_cb_pause(GameData *game, bool is_paused, void *cb_data)
|
||||
{
|
||||
UNUSED_VAR(game);
|
||||
|
||||
if (!cb_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
SnakeState *state = (SnakeState *)cb_data;
|
||||
|
||||
TIME_S t = get_unix_time();
|
||||
|
||||
if (is_paused) {
|
||||
state->pause_time = t;
|
||||
} else {
|
||||
state->powerup_timer += (t - state->pause_time);
|
||||
state->last_powerup_time += (t - state->pause_time);
|
||||
}
|
||||
}
|
||||
|
||||
static void snake_initialize_snake_head(const GameData *game, Snake *snake)
|
||||
{
|
||||
int max_x;
|
||||
int max_y;
|
||||
game_max_x_y(game, &max_x, &max_y);
|
||||
|
||||
snake[0].coords.x = max_x / 2;
|
||||
snake[0].coords.y = max_y / 2;
|
||||
snake[0].colour = SNAKE_HEAD_COLOUR;
|
||||
snake[0].attributes = A_BOLD;
|
||||
}
|
||||
|
||||
int snake_initialize(GameData *game)
|
||||
{
|
||||
// note: if this changes we must update SNAKE_MAX_SNAKE_LENGTH and SNAKE_AGENT_MAX_LIST_SIZE
|
||||
if (game_set_window_shape(game, GW_ShapeSquare) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
SnakeState *state = calloc(1, sizeof(SnakeState));
|
||||
|
||||
if (state == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->snake = calloc(1, SNAKE_MAX_SNAKE_LENGTH * sizeof(Snake));
|
||||
|
||||
if (state->snake == NULL) {
|
||||
free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
state->agents = calloc(1, SNAKE_AGENT_MAX_LIST_SIZE * sizeof(NasaAgent));
|
||||
|
||||
if (state->agents == NULL) {
|
||||
free(state->snake);
|
||||
free(state);
|
||||
return -1;
|
||||
}
|
||||
|
||||
snake_initialize_snake_head(game, state->snake);
|
||||
|
||||
state->snake_speed = SNAKE_DEFAULT_SNAKE_SPEED;
|
||||
state->snake_length = 1;
|
||||
state->direction = NORTH;
|
||||
snake_set_head_char(state);
|
||||
|
||||
state->powerup.x = -1;
|
||||
state->powerup.y = -1;
|
||||
|
||||
state->last_powerup_time = get_unix_time();
|
||||
|
||||
game_show_level(game, true);
|
||||
game_show_score(game, true);
|
||||
game_show_high_score(game, true);
|
||||
|
||||
game_increment_level(game);
|
||||
game_set_update_interval(game, SNAKE_DEFAULT_UPDATE_INTERVAL);
|
||||
game_random_coords(game, &state->food);
|
||||
|
||||
game_set_cb_update_state(game, snake_cb_update_game_state, state);
|
||||
game_set_cb_render_window(game, snake_cb_render_window, state);
|
||||
game_set_cb_on_keypress(game, snake_cb_on_keypress, state);
|
||||
game_set_cb_kill(game, snake_cb_kill, state);
|
||||
game_set_cb_on_pause(game, snake_cb_pause, state);
|
||||
|
||||
return 0;
|
||||
}
|
30
src/game_snake.h
Normal file
30
src/game_snake.h
Normal file
@ -0,0 +1,30 @@
|
||||
/* game_snake.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GAME_SNAKE
|
||||
#define GAME_SNAKE
|
||||
|
||||
#include "game_base.h"
|
||||
|
||||
int snake_initialize(GameData *game);
|
||||
|
||||
#endif // GAME_SNAKE
|
200
src/game_util.c
Normal file
200
src/game_util.c
Normal file
@ -0,0 +1,200 @@
|
||||
/* game_util.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "game_util.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
Direction game_util_get_direction(int key)
|
||||
{
|
||||
switch (key) {
|
||||
case KEY_UP: {
|
||||
return NORTH;
|
||||
}
|
||||
|
||||
case KEY_DOWN: {
|
||||
return SOUTH;
|
||||
}
|
||||
|
||||
case KEY_RIGHT: {
|
||||
return EAST;
|
||||
}
|
||||
|
||||
case KEY_LEFT: {
|
||||
return WEST;
|
||||
}
|
||||
|
||||
default: {
|
||||
return INVALID_DIRECTION;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Direction game_util_move_towards(const Coords *coords_a, const Coords *coords_b, bool inverse)
|
||||
{
|
||||
const int x1 = coords_a->x;
|
||||
const int y1 = coords_a->y;
|
||||
const int x2 = coords_b->x;
|
||||
const int y2 = coords_b->y;
|
||||
|
||||
const int x_diff = abs(x1 - x2);
|
||||
const int y_diff = abs(y1 - y2);
|
||||
|
||||
if (inverse) {
|
||||
if (x_diff > y_diff) {
|
||||
return x2 >= x1 ? WEST : EAST;
|
||||
} else {
|
||||
return y2 >= y1 ? NORTH : SOUTH;
|
||||
}
|
||||
} else {
|
||||
if (x_diff > y_diff) {
|
||||
return x2 < x1 ? WEST : EAST;
|
||||
} else {
|
||||
return y2 < y1 ? NORTH : SOUTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Direction game_util_random_direction(void)
|
||||
{
|
||||
int r = rand() % 4;
|
||||
|
||||
switch (r) {
|
||||
case 0:
|
||||
return NORTH;
|
||||
|
||||
case 1:
|
||||
return SOUTH;
|
||||
|
||||
case 2:
|
||||
return EAST;
|
||||
|
||||
case 3:
|
||||
return WEST;
|
||||
|
||||
default: // impossible
|
||||
return NORTH;
|
||||
}
|
||||
}
|
||||
|
||||
void game_util_move_coords(Direction direction, Coords *coords)
|
||||
{
|
||||
switch (direction) {
|
||||
case NORTH: {
|
||||
if (coords->y > 0) {
|
||||
--(coords->y);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SOUTH: {
|
||||
++(coords->y); // Will rollover if you do something stupid
|
||||
break;
|
||||
}
|
||||
|
||||
case EAST: {
|
||||
++(coords->x); // Will rollover if you do something stupid
|
||||
break;
|
||||
}
|
||||
|
||||
case WEST: {
|
||||
if (coords->x > 0) {
|
||||
--(coords->x);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
fprintf(stderr, "Warning: tried to move in an invalid direction\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int game_util_random_colour(void)
|
||||
{
|
||||
int r = rand() % 6;
|
||||
|
||||
switch (r) {
|
||||
case 0:
|
||||
return GREEN;
|
||||
|
||||
case 1:
|
||||
return CYAN;
|
||||
|
||||
case 2:
|
||||
return RED;
|
||||
|
||||
case 3:
|
||||
return BLUE;
|
||||
|
||||
case 4:
|
||||
return YELLOW;
|
||||
|
||||
case 5:
|
||||
return MAGENTA;
|
||||
|
||||
default: // impossible
|
||||
return RED;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t net_pack_u16(uint8_t *bytes, uint16_t v)
|
||||
{
|
||||
bytes[0] = (v >> 8) & 0xff;
|
||||
bytes[1] = v & 0xff;
|
||||
return sizeof(v);
|
||||
}
|
||||
|
||||
static size_t net_unpack_u16(const uint8_t *bytes, uint16_t *v)
|
||||
{
|
||||
uint8_t hi = bytes[0];
|
||||
uint8_t lo = bytes[1];
|
||||
*v = ((uint16_t)hi << 8) | lo;
|
||||
return sizeof(*v);
|
||||
}
|
||||
|
||||
size_t game_util_pack_u32(uint8_t *bytes, uint32_t v)
|
||||
{
|
||||
uint8_t *p = bytes;
|
||||
p += net_pack_u16(p, (v >> 16) & 0xffff);
|
||||
p += net_pack_u16(p, v & 0xffff);
|
||||
return p - bytes;
|
||||
}
|
||||
|
||||
size_t game_util_unpack_u32(const uint8_t *bytes, uint32_t *v)
|
||||
{
|
||||
const uint8_t *p = bytes;
|
||||
uint16_t hi;
|
||||
uint16_t lo;
|
||||
p += net_unpack_u16(p, &hi);
|
||||
p += net_unpack_u16(p, &lo);
|
||||
*v = ((uint32_t)hi << 16) | lo;
|
||||
return p - bytes;
|
||||
}
|
||||
|
103
src/game_util.h
Normal file
103
src/game_util.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* game_util.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GAME_UTIL
|
||||
#define GAME_UTIL
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct Coords {
|
||||
int x;
|
||||
int y;
|
||||
} Coords;
|
||||
|
||||
// don't change these
|
||||
typedef enum Direction {
|
||||
NORTH = 0u,
|
||||
SOUTH = 1,
|
||||
EAST = 3,
|
||||
WEST = 4,
|
||||
INVALID_DIRECTION = 5
|
||||
} Direction;
|
||||
|
||||
|
||||
/*
|
||||
* Use these for ms and second timestamps respectively so we don't accidentally interchange them
|
||||
*/
|
||||
typedef int64_t TIME_MS;
|
||||
typedef time_t TIME_S;
|
||||
|
||||
|
||||
/*
|
||||
* Return true if coordinates x1, y1 overlap with x2, y2.
|
||||
*/
|
||||
#define COORDINATES_OVERLAP(x1, y1, x2, y2)(((x1) == (x2)) && ((y1) == (y2)))
|
||||
|
||||
/*
|
||||
* Halves speed if moving north or south. This accounts for the fact that Y steps are twice as large as X steps.
|
||||
*/
|
||||
#define GAME_UTIL_REAL_SPEED(dir, speed)((((dir) == (NORTH)) || ((dir) == (SOUTH))) ? (MAX(1, ((speed) / 2))) : (speed))
|
||||
|
||||
/*
|
||||
* Return true if dir is a valid Direction.
|
||||
*/
|
||||
#define GAME_UTIL_DIRECTION_VALID(dir)((dir) < (INVALID_DIRECTION))
|
||||
|
||||
/*
|
||||
* Returns cardinal direction mapped to `key`.
|
||||
*/
|
||||
Direction game_util_get_direction(int key);
|
||||
|
||||
/*
|
||||
* Returns the direction that will move `coords_a` closest to `coords_b`.
|
||||
*
|
||||
* If `inverse` is true, returns the opposite result.
|
||||
*/
|
||||
Direction game_util_move_towards(const Coords *coords_a, const Coords *coords_b, bool inverse);
|
||||
|
||||
/*
|
||||
* Returns a random direction.
|
||||
*/
|
||||
Direction game_util_random_direction(void);
|
||||
|
||||
/*
|
||||
* Moves `coords` one square towards `direction`.
|
||||
*/
|
||||
void game_util_move_coords(Direction direction, Coords *coords);
|
||||
|
||||
/*
|
||||
* Returns a random colour.
|
||||
*/
|
||||
int game_util_random_colour(void);
|
||||
|
||||
/*
|
||||
* Packs an unsigned 32 bit integer `v` into `bytes`.
|
||||
*/
|
||||
size_t game_util_pack_u32(uint8_t *bytes, uint32_t v);
|
||||
|
||||
/*
|
||||
* Unpacks an unsigned 32 bit integer in `bytes` to `v`.
|
||||
*/
|
||||
size_t game_util_unpack_u32(const uint8_t *bytes, uint32_t *v);
|
||||
|
||||
#endif // GAME_UTIL
|
File diff suppressed because it is too large
Load Diff
@ -23,19 +23,23 @@
|
||||
#ifndef GLOBAL_COMMANDS_H
|
||||
#define GLOBAL_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
void cmd_accept(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_add(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_avatar(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_clear(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_conference(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_connect(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_decline(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_groupchat(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_join(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_log(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_myid(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#ifdef QRCODE
|
||||
void cmd_myqr(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* QRCODE */
|
||||
void cmd_nick(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_note(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_nospam(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
@ -56,4 +60,12 @@ void cmd_list_video_devices(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)
|
||||
void cmd_change_video_device(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* VIDEO */
|
||||
|
||||
#endif /* #define GLOBAL_COMMANDS_H */
|
||||
#ifdef PYTHON
|
||||
void cmd_run(WINDOW *, ToxWindow *, Tox *, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* PYTHON */
|
||||
|
||||
#ifdef GAMES
|
||||
void cmd_game(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
#endif /* GAMES */
|
||||
|
||||
#endif /* GLOBAL_COMMANDS_H */
|
||||
|
@ -1,79 +0,0 @@
|
||||
/* group_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <string.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
|
||||
void cmd_set_title(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char title[MAX_STR_SIZE];
|
||||
|
||||
if (argc < 1) {
|
||||
int tlen = tox_group_get_title(m, self->num, (uint8_t *) title, TOX_MAX_NAME_LENGTH);
|
||||
|
||||
if (tlen != -1) {
|
||||
title[tlen] = '\0';
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is set to: %s", title);
|
||||
} else {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title is not set");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (argv[1][0] != '\"') {
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Title must be enclosed in quotes.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove opening and closing quotes */
|
||||
snprintf(title, sizeof(title), "%s", &argv[1][1]);
|
||||
int len = strlen(title) - 1;
|
||||
title[len] = '\0';
|
||||
|
||||
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.");
|
||||
return;
|
||||
}
|
||||
|
||||
set_window_title(self, title, len);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
char selfnick[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
tox_self_get_name(m, (uint8_t *) selfnick);
|
||||
size_t sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
line_info_add(self, timefrmt, selfnick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
write_to_log(tmp_event, selfnick, self->chatwin->log, true);
|
||||
}
|
924
src/groupchat.c
924
src/groupchat.c
@ -1,924 +0,0 @@
|
||||
/* groupchat.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 _GNU_SOURCE
|
||||
#define _GNU_SOURCE /* needed for strcasestr() and wcswidth() */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <time.h>
|
||||
#include <wchar.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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 */
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "execute.h"
|
||||
#include "misc_tools.h"
|
||||
#include "groupchat.h"
|
||||
#include "prompt.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "log.h"
|
||||
#include "line_info.h"
|
||||
#include "settings.h"
|
||||
#include "input.h"
|
||||
#include "help.h"
|
||||
#include "notify.h"
|
||||
#include "autocomplete.h"
|
||||
#include "audio_device.h"
|
||||
|
||||
extern char *DATA_FILE;
|
||||
|
||||
static GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
||||
static int max_groupchat_index = 0;
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
extern struct Winthread Winthread;
|
||||
|
||||
#ifdef AUDIO
|
||||
#define AC_NUM_GROUP_COMMANDS 24
|
||||
#else
|
||||
#define AC_NUM_GROUP_COMMANDS 20
|
||||
#endif /* AUDIO */
|
||||
|
||||
/* Array of groupchat command names used for tab completion. */
|
||||
static const char group_cmd_list[AC_NUM_GROUP_COMMANDS][MAX_CMDNAME_SIZE] = {
|
||||
{ "/accept" },
|
||||
{ "/add" },
|
||||
{ "/avatar" },
|
||||
{ "/clear" },
|
||||
{ "/close" },
|
||||
{ "/connect" },
|
||||
{ "/decline" },
|
||||
{ "/exit" },
|
||||
{ "/group" },
|
||||
{ "/help" },
|
||||
{ "/log" },
|
||||
{ "/myid" },
|
||||
{ "/myqr" },
|
||||
{ "/nick" },
|
||||
{ "/note" },
|
||||
{ "/nospam" },
|
||||
{ "/quit" },
|
||||
{ "/requests" },
|
||||
{ "/status" },
|
||||
{ "/title" },
|
||||
|
||||
#ifdef AUDIO
|
||||
|
||||
{ "/lsdev" },
|
||||
{ "/sdev" },
|
||||
{ "/mute" },
|
||||
{ "/sense" },
|
||||
|
||||
#endif /* AUDIO */
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
if (groupnum > MAX_GROUPCHAT_NUM)
|
||||
return -1;
|
||||
|
||||
ToxWindow self = new_group_chat(m, groupnum);
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= max_groupchat_index; ++i) {
|
||||
if (!groupchats[i].active) {
|
||||
groupchats[i].chatwin = add_window(m, self);
|
||||
groupchats[i].active = true;
|
||||
groupchats[i].num_peers = 0;
|
||||
groupchats[i].type = type;
|
||||
groupchats[i].start_time = get_unix_time();
|
||||
|
||||
groupchats[i].peer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].oldpeer_names = malloc(sizeof(uint8_t) * TOX_MAX_NAME_LENGTH);
|
||||
groupchats[i].peer_name_lengths = malloc(sizeof(uint16_t));
|
||||
groupchats[i].oldpeer_name_lengths = malloc(sizeof(uint16_t));
|
||||
|
||||
if (groupchats[i].peer_names == NULL || groupchats[i].oldpeer_names == NULL
|
||||
|| groupchats[i].peer_name_lengths == NULL || groupchats[i].oldpeer_name_lengths == NULL)
|
||||
exit_toxic_err("failed in init_groupchat_win", FATALERR_MEMORY);
|
||||
|
||||
memcpy(&groupchats[i].oldpeer_names[0], UNKNOWN_NAME, sizeof(UNKNOWN_NAME));
|
||||
groupchats[i].oldpeer_name_lengths[0] = (uint16_t) strlen(UNKNOWN_NAME);
|
||||
|
||||
#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)
|
||||
++max_groupchat_index;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void kill_groupchat_window(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
log_disable(ctx->log);
|
||||
line_info_cleanup(ctx->hst);
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(ctx->sidebar);
|
||||
free(ctx->log);
|
||||
free(ctx);
|
||||
free(self->help);
|
||||
del_window(self);
|
||||
}
|
||||
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int 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);
|
||||
memset(&groupchats[groupnum], 0, sizeof(GroupChat));
|
||||
|
||||
int i;
|
||||
|
||||
for (i = max_groupchat_index; i > 0; --i) {
|
||||
if (groupchats[i - 1].active)
|
||||
break;
|
||||
}
|
||||
|
||||
max_groupchat_index = i;
|
||||
kill_groupchat_window(self);
|
||||
}
|
||||
|
||||
/* destroys and re-creates groupchat window with or without the peerlist */
|
||||
void redraw_groupchat_win(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
endwin();
|
||||
refresh();
|
||||
clear();
|
||||
|
||||
int x2, y2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
y2 -= 2;
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
return;
|
||||
|
||||
if (ctx->sidebar) {
|
||||
delwin(ctx->sidebar);
|
||||
ctx->sidebar = NULL;
|
||||
}
|
||||
|
||||
delwin(ctx->linewin);
|
||||
delwin(ctx->history);
|
||||
delwin(self->window);
|
||||
|
||||
self->window = newwin(y2, x2, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
|
||||
if (self->show_peerlist) {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
} else {
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2, 0, 0);
|
||||
}
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
|
||||
}
|
||||
|
||||
static void groupchat_onGroupMessage(ToxWindow *self, Tox *m, int groupnum, int peernum,
|
||||
const char *msg, 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 sn_len = tox_self_get_name_size(m);
|
||||
selfnick[sn_len] = '\0';
|
||||
|
||||
int nick_clr = strcmp(nick, selfnick) == 0 ? GREEN : CYAN;
|
||||
|
||||
/* Only play sound if mentioned by someone else */
|
||||
if (strcasestr(msg, selfnick) && strcmp(selfnick, nick)) {
|
||||
sound_notify(self, generic_message, NT_WNDALERT_0 | user_settings->bell_on_message, NULL);
|
||||
|
||||
if (self->active_box != -1)
|
||||
box_silent_notify2(self, NT_NOFOCUS, self->active_box, "%s %s", nick, msg);
|
||||
else
|
||||
box_silent_notify(self, NT_NOFOCUS, &self->active_box, self->name, "%s %s", nick, msg);
|
||||
|
||||
nick_clr = RED;
|
||||
}
|
||||
else {
|
||||
sound_notify(self, silent, NT_WNDALERT_1, NULL);
|
||||
}
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
line_info_add(self, timefrmt, nick, NULL, IN_MSG, 0, nick_clr, "%s", msg);
|
||||
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)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
|
||||
set_window_title(self, title, length);
|
||||
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
/* don't announce title when we join the room */
|
||||
if (!timed_out(groupchats[self->num].start_time, GROUP_EVENT_WAIT))
|
||||
return;
|
||||
|
||||
char nick[TOX_MAX_NAME_LENGTH];
|
||||
get_group_nick_truncate(m, nick, peernum, groupnum);
|
||||
line_info_add(self, timefrmt, nick, NULL, NAME_CHANGE, 0, 0, " set the group title to: %s", title);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set title to %s", title);
|
||||
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)
|
||||
{
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
uint16_t u_len = strlen(UNKNOWN_NAME);
|
||||
int i;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(groupchats[gnum].oldpeer_names, groupchats[gnum].peer_names, N * npeers);
|
||||
memcpy(groupchats[gnum].oldpeer_name_lengths, groupchats[gnum].peer_name_lengths, sizeof(uint16_t) * npeers);
|
||||
}
|
||||
|
||||
struct group_add_thrd {
|
||||
Tox *m;
|
||||
ToxWindow *self;
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
const char *event = "has joined the room";
|
||||
char timefrmt[TIME_STR_SIZE];
|
||||
get_time_str(timefrmt, sizeof(timefrmt));
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_add(self, timefrmt, (char *) peername, NULL, CONNECTION, 0, GREEN, event);
|
||||
write_to_log(event, (char *) peername, self->chatwin->log, true);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
pthread_attr_destroy(&thrd->attr);
|
||||
free(thrd);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void groupchat_onGroupNamelistChange(ToxWindow *self, Tox *m, int groupnum, int peernum, uint8_t change)
|
||||
{
|
||||
if (self->num != groupnum)
|
||||
return;
|
||||
|
||||
if (groupnum > max_groupchat_index)
|
||||
return;
|
||||
|
||||
groupchats[groupnum].num_peers = tox_group_number_peers(m, groupnum);
|
||||
int num_peers = groupchats[groupnum].num_peers;
|
||||
|
||||
if (peernum > num_peers)
|
||||
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];
|
||||
|
||||
if (tox_group_get_names(m, groupnum, tmp_peerlist, tmp_peerlens, num_peers) == -1) {
|
||||
memset(tmp_peerlist, 0, sizeof(tmp_peerlist));
|
||||
memset(tmp_peerlens, 0, sizeof(tmp_peerlens));
|
||||
}
|
||||
|
||||
copy_peernames(groupnum, tmp_peerlist, tmp_peerlens, num_peers);
|
||||
|
||||
/* get current peername then sort namelist */
|
||||
uint8_t peername[TOX_MAX_NAME_LENGTH];
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
||||
sound_notify(self, silent, NT_WNDALERT_2, NULL);
|
||||
}
|
||||
|
||||
static void send_group_action(ToxWindow *self, ChatContext *ctx, Tox *m, char *action)
|
||||
{
|
||||
if (action == NULL) {
|
||||
wprintw(ctx->history, "Invalid syntax.\n");
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
static void groupchat_onKey(ToxWindow *self, Tox *m, wint_t key, bool ltr)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
int x, y, y2, x2;
|
||||
getyx(self->window, y, x);
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
return;
|
||||
|
||||
if (self->help->active) {
|
||||
help_onKey(self, key);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx->pastemode && key == '\r')
|
||||
key = '\n';
|
||||
|
||||
if (ltr || key == '\n') { /* char is printable */
|
||||
input_new_char(self, key, x, y, x2, y2);
|
||||
return;
|
||||
}
|
||||
|
||||
if (line_info_onKey(self, key))
|
||||
return;
|
||||
|
||||
if (input_handle(self, key, x, y, x2, y2))
|
||||
return;
|
||||
|
||||
if (key == '\t') { /* TAB key: auto-completes peer name or command */
|
||||
if (ctx->len > 0) {
|
||||
int diff;
|
||||
|
||||
/* 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,
|
||||
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 {
|
||||
diff = complete_line(self, group_cmd_list, AC_NUM_GROUP_COMMANDS, MAX_CMDNAME_SIZE);
|
||||
}
|
||||
|
||||
if (diff != -1) {
|
||||
if (x + diff > x2 - 1) {
|
||||
int wlen = MAX(0, wcswidth(ctx->line, sizeof(ctx->line) / sizeof(wchar_t)));
|
||||
ctx->start = wlen < x2 ? 0 : wlen - x2 + 1;
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
} else if (key == user_settings->key_peer_list_down) { /* Scroll peerlist up and down one position */
|
||||
int L = y2 - CHATBOX_HEIGHT - SDBAR_OFST;
|
||||
|
||||
if (groupchats[self->num].side_pos < groupchats[self->num].num_peers - L)
|
||||
++groupchats[self->num].side_pos;
|
||||
} else if (key == user_settings->key_peer_list_up) {
|
||||
if (groupchats[self->num].side_pos > 0)
|
||||
--groupchats[self->num].side_pos;
|
||||
} else if (key == '\r') {
|
||||
rm_trailing_spaces_buf(ctx);
|
||||
|
||||
if (!wstring_is_empty(ctx->line))
|
||||
{
|
||||
add_line_to_hist(ctx);
|
||||
|
||||
wstrsubst(ctx->line, L'¶', L'\n');
|
||||
|
||||
char line[MAX_STR_SIZE];
|
||||
|
||||
if (wcs_to_mbs_buf(line, ctx->line, MAX_STR_SIZE) == -1)
|
||||
memset(&line, 0, sizeof(line));
|
||||
|
||||
if (line[0] == '/') {
|
||||
if (strcmp(line, "/close") == 0) {
|
||||
close_groupchat(self, m, self->num);
|
||||
return;
|
||||
} else if (strncmp(line, "/me ", strlen("/me ")) == 0) {
|
||||
send_group_action(self, ctx, m, line + strlen("/me "));
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wclear(ctx->linewin);
|
||||
wmove(self->window, y2 - CURS_Y_OFFSET, 0);
|
||||
reset_buf(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void groupchat_onDraw(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
return;
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
line_info_print(self);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wclear(ctx->linewin);
|
||||
|
||||
curs_set(1);
|
||||
|
||||
if (ctx->len > 0)
|
||||
mvwprintw(ctx->linewin, 1, 0, "%ls", &ctx->line[ctx->start]);
|
||||
|
||||
wclear(ctx->sidebar);
|
||||
mvwhline(self->window, y2 - CHATBOX_HEIGHT, 0, ACS_HLINE, x2);
|
||||
|
||||
if (self->show_peerlist) {
|
||||
mvwvline(ctx->sidebar, 0, 0, ACS_VLINE, y2 - CHATBOX_HEIGHT);
|
||||
mvwaddch(ctx->sidebar, y2 - CHATBOX_HEIGHT, 0, ACS_BTEE);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
int num_peers = groupchats[self->num].num_peers;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
wmove(ctx->sidebar, 0, 1);
|
||||
wattron(ctx->sidebar, A_BOLD);
|
||||
wprintw(ctx->sidebar, "Peers: %d\n", num_peers);
|
||||
wattroff(ctx->sidebar, A_BOLD);
|
||||
|
||||
mvwaddch(ctx->sidebar, 1, 0, ACS_LTEE);
|
||||
mvwhline(ctx->sidebar, 1, 1, ACS_HLINE, SIDEBAR_WIDTH - 1);
|
||||
|
||||
int maxlines = y2 - SDBAR_OFST - CHATBOX_HEIGHT;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_peers && i < maxlines; ++i) {
|
||||
wmove(ctx->sidebar, i + 2, 1);
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
int peer = i + groupchats[self->num].side_pos;
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
/* truncate nick to fit in side panel without modifying list */
|
||||
char tmpnck[TOX_MAX_NAME_LENGTH];
|
||||
int maxlen = SIDEBAR_WIDTH - 2;
|
||||
|
||||
pthread_mutex_lock(&Winthread.lock);
|
||||
memcpy(tmpnck, &groupchats[self->num].peer_names[peer * TOX_MAX_NAME_LENGTH], maxlen);
|
||||
pthread_mutex_unlock(&Winthread.lock);
|
||||
|
||||
tmpnck[maxlen] = '\0';
|
||||
|
||||
wprintw(ctx->sidebar, "%s\n", tmpnck);
|
||||
}
|
||||
}
|
||||
|
||||
int y, x;
|
||||
getyx(self->window, y, x);
|
||||
(void) x;
|
||||
int new_x = ctx->start ? x2 - 1 : MAX(0, wcswidth(ctx->line, ctx->pos));
|
||||
wmove(self->window, y + 1, new_x);
|
||||
|
||||
wrefresh(self->window);
|
||||
|
||||
if (self->help->active)
|
||||
help_onDraw(self);
|
||||
}
|
||||
|
||||
static void groupchat_onInit(ToxWindow *self, Tox *m)
|
||||
{
|
||||
int x2, y2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (x2 <= 0 || y2 <= 0)
|
||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_CURSES);
|
||||
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
ctx->history = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, x2 - SIDEBAR_WIDTH - 1, 0, 0);
|
||||
ctx->linewin = subwin(self->window, CHATBOX_HEIGHT, x2, y2 - CHATBOX_HEIGHT, 0);
|
||||
ctx->sidebar = subwin(self->window, y2 - CHATBOX_HEIGHT + 1, SIDEBAR_WIDTH, 0, x2 - SIDEBAR_WIDTH);
|
||||
|
||||
ctx->hst = calloc(1, sizeof(struct history));
|
||||
ctx->log = calloc(1, sizeof(struct chatlog));
|
||||
|
||||
if (ctx->log == NULL || ctx->hst == NULL)
|
||||
exit_toxic_err("failed in groupchat_onInit", FATALERR_MEMORY);
|
||||
|
||||
line_info_init(ctx->hst);
|
||||
|
||||
if (user_settings->autolog == AUTOLOG_ON) {
|
||||
char myid[TOX_ADDRESS_SIZE];
|
||||
tox_self_get_address(m, (uint8_t *) myid);
|
||||
|
||||
if (log_enable(self->name, myid, NULL, ctx->log, LOG_GROUP) == -1)
|
||||
line_info_add(self, NULL, NULL, NULL, SYS_MSG, 0, 0, "Warning: Log failed to initialize.");
|
||||
}
|
||||
|
||||
execute(ctx->history, self, m, "/log", GLOBAL_COMMAND_MODE);
|
||||
|
||||
scrollok(ctx->history, 0);
|
||||
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 ret;
|
||||
memset(&ret, 0, sizeof(ret));
|
||||
|
||||
ret.active = true;
|
||||
ret.is_groupchat = true;
|
||||
|
||||
ret.onKey = &groupchat_onKey;
|
||||
ret.onDraw = &groupchat_onDraw;
|
||||
ret.onInit = &groupchat_onInit;
|
||||
ret.onGroupMessage = &groupchat_onGroupMessage;
|
||||
ret.onGroupNamelistChange = &groupchat_onGroupNamelistChange;
|
||||
ret.onGroupAction = &groupchat_onGroupAction;
|
||||
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));
|
||||
Help *help = calloc(1, sizeof(Help));
|
||||
|
||||
if (chatwin == NULL || help == NULL)
|
||||
exit_toxic_err("failed in new_group_chat", FATALERR_MEMORY);
|
||||
|
||||
ret.chatwin = chatwin;
|
||||
ret.help = help;
|
||||
|
||||
ret.num = groupnum;
|
||||
ret.show_peerlist = true;
|
||||
ret.active_box = -1;
|
||||
|
||||
return ret;
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/* groupchat.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2014 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GROUPCHAT_H
|
||||
#define GROUPCHAT_H
|
||||
|
||||
#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 {
|
||||
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
|
||||
} GroupChat;
|
||||
|
||||
void close_groupchat(ToxWindow *self, Tox *m, int groupnum);
|
||||
int init_groupchat_win(ToxWindow *prompt, Tox *m, int 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);
|
||||
|
||||
#endif /* #define GROUPCHAT_H */
|
942
src/groupchat_commands.c
Normal file
942
src/groupchat_commands.c
Normal file
@ -0,0 +1,942 @@
|
||||
/* groupchat_commands.c
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "log.h"
|
||||
#include "groupchats.h"
|
||||
|
||||
extern GroupChat groupchats[MAX_GROUPCHAT_NUM];
|
||||
|
||||
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
char chatid[TOX_GROUP_CHAT_ID_SIZE * 2 + 1] = {0};
|
||||
char chat_public_key[TOX_GROUP_CHAT_ID_SIZE];
|
||||
|
||||
Tox_Err_Group_State_Queries err;
|
||||
|
||||
if (!tox_group_get_chat_id(m, self->num, (uint8_t *) chat_public_key, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve the Chat ID (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < TOX_GROUP_CHAT_ID_SIZE; ++i) {
|
||||
char xx[3];
|
||||
snprintf(xx, sizeof(xx), "%02X", chat_public_key[i] & 0xff);
|
||||
strcat(chatid, xx);
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s", chatid);
|
||||
}
|
||||
|
||||
void cmd_disconnect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
Tox_Err_Group_Disconnect err;
|
||||
tox_group_disconnect(m, self->num, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_DISCONNECT_OK: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Disconnected from group. Type '/rejoin' to reconnect.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_DISCONNECT_ALREADY_DISCONNECTED: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Already disconnected. Type '/rejoin' to connect.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to disconnect from group. Error: %d", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_group_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
UNUSED_VAR(window);
|
||||
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Input required.");
|
||||
return;
|
||||
}
|
||||
|
||||
char nick[MAX_STR_SIZE];
|
||||
snprintf(nick, sizeof(nick), "%s", argv[1]);
|
||||
size_t len = strlen(nick);
|
||||
|
||||
if (!valid_nick(nick)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Invalid name.");
|
||||
return;
|
||||
}
|
||||
|
||||
len = MIN(len, TOXIC_MAX_NAME_LENGTH - 1);
|
||||
nick[len] = '\0';
|
||||
|
||||
set_nick_this_group(self, m, nick, len);
|
||||
|
||||
store_data(m, DATA_FILE);
|
||||
}
|
||||
|
||||
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Set_Ignore err;
|
||||
tox_group_set_ignore(m, self->num, peer_id, true, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_SET_IGNORE_OK: {
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_SET_IGNORE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot ignore yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_SET_IGNORE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to toggle ignore on %s (error %d).", nick, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, BLUE, "-!- Ignoring %s", nick);
|
||||
|
||||
group_toggle_peer_ignore(self->num, peer_id, true);
|
||||
}
|
||||
|
||||
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
|
||||
uint32_t target_peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Mod_Kick_Peer err;
|
||||
tox_group_mod_kick_peer(m, self->num, target_peer_id, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_MOD_KICK_PEER_OK: {
|
||||
char self_nick[TOX_MAX_NAME_LENGTH + 1];
|
||||
get_group_self_nick_truncate(m, self_nick, self->num);
|
||||
|
||||
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, RED, "-!- %s has been kicked by %s", nick, self_nick);
|
||||
groupchat_onGroupPeerExit(self, m, self->num, target_peer_id, TOX_GROUP_EXIT_TYPE_KICK, nick, strlen(nick), NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_KICK_PEER_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to kick %s.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_KICK_PEER_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot kick yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_KICK_PEER_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to kick %s from the group (error %d).", nick,
|
||||
err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_list(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
GroupChat *chat = get_groupchat(self->num);
|
||||
|
||||
if (!chat) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch GroupChat object.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < chat->max_idx; ++i) {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
if (!peer->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
|
||||
|
||||
for (size_t j = 0; j < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++j) {
|
||||
char d[3];
|
||||
snprintf(d, sizeof(d), "%02X", peer->public_key[j] & 0xff);
|
||||
strcat(pk_string, d);
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s : %s", pk_string, peer->name);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t target_peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Self_Query s_err;
|
||||
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
|
||||
|
||||
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Mod_Set_Role err;
|
||||
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_MODERATOR, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
|
||||
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_MODERATOR);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to promote moderators.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is already a moderator.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot make yourself a moderator.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to promote peer to moderator (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t target_peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Self_Query s_err;
|
||||
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
|
||||
|
||||
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_group_peer_get_role(m, self->num, target_peer_id, NULL) != TOX_GROUP_ROLE_MODERATOR) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not a moderator.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Mod_Set_Role err;
|
||||
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_USER, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
|
||||
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unmod %s.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot remove your own moderator status.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to revoke moderator powers from %s (error %d).", nick,
|
||||
err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *passwd = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
if (argc > 0) {
|
||||
passwd = argv[1];
|
||||
len = strlen(passwd);
|
||||
}
|
||||
|
||||
Tox_Err_Group_Founder_Set_Password err;
|
||||
tox_group_founder_set_password(m, self->num, (uint8_t *) passwd, len, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_OK: {
|
||||
if (len > 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password has been set to %s.", passwd);
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password has been unset.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_TOO_LONG: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Password length must not exceed %d.",
|
||||
TOX_GROUP_MAX_PASSWORD_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PASSWORD_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the password.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set password (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
int maxpeers = 0;
|
||||
|
||||
if (argc < 1) {
|
||||
Tox_Err_Group_State_Queries err;
|
||||
uint32_t maxpeers = tox_group_get_peer_limit(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve peer limit (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit is set to %d", maxpeers);
|
||||
return;
|
||||
}
|
||||
|
||||
maxpeers = atoi(argv[1]);
|
||||
|
||||
if (maxpeers <= 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit must be a value greater than 0.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Founder_Set_Peer_Limit err;
|
||||
tox_group_founder_set_peer_limit(m, self->num, maxpeers, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_OK: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer limit has been set to %d.", maxpeers);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PEER_LIMIT_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the peer limit.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the peer limit (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_set_voice(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
Tox_Group_Voice_State voice_state;
|
||||
|
||||
if (argc < 1) {
|
||||
Tox_Err_Group_State_Queries err;
|
||||
voice_state = tox_group_get_voice_state(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve voice state (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (voice_state) {
|
||||
case TOX_GROUP_VOICE_STATE_ALL: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to ALL");
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_GROUP_VOICE_STATE_MODERATOR: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to MODERATOR");
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_GROUP_VOICE_STATE_FOUNDER: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state is set to FOUNDER");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error: Unknown voice state: %d", voice_state);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *vstate_str = argv[1];
|
||||
|
||||
if (strcasecmp(vstate_str, "mod") == 0) {
|
||||
voice_state = TOX_GROUP_VOICE_STATE_MODERATOR;
|
||||
} else if (strcasecmp(vstate_str, "founder") == 0) {
|
||||
voice_state = TOX_GROUP_VOICE_STATE_FOUNDER;
|
||||
} else if (strcasecmp(vstate_str, "all") == 0) {
|
||||
voice_state = TOX_GROUP_VOICE_STATE_ALL;
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0,
|
||||
"voice state must be \"all\", \"mod\", or \"founder\".");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Founder_Set_Voice_State err;
|
||||
tox_group_founder_set_voice_state(m, self->num, voice_state, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_OK: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Voice state has been set to %s.", vstate_str);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_VOICE_STATE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the voice state.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting voice state (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
const char *pstate_str = NULL;
|
||||
Tox_Group_Privacy_State privacy_state;
|
||||
|
||||
if (argc < 1) {
|
||||
Tox_Err_Group_State_Queries err;
|
||||
privacy_state = tox_group_get_privacy_state(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve privacy state (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
pstate_str = privacy_state == TOX_GROUP_PRIVACY_STATE_PRIVATE ? "private" : "public";
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state is set to %s.", pstate_str);
|
||||
return;
|
||||
}
|
||||
|
||||
pstate_str = argv[1];
|
||||
|
||||
if (strcasecmp(pstate_str, "private") != 0 && strcasecmp(pstate_str, "public") != 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state must be \"private\" or \"public\".");
|
||||
return;
|
||||
}
|
||||
|
||||
privacy_state = strcasecmp(pstate_str,
|
||||
"private") == 0 ? TOX_GROUP_PRIVACY_STATE_PRIVATE : TOX_GROUP_PRIVACY_STATE_PUBLIC;
|
||||
|
||||
Tox_Err_Group_Founder_Set_Privacy_State err;
|
||||
tox_group_founder_set_privacy_state(m, self->num, privacy_state, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_OK: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Privacy state has been set to %s.", pstate_str);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_PRIVACY_STATE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the privacy state.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting privacy state (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_set_topic_lock(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
Tox_Group_Topic_Lock topic_lock;
|
||||
const char *tlock_str = NULL;
|
||||
|
||||
if (argc < 1) {
|
||||
Tox_Err_Group_State_Queries err;
|
||||
topic_lock = tox_group_get_topic_lock(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic lock (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
tlock_str = topic_lock == TOX_GROUP_TOPIC_LOCK_ENABLED ? "Enabled" : "Disabled";
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock is %s.", tlock_str);
|
||||
return;
|
||||
}
|
||||
|
||||
tlock_str = argv[1];
|
||||
|
||||
if (strcasecmp(tlock_str, "on") != 0 && strcasecmp(tlock_str, "off") != 0) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock must be \"on\" or \"off\".");
|
||||
return;
|
||||
}
|
||||
|
||||
topic_lock = strcasecmp(tlock_str, "on") == 0 ? TOX_GROUP_TOPIC_LOCK_ENABLED : TOX_GROUP_TOPIC_LOCK_DISABLED;
|
||||
const char *display_str = (topic_lock == TOX_GROUP_TOPIC_LOCK_ENABLED) ? "enabled" : "disabled";
|
||||
|
||||
Tox_Err_Group_Founder_Set_Topic_Lock err;
|
||||
tox_group_founder_set_topic_lock(m, self->num, topic_lock, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_OK: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic lock has been %s.", display_str);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_FOUNDER_SET_TOPIC_LOCK_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic lock.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Error setting topic lock (%d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t target_peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Self_Query s_err;
|
||||
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
|
||||
|
||||
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Mod_Set_Role err;
|
||||
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_OBSERVER, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
|
||||
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_OBSERVER);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to silence %s.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is already silenced.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot silence yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to silence %s (error %d).", nick, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer name or public key must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t target_peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &target_peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tox_group_peer_get_role(m, self->num, target_peer_id, NULL) != TOX_GROUP_ROLE_OBSERVER) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Self_Query s_err;
|
||||
uint32_t self_peer_id = tox_group_self_get_peer_id(m, self->num, &s_err);
|
||||
|
||||
if (s_err != TOX_ERR_GROUP_SELF_QUERY_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch self peer_id.");
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Mod_Set_Role err;
|
||||
tox_group_mod_set_role(m, self->num, target_peer_id, TOX_GROUP_ROLE_USER, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_OK: {
|
||||
groupchat_onGroupModeration(self, m, self->num, self_peer_id, target_peer_id, TOX_GROUP_MOD_EVENT_USER);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to unsilence %s.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_ASSIGNMENT: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "%s is not silenced.", nick);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot unsilence yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_MOD_SET_ROLE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to unsilence %s (error %d).", nick, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
Tox_Err_Group_Reconnect err;
|
||||
|
||||
if (!tox_group_reconnect(m, self->num, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to rejoin group (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Reconnecting to group...");
|
||||
|
||||
groupchat_rejoin(self, m);
|
||||
}
|
||||
|
||||
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
Tox_Err_Group_State_Queries err;
|
||||
size_t tlen = tox_group_get_topic_size(m, self->num, &err);
|
||||
|
||||
if (err != TOX_ERR_GROUP_STATE_QUERIES_OK) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic length (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tlen > 0) {
|
||||
char cur_topic[TOX_GROUP_MAX_TOPIC_LENGTH + 1];
|
||||
|
||||
if (!tox_group_get_topic(m, self->num, (uint8_t *) cur_topic, &err)) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to retrieve topic (error %d).", err);
|
||||
return;
|
||||
}
|
||||
|
||||
cur_topic[tlen] = 0;
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic is set to: %s", cur_topic);
|
||||
} else {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic is not set.");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const char *topic = argv[1];
|
||||
|
||||
Tox_Err_Group_Topic_Set err;
|
||||
tox_group_set_topic(m, self->num, (uint8_t *) topic, strlen(topic), &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_TOPIC_SET_OK: {
|
||||
/* handled below switch */
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_TOPIC_SET_TOO_LONG: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Topic length must not exceed %d.", TOX_GROUP_MAX_TOPIC_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_TOPIC_SET_PERMISSIONS: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You do not have permission to set the topic.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_TOPIC_SET_DISCONNECTED: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You are disconnected from the group.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to set the topic (error %d).", err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
char self_nick[TOX_MAX_NAME_LENGTH + 1];
|
||||
get_group_self_nick_truncate(m, self_nick, self->num);
|
||||
|
||||
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, MAGENTA, "-!- You set the topic to: %s", topic);
|
||||
|
||||
char tmp_event[MAX_STR_SIZE];
|
||||
snprintf(tmp_event, sizeof(tmp_event), "set topic to %s", topic);
|
||||
write_to_log(tmp_event, self_nick, self->chatwin->log, true);
|
||||
}
|
||||
|
||||
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *nick = argv[1];
|
||||
uint32_t peer_id;
|
||||
|
||||
if (group_get_peer_id_of_identifier(self, nick, &peer_id) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tox_Err_Group_Set_Ignore err;
|
||||
tox_group_set_ignore(m, self->num, peer_id, false, &err);
|
||||
|
||||
switch (err) {
|
||||
case TOX_ERR_GROUP_SET_IGNORE_OK: {
|
||||
break;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_SET_IGNORE_SELF: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "You cannot unignore yourself.");
|
||||
return;
|
||||
}
|
||||
|
||||
case TOX_ERR_GROUP_SET_IGNORE_PEER_NOT_FOUND: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "The specified nick or public key is invalid.");
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to toggle ignore on %s (error %d).", nick, err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
line_info_add(self, true, NULL, NULL, SYS_MSG, 1, BLUE, "-!- You are no longer ignoring %s", nick);
|
||||
|
||||
group_toggle_peer_ignore(self->num, peer_id, false);
|
||||
}
|
||||
|
||||
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE])
|
||||
{
|
||||
if (argc < 1) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Peer must be specified.");
|
||||
return;
|
||||
}
|
||||
|
||||
GroupChat *chat = get_groupchat(self->num);
|
||||
|
||||
if (!chat) {
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Failed to fetch GroupChat object.");
|
||||
return;
|
||||
}
|
||||
|
||||
const char *identifier = argv[1];
|
||||
bool is_public_key = false;
|
||||
|
||||
for (size_t i = 0; i < chat->max_idx && !is_public_key; ++i) {
|
||||
uint32_t peer_id;
|
||||
|
||||
if (group_get_public_key_peer_id(self->num, identifier, &peer_id) == 0) {
|
||||
is_public_key = true;
|
||||
} else {
|
||||
GroupPeer *peer = &chat->peer_list[i];
|
||||
|
||||
if (!peer->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(identifier, peer->name) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
peer_id = peer->peer_id;
|
||||
}
|
||||
|
||||
int peer_index = get_peer_index(self->num, peer_id);
|
||||
|
||||
if (peer_index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
GroupPeer *peer = &chat->peer_list[peer_index];
|
||||
|
||||
const char *status_str = "Online";
|
||||
|
||||
if (peer->status == TOX_USER_STATUS_BUSY) {
|
||||
status_str = "Busy";
|
||||
} else if (peer->status == TOX_USER_STATUS_AWAY) {
|
||||
status_str = "Away";
|
||||
}
|
||||
|
||||
const char *role_str = "User";
|
||||
|
||||
if (peer->role == TOX_GROUP_ROLE_FOUNDER) {
|
||||
role_str = "Founder";
|
||||
} else if (peer->role == TOX_GROUP_ROLE_MODERATOR) {
|
||||
role_str = "Moderator";
|
||||
} else if (peer->role == TOX_GROUP_ROLE_OBSERVER) {
|
||||
role_str = "Observer";
|
||||
}
|
||||
|
||||
char last_seen_str[128];
|
||||
get_elapsed_time_str_alt(last_seen_str, sizeof(last_seen_str),
|
||||
get_unix_time() - peer->last_active);
|
||||
|
||||
char pk_string[TOX_GROUP_PEER_PUBLIC_KEY_SIZE * 2 + 1] = {0};
|
||||
|
||||
for (size_t i = 0; i < TOX_GROUP_PEER_PUBLIC_KEY_SIZE; ++i) {
|
||||
char d[3];
|
||||
snprintf(d, sizeof(d), "%02X", peer->public_key[i] & 0xff);
|
||||
strcat(pk_string, d);
|
||||
}
|
||||
|
||||
Tox_Err_Group_Peer_Query conn_err;
|
||||
Tox_Connection connection_type = tox_group_peer_get_connection_status(m, self->num, peer_id, &conn_err);
|
||||
|
||||
const char *connection_type_str = "Unknown";
|
||||
|
||||
if (conn_err == TOX_ERR_GROUP_PEER_QUERY_OK || connection_type != TOX_CONNECTION_NONE) {
|
||||
connection_type_str = connection_type == TOX_CONNECTION_UDP ? "UDP" : "TCP";
|
||||
}
|
||||
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Public key: %s", pk_string);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Name: %s", peer->name);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Role: %s", role_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Status: %s", status_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Connection: %s", connection_type_str);
|
||||
line_info_add(self, false, NULL, NULL, SYS_MSG, 0, 0, "Last active: %s", last_seen_str);
|
||||
}
|
||||
}
|
||||
|
51
src/groupchat_commands.h
Normal file
51
src/groupchat_commands.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* groupchat_commands.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GROUPCHAT_COMMANDS_H
|
||||
#define GROUPCHAT_COMMANDS_H
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
|
||||
void cmd_chatid(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_disconnect(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_group_nick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_ignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_kick(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_list(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_mod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_prune(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_passwd(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_peerlimit(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_privacy(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_voice(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_topic_lock(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_rejoin(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_set_topic(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_silence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_unsilence(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_unignore(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_unmod(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
void cmd_whois(WINDOW *window, ToxWindow *self, Tox *m, int argc, char (*argv)[MAX_STR_SIZE]);
|
||||
|
||||
#endif /* GROUPCHAT_COMMANDS_H */
|
||||
|
2034
src/groupchats.c
Normal file
2034
src/groupchats.c
Normal file
File diff suppressed because it is too large
Load Diff
120
src/groupchats.h
Normal file
120
src/groupchats.h
Normal file
@ -0,0 +1,120 @@
|
||||
/* groupchats.h
|
||||
*
|
||||
*
|
||||
* Copyright (C) 2020 Toxic All Rights Reserved.
|
||||
*
|
||||
* 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 GROUPCHATS_H
|
||||
#define GROUPCHATS_H
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifndef SIDEWAR_WIDTH
|
||||
#define SIDEBAR_WIDTH 16
|
||||
#endif
|
||||
|
||||
#define MAX_GROUPCHAT_NUM (MAX_WINDOWS_NUM - 2)
|
||||
|
||||
typedef enum Group_Join_Type {
|
||||
Group_Join_Type_Create,
|
||||
Group_Join_Type_Join,
|
||||
Group_Join_Type_Load,
|
||||
} Group_Join_Type;
|
||||
|
||||
typedef struct GroupPeer {
|
||||
bool active;
|
||||
char name[TOX_MAX_NAME_LENGTH];
|
||||
size_t name_length;
|
||||
char prev_name[TOX_MAX_NAME_LENGTH];
|
||||
uint32_t peer_id;
|
||||
uint8_t public_key[TOX_GROUP_PEER_PUBLIC_KEY_SIZE];
|
||||
TOX_USER_STATUS status;
|
||||
Tox_Group_Role role;
|
||||
bool is_ignored;
|
||||
uint64_t last_active;
|
||||
} GroupPeer;
|
||||
|
||||
typedef struct {
|
||||
GroupPeer *peer_list;
|
||||
char **name_list; /* List of peer names, needed for tab completion */
|
||||
uint32_t num_peers; /* Number of peers in the chat/name_list array */
|
||||
uint32_t max_idx; /* Maximum peer list index - 1 */
|
||||
|
||||
uint8_t **ignored_list; /* List of keys of peers that we're ignoring */
|
||||
uint16_t num_ignored;
|
||||
|
||||
char group_name[TOX_GROUP_MAX_GROUP_NAME_LENGTH + 1];
|
||||
size_t group_name_length;
|
||||
uint32_t groupnumber;
|
||||
bool active;
|
||||
uint64_t time_connected; /* The time we successfully connected to the group */
|
||||
|
||||
int chatwin;
|
||||
int side_pos; /* current position of the sidebar - used for scrolling up and down */
|
||||
} GroupChat;
|
||||
|
||||
void exit_groupchat(ToxWindow *self, Tox *m, uint32_t groupnumber, const char *partmessage, size_t length);
|
||||
int init_groupchat_win(Tox *m, uint32_t groupnumber, const char *groupname, size_t length, Group_Join_Type join_type);
|
||||
void set_nick_this_group(ToxWindow *self, Tox *m, const char *new_nick, size_t length);
|
||||
void set_status_all_groups(Tox *m, uint8_t status);
|
||||
int get_peer_index(uint32_t groupnumber, uint32_t peer_id);
|
||||
void groupchat_onGroupPeerExit(ToxWindow *self, Tox *m, uint32_t groupnumber, uint32_t peer_id,
|
||||
Tox_Group_Exit_Type exit_type,
|
||||
const char *name, size_t name_len, const char *partmessage, size_t len);
|
||||
void groupchat_onGroupModeration(ToxWindow *self, Tox *m, uint32_t groupnumber, uint32_t src_peer_id,
|
||||
uint32_t tgt_peer_id, Tox_Group_Mod_Event type);
|
||||
|
||||
void groupchat_rejoin(ToxWindow *self, Tox *m);
|
||||
|
||||
/* Puts the peer_id associated with `identifier` in `peer_id`. The string may be
|
||||
* either a nick or a public key.
|
||||
*
|
||||
* On failure, `peer_id` is set to (uint32_t)-1.
|
||||
*
|
||||
* This function is intended to be a helper for groupchat_commands.c and will print
|
||||
* error messages to `self`.
|
||||
* Return 0 on success.
|
||||
* Return -1 if the identifier does not correspond with a peer in the group.
|
||||
* Return -2 if the identifier is a nick and the nick is in use by multiple peers.
|
||||
*/
|
||||
int group_get_peer_id_of_identifier(ToxWindow *self, const char *identifier, uint32_t *peer_id);
|
||||
|
||||
/* Gets the peer_id associated with `public_key`.
|
||||
*
|
||||
* Returns 0 on success.
|
||||
* Returns -1 on failure or if `public_key` is invalid.
|
||||
*/
|
||||
int group_get_public_key_peer_id(uint32_t groupnumber, const char *public_key, uint32_t *peer_id);
|
||||
|
||||
/* destroys and re-creates groupchat window */
|
||||
void redraw_groupchat_win(ToxWindow *self);
|
||||
|
||||
/*
|
||||
* Return a GroupChat pointer associated with groupnumber.
|
||||
* Return NULL if groupnumber is invalid.
|
||||
*/
|
||||
GroupChat *get_groupchat(uint32_t groupnumber);
|
||||
|
||||
/**
|
||||
* Toggles the ignore status of the peer associated with `peer_id`.
|
||||
*/
|
||||
void group_toggle_peer_ignore(uint32_t groupnumber, int peer_id, bool ignore);
|
||||
|
||||
#endif /* #define GROUPCHATS_H */
|
262
src/help.c
262
src/help.c
@ -22,24 +22,34 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "windows.h"
|
||||
#include "toxic.h"
|
||||
#include "help.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
|
||||
#ifdef PYTHON
|
||||
#include "api.h"
|
||||
#endif /* PYTHON */
|
||||
|
||||
#ifdef PYTHON
|
||||
#define HELP_MENU_HEIGHT 10
|
||||
#else
|
||||
#define HELP_MENU_HEIGHT 9
|
||||
#endif /* PYTHON */
|
||||
#define HELP_MENU_WIDTH 26
|
||||
|
||||
void help_init_menu(ToxWindow *self)
|
||||
{
|
||||
if (self->help->win)
|
||||
if (self->help->win) {
|
||||
delwin(self->help->win);
|
||||
}
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(self->window, y2, x2);
|
||||
|
||||
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH)
|
||||
if (y2 < HELP_MENU_HEIGHT || x2 < HELP_MENU_WIDTH) {
|
||||
return;
|
||||
}
|
||||
|
||||
self->help->win = newwin(HELP_MENU_HEIGHT, HELP_MENU_WIDTH, 3, 3);
|
||||
self->help->active = true;
|
||||
@ -49,19 +59,24 @@ void help_init_menu(ToxWindow *self)
|
||||
static void help_exit(ToxWindow *self)
|
||||
{
|
||||
delwin(self->help->win);
|
||||
memset(self->help, 0, sizeof(Help));
|
||||
|
||||
*(self->help) = (struct Help) {
|
||||
0
|
||||
};
|
||||
}
|
||||
|
||||
static void help_init_window(ToxWindow *self, int height, int width)
|
||||
{
|
||||
if (self->help->win)
|
||||
if (self->help->win) {
|
||||
delwin(self->help->win);
|
||||
}
|
||||
|
||||
int y2, x2;
|
||||
getmaxyx(stdscr, y2, x2);
|
||||
|
||||
if (y2 <= 0 || x2 <= 0)
|
||||
if (y2 <= 0 || x2 <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
height = MIN(height, y2);
|
||||
width = MIN(width, x2);
|
||||
@ -89,11 +104,24 @@ static void help_draw_menu(ToxWindow *self)
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "hat commands\n");
|
||||
|
||||
wprintw(win, " c");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "o");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "nference commands\n");
|
||||
|
||||
wprintw(win, " g");
|
||||
wattron(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "r");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(BLUE));
|
||||
wprintw(win, "oup commands\n");
|
||||
wprintw(win, "oupchat 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");
|
||||
@ -112,14 +140,15 @@ 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)
|
||||
{
|
||||
int y2, x2;
|
||||
getmaxyx(win, y2, x2);
|
||||
(void) x2;
|
||||
|
||||
UNUSED_VAR(x2);
|
||||
|
||||
wmove(win, y2 - 2, 1);
|
||||
|
||||
@ -148,17 +177,29 @@ static void help_draw_global(ToxWindow *self)
|
||||
wprintw(win, " /add <addr> <msg> : Add contact with optional message\n");
|
||||
wprintw(win, " /accept <id> : Accept friend request\n");
|
||||
wprintw(win, " /avatar <path> : Set an avatar (leave path empty to unset)\n");
|
||||
wprintw(win, " /conference <type> : Create a conference where type: text | audio\n");
|
||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||
wprintw(win, " /decline <id> : Decline friend request\n");
|
||||
wprintw(win, " /requests : List pending friend requests\n");
|
||||
wprintw(win, " /connect <ip> <port> <key> : Manually connect to a DHT node\n");
|
||||
wprintw(win, " /status <type> <msg> : Set status with optional note\n");
|
||||
wprintw(win, " /status <type> : Set status (Online, Busy, Away)\n");
|
||||
wprintw(win, " /note <msg> : Set a personal note\n");
|
||||
wprintw(win, " /nick <nick> : Set your nickname\n");
|
||||
wprintw(win, " /nick <name> : Set your global name (doesn't affect groups)\n");
|
||||
wprintw(win, " /nospam <value> : Change part of your Tox ID to stop spam\n");
|
||||
wprintw(win, " /log <on> or <off> : Enable/disable logging\n");
|
||||
wprintw(win, " /group <type> : Create a group chat where type: text | audio\n");
|
||||
wprintw(win, " /myid : Print your Tox ID\n");
|
||||
wprintw(win, " /group <name> : Create a new group chat\n");
|
||||
wprintw(win, " /join <chatid> : Join a groupchat using a Chat ID\n");
|
||||
#ifdef GAMES
|
||||
wprintw(win, " /game : Play a game\n");
|
||||
#endif /* GAMES */
|
||||
|
||||
#ifdef QRCODE
|
||||
#ifdef QRPNG
|
||||
wprintw(win, " /myqr <txt> or <png> : Print your Tox ID's QR code to a file.\n");
|
||||
#else
|
||||
wprintw(win, " /myqr : Print your Tox ID's QR code to a file.\n");
|
||||
#endif /* QRPNG */
|
||||
#endif /* QRCODE */
|
||||
wprintw(win, " /clear : Clear window history\n");
|
||||
wprintw(win, " /close : Close the current chat window\n");
|
||||
wprintw(win, " /quit or /exit : Exit Toxic\n");
|
||||
@ -181,10 +222,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)
|
||||
@ -197,8 +246,10 @@ static void help_draw_chat(ToxWindow *self)
|
||||
wprintw(win, "Chat Commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /invite <n> : Invite contact to a group chat\n");
|
||||
wprintw(win, " /join : Join a pending group chat\n");
|
||||
wprintw(win, " /cinvite <n> : Invite contact to a conference \n");
|
||||
wprintw(win, " /cjoin : Join a pending conference\n");
|
||||
wprintw(win, " /invite <n> : Invite contact to a groupchat \n");
|
||||
wprintw(win, " /gaccept <password> : Accept a pending groupchat invite\n");
|
||||
wprintw(win, " /sendfile <path> : Send a file\n");
|
||||
wprintw(win, " /savefile <id> : Receive a file\n");
|
||||
wprintw(win, " /cancel <type> <id> : Cancel file transfer where type: in|out\n");
|
||||
@ -215,19 +266,65 @@ 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
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Video:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /video : Toggle video call\n");
|
||||
wprintw(win, " /res <width> <height> : Set video resolution\n");
|
||||
wprintw(win, " /vcall : Video call\n");
|
||||
wprintw(win, " /video : Toggle video in call\n");
|
||||
#endif /* VIDEO */
|
||||
|
||||
#ifdef GAMES
|
||||
wprintw(win, " /game : Play a game with contact\n");
|
||||
#endif /* GAMES */
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_groupchats(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Groupchat commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /chatid : Print this group's ID\n");
|
||||
wprintw(win, " /close <m> : Leave the group with an optional part message\n");
|
||||
wprintw(win, " /disconnect : Disconnect from the group (credentials retained)\n");
|
||||
wprintw(win, " /ignore <name> : Ignore a peer\n");
|
||||
wprintw(win, " /unignore <name> : Unignore an ignored peer\n");
|
||||
wprintw(win, " /kick <name> : Remove a peer from the group\n");
|
||||
wprintw(win, " /list : Print a list of peers currently in the group\n");
|
||||
wprintw(win, " /locktopic : Set the topic lock: on | off\n");
|
||||
wprintw(win, " /mod <name> : Promote a peer to moderator\n");
|
||||
wprintw(win, " /nick <name> : Set your name for this group only\n");
|
||||
wprintw(win, " /passwd <s> : Set a password needed to join the group\n");
|
||||
wprintw(win, " /peerlimit <n> : Set the maximum number of peers that can join\n");
|
||||
wprintw(win, " /privacy <state> : Set the privacy state: private | public\n");
|
||||
wprintw(win, " /rejoin : Reconnect to the groupchat\n");
|
||||
wprintw(win, " /silence <name> : Silence a peer for the entire group\n");
|
||||
wprintw(win, " /unsilence <name> : Unsilence a silenced peer\n");
|
||||
wprintw(win, " /status <type> : Set your status\n");
|
||||
wprintw(win, " /topic <m> : Set the group topic\n");
|
||||
wprintw(win, " /unmod <name> : Demote a moderator\n");
|
||||
wprintw(win, " /voice <state> : Set the voice state: all | mod | founder\n");
|
||||
wprintw(win, " /whisper <name> <m> : Send a private message to a peer\n");
|
||||
wprintw(win, " /whois <name> : Print whois info for a peer\n");
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
static void help_draw_keys(ToxWindow *self)
|
||||
@ -244,8 +341,8 @@ static void help_draw_keys(ToxWindow *self)
|
||||
wprintw(win, " Page Up and Page Down : Scroll window history one line\n");
|
||||
wprintw(win, " Ctrl+F and Ctrl+V : Scroll window history half a page\n");
|
||||
wprintw(win, " Ctrl+H : Move to the bottom of window history\n");
|
||||
wprintw(win, " Ctrl+[ and Ctrl+] : Scroll peer list in groupchats\n");
|
||||
wprintw(win, " Ctrl+B : Toggle the groupchat peerlist\n");
|
||||
wprintw(win, " Ctrl+up and Ctrl+down : Scroll peer list in conference\n");
|
||||
wprintw(win, " Ctrl+B : Toggle the conference peerlist\n");
|
||||
wprintw(win, " Ctrl+J : Insert new line\n");
|
||||
wprintw(win, " Ctrl+T : Toggle paste mode\n\n");
|
||||
wprintw(win, " (Note: Custom keybindings override these defaults.)\n\n");
|
||||
@ -253,27 +350,57 @@ 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)
|
||||
static void help_draw_conference(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Group commands:\n");
|
||||
wprintw(win, "Conference commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
wprintw(win, " /title <msg> : Set group title (show current title if no msg)\n\n");
|
||||
wprintw(win, " /title <msg> : Show/set conference title\n");
|
||||
#ifdef AUDIO
|
||||
wattron(win, A_BOLD);
|
||||
wprintw(win, "\n Audio:\n");
|
||||
wattroff(win, A_BOLD);
|
||||
wprintw(win, " /audio <on> or <off> : Enable/disable audio in an audio conference\n");
|
||||
wprintw(win, " /mute : Toggle self audio mute status\n");
|
||||
wprintw(win, " /mute <nick> or <pubkey> : Toggle peer audio mute status\n");
|
||||
wprintw(win, " /ptt <on> or <off> : Toggle audio input Push-To-Talk (F2 to activate)\n");
|
||||
wprintw(win, " /sense <n> : VAD sensitivity threshold\n\n");
|
||||
#endif
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
#ifdef PYTHON
|
||||
static void help_draw_plugin(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
|
||||
wmove(win, 1, 1);
|
||||
|
||||
wattron(win, A_BOLD | COLOR_PAIR(RED));
|
||||
wprintw(win, "Plugin commands:\n");
|
||||
wattroff(win, A_BOLD | COLOR_PAIR(RED));
|
||||
|
||||
draw_handler_help(win);
|
||||
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
#endif /* PYTHON */
|
||||
|
||||
static void help_draw_contacts(ToxWindow *self)
|
||||
{
|
||||
WINDOW *win = self->help->win;
|
||||
@ -293,66 +420,92 @@ static void help_draw_contacts(ToxWindow *self)
|
||||
help_draw_bottom_menu(win);
|
||||
|
||||
box(win, ACS_VLINE, ACS_HLINE);
|
||||
wrefresh(win);
|
||||
wnoutrefresh(win);
|
||||
}
|
||||
|
||||
void help_onKey(ToxWindow *self, wint_t key)
|
||||
{
|
||||
switch(key) {
|
||||
case 'x':
|
||||
int height;
|
||||
|
||||
switch (key) {
|
||||
case L'x':
|
||||
case T_KEY_ESC:
|
||||
help_exit(self);
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
case L'c':
|
||||
height = 12;
|
||||
#ifdef VIDEO
|
||||
help_init_window(self, 22, 80);
|
||||
height += 15;
|
||||
#elif AUDIO
|
||||
help_init_window(self, 19, 80);
|
||||
#else
|
||||
help_init_window(self, 10, 80);
|
||||
height += 5;
|
||||
#endif
|
||||
#ifdef GAMES
|
||||
height += 1;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_CHAT;
|
||||
break;
|
||||
|
||||
case 'g':
|
||||
case L'g':
|
||||
height = 24;
|
||||
#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
|
||||
#ifdef GAMES
|
||||
height += 1;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_GLOBAL;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
help_init_window(self, 6, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
case L'o':
|
||||
height = 6;
|
||||
#ifdef AUDIO
|
||||
height += 7;
|
||||
#endif
|
||||
help_init_window(self, height, 80);
|
||||
self->help->type = HELP_CONFERENCE;
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
#ifdef PYTHON
|
||||
|
||||
case L'p':
|
||||
help_init_window(self, 4 + num_registered_handlers(), help_max_width());
|
||||
self->help->type = HELP_PLUGIN;
|
||||
break;
|
||||
#endif /* PYTHON */
|
||||
|
||||
case L'f':
|
||||
help_init_window(self, 10, 80);
|
||||
self->help->type = HELP_CONTACTS;
|
||||
break;
|
||||
|
||||
case 'k':
|
||||
case L'k':
|
||||
help_init_window(self, 15, 80);
|
||||
self->help->type = HELP_KEYS;
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
case L'm':
|
||||
help_init_menu(self);
|
||||
self->help->type = HELP_MENU;
|
||||
break;
|
||||
|
||||
case L'r':
|
||||
help_init_window(self, 27, 80);
|
||||
self->help->type = HELP_GROUP;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void help_onDraw(ToxWindow *self)
|
||||
{
|
||||
curs_set(0);
|
||||
|
||||
switch(self->help->type) {
|
||||
switch (self->help->type) {
|
||||
case HELP_MENU:
|
||||
help_draw_menu(self);
|
||||
return;
|
||||
@ -373,8 +526,19 @@ void help_onDraw(ToxWindow *self)
|
||||
help_draw_contacts(self);
|
||||
break;
|
||||
|
||||
case HELP_CONFERENCE:
|
||||
help_draw_conference(self);
|
||||
break;
|
||||
|
||||
#ifdef PYTHON
|
||||
|
||||
case HELP_PLUGIN:
|
||||
help_draw_plugin(self);
|
||||
break;
|
||||
#endif /* PYTHON */
|
||||
|
||||
case HELP_GROUP:
|
||||
help_draw_group(self);
|
||||
help_draw_groupchats(self);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -29,14 +29,18 @@
|
||||
typedef enum {
|
||||
HELP_MENU,
|
||||
HELP_GLOBAL,
|
||||
HELP_CHAT,
|
||||
HELP_GROUP,
|
||||
HELP_CHAT,
|
||||
HELP_CONFERENCE,
|
||||
HELP_KEYS,
|
||||
HELP_CONTACTS,
|
||||
#ifdef PYTHON
|
||||
HELP_PLUGIN,
|
||||
#endif
|
||||
} HELP_TYPES;
|
||||
|
||||
void help_onDraw(ToxWindow *self);
|
||||
void help_init_menu(ToxWindow *self);
|
||||
void help_onKey(ToxWindow *self, wint_t key);
|
||||
|
||||
#endif /* #define HELP_H */
|
||||
#endif /* HELP_H */
|
||||
|
125
src/input.c
125
src/input.c
@ -26,25 +26,27 @@
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "toxic.h"
|
||||
#include "windows.h"
|
||||
#include "misc_tools.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "conference.h"
|
||||
#include "line_info.h"
|
||||
#include "misc_tools.h"
|
||||
#include "notify.h"
|
||||
#include "groupchat.h"
|
||||
#include "groupchats.h"
|
||||
#include "settings.h"
|
||||
#include "toxic.h"
|
||||
#include "toxic_strings.h"
|
||||
#include "windows.h"
|
||||
|
||||
extern struct user_settings *user_settings;
|
||||
|
||||
/* add a char to input field and buffer */
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
/* this is the only place we need to do this check */
|
||||
if (key == '\n')
|
||||
if (key == '\n') {
|
||||
key = L'¶';
|
||||
}
|
||||
|
||||
int cur_len = wcwidth(key);
|
||||
|
||||
@ -77,42 +79,45 @@ static void input_backspace(ToxWindow *self, int x, int mx_x)
|
||||
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
|
||||
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
|
||||
|
||||
if (ctx->start && (x >= mx_x - cur_len))
|
||||
if (ctx->start && (x >= mx_x - cur_len)) {
|
||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||
else if (ctx->start)
|
||||
} else if (ctx->start) {
|
||||
ctx->start = MAX(0, ctx->start - cur_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete a char via delete key from input field and buffer */
|
||||
static void input_delete(ToxWindow *self)
|
||||
{
|
||||
if (del_char_buf_frnt(self->chatwin) == -1)
|
||||
if (del_char_buf_frnt(self->chatwin) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete last typed word */
|
||||
static void input_del_word(ToxWindow *self, int x, int mx_x)
|
||||
static void input_del_word(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (del_word_buf(ctx) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* deletes entire line before cursor from input field and buffer */
|
||||
static void input_discard(ToxWindow *self)
|
||||
{
|
||||
if (discard_buf(self->chatwin) == -1)
|
||||
if (discard_buf(self->chatwin) == -1) {
|
||||
sound_notify(self, notif_error, 0, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* deletes entire line after cursor from input field and buffer */
|
||||
static void input_kill(ChatContext *ctx)
|
||||
{
|
||||
if (kill_buf(ctx) == -1)
|
||||
if (kill_buf(ctx) == -1) {
|
||||
sound_notify(NULL, notif_error, NT_ALWAYS, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
@ -134,7 +139,7 @@ static void input_yank(ToxWindow *self, int x, int mx_x)
|
||||
}
|
||||
|
||||
/* moves cursor/line position to end of line in input field and buffer */
|
||||
static void input_mv_end(ToxWindow *self, int y, int mx_x)
|
||||
static void input_mv_end(ToxWindow *self, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
@ -149,8 +154,9 @@ static void input_mv_home(ToxWindow *self)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0)
|
||||
if (ctx->pos <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->pos = 0;
|
||||
ctx->start = 0;
|
||||
@ -161,18 +167,44 @@ static void input_mv_left(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0)
|
||||
if (ctx->pos <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int cur_len = ctx->pos > 0 ? wcwidth(ctx->line[ctx->pos - 1]) : 0;
|
||||
int s_len = ctx->start > 0 ? wcwidth(ctx->line[ctx->start - 1]) : 0;
|
||||
|
||||
--ctx->pos;
|
||||
|
||||
if (ctx->start && (x >= mx_x - cur_len))
|
||||
if (ctx->start > 0 && (x >= mx_x - cur_len)) {
|
||||
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - cur_len));
|
||||
else if (ctx->start)
|
||||
} else if (ctx->start > 0) {
|
||||
ctx->start = MAX(0, ctx->start - cur_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* moves the cursor to the beginning of the previous word in input field and buffer */
|
||||
static void input_skip_left(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
--ctx->pos;
|
||||
count += wcwidth(ctx->line[ctx->pos]);
|
||||
} while (ctx->pos > 0 && (ctx->line[ctx->pos - 1] != L' ' || ctx->line[ctx->pos] == L' '));
|
||||
|
||||
if (ctx->start > 0 && (x >= mx_x - count)) {
|
||||
int s_len = wcwidth(ctx->line[ctx->start - 1]);
|
||||
ctx->start = MAX(0, ctx->start - 1 + (s_len - count));
|
||||
} else if (ctx->start > 0) {
|
||||
ctx->start = MAX(0, ctx->start - count);
|
||||
}
|
||||
}
|
||||
|
||||
/* moves cursor/line position right in input field and buffer */
|
||||
@ -180,8 +212,9 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos >= ctx->len)
|
||||
if (ctx->pos >= ctx->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
++ctx->pos;
|
||||
|
||||
@ -193,6 +226,29 @@ static void input_mv_right(ToxWindow *self, int x, int mx_x)
|
||||
}
|
||||
}
|
||||
|
||||
/* moves the cursor to the end of the next word in input field and buffer */
|
||||
static void input_skip_right(ToxWindow *self, int x, int mx_x)
|
||||
{
|
||||
ChatContext *ctx = self->chatwin;
|
||||
|
||||
if (ctx->pos >= ctx->len) {
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
|
||||
do {
|
||||
count += wcwidth(ctx->line[ctx->pos]);
|
||||
++ctx->pos;
|
||||
} while (ctx->pos < ctx->len && !(ctx->line[ctx->pos] == L' ' && ctx->line[ctx->pos - 1] != L' '));
|
||||
|
||||
int newpos = x + count;
|
||||
|
||||
if (newpos >= mx_x) {
|
||||
ctx->start += (1 + (newpos - mx_x));
|
||||
}
|
||||
}
|
||||
|
||||
/* puts a line history item in input field and buffer */
|
||||
static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
||||
{
|
||||
@ -205,7 +261,7 @@ static void input_history(ToxWindow *self, wint_t key, int mx_x)
|
||||
|
||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||
return true if key matches a function, false otherwise */
|
||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x)
|
||||
{
|
||||
bool match = true;
|
||||
|
||||
@ -232,7 +288,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
break;
|
||||
|
||||
case T_KEY_C_W:
|
||||
input_del_word(self, x, mx_x);
|
||||
input_del_word(self);
|
||||
break;
|
||||
|
||||
case KEY_HOME:
|
||||
@ -242,7 +298,7 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
|
||||
case KEY_END:
|
||||
case T_KEY_C_E:
|
||||
input_mv_end(self, y, mx_x);
|
||||
input_mv_end(self, mx_x);
|
||||
break;
|
||||
|
||||
case KEY_LEFT:
|
||||
@ -262,6 +318,14 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
force_refresh(self->chatwin->history);
|
||||
break;
|
||||
|
||||
case T_KEY_C_LEFT:
|
||||
input_skip_left(self, x, mx_x);
|
||||
break;
|
||||
|
||||
case T_KEY_C_RIGHT:
|
||||
input_skip_right(self, x, mx_x);
|
||||
break;
|
||||
|
||||
default:
|
||||
match = false;
|
||||
break;
|
||||
@ -271,17 +335,24 @@ bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y)
|
||||
maybe convert entire function to if/else and make them all customizable keys? */
|
||||
if (!match) {
|
||||
if (key == user_settings->key_toggle_peerlist) {
|
||||
if (self->is_groupchat) {
|
||||
if (self->type == WINDOW_TYPE_CONFERENCE) {
|
||||
self->show_peerlist ^= 1;
|
||||
redraw_conference_win(self);
|
||||
} else if (self->type == WINDOW_TYPE_GROUPCHAT) {
|
||||
self->show_peerlist ^= 1;
|
||||
redraw_groupchat_win(self);
|
||||
}
|
||||
|
||||
match = true;
|
||||
}
|
||||
else if (key == user_settings->key_toggle_pastemode) {
|
||||
} else if (key == user_settings->key_toggle_pastemode) {
|
||||
self->chatwin->pastemode ^= 1;
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
flag_interface_refresh();
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
|
@ -24,10 +24,10 @@
|
||||
#define INPUT_H
|
||||
|
||||
/* add a char to input field and buffer for given chatcontext */
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||
void input_new_char(ToxWindow *self, wint_t key, int x, int mx_x);
|
||||
|
||||
/* Handles non-printable input keys that behave the same for all types of chat windows.
|
||||
return true if key matches a function, false otherwise */
|
||||
bool input_handle(ToxWindow *self, wint_t key, int x, int y, int mx_x, int mx_y);
|
||||
bool input_handle(ToxWindow *self, wint_t key, int x, int mx_x);
|
||||
|
||||
#endif /* #define INPUT_H */
|
||||
#endif /* INPUT_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user