Compare commits

..

1 commit

Author SHA1 Message Date
Maxim Logaev 2935b5de1c Fixed flatpak bundle name
Signed-off-by: Maxim Logaev <maxlogaev@proton.me>
2024-04-02 20:09:03 +03:00
83 changed files with 633 additions and 1127 deletions

View file

@ -17,26 +17,26 @@ jobs:
- name: Install build-dependencies - name: Install build-dependencies
run: | run: |
msys2 -c './build-win64.sh --prepare' msys2 -c './build-win64.sh --prepare'
- name: Build Dino+ (Meson, without saving) - name: Build Dino (Meson, without saving)
run: | run: |
msys2 -c './build-win64.sh -s meson -c -b -t -w' msys2 -c './build-win64.sh -s meson -c -b -w'
- name: Build Dino+ (CMake) - name: Build Dino (CMake)
run: | run: |
msys2 -c './build-win64.sh -s cmake -c -b -t -i' msys2 -c './build-win64.sh -s cmake -c -b -i'
- name: Build Dino+ installer - name: Build Dino installer
run: | run: |
msys2 -c './build-win64.sh --build-installer' msys2 -c './build-win64.sh --build-installer'
- name: Upload Dino+ installer - name: Upload Dino installer
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: dino-plus-installer name: dino-installer
path: windows-installer/dino-installer.exe path: windows-installer/dino-installer.exe
- name: Release Dino+ installer - name: Release Dino installer
if: ${{ github.ref_type == 'tag' }} if: ${{ github.ref_type == 'tag' }}
uses: svenstaro/upload-release-action@2.9.0 uses: svenstaro/upload-release-action@2.9.0
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: windows-installer/dino-installer.exe file: windows-installer/dino-installer.exe
asset_name: dino-plus-installer.exe asset_name: dino-installer.exe
tag: ${{ github.ref }} tag: ${{ github.ref }}
release_name: Dino+ ${{ github.ref_name }} release_name: Dino ${{ github.ref_name }}

View file

@ -7,64 +7,37 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
- run: sudo rm /etc/apt/sources.list.d/microsoft-prod.list
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt-get remove libunwind-14-dev - run: sudo apt-get remove libunwind-14-dev
- run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libadwaita-1-dev libsignal-protocol-c-dev libcanberra-dev - run: sudo apt-get install -y build-essential gettext cmake valac libgee-0.8-dev libsqlite3-dev libgtk-4-dev libnotify-dev libgpgme-dev libsoup2.4-dev libgcrypt20-dev libqrencode-dev libnice-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libsrtp2-dev libwebrtc-audio-processing-dev libadwaita-1-dev libsignal-protocol-c-dev libcanberra-dev
- run: ./configure --release --no-debug --with-tests --enable-plugin=notification-sound --prefix=/usr --without-webrtc - run: ./configure --with-tests --with-libsignal-in-tree
- run: cmake --build build - run: make
- run: cmake --build build --target=test - run: build/xmpp-vala-test
- name: Build DEB-package - run: build/omemo-test
run: cd build && cpack -G DEB
- name: Upload Dino+ DEB-package
uses: actions/upload-artifact@v4
with:
name: dino-plus-deb
path: _packages/dino-plus.deb
- name: Release Dino+ DEB-package
if: ${{ github.ref_type == 'tag' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: _packages/dino-plus.deb
asset_name: dino-plus.deb
tag: ${{ github.ref }}
release_name: Dino+ ${{ github.ref_name }}
build-meson: build-meson:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
fetch-depth: 0 fetch-depth: 0
- run: sudo rm /etc/apt/sources.list.d/microsoft-prod.list
- run: sudo apt-get update - run: sudo apt-get update
- run: sudo apt-get remove libunwind-14-dev - run: sudo apt-get remove libunwind-14-dev
- run: sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libsignal-protocol-c-dev libsoup2.4-dev libsqlite3-dev libsrtp2-dev meson valac - run: sudo apt-get install -y build-essential gettext libadwaita-1-dev libcanberra-dev libgcrypt20-dev libgee-0.8-dev libgpgme-dev libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev libgtk-4-dev libnice-dev libnotify-dev libqrencode-dev libsignal-protocol-c-dev libsoup2.4-dev libsqlite3-dev libsrtp2-dev libwebrtc-audio-processing-dev meson valac
- run: meson setup build -Duse-soup2=true -Dplugin-rtp-webrtc-audio-processing=disabled - run: meson setup build -Dcrypto-backend=auto -Dplugin-ice=enabled -Duse-soup2=true
- run: meson compile -C build - run: meson compile -C build
- run: meson test -C build - run: build/plugins/omemo/test_omemo
build-flatpak: build-flatpak:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
container:
image: bilelmoussaoui/flatpak-github-actions:gnome-44
options: --privileged
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
submodules: true submodules: true
- run: flatpak repair --user - run: sudo apt-get update
- uses: flatpak/flatpak-github-actions/flatpak-builder@v6.1 - run: sudo apt-get install -y flatpak flatpak-builder
- run: ./build-flatpack.sh
- name: Upload Dino flatpak
uses: actions/upload-artifact@v4
with: with:
manifest-path: im.dino.Dino.json name: im.dino.Dino.flatpak
build-bundle: true path: im.dino.Dino.flatpak
- name: Release Dino+ flatpak
if: ${{ github.ref_type == 'tag' }}
uses: svenstaro/upload-release-action@2.9.0
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: app.flatpak
asset_name: app.flatpak
tag: ${{ github.ref }}
release_name: Dino+ ${{ github.ref_name }}

View file

@ -1,54 +0,0 @@
Here are the instructions to anyone whom wants to try the build themselves.
It is assumed that you have `brew` installed, know your way around terminal, and generally understand what you're doing.
## Build
1. Start with an already existing build instructions of the original project:
`https://github.com/dino/dino/wiki/macOS`
2. On step 4, replace the path to the upstream repo with this one:
```
git clone https://github.com/mxlgv/dino
cd dino
```
3. On step 5 run `./configure --with-libsoup3`.
4. Continue the build according to the original instructions.
## Install with Brew
To build Dino using this formula, follow these instructions:
```
brew tap mxlgv/homebrew-dino
brew install mxlgv/homebrew-dino/dino
```
You can start the Dino client installed via homebrew with the following command:
```
./opt/homebrew/Cellar/dino/3/bin/dino
```
You can create a shortcut with a symbolic link pointing to this file, so that you can open it in a more convenient way.
If you encounter an error related to rpath, you need to add `DYLD_LIBRARY_PATH` to the environment variable:
```
export DYLD_LIBRARY_PATH=/opt/homebrew/Cellar/dino/3/lib
```
## Notifications
There is a draft PR which can be used to enable notifications: https://github.com/mxlgv/dino/pull/45
## Start Dino
In order to run Dino, just run in the build folder:
```
./dino
```

View file

@ -10,16 +10,6 @@ else ()
set(PROJECT_VERSION ${VERSION_FULL}) set(PROJECT_VERSION ${VERSION_FULL})
endif () endif ()
include(CTest)
option(PLUGIN_RTP_WEBRTC_AUDIO_PROCESSING "Use WebRTC audio processing" ON)
option(WITH_WASAPI "Use wasapi instead of directsound on windows" ON)
# https://gitlab.kitware.com/cmake/cmake/-/issues/19804
if (WIN32)
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .dll)
endif()
# Prepare Plugins # Prepare Plugins
set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp) set(DEFAULT_PLUGINS omemo;openpgp;http-files;ice;rtp)
if (WIN32) if (WIN32)
@ -81,8 +71,6 @@ macro(set_path what val desc)
endif() endif()
endmacro(set_path) endmacro(set_path)
set(LOCALEDIR_NAME "share/locale")
string(REGEX REPLACE "^liblib" "lib" LIBDIR_NAME "lib${LIB_SUFFIX}") string(REGEX REPLACE "^liblib" "lib" LIBDIR_NAME "lib${LIB_SUFFIX}")
set_path(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-independent files") set_path(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-independent files")
set_path(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-dependent files") set_path(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" "Installation directory for architecture-dependent files")
@ -96,7 +84,7 @@ set_path(SERVICE_FILE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/dbus-1/services" "Ins
set_path(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "Installation directory for icons") set_path(ICON_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/icons" "Installation directory for icons")
set_path(INCLUDE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/include" "Installation directory for C header files") set_path(INCLUDE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/include" "Installation directory for C header files")
set_path(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${LIBDIR_NAME}" "Installation directory for object code libraries") set_path(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${LIBDIR_NAME}" "Installation directory for object code libraries")
set_path(LOCALE_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/${LOCALEDIR_NAME}" "Installation directory for locale files") set_path(LOCALE_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/locale" "Installation directory for locale files")
set_path(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/dino/plugins" "Installation directory for dino plugin object code files") set_path(PLUGIN_INSTALL_DIR "${LIB_INSTALL_DIR}/dino/plugins" "Installation directory for dino plugin object code files")
set_path(VAPI_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/vala/vapi" "Installation directory for Vala API files") set_path(VAPI_INSTALL_DIR "${SHARE_INSTALL_PREFIX}/vala/vapi" "Installation directory for Vala API files")
@ -233,9 +221,6 @@ add_subdirectory(main)
add_subdirectory(crypto-vala) add_subdirectory(crypto-vala)
add_subdirectory(plugins) add_subdirectory(plugins)
# To generate a DEB package
include(Packing)
# uninstall target # uninstall target
configure_file("${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) configure_file("${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake_uninstall.cmake COMMENT "Uninstall the project...") add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/cmake_uninstall.cmake COMMENT "Uninstall the project...")

View file

@ -32,18 +32,15 @@ OS support
------------ ------------
* Linux (flatpaks are targeted for Ubuntu 22.04+) * Linux (flatpaks are targeted for Ubuntu 22.04+)
* Windows 10\11 * Windows 10\11
* MacOS via [brew](https://brew.sh/) (very experimental, see below)
Officially, we support only x86_64 architecture. Whilst you may be able to compile this code for ARM-based platforms (like Pinephone or MacOS), there's zero guarantees that it'll compile or function properly, as we don't have the hardware and the time to test it out. We only support 64 bit platforms.
Installation (prebuilt packages & AUR) Installation (prebuilt packages)
------------ ------------
Have a look at our [releases](https://github.com/mxlgv/dino/releases). Have a look at our [releases](https://github.com/mxlgv/dino/releases).
Windows installer is self-explanatory, and will place a shortcut on your desktop. Ignore the possible warning from Windows Defender: it's caused by the fact that installer does not have a digital signature. Windows installer is self-explanatory, and will place a shortcut on your desktop. Ignore the possible warning from Windows Defender: it's caused by the fact that installer does not have a digital signature.
Deb package is built and tested only for Ubuntu 22.04, but it may work on its derivatives as well, such as Pop!_OS or Linux Mint.
Flatpak is not present in Flathub yet, but you can install it manually. The commands below assume that you have "flatpak" package of your distro installed in your system. If it is not, please refer to https://flatpak.org/setup/ Flatpak is not present in Flathub yet, but you can install it manually. The commands below assume that you have "flatpak" package of your distro installed in your system. If it is not, please refer to https://flatpak.org/setup/
1) Download the .flatpak file from [releases](https://github.com/mxlgv/dino/releases) 1) Download the .flatpak file from [releases](https://github.com/mxlgv/dino/releases)
@ -51,7 +48,7 @@ Flatpak is not present in Flathub yet, but you can install it manually. The comm
``` ```
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
``` ```
3) Change the directory to the one you've downloaded the .flatpak to and run (you may have to change the file name in this command): 3) Change the directory to the one you've downloaded the .flatpak to and run:
``` ```
flatpak install ./im.dino.Dino.flatpak flatpak install ./im.dino.Dino.flatpak
``` ```
@ -64,38 +61,23 @@ Flatpak is not present in Flathub yet, but you can install it manually. The comm
flatpak run im.dino.Dino flatpak run im.dino.Dino
``` ```
Flatpak distribution is confirmed to be working on Arch Testing, Manjaro Stable, Void Linux and Linux Mint. Flatpak distribution is confirmed to be working on Arch Testing, Manjaro Stable and Void Linux.
Arch Linux & Manjaro users may install `dino-plus-git` package from [AUR](https://aur.archlinux.org/packages/dino-plus-git) via any helper of their choice.
Build on Linux Build on Linux
----- -----
Make sure to install all [dependencies](https://github.com/dino/dino/wiki/Build#dependencies). Make sure to install all [dependencies](https://github.com/dino/dino/wiki/Build#dependencies).
./configure --release --no-debug --with-tests --enable-plugin=notification-sound --prefix=/usr ./configure
make make
build/dino build/dino
Additionally, you might need to install `gst-plugins-bad` and `webrtc-audio-processing` packages (or however they might be called in your distro), if you want to have noise cancelling and gain control. If you don't want that or your distro don't have that package, add "--without-webrtc" (without quotes) to the first command.
If you want to use `meson` build system, follow the next instructions: If you want to use `meson` build system, follow the next instructions:
meson setup build meson setup build -Duse-soup2=true -Dcrypto-backend=gnutls -Dplugin-ice=enabled
meson configure --prefix $PWD/build/install --libdir lib build
meson compile -C build meson compile -C build
meson install -C build build/main/dino
LD_LIBRARY_PATH+=:$PWD/build/install/lib build/install/bin/dino
If your `nice` library depends on `libsoup-2.4` (consider `ldd` output for the `libnice.so`), you should additionally specify `-Duse-soup2=true` option. If your `nice` library depends on `libsoup-3.0`, you can omit `-Duse-soup2=true` option.
`LD_LIBRARY_PATH` should point to the directory containing the `libdino.so` library.
Skip `meson configure` step, if you want to install the program globally.
You can specify any convenient directory in the option `--prefix` where the program will be installed.
If there is no `webrtcdsp` plugin in your system (check this by calling `gst-inspect-1.0 webrtcdsp`) you should pass extra argument:
* `--without-webrtcdsp` for `./configure`;
* `-Dplugin-rtp-webrtc-audio-processing=disabled` for `meson`;
* `-DPLUGIN_RTP_WEBRTC_AUDIO_PROCESSING=OFF` for `cmake`.
Build on Windows (x86_64) Build on Windows (x86_64)
------------ ------------
@ -129,12 +111,6 @@ Now you should run:
./build-win64.sh --build-installer ./build-win64.sh --build-installer
``` ```
Build on MacOS
------------
Builds for Mac are currently available only as a [brew](https://brew.sh/) formulae. Please follow [these instructions](https://github.com/mxlgv/dino/blob/master/BUILD_MACOS.md) to produce a build.
Please note that Mac support is very experimental, and currently missing support for event notifications, among other things. You have been warned.
Resources Resources
--------- ---------
- Original project's [website](https://dino.im). - Original project's [website](https://dino.im).

View file

@ -21,9 +21,9 @@ fatal()
get_flatpak_dependencies() get_flatpak_dependencies()
{ {
msg "Installing Flatpak dependencies..." msg "Installing Flatpak dependencies..."
flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo flatpak remote-add --user --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
flatpak install flathub org.gnome.Sdk//44 flatpak install --user -y flathub org.gnome.Sdk//44
flatpak install flathub org.gnome.Platform//44 flatpak install --user -y flathub org.gnome.Platform//44
msg "Flatpak dependencies installed" msg "Flatpak dependencies installed"
} }

View file

@ -21,13 +21,13 @@ fatal()
download_yolort() download_yolort()
{ {
file_name=yolort.zip file_name=cppwinrt-2.0.210122.3+windows-10.0.19041+yolort-835cd4e.zip
yolort_dir="$PROJ_DIR/plugins/windows-notification/yolort" yolort_dir="$PROJ_DIR/plugins/windows-notification/yolort"
rm -rf "$yolort_dir" rm -rf "$yolort_dir"
mkdir "$yolort_dir" mkdir "$yolort_dir"
curl -L -o "$file_name" "https://github.com/mxlgv/YoloRT/releases/download/dev1/$file_name" curl -L -o "$file_name" "https://github.com/LAGonauta/YoloRT/releases/download/v1.0.0/$file_name"
echo "c2727e390da7e842f66e0a4cf0a9f5d9dfb665115bb554152d98f108d322bbc1 $file_name" | sha256sum --check --status echo "675a6d943c97b4acdbfaa473f68d3241d1798b31a67b5529c8d29fc0176a1707 $file_name" | sha256sum --check --status
unzip -o "$file_name" -d "$yolort_dir" unzip -o "$file_name" -d "$yolort_dir"
rm -f "$file_name" rm -f "$file_name"
} }
@ -74,6 +74,7 @@ prepare()
mingw64/mingw-w64-x86_64-nsis \ mingw64/mingw-w64-x86_64-nsis \
mingw64/mingw-w64-x86_64-libsignal-protocol-c \ mingw64/mingw-w64-x86_64-libsignal-protocol-c \
mingw64/mingw-w64-x86_64-icu \ mingw64/mingw-w64-x86_64-icu \
mingw64/mingw-w64-x86_64-webrtc-audio-processing \
mingw64/mingw-w64-x86_64-meson \ mingw64/mingw-w64-x86_64-meson \
git \ git \
make \ make \
@ -94,7 +95,7 @@ prepare()
configure_cmake() configure_cmake()
{ {
msg "Running configuration for Windows" msg "Running configuration for Windows"
./configure --program-prefix="$DIST_DIR" --no-debug --release --disable-fast-vapi --with-libsoup3 --with-tests ./configure --program-prefix="$DIST_DIR" --no-debug --release --disable-fast-vapi --with-libsoup3
msg "Configured!" msg "Configured!"
} }
@ -107,12 +108,6 @@ build_cmake()
make install make install
} }
test_cmake()
{
msg "Run tests"
make test
}
configure_meson() configure_meson()
{ {
arg=${1:-"none"} arg=${1:-"none"}
@ -130,26 +125,15 @@ configure_meson()
build_meson() build_meson()
{ {
meson compile -C $BUILD_DIR cd $BUILD_DIR && ninja
meson install -C $BUILD_DIR ninja install
} cd $PROJ_DIR
test_meson()
{
msg "Run tests"
meson test -C $BUILD_DIR
} }
dist_install() dist_install()
{ {
_dist_arg=${1:-$DIST_DIR} _dist_arg=${1:-$DIST_DIR}
msg "Generate dino-with-console.exe"
cp -f "$_dist_arg/bin/dino.exe" "$_dist_arg/bin/dino-with-console.exe"
# IMAGE_SUBSYSTEM_WINDOWS_CUI = 0x0003
# SUBSYSTEM_OFFSET = 0xDC (220)
printf '\x03\x00' | dd of="$_dist_arg/bin/dino-with-console.exe" bs=1 seek=220 count=2 conv=notrunc
msg "Copying MINGW64 dependencies" msg "Copying MINGW64 dependencies"
cp /mingw64/bin/gdbus.exe "$_dist_arg/bin" cp /mingw64/bin/gdbus.exe "$_dist_arg/bin"
cp /mingw64/bin/gspawn-win64-helper.exe "$_dist_arg/bin" cp /mingw64/bin/gspawn-win64-helper.exe "$_dist_arg/bin"
@ -215,7 +199,7 @@ help()
Usage: $0 [option] Usage: $0 [option]
Note: you may set the multiple options, but be sure that they will be Note: you may set the multiple options, but be surem that they will be
processed sequentially (one-by-one), e.g. command processed sequentially (one-by-one), e.g. command
$0 -s meson -c -b $0 -s meson -c -b
will run buld config and _after_ that run build using meson, while will run buld config and _after_ that run build using meson, while
@ -240,10 +224,7 @@ help()
configure build using selected build-system. configure build using selected build-system.
--build, -b --build, -b
invoke build. invoked build.
--test, -t
run tests.
--reconfig, -r --reconfig, -r
reconfigure project, if minor changes were reconfigure project, if minor changes were
@ -294,9 +275,6 @@ do
--build|-b) --build|-b)
build_${build_sys} build_${build_sys}
;; ;;
--test|-t)
test_${build_sys}
;;
--reconfig|-r) --reconfig|-r)
configure_${build_sys} reconfig configure_${build_sys} reconfig
;; ;;
@ -322,7 +300,6 @@ do
exit 1; exit 1;
fi fi
build_sys=$2 build_sys=$2
shift
;; ;;
-*) -*)
echo "Unknown option $1" echo "Unknown option $1"

View file

@ -47,7 +47,7 @@ function(_compute_version_from_git)
return() return()
endif (NOT git_result EQUAL 0) endif (NOT git_result EQUAL 0)
if (git_tag MATCHES "^v?([0-9]+[.]?[0-9]*[.]?[0-9]*[.]?[0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$") if (git_tag MATCHES "^v?([0-9]+[.]?[0-9]*[.]?[0-9]*)(-[.0-9A-Za-z-]+)?([+][.0-9A-Za-z-]+)?$")
set(VERSION_LAST_RELEASE "${CMAKE_MATCH_1}") set(VERSION_LAST_RELEASE "${CMAKE_MATCH_1}")
else () else ()
return() return()

View file

@ -1,11 +0,0 @@
find_library(GstWebrtcDsp_LIBRARY gstwebrtcdsp PATH_SUFFIXES gstreamer-1.0)
if(GstWebrtcDsp_LIBRARY_FOUND)
find_package(Gst)
set(GstWebrtcDsp_VERSION ${Gst_VERSION})
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(GstWebrtcDsp
REQUIRED_VARS GstWebrtcDsp_LIBRARY
VERSION_VAR GstWebrtcDsp_VERSION)

View file

@ -0,0 +1,12 @@
include(PkgConfigWithFallback)
find_pkg_config_with_fallback(WebRTCAudioProcessing
PKG_CONFIG_NAME webrtc-audio-processing
LIB_NAMES webrtc_audio_processing
INCLUDE_NAMES webrtc/modules/audio_processing/include/audio_processing.h
INCLUDE_DIR_SUFFIXES webrtc-audio-processing webrtc_audio_processing
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(WebRTCAudioProcessing
REQUIRED_VARS WebRTCAudioProcessing_LIBRARY
VERSION_VAR WebRTCAudioProcessing_VERSION)

View file

@ -1,34 +0,0 @@
# This is a package creation module using CPack.
# Currently only DEB package generation is supported.
set(CPACK_PACKAGE_NAME dino-plus)
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY
"modern XMPP/Jabber client software, based on Dino.
Dino+ is a fork of Dino, a modern XMPP/Jabber client
written in Vala using GTK+, which includes a few
relatively minor but important quality-of-life features.")
set(CPACK_VERBATIM_VARIABLES YES)
set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME})
set(CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_SOURCE_DIR}/_packages")
set(CPACK_PACKAGE_VERSION ${VERSION_FULL})
set(CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/mxlgv/dino")
set(CPACK_PACKAGE_CONTACT "maxlogaev@proton.me")
set(CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE)
set(CPACK_STRIP_FILES TRUE)
# For DEB only
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Maxim Logaev <${CPACK_PACKAGE_CONTACT}>")
set(CPACK_DEB_COMPONENT_INSTALL YES)
set(CPACK_DEBIAN_PACKAGE_SECTION "net")
set(CPACK_DEBIAN_PACKAGE_CONFLICTS "dino-im, dino-im-common")
set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "ca-certificates, dbus, fonts-noto-color-emoji, network-manager")
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY ">=")
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE_SHORT DESTINATION ${SHARE_INSTALL_PREFIX}/doc/${CPACK_PACKAGE_NAME} RENAME copyright)
install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${SHARE_INSTALL_PREFIX}/doc/${CPACK_PACKAGE_NAME})
include(CPack)

12
configure vendored
View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
OPTS=`getopt -o "h" --long \ OPTS=`getopt -o "h" --long \
help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsoup3,without-webrtcdsp,\ help,fetch-only,no-debug,disable-fast-vapi,with-tests,release,with-libsoup3,\
enable-plugin:,disable-plugin:,\ enable-plugin:,disable-plugin:,\
prefix:,program-prefix:,exec-prefix:,lib-suffix:,\ prefix:,program-prefix:,exec-prefix:,lib-suffix:,\
bindir:,libdir:,includedir:,datadir:,\ bindir:,libdir:,includedir:,datadir:,\
@ -15,13 +15,12 @@ eval set -- "$OPTS"
PREFIX=${PREFIX:-/usr/local} PREFIX=${PREFIX:-/usr/local}
ENABLED_PLUGINS= ENABLED_PLUGINS=
DISABLED_PLUGINS= DISABLED_PLUGINS=
BUILD_TESTS=no BUILD_TESTS=
BUILD_TYPE=Debug BUILD_TYPE=Debug
DISABLE_FAST_VAPI= DISABLE_FAST_VAPI=
LIB_SUFFIX= LIB_SUFFIX=
NO_DEBUG= NO_DEBUG=
USE_SOUP3= USE_SOUP3=
PLUGIN_RTP_WEBRTC_AUDIO_PROCESSING=yes
EXEC_PREFIX= EXEC_PREFIX=
BINDIR= BINDIR=
@ -55,7 +54,6 @@ Configuration:
--no-debug Build without debug symbols --no-debug Build without debug symbols
--release Configure to build an optimized release version --release Configure to build an optimized release version
--with-libsoup3 Build with libsoup-3.0 --with-libsoup3 Build with libsoup-3.0
--without-webrtcdsp Build without WebRTC audio processing
--with-tests Also build tests. --with-tests Also build tests.
Plugin configuration: Plugin configuration:
@ -111,7 +109,6 @@ while true; do
--valac-flags ) VALACFLAGS="$2"; shift; shift ;; --valac-flags ) VALACFLAGS="$2"; shift; shift ;;
--lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;; --lib-suffix ) LIB_SUFFIX="$2"; shift; shift ;;
--with-libsoup3 ) USE_SOUP3=yes; shift ;; --with-libsoup3 ) USE_SOUP3=yes; shift ;;
--without-webrtcdsp ) PLUGIN_RTP_WEBRTC_AUDIO_PROCESSING=no; shift ;;
--disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;; --disable-fast-vapi ) DISABLE_FAST_VAPI=yes; shift ;;
--no-debug ) NO_DEBUG=yes; shift ;; --no-debug ) NO_DEBUG=yes; shift ;;
--release ) BUILD_TYPE=RelWithDebInfo; shift ;; --release ) BUILD_TYPE=RelWithDebInfo; shift ;;
@ -210,7 +207,7 @@ cmake -G "$cmake_type" \
-DCMAKE_BUILD_TYPE="$BUILD_TYPE" \ -DCMAKE_BUILD_TYPE="$BUILD_TYPE" \
-DENABLED_PLUGINS="$ENABLED_PLUGINS" \ -DENABLED_PLUGINS="$ENABLED_PLUGINS" \
-DDISABLED_PLUGINS="$DISABLED_PLUGINS" \ -DDISABLED_PLUGINS="$DISABLED_PLUGINS" \
-DBUILD_TESTING="$BUILD_TESTS" \ -DBUILD_TESTS="$BUILD_TESTS" \
-DUSE_SOUP3="$USE_SOUP3" \ -DUSE_SOUP3="$USE_SOUP3" \
-DVALA_EXECUTABLE="$VALAC" \ -DVALA_EXECUTABLE="$VALAC" \
-DCMAKE_VALA_FLAGS="$VALACFLAGS" \ -DCMAKE_VALA_FLAGS="$VALACFLAGS" \
@ -222,7 +219,6 @@ cmake -G "$cmake_type" \
-DBIN_INSTALL_DIR="$BINDIR" \ -DBIN_INSTALL_DIR="$BINDIR" \
-DINCLUDE_INSTALL_DIR="$INCLUDEDIR" \ -DINCLUDE_INSTALL_DIR="$INCLUDEDIR" \
-DLIB_INSTALL_DIR="$LIBDIR" \ -DLIB_INSTALL_DIR="$LIBDIR" \
-DPLUGIN_RTP_WEBRTC_AUDIO_PROCESSING="$PLUGIN_RTP_WEBRTC_AUDIO_PROCESSING" \
-Wno-dev \ -Wno-dev \
.. || exit 9 .. || exit 9
@ -242,6 +238,8 @@ default:
@sh -c "cd build; $exec_command" @sh -c "cd build; $exec_command"
distclean: clean uninstall distclean: clean uninstall
test: default
echo "make test not yet supported"
%: %:
@sh -c "cd build; $exec_command \"\$@\"" @sh -c "cd build; $exec_command \"\$@\""
EOF EOF

View file

@ -1,7 +1,7 @@
{ {
"id": "im.dino.Dino", "id": "im.dino.Dino",
"runtime": "org.gnome.Platform", "runtime": "org.gnome.Platform",
"runtime-version": "46", "runtime-version": "44",
"sdk": "org.gnome.Sdk", "sdk": "org.gnome.Sdk",
"command": "dino", "command": "dino",
"finish-args": [ "finish-args": [
@ -60,11 +60,7 @@
"buildsystem": "cmake-ninja", "buildsystem": "cmake-ninja",
"builddir": true, "builddir": true,
"config-opts": [ "config-opts": [
"-DSOUP_VERSION=3", "-DSOUP_VERSION=3"
"-DNO_DEBUG=yes",
"-DCMAKE_BUILD_TYPE=Release",
"-DENABLED_PLUGINS=notification-sound",
"-DPLUGIN_RTP_WEBRTC_AUDIO_PROCESSING=OFF"
], ],
"cleanup": [ "cleanup": [
"/include", "/include",

View file

@ -102,8 +102,7 @@ DEPENDS
${CMAKE_BINARY_DIR}/exports/dino_i18n.h ${CMAKE_BINARY_DIR}/exports/dino_i18n.h
) )
add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" add_definitions(${VALA_CFLAGS} -DDINO_SYSTEM_PLUGIN_DIR="${PLUGIN_INSTALL_DIR}" -DDINO_SYSTEM_LIBDIR_NAME="${LIBDIR_NAME}" -DG_LOG_DOMAIN="libdino" -DDINO_VERSION=\"${PROJECT_VERSION}\")
-DDINO_VERSION=\"${PROJECT_VERSION}\" -DDINO_SYSTEM_LOCALEDIR_NAME="${LOCALEDIR_NAME}")
add_library(libdino SHARED ${LIBDINO_VALA_C} ${CMAKE_BINARY_DIR}/exports/dino_i18n.h) add_library(libdino SHARED ${LIBDINO_VALA_C} ${CMAKE_BINARY_DIR}/exports/dino_i18n.h)
add_dependencies(libdino dino-vapi) add_dependencies(libdino dino-vapi)
target_link_libraries(libdino xmpp-vala qlite ${LIBDINO_PACKAGES} m) target_link_libraries(libdino xmpp-vala qlite ${LIBDINO_PACKAGES} m)
@ -113,13 +112,12 @@ install(TARGETS libdino ${TARGET_INSTALL})
install(FILES ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/dino.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/dino.deps DESTINATION ${VAPI_INSTALL_DIR})
install(FILES ${CMAKE_BINARY_DIR}/exports/dino.h ${CMAKE_BINARY_DIR}/exports/dino_i18n.h DESTINATION ${INCLUDE_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/dino.h ${CMAKE_BINARY_DIR}/exports/dino_i18n.h DESTINATION ${INCLUDE_INSTALL_DIR})
if(BUILD_TESTING) if(BUILD_TESTS)
vala_precompile(LIBDINO_TEST_VALA_C vala_precompile(LIBDINO_TEST_VALA_C
SOURCES SOURCES
"tests/weak_map.vala" "tests/weak_map.vala"
"tests/testcase.vala" "tests/testcase.vala"
"tests/common.vala" "tests/common.vala"
"tests/jid.vala"
CUSTOM_VAPIS CUSTOM_VAPIS
${CMAKE_BINARY_DIR}/exports/dino_internal.vapi ${CMAKE_BINARY_DIR}/exports/dino_internal.vapi
${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi
@ -133,5 +131,4 @@ if(BUILD_TESTING)
add_definitions(${VALA_CFLAGS}) add_definitions(${VALA_CFLAGS})
add_executable(libdino-test ${LIBDINO_TEST_VALA_C}) add_executable(libdino-test ${LIBDINO_TEST_VALA_C})
target_link_libraries(libdino-test libdino) target_link_libraries(libdino-test libdino)
add_test(NAME libdino COMMAND libdino-test) endif(BUILD_TESTS)
endif(BUILD_TESTING)

View file

@ -79,9 +79,8 @@ sources = files(
) )
sources += [version_vala] sources += [version_vala]
c_args = [ c_args = [
'-DDINO_SYSTEM_LIBDIR_NAME="@0@"'.format(get_option('libdir')), '-DDINO_SYSTEM_LIBDIR_NAME="@0@"'.format(get_option('prefix') / get_option('libdir')),
'-DDINO_SYSTEM_LOCALEDIR_NAME="@0@"'.format(get_option('localedir')), '-DDINO_SYSTEM_PLUGIN_DIR="@0@"'.format(get_option('prefix') / get_option('plugindir')),
'-DDINO_SYSTEM_PLUGIN_DIR="@0@"'.format(get_option('prefix') / get_option('libdir') / get_option('plugindir')),
'-DG_LOG_DOMAIN="libdino"', '-DG_LOG_DOMAIN="libdino"',
] ]
lib_dino = library('dino', sources, c_args: c_args, include_directories: include_directories('src'), dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true]) lib_dino = library('dino', sources, c_args: c_args, include_directories: include_directories('src'), dependencies: dependencies, version: '0.0', install: true, install_dir: [true, true, true])
@ -89,12 +88,3 @@ dep_dino = declare_dependency(link_with: lib_dino, include_directories: include_
install_data('dino.deps', install_dir: get_option('datadir') / 'vala/vapi') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 install_data('dino.deps', install_dir: get_option('datadir') / 'vala/vapi') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756
install_headers('src/dino_i18n.h') install_headers('src/dino_i18n.h')
sources = files(
'tests/weak_map.vala',
'tests/testcase.vala',
'tests/common.vala',
'tests/jid.vala',
)
test_libdino = executable('test_libdino', sources, dependencies: dependencies + [dep_dino])
test('libdino', test_libdino)

View file

@ -25,13 +25,13 @@ public interface Application : GLib.Application {
public abstract void handle_uri(string jid, string query, Gee.Map<string, string> options); public abstract void handle_uri(string jid, string query, Gee.Map<string, string> options);
public void init(bool default_dark_theme) throws Error { public void init() throws Error {
if (DirUtils.create_with_parents(get_storage_dir(), 0700) == -1) { if (DirUtils.create_with_parents(get_storage_dir(), 0700) == -1) {
throw new Error(-1, 0, "Could not create storage dir \"%s\": %s", get_storage_dir(), FileUtils.error_from_errno(errno).to_string()); throw new Error(-1, 0, "Could not create storage dir \"%s\": %s", get_storage_dir(), FileUtils.error_from_errno(errno).to_string());
} }
this.db = new Database(Path.build_filename(get_storage_dir(), "dino.db")); this.db = new Database(Path.build_filename(get_storage_dir(), "dino.db"));
this.settings = new Dino.Entities.Settings.from_db(db, default_dark_theme); this.settings = new Dino.Entities.Settings.from_db(db);
this.stream_interactor = new StreamInteractor(db); this.stream_interactor = new StreamInteractor(db);
MessageProcessor.start(stream_interactor, db); MessageProcessor.start(stream_interactor, db);

View file

@ -71,7 +71,6 @@ public class FileTransfer : Object {
public int provider { get; set; } public int provider { get; set; }
public string info { get; set; } public string info { get; set; }
public Cancellable cancellable { get; default=new Cancellable(); } public Cancellable cancellable { get; default=new Cancellable(); }
public uint64 transferred_bytes { get; set; }
private Database? db; private Database? db;
private string storage_dir; private string storage_dir;

View file

@ -4,7 +4,7 @@ public class Settings : Object {
private Database db; private Database db;
public Settings.from_db(Database db, bool default_dark_theme) { public Settings.from_db(Database db) {
this.db = db; this.db = db;
send_typing_ = col_to_bool_or_default("send_typing", true); send_typing_ = col_to_bool_or_default("send_typing", true);
@ -15,7 +15,6 @@ public class Settings : Object {
default_encryption = col_to_encryption_or_default("default_encryption", Encryption.UNKNOWN); default_encryption = col_to_encryption_or_default("default_encryption", Encryption.UNKNOWN);
send_button = col_to_bool_or_default("send_button", false); send_button = col_to_bool_or_default("send_button", false);
enter_newline = col_to_bool_or_default("enter_newline", false); enter_newline = col_to_bool_or_default("enter_newline", false);
dark_theme = col_to_bool_or_default("dark_theme", default_dark_theme);
} }
private bool col_to_bool_or_default(string key, bool def) { private bool col_to_bool_or_default(string key, bool def) {
@ -129,20 +128,6 @@ public class Settings : Object {
enter_newline_ = value; enter_newline_ = value;
} }
} }
public signal void dark_theme_update(bool is_dark);
private bool dark_theme_;
public bool dark_theme {
get { return dark_theme_; }
set {
db.settings.upsert()
.value(db.settings.key, "dark_theme", true)
.value(db.settings.value, value.to_string())
.perform();
dark_theme_ = value;
dark_theme_update(value);
}
}
} }
} }

View file

@ -46,15 +46,6 @@ public class FileManager : StreamInteractionModule, Object {
return ret; return ret;
} }
private string sanitize_filename(string filename) {
#if _WIN32
GLib.Regex regex = new GLib.Regex("[<>:\"/\\|?*]");
#else
GLib.Regex regex = new GLib.Regex("[/]");
#endif
return regex.replace(filename, -1, 0, "_");
}
public async void send_file(File file, Conversation conversation) { public async void send_file(File file, Conversation conversation) {
FileTransfer file_transfer = new FileTransfer(); FileTransfer file_transfer = new FileTransfer();
file_transfer.account = conversation.account; file_transfer.account = conversation.account;
@ -129,7 +120,6 @@ public class FileManager : StreamInteractionModule, Object {
} }
yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta); yield file_sender.send_file(conversation, file_transfer, file_send_data, file_meta);
file_transfer.state = FileTransfer.State.COMPLETE;
} catch (Error e) { } catch (Error e) {
warning("Send file error: %s", e.message); warning("Send file error: %s", e.message);
@ -252,7 +242,7 @@ public class FileManager : StreamInteractionModule, Object {
} }
// Save file // Save file
string filename = Random.next_int().to_string("%x") + "_" + sanitize_filename(file_transfer.file_name); string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name;
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
@ -260,7 +250,6 @@ public class FileManager : StreamInteractionModule, Object {
ssize_t read; ssize_t read;
while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) { while ((read = yield input_stream.read_async(buffer, Priority.LOW, file_transfer.cancellable)) > 0) {
buffer.length = (int) read; buffer.length = (int) read;
file_transfer.transferred_bytes += (uint64)read;
yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable); yield os.write_async(buffer, Priority.LOW, file_transfer.cancellable);
buffer.length = 1024; buffer.length = 1024;
} }
@ -308,7 +297,6 @@ public class FileManager : StreamInteractionModule, Object {
file_transfer.file_name = file_meta.file_name; file_transfer.file_name = file_meta.file_name;
file_transfer.size = (int)file_meta.size; file_transfer.size = (int)file_meta.size;
file_transfer.info = info; file_transfer.info = info;
file_transfer.transferred_bytes = 0;
var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta); var encryption = file_provider.get_encryption(file_transfer, receive_data, file_meta);
if (encryption != Encryption.NONE) file_transfer.encryption = encryption; if (encryption != Encryption.NONE) file_transfer.encryption = encryption;
@ -341,11 +329,11 @@ public class FileManager : StreamInteractionModule, Object {
private async void save_file(FileTransfer file_transfer) throws FileSendError { private async void save_file(FileTransfer file_transfer) throws FileSendError {
try { try {
string filename = Random.next_int().to_string("%x") + "_" + sanitize_filename(file_transfer.file_name); string filename = Random.next_int().to_string("%x") + "_" + file_transfer.file_name;
File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename)); File file = File.new_for_path(Path.build_filename(get_storage_dir(), filename));
OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION); OutputStream os = file.create(FileCreateFlags.REPLACE_DESTINATION);
yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET); yield os.splice_async(file_transfer.input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
file_transfer.state = FileTransfer.State.IN_PROGRESS; file_transfer.state = FileTransfer.State.COMPLETE;
file_transfer.path = filename; file_transfer.path = filename;
file_transfer.input_stream = yield file.read_async(); file_transfer.input_stream = yield file.read_async();
} catch (Error e) { } catch (Error e) {

View file

@ -220,19 +220,7 @@ public class JingleFileSender : FileSender, Object {
} }
} }
try { try {
var? module = stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY); yield stream.get_module(Xep.JingleFileTransfer.Module.IDENTITY).offer_file_stream(stream, full_jid, file_transfer.input_stream, file_transfer.server_file_name, file_meta.size, precondition_name, precondition_options);
if (module == null)
throw new FileSendError.UPLOAD_FAILED("unexpected null module");
module.transferred_bytes.connect((bytes) => {
file_transfer.transferred_bytes += bytes;
});
yield module.offer_file_stream(stream, full_jid,
file_transfer.cancellable, file_transfer.input_stream,
file_transfer.server_file_name, file_meta.size,
precondition_name, precondition_options);
} catch (Error e) { } catch (Error e) {
throw new FileSendError.UPLOAD_FAILED(@"offer_file_stream failed: $(e.message)"); throw new FileSendError.UPLOAD_FAILED(@"offer_file_stream failed: $(e.message)");
} }

View file

@ -6,7 +6,7 @@ namespace Dino {
public class Util { public class Util {
#if _WIN32 #if _WIN32
[CCode (cname = "ShellExecuteA", cheader_filename = "windows.h")] [CCode (cname = "ShellExecuteA", cheader_filename = "windows.h")]
private static extern int* ShellExecuteA(int* hwnd, string operation, string file, string parameters, string directory, int showCmd); private static extern int ShellExecuteA(int* hwnd, string operation, string file, string parameters, string directory, int showCmd);
[CCode (cname = "CoInitialize", cheader_filename = "windows.h")] [CCode (cname = "CoInitialize", cheader_filename = "windows.h")]
private static extern int CoInitialize(void* reserved); private static extern int CoInitialize(void* reserved);
@ -14,7 +14,7 @@ public class Util {
[CCode (cname = "CoUninitialize", cheader_filename = "windows.h")] [CCode (cname = "CoUninitialize", cheader_filename = "windows.h")]
private static extern void CoUninitialize(); private static extern void CoUninitialize();
private static int* ShellExecute(string operation, string file) { private static int ShellExecute(string operation, string file) {
CoInitialize(null); CoInitialize(null);
var result = ShellExecuteA(null, operation, file, null, null, 1); var result = ShellExecuteA(null, operation, file, null, null, 1);
CoUninitialize(); CoUninitialize();

View file

@ -2,7 +2,6 @@ namespace Dino {
private extern const string SYSTEM_LIBDIR_NAME; private extern const string SYSTEM_LIBDIR_NAME;
private extern const string SYSTEM_PLUGIN_DIR; private extern const string SYSTEM_PLUGIN_DIR;
private extern const string SYSTEM_LOCALEDIR_NAME;
public class SearchPathGenerator { public class SearchPathGenerator {
@ -13,21 +12,14 @@ public class SearchPathGenerator {
} }
public string get_locale_path(string gettext_package, string locale_install_dir) { public string get_locale_path(string gettext_package, string locale_install_dir) {
if (exec_path != null) { string? locale_dir = null;
var exec_dir = Path.get_dirname(exec_path); if (Path.get_dirname(exec_path).contains("dino") || Path.get_dirname(exec_path) == "." || Path.get_dirname(exec_path).contains("build")) {
string[] search_paths = new string[] { string exec_locale = Path.build_filename(Path.get_dirname(exec_path), "locale");
Path.build_filename(exec_dir, "locale"), if (FileUtils.test(Path.build_filename(exec_locale, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) {
Path.build_filename(Path.get_dirname(exec_dir), SYSTEM_LOCALEDIR_NAME) locale_dir = exec_locale;
};
foreach (var path in search_paths) {
if (FileUtils.test(Path.build_filename(path, "en", "LC_MESSAGES", gettext_package + ".mo"), FileTest.IS_REGULAR)) {
debug(@"Found locale $(gettext_package).mo in $(path)");
return path;
}
} }
} }
return locale_dir ?? locale_install_dir;
return locale_install_dir;
} }
public string[] get_plugin_paths() { public string[] get_plugin_paths() {

View file

@ -4,7 +4,6 @@ int main(string[] args) {
GLib.Test.init(ref args); GLib.Test.init(ref args);
GLib.Test.set_nonfatal_assertions(); GLib.Test.set_nonfatal_assertions();
TestSuite.get_root().add_suite(new WeakMapTest().get_suite()); TestSuite.get_root().add_suite(new WeakMapTest().get_suite());
TestSuite.get_root().add_suite(new JidTest().get_suite());
return GLib.Test.run(); return GLib.Test.run();
} }

View file

@ -1,5 +1,4 @@
using Dino.Entities; using Dino.Entities;
using Xmpp;
namespace Dino.Test { namespace Dino.Test {
@ -13,39 +12,27 @@ class JidTest : Gee.TestCase {
} }
private void test_parse() { private void test_parse() {
try { Jid jid = new Jid("user@example.com/res");
Jid jid = new Jid("user@example.com/res"); fail_if(jid.localpart != "user");
fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com");
fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res");
fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res");
fail_if(jid.to_string() != "user@example.com/res");
} catch (Error e) {
fail_if_reached(@"Throws $(e.message)");
}
} }
private void test_components() { private void test_components() {
try { Jid jid = new Jid.components("user", "example.com", "res");
Jid jid = new Jid.components("user", "example.com", "res"); fail_if(jid.localpart != "user");
fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com");
fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res");
fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res");
fail_if(jid.to_string() != "user@example.com/res");
} catch (Error e) {
fail_if_reached(@"Throws $(e.message)");
}
} }
private void test_with_res() { private void test_with_res() {
try { Jid jid = new Jid.with_resource("user@example.com", "res");
Jid jid = new Jid("user@example.com").with_resource("res"); fail_if(jid.localpart != "user");
fail_if(jid.localpart != "user"); fail_if(jid.domainpart != "example.com");
fail_if(jid.domainpart != "example.com"); fail_if(jid.resourcepart != "res");
fail_if(jid.resourcepart != "res"); fail_if(jid.to_string() != "user@example.com/res");
fail_if(jid.to_string() != "user@example.com/res");
} catch (Error e) {
fail_if_reached(@"Throws $(e.message)");
}
} }
} }

View file

@ -49,8 +49,6 @@ set(RESOURCE_LIST
icons/scalable/status/dino-status-chat.svg icons/scalable/status/dino-status-chat.svg
icons/scalable/status/dino-status-dnd.svg icons/scalable/status/dino-status-dnd.svg
icons/scalable/status/dino-status-online.svg icons/scalable/status/dino-status-online.svg
icons/scalable/status/dino-status-online-bright.svg
icons/scalable/status/dino-status-offline.svg
icons/scalable/status/dino-tick-symbolic.svg icons/scalable/status/dino-tick-symbolic.svg
icons/scalable/status/dino-video-off-symbolic.svg icons/scalable/status/dino-video-off-symbolic.svg
icons/scalable/status/dino-video-symbolic.svg icons/scalable/status/dino-video-symbolic.svg

View file

@ -51,8 +51,6 @@
<file>icons/scalable/status/dino-status-chat.svg</file> <file>icons/scalable/status/dino-status-chat.svg</file>
<file>icons/scalable/status/dino-status-dnd.svg</file> <file>icons/scalable/status/dino-status-dnd.svg</file>
<file>icons/scalable/status/dino-status-online.svg</file> <file>icons/scalable/status/dino-status-online.svg</file>
<file>icons/scalable/status/dino-status-online-bright.svg</file>
<file>icons/scalable/status/dino-status-offline.svg</file>
<file>icons/scalable/status/dino-tick-symbolic.svg</file> <file>icons/scalable/status/dino-tick-symbolic.svg</file>
<file>icons/scalable/status/dino-video-off-symbolic.svg</file> <file>icons/scalable/status/dino-video-off-symbolic.svg</file>
<file>icons/scalable/status/dino-video-symbolic.svg</file> <file>icons/scalable/status/dino-video-symbolic.svg</file>

View file

@ -1,11 +0,0 @@
<?xml version="1.0"?>
<svg width="99.999997" height="99.999997" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<g class="layer">
<title>Layer 1</title>
<g id="svg_1">
<circle cx="50" cy="51" fill="#ff0000" fill-rule="evenodd" id="svg_2" r="50" stroke="#ff0000"/>
<path d="m20,51l60,0" fill="#ff0000" id="svg_3" stroke="#ff0000" stroke-linecap="round" stroke-width="15"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 444 B

View file

@ -1,10 +0,0 @@
<?xml version="1.0"?>
<svg width="99.999997" height="99.999997" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1">
<g class="layer">
<title>Layer 1</title>
<g id="svg_1">
<circle cx="50" cy="50" fill="#00ff00" fill-rule="evenodd" id="svg_2" r="50"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 316 B

View file

@ -1,10 +1,6 @@
<?xml version="1.0"?> <?xml version="1.0" encoding="UTF-8"?>
<svg width="99.999997" height="99.999997" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" version="1.1"> <svg width="28.222mm" height="28.222mm" version="1.1" viewBox="0 0 99.999997 99.999997" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-62.857 -678.08)">
<g class="layer"> <circle cx="112.86" cy="728.08" r="50" style="fill-rule:evenodd;fill:#81c784"/>
<title>Layer 1</title>
<g id="svg_1">
<circle cx="50" cy="50" fill="#00ff00" fill-rule="evenodd" id="svg_2" r="50"/>
</g>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 316 B

After

Width:  |  Height:  |  Size: 302 B

View file

@ -4,7 +4,7 @@
<launchable type="desktop-id">im.dino.Dino.desktop</launchable> <launchable type="desktop-id">im.dino.Dino.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license> <project_license>GPL-3.0+</project_license>
<name>Dino+</name> <name>Dino</name>
<summary>Modern XMPP Chat Client</summary> <summary>Modern XMPP Chat Client</summary>
<summary xml:lang="zh_TW">現代化的 XMPP 用戶端聊天軟件</summary> <summary xml:lang="zh_TW">現代化的 XMPP 用戶端聊天軟件</summary>
<summary xml:lang="zh_CN">现代 XMPP 聊天客户端</summary> <summary xml:lang="zh_CN">现代 XMPP 聊天客户端</summary>

View file

@ -4,7 +4,7 @@
<launchable type="desktop-id">im.dino.Dino.desktop</launchable> <launchable type="desktop-id">im.dino.Dino.desktop</launchable>
<metadata_license>CC0-1.0</metadata_license> <metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-3.0+</project_license> <project_license>GPL-3.0+</project_license>
<name>Dino+</name> <name>Dino</name>
<summary>Modern XMPP Chat Client</summary> <summary>Modern XMPP Chat Client</summary>
<description> <description>
<p>Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.</p> <p>Dino is a modern open-source chat client for the desktop. It focuses on providing a clean and reliable Jabber/XMPP experience while having your privacy in mind.</p>

View file

@ -15,8 +15,7 @@
</child> </child>
<child> <child>
<object class="GtkLabel" id="name_label"> <object class="GtkLabel" id="name_label">
<property name="width-chars">20</property> <property name="max_width_chars">1</property>
<property name="max-width-chars">40</property>
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<property name="hexpand">1</property> <property name="hexpand">1</property>
<property name="xalign">0</property> <property name="xalign">0</property>
@ -26,5 +25,17 @@
</layout> </layout>
</object> </object>
</child> </child>
<child>
<object class="GtkLabel" id="status_label">
<property name="max_width_chars">1</property>
<property name="ellipsize">end</property>
<property name="hexpand">1</property>
<property name="xalign">0</property>
<layout>
<property name="column">2</property>
<property name="row">0</property>
</layout>
</object>
</child>
</object> </object>
</interface> </interface>

View file

@ -140,22 +140,6 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">_Dark theme</property>
<property name="use-underline">True</property>
<property name="activatable-widget">dark_theme</property>
<child type="suffix">
<object class="GtkSwitch" id="dark_theme">
<property name="valign">center</property>
</object>
</child>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
</template> </template>

View file

@ -49,11 +49,7 @@ window.dino-main .dino-conversation .message-box.highlight:not(.highlight-once)
} }
window.dino-main .dino-conversation .message-box { window.dino-main .dino-conversation .message-box {
padding: 3px 15px 3px 15px; padding: 6px 15px 6px 15px;
}
window.dino-main .dino-conversation .has-skeleton {
margin-top: 10px;
} }
window.dino-main .dino-conversation .message-box:not(.has-skeleton) { window.dino-main .dino-conversation .message-box:not(.has-skeleton) {

View file

@ -128,7 +128,7 @@ if host_machine.system() == 'windows'
sources += windows.compile_resources('dino-info.rc') sources += windows.compile_resources('dino-info.rc')
endif endif
exe_dino = executable('dino', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, install: true, link_args: link_args, win_subsystem: 'windows') exe_dino = executable('dino', sources, c_args: c_args, vala_args: vala_args, dependencies: dependencies, install: true, link_args: link_args)
install_data('data/icons/scalable/apps/im.dino.Dino-symbolic.svg', install_dir: get_option('datadir') / 'hicolor/symbolic/apps') install_data('data/icons/scalable/apps/im.dino.Dino-symbolic.svg', install_dir: get_option('datadir') / 'hicolor/symbolic/apps')
install_data('data/icons/scalable/apps/im.dino.Dino.svg', install_dir: get_option('datadir') / 'hicolor/scalable/apps') install_data('data/icons/scalable/apps/im.dino.Dino.svg', install_dir: get_option('datadir') / 'hicolor/scalable/apps')

View file

@ -533,10 +533,12 @@ msgid "Local Settings"
msgstr "" msgstr ""
#: main/src/ui/contact_details/settings_provider.vala:28 #: main/src/ui/contact_details/settings_provider.vala:28
#: main/data/settings_dialog.ui:23
msgid "Send typing notifications" msgid "Send typing notifications"
msgstr "" msgstr ""
#: main/src/ui/contact_details/settings_provider.vala:33 #: main/src/ui/contact_details/settings_provider.vala:33
#: main/data/settings_dialog.ui:32
msgid "Send read receipts" msgid "Send read receipts"
msgstr "" msgstr ""
@ -1010,48 +1012,16 @@ msgstr ""
msgid "Add Contact" msgid "Add Contact"
msgstr "" msgstr ""
#: main/data/settings_dialog.ui:14 #: main/data/settings_dialog.ui:41
msgid "Default encryption for 1-on-1 chats"
msgstr ""
#: main/data/settings_dialog.ui:19
msgid "Ask"
msgstr ""
#: main/data/settings_dialog.ui:57
msgid "Send _Typing Notifications"
msgstr ""
#: main/data/settings_dialog.ui:85
msgid "_Notifications"
msgstr ""
#: main/data/settings_dialog.ui:69
msgid "Send _Read Receipts"
msgstr ""
#: main/data/settings_dialog.ui:86
msgid "Notify when a new message arrives" msgid "Notify when a new message arrives"
msgstr "" msgstr ""
#: main/data/settings_dialog.ui:102 #: main/data/settings_dialog.ui:50
msgid "_Convert Smileys to Emoji" msgid "Convert smileys to emojis"
msgstr "" msgstr ""
#: main/data/settings_dialog.ui:118 #: main/data/settings_dialog.ui:59
msgid "_Display send button" msgid "Check spelling"
msgstr ""
#: main/data/settings_dialog.ui:130
msgid "_Use Enter key to start a new line"
msgstr ""
#: main/data/settings_dialog.ui:131
msgid "If disabled, use Shift+Enter to start a new line"
msgstr ""
#: main/data/settings_dialog.ui:147
msgid "_Dark theme"
msgstr "" msgstr ""
#: main/data/im.dino.Dino.appdata.xml.in:7 #: main/data/im.dino.Dino.appdata.xml.in:7

View file

@ -563,7 +563,7 @@ msgstr "Уведомления"
#: main/src/ui/contact_details/settings_provider.vala:55 #: main/src/ui/contact_details/settings_provider.vala:55
msgid "Pin conversation" msgid "Pin conversation"
msgstr "Закрепить беседу" msgstr ""
#: main/src/ui/contact_details/settings_provider.vala:55 #: main/src/ui/contact_details/settings_provider.vala:55
msgid "Pins the conversation to the top of the conversation list" msgid "Pins the conversation to the top of the conversation list"
@ -826,16 +826,16 @@ msgstr "Прочтите %s, чтобы узнать о процессе авт
#: main/src/ui/conversation_content_view/message_widget.vala:213 #: main/src/ui/conversation_content_view/message_widget.vala:213
msgid "Edit message" msgid "Edit message"
msgstr "Редактировать сообщение" msgstr ""
#: main/src/ui/conversation_content_view/reactions_widget.vala:102 #: main/src/ui/conversation_content_view/reactions_widget.vala:102
msgid "You" msgid "You"
msgstr "Вы" msgstr ""
#: main/src/ui/conversation_content_view/reactions_widget.vala:128 #: main/src/ui/conversation_content_view/reactions_widget.vala:128
#: main/src/ui/conversation_content_view/item_actions.vala:8 #: main/src/ui/conversation_content_view/item_actions.vala:8
msgid "Add reaction" msgid "Add reaction"
msgstr "Добавить реакцию" msgstr ""
#: main/src/ui/conversation_content_view/file_image_widget.vala:53 #: main/src/ui/conversation_content_view/file_image_widget.vala:53
#: main/src/ui/conversation_content_view/file_default_widget.vala:57 #: main/src/ui/conversation_content_view/file_default_widget.vala:57
@ -947,7 +947,7 @@ msgstr "несколько секунд"
#: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:191
msgid "Delivered" msgid "Delivered"
msgstr "Доставленo" msgstr ""
#: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195 #: main/src/ui/conversation_content_view/conversation_item_skeleton.vala:195
msgid "Read" msgid "Read"
@ -959,15 +959,15 @@ msgstr "Этот контакт хочет добавить вас в свой
#: main/src/ui/conversation_content_view/item_actions.vala:21 #: main/src/ui/conversation_content_view/item_actions.vala:21
msgid "This conversation does not support reactions." msgid "This conversation does not support reactions."
msgstr "Этот разговор не поддерживает реакции." msgstr ""
#: main/src/ui/conversation_content_view/item_actions.vala:33 #: main/src/ui/conversation_content_view/item_actions.vala:33
msgid "Reply" msgid "Reply"
msgstr "Ответить" msgstr ""
#: main/src/ui/conversation_content_view/item_actions.vala:43 #: main/src/ui/conversation_content_view/item_actions.vala:43
msgid "This conversation does not support replies." msgid "This conversation does not support replies."
msgstr "Этот разговор не поддерживает ответы" msgstr ""
#: main/src/ui/conversation_content_view/file_default_widget.vala:64 #: main/src/ui/conversation_content_view/file_default_widget.vala:64
#, c-format #, c-format
@ -1006,7 +1006,7 @@ msgstr "Тут пустовато"
#: main/data/unified_main_content.ui:47 #: main/data/unified_main_content.ui:47
msgid "Click + to start a chat or join a channel" msgid "Click + to start a chat or join a channel"
msgstr "Нажмите +, чтобы начать чат или присоединиться к каналу" msgstr ""
#: main/data/add_conversation/conference_details_fragment.ui:20 #: main/data/add_conversation/conference_details_fragment.ui:20
#: main/data/add_conversation/add_groupchat_dialog.ui:40 #: main/data/add_conversation/add_groupchat_dialog.ui:40
@ -1028,49 +1028,17 @@ msgstr "Псевдоним"
msgid "Add Contact" msgid "Add Contact"
msgstr "Добавить контакт" msgstr "Добавить контакт"
#: main/data/settings_dialog.ui:14 #: main/data/settings_dialog.ui:41
msgid "Default encryption for 1-on-1 chats"
msgstr "Шифрование по умолчанию для 1-на-1 чатов"
#: main/data/settings_dialog.ui:19
msgid "Ask"
msgstr "Спрашивать"
#: main/data/settings_dialog.ui:57
msgid "Send _Typing Notifications"
msgstr "Отправлять уведомления при наборе сообщения"
#: main/data/settings_dialog.ui:85
msgid "_Notifications"
msgstr "Уведомления"
#: main/data/settings_dialog.ui:69
msgid "Send _Read Receipts"
msgstr "Отправлять уведомления о прочтении"
#: main/data/settings_dialog.ui:86
msgid "Notify when a new message arrives" msgid "Notify when a new message arrives"
msgstr "Уведомлять о новых сообщениях" msgstr "Уведомлять о новых сообщениях"
#: main/data/settings_dialog.ui:102 #: main/data/settings_dialog.ui:50
msgid "_Convert Smileys to Emoji" msgid "Convert smileys to emojis"
msgstr "Превращать смайлы в эмодзи" msgstr "Превращать смайлы в эмодзи"
#: main/data/settings_dialog.ui:118 #: main/data/settings_dialog.ui:59
msgid "_Display send button" msgid "Check spelling"
msgstr "Показывать кнопку отправки" msgstr "Проверка орфографии"
#: main/data/settings_dialog.ui:130
msgid "_Use Enter key to start a new line"
msgstr "Использовать Enter для вставки новой строки"
#: main/data/settings_dialog.ui:131
msgid "If disabled, use Shift+Enter to start a new line"
msgstr "Если опция недоступна, используйте Shift+Enter для вставки новой строки"
#: main/data/settings_dialog.ui:147
msgid "_Dark theme"
msgstr "Тёмная тема"
#: main/data/im.dino.Dino.appdata.xml.in:7 #: main/data/im.dino.Dino.appdata.xml.in:7
msgid "Modern XMPP Chat Client" msgid "Modern XMPP Chat Client"

View file

@ -34,13 +34,7 @@ protected class RosterList {
foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id); foreach (ulong handler_id in handler_ids) stream_interactor.get_module(RosterManager.IDENTITY).disconnect(handler_id);
}); });
foreach (Account a in accounts) { foreach (Account a in accounts) fetch_roster_items(a);
ListRow own_account_row = new ListRow.from_jid(stream_interactor, a.bare_jid, a, accounts.size > 1);
ListBoxRow own_account_lbrow = new ListBoxRow() { child = own_account_row };
list_box.append(own_account_lbrow);
fetch_roster_items(a);
}
} }
private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) { private void on_removed_roster_item(Account account, Jid jid, Roster.Item roster_item) {
@ -53,7 +47,7 @@ protected class RosterList {
private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) { private void on_updated_roster_item(Account account, Jid jid, Roster.Item roster_item) {
on_removed_roster_item(account, jid, roster_item); on_removed_roster_item(account, jid, roster_item);
ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1); ListRow row = new ListRow.from_jid(stream_interactor, roster_item.jid, account, accounts.size > 1);
ListBoxRow list_box_row = new ListBoxRow() { child = row }; ListBoxRow list_box_row = new ListBoxRow() { child=row };
rows[account][jid] = list_box_row; rows[account][jid] = list_box_row;
list_box.append(list_box_row); list_box.append(list_box_row);
list_box.invalidate_sort(); list_box.invalidate_sort();

View file

@ -29,10 +29,7 @@ public class Dino.Ui.Application : Adw.Application, Dino.Application {
public Application() throws Error { public Application() throws Error {
Object(application_id: "im.dino.Dino", flags: ApplicationFlags.HANDLES_OPEN); Object(application_id: "im.dino.Dino", flags: ApplicationFlags.HANDLES_OPEN);
init();
var style_manager = Adw.StyleManager.get_default();
bool system_dark_theme = style_manager.system_supports_color_schemes && style_manager.dark;
init(system_dark_theme);
Environment.set_application_name("Dino"); Environment.set_application_name("Dino");
Window.set_default_icon_name("im.dino.Dino"); Window.set_default_icon_name("im.dino.Dino");
@ -71,19 +68,6 @@ public class Dino.Ui.Application : Adw.Application, Dino.Application {
} }
} }
}); });
settings.dark_theme_update.connect((is_dark) => {
var manager = Adw.StyleManager.get_default();
if (is_dark != manager.dark) {
if (is_dark) {
manager.set_color_scheme(Adw.ColorScheme.FORCE_DARK);
} else {
manager.set_color_scheme(Adw.ColorScheme.FORCE_LIGHT);
}
}
});
settings.dark_theme_update(settings.dark_theme); // Change theme at startup.
}); });
activate.connect(() => { activate.connect(() => {
@ -289,7 +273,7 @@ public class Dino.Ui.Application : Adw.Application, Dino.Application {
about_window.title = _("About Dino+"); about_window.title = _("About Dino+");
about_window.version = version; about_window.version = version;
about_window.website = "https://github.com/mxlgv/dino/"; about_window.website = "https://github.com/mxlgv/dino/";
about_window.copyright = "Copyright © 2016-2024 - Dino and Dino+ Teams"; about_window.copyright = "Copyright © 2016-2024 - Dino & Dino+ Teams";
about_window.license_type = License.GPL_3_0; about_window.license_type = License.GPL_3_0;
if (!use_csd()) { if (!use_csd()) {

View file

@ -39,8 +39,7 @@ public class FileDefaultWidget : Box {
}); });
} }
public void update_file_info(string? mime_type, uint64 transferred_bytes, public void update_file_info(string? mime_type, FileTransfer.State state, long size) {
bool direction, FileTransfer.State state, long size) {
this.state = state; this.state = state;
spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap spinner.stop(); // A hidden spinning spinner still uses CPU. Deactivate asap
@ -50,7 +49,7 @@ public class FileDefaultWidget : Box {
switch (state) { switch (state) {
case FileTransfer.State.COMPLETE: case FileTransfer.State.COMPLETE:
mime_label.label = _("%s offered: %s").printf(mime_description, get_size_string(size)); mime_label.label = mime_description;
image_stack.set_visible_child_name("content_type_image"); image_stack.set_visible_child_name("content_type_image");
// Create a menu // Create a menu
@ -62,17 +61,7 @@ public class FileDefaultWidget : Box {
popover_menu.closed.connect(on_pointer_left); popover_menu.closed.connect(on_pointer_left);
break; break;
case FileTransfer.State.IN_PROGRESS: case FileTransfer.State.IN_PROGRESS:
uint progress = 0; mime_label.label = _("Downloading %s…").printf(get_size_string(size));
if (size > 0)
progress = (uint)((transferred_bytes * (uint64)100) / (uint64)size);
if (direction == FileTransfer.DIRECTION_SENT) {
mime_label.label = _("Uploading %s (%u%%)…").printf(get_size_string(size), progress);
}
else {
mime_label.label = _("Downloading %s (%u%%)…").printf(get_size_string(size), progress);
}
spinner.start(); spinner.start();
image_stack.set_visible_child_name("spinner"); image_stack.set_visible_child_name("spinner");

View file

@ -73,9 +73,6 @@ public class FileImageWidget : Box {
image_overlay_toolbar.visible = false; image_overlay_toolbar.visible = false;
}); });
// Set tooltip to display the file name on hover
image.set_tooltip_text(file_name);
this.append(overlay); this.append(overlay);
} }
} }

View file

@ -89,9 +89,7 @@ public class FileWidget : SizeRequestBox {
} }
private async void update_widget() { private async void update_widget() {
if (show_image() && state != State.IMAGE if (show_image() && state != State.IMAGE) {
&& file_transfer.state == FileTransfer.State.COMPLETE
&& file_transfer.get_file().query_exists()) {
var content_bak = content; var content_bak = content;
FileImageWidget file_image_widget = null; FileImageWidget file_image_widget = null;
@ -110,8 +108,7 @@ public class FileWidget : SizeRequestBox {
} catch (Error e) { } } catch (Error e) { }
} }
if (state != State.DEFAULT || if (!show_image() && state != State.DEFAULT) {
(file_transfer.state == FileTransfer.State.COMPLETE && !file_transfer.get_file().query_exists())) {
if (content != null) this.remove(content); if (content != null) this.remove(content);
FileDefaultWidget default_file_widget = new FileDefaultWidget(); FileDefaultWidget default_file_widget = new FileDefaultWidget();
default_widget_controller = new FileDefaultWidgetController(default_file_widget); default_widget_controller = new FileDefaultWidgetController(default_file_widget);
@ -178,16 +175,10 @@ public class FileWidgetController : Object {
} }
private void open_file() { private void open_file() {
if (file_transfer.get_file().query_exists()) { try{
try { Dino.Util.launch_default_for_uri(file_transfer.get_file().get_uri());
Dino.Util.launch_default_for_uri(file_transfer.get_file().get_uri()); } catch (Error err) {
} catch (Error err) { warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
warning("Failed to open %s - %s", file_transfer.get_file().get_uri(), err.message);
}
} else {
warning("File %s does not exist", file_transfer.get_file().get_uri());
file_transfer.state = FileTransfer.State.NOT_STARTED;
widget.activate_action("file.download", null);
} }
} }
@ -224,7 +215,6 @@ public class FileDefaultWidgetController : Object {
private FileTransfer? file_transfer; private FileTransfer? file_transfer;
public string file_transfer_state { get; set; } public string file_transfer_state { get; set; }
public string file_transfer_mime_type { get; set; } public string file_transfer_mime_type { get; set; }
public uint64 file_transfer_transferred_bytes { get; set; }
private FileTransfer.State state; private FileTransfer.State state;
@ -235,7 +225,6 @@ public class FileDefaultWidgetController : Object {
this.notify["file-transfer-state"].connect(update_file_info); this.notify["file-transfer-state"].connect(update_file_info);
this.notify["file-transfer-mime-type"].connect(update_file_info); this.notify["file-transfer-mime-type"].connect(update_file_info);
this.notify["file-transfer-transferred-bytes"].connect(update_file_info);
} }
public void set_file_transfer(FileTransfer file_transfer) { public void set_file_transfer(FileTransfer file_transfer) {
@ -245,19 +234,13 @@ public class FileDefaultWidgetController : Object {
file_transfer.bind_property("state", this, "file-transfer-state"); file_transfer.bind_property("state", this, "file-transfer-state");
file_transfer.bind_property("mime-type", this, "file-transfer-mime-type"); file_transfer.bind_property("mime-type", this, "file-transfer-mime-type");
file_transfer.bind_property("transferred-bytes", this, "file-transfer-transferred-bytes");
update_file_info(); update_file_info();
} }
private void update_file_info() { private void update_file_info() {
state = file_transfer.state; state = file_transfer.state;
if (state == FileTransfer.State.COMPLETE && !file_transfer.get_file().query_exists()) { widget.update_file_info(file_transfer.mime_type, file_transfer.state, file_transfer.size);
state = FileTransfer.State.NOT_STARTED;
file_transfer.state = FileTransfer.State.NOT_STARTED;
}
widget.update_file_info(file_transfer.mime_type, file_transfer.transferred_bytes,
file_transfer.direction, state, file_transfer.size);
} }
private void on_clicked() { private void on_clicked() {
@ -266,11 +249,10 @@ public class FileDefaultWidgetController : Object {
widget.activate_action("file.open", null); widget.activate_action("file.open", null);
break; break;
case FileTransfer.State.NOT_STARTED: case FileTransfer.State.NOT_STARTED:
case FileTransfer.State.FAILED:
widget.activate_action("file.download", null); widget.activate_action("file.download", null);
break; break;
default: default:
// Clicking doesn't do anything in IN_PROGRESS state // Clicking doesn't do anything in FAILED and IN_PROGRESS states
break; break;
} }
} }

View file

@ -81,8 +81,7 @@ public class FileSendOverlay {
if (widget == null) { if (widget == null) {
FileDefaultWidget default_widget = new FileDefaultWidget(); FileDefaultWidget default_widget = new FileDefaultWidget();
default_widget.name_label.label = file_name; default_widget.name_label.label = file_name;
default_widget.update_file_info(mime_type, 0, FileTransfer.DIRECTION_SENT, default_widget.update_file_info(mime_type, FileTransfer.State.COMPLETE, (long)file_info.get_size());
FileTransfer.State.COMPLETE, (long)file_info.get_size());
widget = default_widget; widget = default_widget;
} }

View file

@ -44,11 +44,7 @@ public class Dialog : Gtk.Dialog {
}); });
image_button.clicked.connect(show_select_avatar); image_button.clicked.connect(show_select_avatar);
alias_hybrid.entry.changed.connect(() => { selected_account.alias = alias_hybrid.text; }); alias_hybrid.entry.changed.connect(() => { selected_account.alias = alias_hybrid.text; });
password_hybrid.entry.changed.connect(() => { password_hybrid.entry.changed.connect(() => { selected_account.password = password_hybrid.text; });
if (password_hybrid.text != "************") {
selected_account.password = password_hybrid.text;
}
});
password_change_btn.clicked.connect(show_change_psswd_dialog); password_change_btn.clicked.connect(show_change_psswd_dialog);
Util.LabelHybridGroup label_hybrid_group = new Util.LabelHybridGroup(); Util.LabelHybridGroup label_hybrid_group = new Util.LabelHybridGroup();
@ -72,7 +68,6 @@ public class Dialog : Gtk.Dialog {
settings_list.attach(widget, 1, row_index, 2); settings_list.attach(widget, 1, row_index, 2);
row_index++; row_index++;
password_hybrid.text = "************";
} }
} }
@ -197,7 +192,6 @@ public class Dialog : Gtk.Dialog {
} }
private void populate_grid_data(Account account) { private void populate_grid_data(Account account) {
active_switch.sensitive = false;
active_switch.state_set.disconnect(change_account_state); active_switch.state_set.disconnect(change_account_state);
picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(new Conversation(account.bare_jid, account, Conversation.Type.CHAT), account.bare_jid); picture.model = new ViewModel.CompatAvatarPictureModel(stream_interactor).add_participant(new Conversation(account.bare_jid, account, Conversation.Type.CHAT), account.bare_jid);
@ -206,7 +200,7 @@ public class Dialog : Gtk.Dialog {
alias_hybrid.text = account.alias ?? ""; alias_hybrid.text = account.alias ?? "";
password_hybrid.entry.input_purpose = InputPurpose.PASSWORD; password_hybrid.entry.input_purpose = InputPurpose.PASSWORD;
password_hybrid.text = account.password;
update_status_label(account); update_status_label(account);
@ -228,14 +222,11 @@ public class Dialog : Gtk.Dialog {
ConnectionManager.ConnectionState state = stream_interactor.connection_manager.get_state(account); ConnectionManager.ConnectionState state = stream_interactor.connection_manager.get_state(account);
switch (state) { switch (state) {
case ConnectionManager.ConnectionState.CONNECTING: case ConnectionManager.ConnectionState.CONNECTING:
active_switch.sensitive = false;
state_label.label = _("Connecting…"); break; state_label.label = _("Connecting…"); break;
case ConnectionManager.ConnectionState.CONNECTED: case ConnectionManager.ConnectionState.CONNECTED:
active_switch.sensitive = true;
password_change_btn.sensitive = true; password_change_btn.sensitive = true;
state_label.label = _("Connected"); break; state_label.label = _("Connected"); break;
case ConnectionManager.ConnectionState.DISCONNECTED: case ConnectionManager.ConnectionState.DISCONNECTED:
active_switch.sensitive = true;
password_change_btn.sensitive = false; password_change_btn.sensitive = false;
state_label.label = _("Disconnected"); break; state_label.label = _("Disconnected"); break;
} }

View file

@ -29,8 +29,8 @@ public class List : Box {
list_box.set_filter_func(filter); list_box.set_filter_func(filter);
search_entry.search_changed.connect(refilter); search_entry.search_changed.connect(refilter);
stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_show_received); stream_interactor.get_module(PresenceManager.IDENTITY).show_received.connect(on_received_online_presence);
stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_quit_received); stream_interactor.get_module(PresenceManager.IDENTITY).received_offline_presence.connect(on_received_offline_presence);
initialize_for_conversation(conversation); initialize_for_conversation(conversation);
} }
@ -63,11 +63,12 @@ public class List : Box {
this.conversation = conversation; this.conversation = conversation;
var identity = stream_interactor.get_module(MucManager.IDENTITY); var identity = stream_interactor.get_module(MucManager.IDENTITY);
Gee.List<Jid>? members = identity.get_members(conversation.counterpart, conversation.account); Gee.List<Jid>? members = identity.get_all_members(conversation.counterpart, conversation.account);
if (members != null) { if (members != null) {
// Add all members and their status to the list // Add all members and their status to the list
foreach (Jid member in members) { foreach (Jid member in members) {
add_member(member); //use add_member_with_status if you want to get online/offline statuses bool online = get_status(member, conversation.account);
add_member(member, online);
} }
} }
list_box.invalidate_filter(); list_box.invalidate_filter();
@ -82,7 +83,7 @@ public class List : Box {
list_box.invalidate_filter(); list_box.invalidate_filter();
} }
public void add_member_with_status(Jid jid, bool online) { public void add_member(Jid jid, bool online) {
// HACK: // HACK:
// Here we track members based on their names (not jids) // Here we track members based on their names (not jids)
// Sometimes the same member can be referenced with different jids, for example: // Sometimes the same member can be referenced with different jids, for example:
@ -123,35 +124,6 @@ public class List : Box {
} }
} }
public void add_member(Jid jid) {
var row_wrapper = new ListRow(stream_interactor, conversation, jid);
var widget = row_wrapper.get_widget();
string member_name = null;
if (jid.resourcepart != null) {
member_name = jid.resourcepart;
} else {
member_name = jid.localpart;
}
if (member_name == null) {
return;
}
row_wrappers[widget] = row_wrapper;
rows[member_name] = widget;
list_box.append(widget);
}
public void remove_member(Jid jid) {
var member_name = jid.resourcepart;
if (member_name == null) {
return;
}
list_box.remove(rows[member_name]);
rows.unset(member_name);
}
private void on_received_offline_presence(Jid jid, Account account) { private void on_received_offline_presence(Jid jid, Account account) {
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
var member_name = jid.resourcepart; var member_name = jid.resourcepart;
@ -167,19 +139,6 @@ public class List : Box {
} }
} }
private void on_quit_received(Jid jid, Account account) {
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
var member_name = jid.resourcepart;
if (member_name == null) {
return;
}
if (rows.has_key(member_name)) {
remove_member(jid);
}
list_box.invalidate_filter();
}
}
private void on_received_online_presence(Jid jid, Account account) { private void on_received_online_presence(Jid jid, Account account) {
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) { if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
var member_name = jid.resourcepart; var member_name = jid.resourcepart;
@ -188,7 +147,7 @@ public class List : Box {
} }
if (!rows.has_key(member_name)) { if (!rows.has_key(member_name)) {
add_member_with_status(jid, true); add_member(jid, true);
} }
row_wrappers[rows[member_name]].set_online(); row_wrappers[rows[member_name]].set_online();
@ -197,20 +156,6 @@ public class List : Box {
} }
} }
private void on_show_received(Jid jid, Account account) {
if (conversation != null && conversation.counterpart.equals_bare(jid) && jid.is_full()) {
var member_name = jid.resourcepart;
if (member_name == null) {
return;
}
if (!rows.has_key(member_name)) {
add_member(jid);
}
list_box.invalidate_filter();
}
}
private void header(ListBoxRow row, ListBoxRow? before_row) { private void header(ListBoxRow row, ListBoxRow? before_row) {
ListRow row_wrapper1 = row_wrappers[row.get_child()]; ListRow row_wrapper1 = row_wrappers[row.get_child()];
Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account); Xmpp.Xep.Muc.Affiliation? a1 = stream_interactor.get_module(MucManager.IDENTITY).get_affiliation(conversation.counterpart, row_wrapper1.jid, row_wrapper1.conversation.account);

View file

@ -10,6 +10,10 @@ public class ListRow : Object {
private Grid main_grid; private Grid main_grid;
private AvatarPicture picture; private AvatarPicture picture;
public Label name_label; public Label name_label;
// TODO: use something more visual for status
public Label status;
public Conversation? conversation; public Conversation? conversation;
public Jid? jid; public Jid? jid;
@ -18,9 +22,8 @@ public class ListRow : Object {
main_grid = (Grid) builder.get_object("main_grid"); main_grid = (Grid) builder.get_object("main_grid");
picture = (AvatarPicture) builder.get_object("picture"); picture = (AvatarPicture) builder.get_object("picture");
name_label = (Label) builder.get_object("name_label"); name_label = (Label) builder.get_object("name_label");
main_grid.set_column_spacing(10); status = (Label) builder.get_object("status_label");
main_grid.set_column_homogeneous(false); status.label = "(unknown)";
main_grid.set_baseline_row(1);
} }
public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) { public ListRow(StreamInteractor stream_interactor, Conversation conversation, Jid jid) {
@ -41,9 +44,11 @@ public class ListRow : Object {
} }
public void set_online() { public void set_online() {
status.label = ("");
} }
public void set_offline() { public void set_offline() {
status.label = ("(offline)");
} }
} }

View file

@ -15,7 +15,6 @@ class SettingsDialog : Adw.PreferencesWindow {
[GtkChild] private unowned CheckButton encryption_radio_openpgp; [GtkChild] private unowned CheckButton encryption_radio_openpgp;
[GtkChild] private unowned Switch send_button_switch; [GtkChild] private unowned Switch send_button_switch;
[GtkChild] private unowned Switch enter_newline_switch; [GtkChild] private unowned Switch enter_newline_switch;
[GtkChild] private unowned Switch dark_theme;
Dino.Entities.Settings settings = Dino.Application.get_default().settings; Dino.Entities.Settings settings = Dino.Application.get_default().settings;
@ -33,7 +32,6 @@ class SettingsDialog : Adw.PreferencesWindow {
send_button_switch.active = settings.send_button; send_button_switch.active = settings.send_button;
enter_newline_switch.active = settings.enter_newline; enter_newline_switch.active = settings.enter_newline;
enter_newline_switch.sensitive = settings.send_button; enter_newline_switch.sensitive = settings.send_button;
dark_theme.active = settings.dark_theme;
typing_switch.notify["active"].connect(() => { settings.send_typing = typing_switch.active; } ); typing_switch.notify["active"].connect(() => { settings.send_typing = typing_switch.active; } );
marker_switch.notify["active"].connect(() => { settings.send_marker = marker_switch.active; } ); marker_switch.notify["active"].connect(() => { settings.send_marker = marker_switch.active; } );
@ -67,7 +65,6 @@ class SettingsDialog : Adw.PreferencesWindow {
enter_newline_switch.active = visible; enter_newline_switch.active = visible;
} }
}); });
dark_theme.notify["active"].connect(() => { settings.dark_theme = dark_theme.active; });
} }
} }

View file

@ -58,8 +58,6 @@ else
libsoup_version = '3.0' libsoup_version = '3.0'
endif endif
cc = meson.get_compiler('c')
dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0') dep_gdk_pixbuf = dependency('gdk-pixbuf-2.0')
dep_gee = dependency('gee-0.8') dep_gee = dependency('gee-0.8')
dep_gio = dependency('gio-2.0') dep_gio = dependency('gio-2.0')
@ -83,12 +81,9 @@ dep_libsrtp2 = dependency('libsrtp2', disabler: true, required: plugin_crypto)
dep_libsignal_protocol_c = dependency('libsignal-protocol-c', version: ['>=2.3.2', '<2.3.4'], disabler: true, required: get_option('plugin-omemo')) dep_libsignal_protocol_c = dependency('libsignal-protocol-c', version: ['>=2.3.2', '<2.3.4'], disabler: true, required: get_option('plugin-omemo'))
dep_libsoup = dependency('libsoup-@0@'.format(libsoup_version), disabler: true, required: get_option('plugin-http-files')) dep_libsoup = dependency('libsoup-@0@'.format(libsoup_version), disabler: true, required: get_option('plugin-http-files'))
dep_nice = dependency('nice', version: '>=0.1.15', disabler: true, required: get_option('plugin-ice')) dep_nice = dependency('nice', version: '>=0.1.15', disabler: true, required: get_option('plugin-ice'))
dep_m = cc.find_library('m', required: false) dep_m = meson.get_compiler('c').find_library('m', required: false)
dep_sqlite3 = dependency('sqlite3', version: '>=3.24') dep_sqlite3 = dependency('sqlite3', version: '>=3.24')
dep_webrtc_audio_processing = dependency('webrtc-audio-processing', version: ['>=0.2', '<0.4'], required: get_option('plugin-rtp-webrtc-audio-processing'))
dep_gstreamer_bad = dependency('gstreamer-plugins-bad-1.0', disabler: true, required: get_option('plugin-rtp-webrtc-audio-processing'))
gstpluginsdir = dep_gstreamer_bad.get_variable('pluginsdir')
dep_webrtcdsp = cc.find_library('gstwebrtcdsp', dirs: gstpluginsdir, disabler: true, required: get_option('plugin-rtp-webrtc-audio-processing'))
prog_git = find_program('git', required: false) prog_git = find_program('git', required: false)
prog_python = python.find_installation() prog_python = python.find_installation()

View file

@ -1,4 +1,4 @@
option('plugindir', type: 'string', value: 'dino/plugins', description: 'Dino plugin directory inside libdir') option('plugindir', type: 'string', value: 'lib/dino/plugins', description: 'Dino plugin directory')
option('crypto-backend', type: 'combo', choices: ['auto', 'openssl', 'gnutls'], value: 'auto', description: 'Preferred crypto backend') option('crypto-backend', type: 'combo', choices: ['auto', 'openssl', 'gnutls'], value: 'auto', description: 'Preferred crypto backend')
@ -16,4 +16,3 @@ option('plugin-rtp-vp9', type: 'feature', value: 'disabled', description: 'VP9 c
option('plugin-rtp-webrtc-audio-processing', type: 'feature', description: 'Voice preprocessing') option('plugin-rtp-webrtc-audio-processing', type: 'feature', description: 'Voice preprocessing')
option('use-soup2', type: 'boolean', value: false, description: 'Use libsoup version 2 instead of 3') option('use-soup2', type: 'boolean', value: false, description: 'Use libsoup version 2 instead of 3')
option('with-wasapi', type: 'boolean', value: true, description: 'Use wasapi insted of directsound on windows')

View file

@ -20,5 +20,5 @@ if dep_libsoup.version().version_compare('>=3.0')
vala_args += ['--define=SOUP_3_0'] vala_args += ['--define=SOUP_3_0']
endif endif
lib_http_files = shared_library('http-files', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, kwargs: install_options) lib_http_files = shared_library('http-files', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
dep_http_files = declare_dependency(link_with: lib_http_files, include_directories: include_directories('.')) dep_http_files = declare_dependency(link_with: lib_http_files, include_directories: include_directories('.'))

View file

@ -103,18 +103,6 @@ public class HttpFileSender : FileSender, Object {
put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); put_message.wrote_headers.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body)); put_message.wrote_chunk.connect(() => transfer_more_bytes(file_transfer.input_stream, put_message.request_body));
#endif #endif
file_transfer.transferred_bytes = 0;
put_message.wrote_body_data.connect((chunk) => {
if (file_transfer.size != 0) {
#if SOUP_3_0
file_transfer.transferred_bytes += chunk;
#else
file_transfer.transferred_bytes += chunk.length;
#endif
}
});
foreach (var entry in file_send_data.headers.entries) { foreach (var entry in file_send_data.headers.entries) {
put_message.request_headers.append(entry.key, entry.value); put_message.request_headers.append(entry.key, entry.value);
} }

View file

@ -32,5 +32,5 @@ c_args = [
vala_args = [ vala_args = [
'--vapidir', meson.current_source_dir() / 'vapi', '--vapidir', meson.current_source_dir() / 'vapi',
] ]
lib_ice = shared_library('ice', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, dependencies: dependencies, kwargs: install_options) lib_ice = shared_library('ice', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
dep_ice = declare_dependency(link_with: lib_ice, include_directories: include_directories('.')) dep_ice = declare_dependency(link_with: lib_ice, include_directories: include_directories('.'))

View file

@ -6,21 +6,6 @@ using Xmpp.Xep;
private extern const size_t NICE_ADDRESS_STRING_LEN; private extern const size_t NICE_ADDRESS_STRING_LEN;
public class Dino.Plugins.Ice.Plugin : RootInterface, Object { public class Dino.Plugins.Ice.Plugin : RootInterface, Object {
private const int64 delay_min = 300; // 10mn
private const int64 delay_max = (int64) uint.MAX;
private class TimerPayload {
public Account account { get; set; }
public uint timeout_handle_id;
public TimerPayload(Account account, uint timeout_handle_id) {
this.account = account;
this.timeout_handle_id = timeout_handle_id;
}
}
private HashMap<XmppStream, TimerPayload> timeouts = new HashMap<XmppStream, TimerPayload>(XmppStream.hash_func, XmppStream.equals_func);
public Dino.Application app; public Dino.Application app;
public void registered(Dino.Application app) { public void registered(Dino.Application app) {
@ -37,10 +22,10 @@ public class Dino.Plugins.Ice.Plugin : RootInterface, Object {
stream.get_module(JingleRawUdp.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses); stream.get_module(JingleRawUdp.Module.IDENTITY).set_local_ip_address_handler(get_local_ip_addresses);
} }
}); });
app.stream_interactor.connection_manager.connection_state_changed.connect(on_connection_state_changed); app.stream_interactor.stream_negotiated.connect(on_stream_negotiated);
} }
private async void external_discovery_refresh_services(Account account, XmppStream stream) { private async void on_stream_negotiated(Account account, XmppStream stream) {
Module? ice_udp_module = stream.get_module(JingleIceUdp.Module.IDENTITY) as Module; Module? ice_udp_module = stream.get_module(JingleIceUdp.Module.IDENTITY) as Module;
if (ice_udp_module == null) return; if (ice_udp_module == null) return;
Gee.List<Xep.ExternalServiceDiscovery.Service> services = yield ExternalServiceDiscovery.request_services(stream); Gee.List<Xep.ExternalServiceDiscovery.Service> services = yield ExternalServiceDiscovery.request_services(stream);
@ -60,28 +45,6 @@ public class Dino.Plugins.Ice.Plugin : RootInterface, Object {
} }
} }
} }
if (ice_udp_module.turn_service != null) {
DateTime? expires = ice_udp_module.turn_service.expires;
if (expires != null) {
int64 delay = (expires.to_unix() - new DateTime.now_utc().to_unix()) / 2;
if (delay >= delay_min && delay <= delay_max) {
debug("Next server external service discovery in %lds (because of TURN credentials' expiry time)", (long) delay);
uint timeout_handle_id = Timeout.add_seconds((uint) delay, () => {
on_timeout(stream);
return false;
});
timeouts[stream] = new TimerPayload(account, timeout_handle_id);
timeouts[stream].account = account;
timeouts[stream].timeout_handle_id = timeout_handle_id;
} else {
warning("Bogus TURN credentials' expiry time (delay value = %ld), *not* planning next service discovery", (long) delay);
}
}
}
if (ice_udp_module.stun_ip == null) { if (ice_udp_module.stun_ip == null) {
InetAddress ip = yield lookup_ipv4_addess("stun.dino.im"); InetAddress ip = yield lookup_ipv4_addess("stun.dino.im");
if (ip == null) return; if (ip == null) return;
@ -93,31 +56,6 @@ public class Dino.Plugins.Ice.Plugin : RootInterface, Object {
} }
} }
public void on_timeout(XmppStream stream) {
if (!timeouts.has_key(stream)) return;
TimerPayload pl = timeouts[stream];
timeouts.unset(stream);
external_discovery_refresh_services.begin(pl.account, stream);
}
public void on_connection_state_changed(Account account, ConnectionManager.ConnectionState state) {
switch(state)
{
case ConnectionManager.ConnectionState.DISCONNECTED:
XmppStream? stream = app.stream_interactor.connection_manager.get_stream(account);
if (stream == null) return;
if (!timeouts.has_key(stream)) return;
Source.remove(timeouts[stream].timeout_handle_id);
timeouts.unset(stream);
break;
case ConnectionManager.ConnectionState.CONNECTED:
XmppStream? stream = app.stream_interactor.connection_manager.get_stream(account);
external_discovery_refresh_services(account, stream);
break;
}
}
public void shutdown() { public void shutdown() {
// Nothing to do // Nothing to do
} }

View file

@ -1,5 +1,3 @@
install_options = {'install': true, 'install_dir': get_option('libdir') / get_option('plugindir')}
subdir('http-files') subdir('http-files')
subdir('ice') subdir('ice')
subdir('notification-sound') subdir('notification-sound')
@ -9,6 +7,4 @@ subdir('rtp')
if host_machine.system() == 'windows' if host_machine.system() == 'windows'
subdir('win32-fonts') subdir('win32-fonts')
subdir('windows-notification') subdir('windows-notification')
else
subdir('phone-ringer')
endif endif

View file

@ -15,5 +15,5 @@ sources = files(
vala_args = [ vala_args = [
'--vapidir', meson.current_source_dir() / 'vapi', '--vapidir', meson.current_source_dir() / 'vapi',
] ]
lib_notification_sound = shared_library('notification-sound', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, kwargs: install_options) lib_notification_sound = shared_library('notification-sound', sources, name_prefix: '', vala_args: vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
dep_notification_sound = declare_dependency(link_with: lib_notification_sound, include_directories: include_directories('.')) dep_notification_sound = declare_dependency(link_with: lib_notification_sound, include_directories: include_directories('.'))

View file

@ -104,7 +104,7 @@ set_target_properties(omemo PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_D
install(TARGETS omemo ${PLUGIN_INSTALL}) install(TARGETS omemo ${PLUGIN_INSTALL})
if(BUILD_TESTING) if(BUILD_TESTS)
vala_precompile(OMEMO_TEST_VALA_C vala_precompile(OMEMO_TEST_VALA_C
SOURCES SOURCES
"tests/signal/common.vala" "tests/signal/common.vala"
@ -127,5 +127,4 @@ if(BUILD_TESTING)
add_executable(omemo-test ${OMEMO_TEST_VALA_C}) add_executable(omemo-test ${OMEMO_TEST_VALA_C})
add_dependencies(omemo-test omemo) add_dependencies(omemo-test omemo)
target_link_libraries(omemo-test omemo ${OMEMO_PACKAGES}) target_link_libraries(omemo-test omemo ${OMEMO_PACKAGES})
add_test(NAME omemo COMMAND omemo-test) endif(BUILD_TESTS)
endif(BUILD_TESTING)

View file

@ -70,7 +70,7 @@ vala_args = [
if crypto_backend == 'gnutls' if crypto_backend == 'gnutls'
vala_args += ['-D', 'GCRYPT'] vala_args += ['-D', 'GCRYPT']
endif endif
lib_omemo = shared_library('omemo', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, kwargs: install_options) lib_omemo = shared_library('omemo', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
dep_omemo = declare_dependency(link_with: lib_omemo, include_directories: include_directories('.')) dep_omemo = declare_dependency(link_with: lib_omemo, include_directories: include_directories('.'))
sources = files( sources = files(

View file

@ -39,5 +39,5 @@ c_args = [
vala_args = [ vala_args = [
'--vapidir', meson.current_source_dir() / 'vapi', '--vapidir', meson.current_source_dir() / 'vapi',
] ]
lib_openpgp = shared_library('openpgp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, kwargs: install_options) lib_openpgp = shared_library('openpgp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
dep_openpgp = declare_dependency(link_with: lib_openpgp, include_directories: include_directories('.')) dep_openpgp = declare_dependency(link_with: lib_openpgp, include_directories: include_directories('.'))

View file

@ -1,10 +1,9 @@
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: dino-openpgp-0.0\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-09 22:16+0200\n" "POT-Creation-Date: 2023-02-07 21:31+0100\n"
"PO-Revision-Date: 2024-06-09 22:39+0200\n" "PO-Revision-Date: 2020-04-16 20:11+0000\n"
"Last-Translator: eerielili \n"
"Language-Team: German <https://hosted.weblate.org/projects/dino/plugin-" "Language-Team: German <https://hosted.weblate.org/projects/dino/plugin-"
"openpgp/de/>\n" "openpgp/de/>\n"
"Language: de\n" "Language: de\n"
@ -12,11 +11,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n" "Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 3.4.2\n" "X-Generator: Weblate 4.0.1-dev\n"
#: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:68
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
#: plugins/openpgp/src/account_settings_entry.vala:113 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "Key publishing disabled" msgid "Key publishing disabled"
msgstr "Schlüsselveröffentlichung deaktiviert" msgstr "Schlüsselveröffentlichung deaktiviert"
@ -25,40 +24,18 @@ msgid "Error in GnuPG"
msgstr "Fehler in GnuPG" msgstr "Fehler in GnuPG"
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
msgid "" msgid "No keys available. Generate one!"
"No keys available. Generate one or check if your keys aren't expired or " msgstr "Keine Schlüssel vorhanden. Erzeuge einen!"
"revoked!"
msgstr ""
"Keine Schlüssel vorhanden. Erstellen Sie einen oder prüfen Sie,"
"ob Ihre Schlüssel nicht abgelaufen sind oder widerrufen!"
#: plugins/openpgp/src/account_settings_entry.vala:101
#: plugins/openpgp/src/account_settings_entry.vala:95
msgid "expired!"
msgstr "abgelaufen"
#: plugins/openpgp/src/account_settings_entry.vala:95
msgid "revoked!"
msgstr "widerrufen!"
#: plugins/openpgp/src/account_settings_entry.vala:96
msgid "Attention required!"
msgstr "Achtung!"
#: plugins/openpgp/src/account_settings_entry.vala:96
#, c-format
msgid "Your key %s is %s"
msgstr "Ihr Schlüssel %s is %s"
#: plugins/openpgp/src/account_settings_entry.vala:113
msgid "Select key" msgid "Select key"
msgstr "Wähle einen Schlüssel" msgstr "Wähle einen Schlüssel"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Loading…" msgid "Loading…"
msgstr "Lade…" msgstr "Lade…"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Querying GnuPG" msgid "Querying GnuPG"
msgstr "Frage GnuPG ab" msgstr "Frage GnuPG ab"
@ -69,3 +46,6 @@ msgstr "Schlüssel nicht im Schlüsselbund"
#: plugins/openpgp/src/contact_details_provider.vala:30 #: plugins/openpgp/src/contact_details_provider.vala:30
msgid "Encryption" msgid "Encryption"
msgstr "Verschlüsselung" msgstr "Verschlüsselung"
#~ msgid "OpenPGP"
#~ msgstr "OpenPGP"

View file

@ -1,68 +1,52 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION+\n" "Project-Id-Version: PACKAGE VERSION\n"
"POT-Creation-Date: 2024-06-09 22:16+0200\n" "Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-06-09 22:18+0200\n" "POT-Creation-Date: 2023-02-07 21:31+0100\n"
"Last-Translator: \n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Language-Team: Dino+\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n" "Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.4.2\n"
"X-Poedit-Basepath: ../src\n"
"X-Poedit-KeywordsList: _(\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-SearchPath-0: .\n"
#: account_settings_entry.vala:68 account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:68
#: account_settings_entry.vala:113 #: plugins/openpgp/src/account_settings_entry.vala:72
#: plugins/openpgp/src/account_settings_entry.vala:101
msgid "Key publishing disabled" msgid "Key publishing disabled"
msgstr "" msgstr ""
#: account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:68
msgid "Error in GnuPG" msgid "Error in GnuPG"
msgstr "" msgstr ""
#: account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
msgid "" msgid "No keys available. Generate one!"
"No keys available. Generate one or check if your keys aren't expired or "
"revoked!"
msgstr "" msgstr ""
#: account_settings_entry.vala:95 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "expired!"
msgstr ""
#: account_settings_entry.vala:95
msgid "revoked!"
msgstr ""
#: account_settings_entry.vala:96
msgid "Attention required!"
msgstr ""
#: account_settings_entry.vala:96
#, c-format
msgid "Your key %s is %s"
msgstr ""
#: account_settings_entry.vala:113
msgid "Select key" msgid "Select key"
msgstr "" msgstr ""
#: account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Loading…" msgid "Loading…"
msgstr "" msgstr ""
#: account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Querying GnuPG" msgid "Querying GnuPG"
msgstr "" msgstr ""
#: contact_details_provider.vala:28 #: plugins/openpgp/src/contact_details_provider.vala:28
msgid "Key not in keychain" msgid "Key not in keychain"
msgstr "" msgstr ""
#: contact_details_provider.vala:30 #: plugins/openpgp/src/contact_details_provider.vala:30
msgid "Encryption" msgid "Encryption"
msgstr "" msgstr ""

View file

@ -20,7 +20,7 @@ msgstr ""
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
msgid "No keys available. Generate one!" msgid "No keys available. Generate one!"
msgstr "No keys available. Generate one or check if your keys aren't expired or revoked!" msgstr ""
#: plugins/openpgp/src/account_settings_entry.vala:101 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "Select key" msgid "Select key"

View file

@ -7,9 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-09 22:16+0200\n" "POT-Creation-Date: 2023-02-07 21:31+0100\n"
"PO-Revision-Date: 2024-06-09 22:22+0200\n" "PO-Revision-Date: 2020-11-12 17:21+0000\n"
"Last-Translator: \n"
"Language-Team: French <https://hosted.weblate.org/projects/dino/plugin-" "Language-Team: French <https://hosted.weblate.org/projects/dino/plugin-"
"openpgp/fr/>\n" "openpgp/fr/>\n"
"Language: fr\n" "Language: fr\n"
@ -17,11 +16,11 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n > 1;\n" "Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Poedit 3.4.2\n" "X-Generator: Weblate 4.4-dev\n"
#: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:68
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
#: plugins/openpgp/src/account_settings_entry.vala:113 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "Key publishing disabled" msgid "Key publishing disabled"
msgstr "La publication des clés est désactivée" msgstr "La publication des clés est désactivée"
@ -30,39 +29,18 @@ msgid "Error in GnuPG"
msgstr "Erreur dans GnuPG" msgstr "Erreur dans GnuPG"
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
msgid "" msgid "No keys available. Generate one!"
"No keys available. Generate one or check if your keys aren't expired or " msgstr "Aucune clé nest disponible. Générez-en une!"
"revoked!"
msgstr ""
"Pas de clés disponibles. Générez-en une nouvelle ou vérifier si vos "
"clés ne seraient pas expirées ou révoquées!"
#: plugins/openpgp/src/account_settings_entry.vala:95 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "expired!"
msgstr "expirée!"
#: plugins/openpgp/src/account_settings_entry.vala:95
msgid "revoked!"
msgstr "révoquée!"
#: plugins/openpgp/src/account_settings_entry.vala:96
msgid "Attention required!"
msgstr "Attention requise!"
#: plugins/openpgp/src/account_settings_entry.vala:96
#, c-format
msgid "Your key %s is %s"
msgstr "Votre clé %s est %s"
#: plugins/openpgp/src/account_settings_entry.vala:113
msgid "Select key" msgid "Select key"
msgstr "Choix dune clé" msgstr "Choix dune clé"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Loading…" msgid "Loading…"
msgstr "Chargement…" msgstr "Chargement…"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Querying GnuPG" msgid "Querying GnuPG"
msgstr "Interrogation de GnuPG" msgstr "Interrogation de GnuPG"

View file

@ -7,9 +7,8 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-06-09 22:16+0200\n" "POT-Creation-Date: 2023-02-07 21:31+0100\n"
"PO-Revision-Date: 2024-06-09 22:39+0200\n" "PO-Revision-Date: 2020-06-02 11:41+0000\n"
"Last-Translator: eerielili\n"
"Language-Team: Russian <https://hosted.weblate.org/projects/dino/plugin-" "Language-Team: Russian <https://hosted.weblate.org/projects/dino/plugin-"
"openpgp/ru/>\n" "openpgp/ru/>\n"
"Language: ru\n" "Language: ru\n"
@ -18,11 +17,11 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && " "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Generator: Poedit 3.4.2\n" "X-Generator: Weblate 4.1-dev\n"
#: plugins/openpgp/src/account_settings_entry.vala:68 #: plugins/openpgp/src/account_settings_entry.vala:68
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
#: plugins/openpgp/src/account_settings_entry.vala:113 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "Key publishing disabled" msgid "Key publishing disabled"
msgstr "Публикация ключа отключена" msgstr "Публикация ключа отключена"
@ -31,37 +30,18 @@ msgid "Error in GnuPG"
msgstr "Ошибка в GnuPG" msgstr "Ошибка в GnuPG"
#: plugins/openpgp/src/account_settings_entry.vala:72 #: plugins/openpgp/src/account_settings_entry.vala:72
msgid "" msgid "No keys available. Generate one!"
"No keys available. Generate one or check if your keys aren't expired or " msgstr "Нет доступных ключей. Стоило бы сгенерировать один!"
"revoked!"
msgstr "Нет доступных ключей. Создайте как минимум один, либо проверьте что уже существующие (ранее созданные) ключи не были отозваны или срок их действия не закончился!"
#: plugins/openpgp/src/account_settings_entry.vala:95 #: plugins/openpgp/src/account_settings_entry.vala:101
msgid "expired!"
msgstr "истёк!"
#: plugins/openpgp/src/account_settings_entry.vala:95
msgid "revoked!"
msgstr "отозван!"
#: plugins/openpgp/src/account_settings_entry.vala:96
msgid "Attention required!"
msgstr "внимание!"
#: plugins/openpgp/src/account_settings_entry.vala:96
#, c-format
msgid "Your key %s is %s"
msgstr "Ваш ключ %s %s"
#: plugins/openpgp/src/account_settings_entry.vala:113
msgid "Select key" msgid "Select key"
msgstr "Выбрать ключ" msgstr "Выбрать ключ"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Loading…" msgid "Loading…"
msgstr "Загрузка…" msgstr "Загрузка…"
#: plugins/openpgp/src/account_settings_entry.vala:126 #: plugins/openpgp/src/account_settings_entry.vala:114
msgid "Querying GnuPG" msgid "Querying GnuPG"
msgstr "Запрос GnuPG" msgstr "Запрос GnuPG"

View file

@ -69,7 +69,7 @@ public class AccountSettingsEntry : Plugins.AccountSettingsEntry {
return; return;
} }
if (keys.size == 0) { if (keys.size == 0) {
label.set_markup(build_markup_string(_("Key publishing disabled"), _("No keys available. Generate one or check if your keys aren't expired or revoked!"))); label.set_markup(build_markup_string(_("Key publishing disabled"), _("No keys available. Generate one!")));
return; return;
} }
@ -88,18 +88,6 @@ public class AccountSettingsEntry : Plugins.AccountSettingsEntry {
set_label_active(selected); set_label_active(selected);
combobox.changed.connect(key_changed); combobox.changed.connect(key_changed);
if (account_key != null) {
try {
GPG.Key key_check = GPGHelper.get_public_key(account_key);
if(key_check.expired || key_check.revoked) {
string status_str = key_check.expired ? _("expired!") : _("revoked!");
label.set_markup(build_markup_string(_("Attention required!"), _("Your key %s is %s").printf("<span color='red'><b>"+ key_check.fpr +"</b></span>", status_str) ) );
}
}
catch {
debug("Coudn't check GPG key status.");
}
}
} }
private void populate_list_store() { private void populate_list_store() {

View file

@ -40,16 +40,6 @@ private class EncryptionListEntry : Plugins.EncryptionListEntry, Object {
return; return;
} }
GPG.Key key_check = GPGHelper.get_public_key(db.get_account_key(conversation.account));
if (key_check.expired || key_check.revoked) {
string status_str = key_check.expired ? " has expired." : " has been revoked.";
debug("GPG public key %s is NOT fine for encryption: it %s.\n", key_check.fpr, status_str);
input_status_callback(new Plugins.InputFieldStatus("Your GPG key " + key_check.fpr + status_str,
Plugins.InputFieldStatus.MessageType.ERROR,
Plugins.InputFieldStatus.InputState.NO_SEND));
return;
}
if (conversation.type_ == Conversation.Type.CHAT) { if (conversation.type_ == Conversation.Type.CHAT) {
string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart); string? key_id = stream_interactor.get_module(Manager.IDENTITY).get_key_id(conversation.account, conversation.counterpart);
if (key_id == null) { if (key_id == null) {

View file

@ -112,12 +112,7 @@ public static Gee.List<Key> get_keylist(string? pattern = null, bool secret_only
try { try {
while (true) { while (true) {
Key key = context.op_keylist_next(); Key key = context.op_keylist_next();
if (!key.expired && !key.revoked) { keys.add(key);
debug("PGP Key " + key.fpr + " is valid!");
keys.add(key);
} else {
debug("PGP Key " + key.fpr + " is either expired or revoked!");
}
} }
} catch (Error e) { } catch (Error e) {
if (e.code != GPGError.ErrorCode.EOF) throw e; if (e.code != GPGError.ErrorCode.EOF) throw e;

View file

@ -55,9 +55,9 @@ namespace GPG {
public string issuer_name; public string issuer_name;
public string chain_id; public string chain_id;
public Validity owner_trust; public Validity owner_trust;
[CCode(array_length = false, array_null_terminated = true)] [CCode(array_null_terminated = true)]
public SubKey[] subkeys; public SubKey[] subkeys;
[CCode(array_length = false, array_null_terminated = true)] [CCode(array_null_terminated = true)]
public UserID[] uids; public UserID[] uids;
public KeylistMode keylist_mode; public KeylistMode keylist_mode;
public string fpr { get { return subkeys[0].fpr; } } public string fpr { get { return subkeys[0].fpr; } }

View file

@ -1,19 +0,0 @@
dependencies = [
dep_dino,
dep_libcanberra,
dep_gee,
dep_glib,
dep_gmodule,
dep_gdk_pixbuf,
dep_qlite,
dep_xmpp_vala,
dep_gtk4,
]
sources = files(
'src/plugin.vala',
'src/register_plugin.vala',
)
lib_phone_ringer = shared_library('phone-ringer', sources, name_prefix: '', dependencies: dependencies, kwargs: install_options)
dep_phone_ringer = declare_dependency(link_with: lib_phone_ringer, include_directories: include_directories('.'))

View file

@ -1,13 +1,5 @@
find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED) find_package(GLib ${GLib_GLOBAL_VERSION} REQUIRED)
find_package(WebRTCAudioProcessing 0.2)
set(RTP_DEFINITIONS)
set(RTP_EXTRA_OPTIONS)
if(PLUGIN_RTP_WEBRTC_AUDIO_PROCESSING)
set(EXTRA_RTP_PACKAGES GstWebrtcDsp)
list(APPEND RTP_DEFINITIONS WITH_VOICE_PROCESSOR)
endif()
find_packages(RTP_PACKAGES REQUIRED find_packages(RTP_PACKAGES REQUIRED
Gee Gee
GLib GLib
@ -19,9 +11,11 @@ find_packages(RTP_PACKAGES REQUIRED
GstAudio GstAudio
GstRtp GstRtp
GstVideo GstVideo
${EXTRA_RTP_PACKAGES}
) )
set(RTP_DEFINITIONS)
set(RTP_EXTRA_OPTIONS)
if(GstRtp_VERSION VERSION_GREATER_EQUAL "1.16") if(GstRtp_VERSION VERSION_GREATER_EQUAL "1.16")
set(RTP_DEFINITIONS ${RTP_DEFINITIONS} GST_1_16) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} GST_1_16)
endif() endif()
@ -58,8 +52,18 @@ if(RTP_ENABLE_MSDK)
set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_MSDK) set(RTP_DEFINITIONS ${RTP_DEFINITIONS} ENABLE_MSDK)
endif() endif()
if(WIN32 AND WITH_WASAPI) if(WebRTCAudioProcessing_VERSION GREATER "0.4")
list(APPEND RTP_DEFINITIONS WITH_WASAPI) message(STATUS "Ignoring WebRTCAudioProcessing, only versions < 0.4 supported so far")
unset(WebRTCAudioProcessing_FOUND)
endif()
if(WebRTCAudioProcessing_FOUND)
set(RTP_DEFINITIONS ${RTP_DEFINITIONS} WITH_VOICE_PROCESSOR)
set(RTP_VOICE_PROCESSOR_VALA src/voice_processor.vala)
set(RTP_VOICE_PROCESSOR_CXX src/voice_processor_native.cpp)
set(RTP_VOICE_PROCESSOR_LIB webrtc-audio-processing)
else()
message(STATUS "WebRTCAudioProcessing not found, build without voice pre-processing!")
endif() endif()
vala_precompile(RTP_VALA_C vala_precompile(RTP_VALA_C

View file

@ -30,8 +30,12 @@ c_args = [
vala_args = [ vala_args = [
'--vapidir', meson.current_source_dir() / 'vapi', '--vapidir', meson.current_source_dir() / 'vapi',
] ]
if dep_webrtcdsp.found() if dep_webrtc_audio_processing.found()
dependencies += [dep_webrtcdsp] dependencies += [dep_webrtc_audio_processing]
sources += files(
'src/voice_processor.vala',
'src/voice_processor_native.cpp',
)
vala_args += ['-D', 'WITH_VOICE_PROCESSOR'] vala_args += ['-D', 'WITH_VOICE_PROCESSOR']
endif endif
if dep_gstreamer_rtp.version() == 'unknown' or dep_gstreamer_rtp.version().version_compare('>=1.16') if dep_gstreamer_rtp.version() == 'unknown' or dep_gstreamer_rtp.version().version_compare('>=1.16')
@ -55,8 +59,5 @@ endif
if get_option('plugin-rtp-vp9').allowed() if get_option('plugin-rtp-vp9').allowed()
vala_args += ['-D', 'ENABLE_VP9'] vala_args += ['-D', 'ENABLE_VP9']
endif endif
if host_machine.system() == 'windows' and get_option('with-wasapi') lib_rtp = shared_library('rtp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, install: true, install_dir: get_option('libdir') / 'dino/plugins')
vala_args += ['-D', 'WITH_WASAPI']
endif
lib_rtp = shared_library('rtp', sources, name_prefix: '', c_args: c_args, vala_args: vala_args, include_directories: include_directories('src'), dependencies: dependencies, kwargs: install_options)
dep_rtp = declare_dependency(link_with: lib_rtp, include_directories: include_directories('.')) dep_rtp = declare_dependency(link_with: lib_rtp, include_directories: include_directories('.'))

View file

@ -55,12 +55,12 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
private string device_name; private string device_name;
private string device_display_name; private string device_display_name;
private Gst.Caps device_caps = null; private Gst.Caps device_caps;
private Gst.Element element = null; private Gst.Element element;
private Gst.Element tee = null; private Gst.Element tee;
private Gst.Element dsp = null; private Gst.Element dsp;
private Gst.Base.Aggregator mixer = null; private Gst.Base.Aggregator mixer;
private Gst.Element filter = null; private Gst.Element filter;
private int links; private int links;
// Codecs // Codecs
@ -348,46 +348,9 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
} }
} }
private Gst.Caps get_best_audio_caps() {
// Choose caps based on the highest rate
int best_index = -1;
int best_rate = 0;
for (int i = 0; i < device.caps.get_size(); i++) {
unowned Gst.Structure? structure = device.caps.get_structure(i);
if (structure == null) {
continue;
}
if (!structure.has_field("rate")) {
continue;
}
int rate = 0;
if (!structure.get_int("rate", out rate)) {
continue;
}
if (rate > best_rate) {
best_rate = rate;
best_index = i;
}
}
if (best_index != -1) {
Gst.Caps caps = caps_copy_nth(device.caps, best_index);
info("Selected Audio Caps: %s", caps.to_string());
return caps;
}
// Default Caps
string default_caps = "audio/x-raw,rate=48000,channels=1";
info("Selected Default Audio Caps: %s", default_caps);
return Gst.Caps.from_string(default_caps);
}
private Gst.Caps get_best_caps() { private Gst.Caps get_best_caps() {
if (media == "audio") { if (media == "audio") {
return get_best_audio_caps(); return Gst.Caps.from_string("audio/x-raw,rate=48000,channels=1");
} else if (media == "video" && device.caps.get_size() > 0) { } else if (media == "video" && device.caps.get_size() > 0) {
int best_index = -1; int best_index = -1;
Value? best_fraction = null; Value? best_fraction = null;
@ -470,29 +433,6 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
return target; return target;
} }
#if WITH_VOICE_PROCESSOR
private Gst.Element create_voice_processor() {
Gst.Bin bin = new Gst.Bin("voiceprocessorbin");
Gst.Element converter = Gst.ElementFactory.make("audioconvert", @"dsp_convert_$id");
Gst.Element resampler = Gst.ElementFactory.make("audioresample", @"dsp_resmaple_$id");
Gst.Element voiceproc = Gst.ElementFactory.make("webrtcdsp", @"dsp_$id");
bin.add_many(converter, resampler, voiceproc);
converter.link(resampler);
resampler.link(voiceproc);
voiceproc.@set("probe", "webrtcechoprobe0");
Gst.Pad sink_pad = bin.find_unlinked_pad(Gst.PadDirection.SINK);
Gst.Pad src_pad = bin.find_unlinked_pad(Gst.PadDirection.SRC);
Gst.Pad ghost_source = new Gst.GhostPad("source", src_pad);
Gst.Pad ghost_sink = new Gst.GhostPad("sink", sink_pad);
bin.add_pad(ghost_source);
bin.add_pad(ghost_sink);
return (Gst.Element)bin;
}
#endif
private void create() { private void create() {
debug("Creating device %s", id); debug("Creating device %s", id);
plugin.pause(); plugin.pause();
@ -511,7 +451,8 @@ public class Dino.Plugins.Rtp.Device : MediaDevice, Object {
element.link(filter); element.link(filter);
#if WITH_VOICE_PROCESSOR #if WITH_VOICE_PROCESSOR
if (media == "audio" && plugin.echoprobe != null) { if (media == "audio" && plugin.echoprobe != null) {
dsp = create_voice_processor(); dsp = new VoiceProcessor(plugin.echoprobe as EchoProbe, element as Gst.Audio.StreamVolume);
dsp.name = @"dsp_$id";
pipe.add(dsp); pipe.add(dsp);
filter.link(dsp); filter.link(dsp);
} }

View file

@ -42,19 +42,6 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
if (pause_count < 0) warning("Pause count below zero!"); if (pause_count < 0) warning("Pause count below zero!");
} }
private bool is_skipped(Gst.Device device) {
bool is_noprops = device.properties == null;
#if WITH_WASAPI
bool is_disabled_sound = device.properties.get_string("device.api") == "directsound";
#else
bool is_disabled_sound = device.properties.get_string("device.api") == "wasapi";
#endif
bool is_proplist = device.properties.has_name("pipewire-proplist") && device.has_classes("Audio");
bool is_monitor = device.properties.get_string("device.class") == "monitor";
bool is_in_devices = devices.any_match((it) => it.matches(device));
return is_noprops || is_disabled_sound || is_proplist || is_monitor || is_in_devices;
}
private void init_device_monitor() { private void init_device_monitor() {
if (device_monitor != null) return; if (device_monitor != null) return;
device_monitor = new Gst.DeviceMonitor(); device_monitor = new Gst.DeviceMonitor();
@ -62,34 +49,15 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message); device_monitor.get_bus().add_watch(Priority.DEFAULT, on_device_monitor_message);
device_monitor.start(); device_monitor.start();
foreach (Gst.Device device in device_monitor.get_devices()) { foreach (Gst.Device device in device_monitor.get_devices()) {
if (is_skipped(device)) continue; if (device.properties == null) continue;
debug(@"(Init) Add name=$(device.name) device=$(device.display_name)"); if (device.properties.get_string("device.api") == "wasapi") continue;
if (device.properties.has_name("pipewire-proplist") && device.has_classes("Audio")) continue;
if (device.properties.get_string("device.class") == "monitor") continue;
if (devices.any_match((it) => it.matches(device))) continue;
devices.add(new Device(this, device)); devices.add(new Device(this, device));
} }
} }
#if WITH_VOICE_PROCESSOR
private Gst.Element create_echo_probe() {
Gst.Bin bin = new Gst.Bin("echoprobebin");
Gst.Element converter = Gst.ElementFactory.make("audioconvert", "echo_convert_");
Gst.Element resampler = Gst.ElementFactory.make("audioresample", "echo_resample_");
Gst.Element webrtcechoprobe = Gst.ElementFactory.make("webrtcechoprobe", "webrtcechoprobe0");
bin.add_many(webrtcechoprobe, converter, resampler);
webrtcechoprobe.link(converter);
converter.link(resampler);
Gst.Pad sink_pad = bin.find_unlinked_pad(Gst.PadDirection.SINK);
Gst.Pad src_pad = bin.find_unlinked_pad(Gst.PadDirection.SRC);
Gst.Pad ghost_source = new Gst.GhostPad("source", src_pad);
Gst.Pad ghost_sink = new Gst.GhostPad("sink", sink_pad);
bin.add_pad(ghost_source);
bin.add_pad(ghost_sink);
return (Gst.Element)bin;
}
#endif
private void init_call_pipe() { private void init_call_pipe() {
if (pipe != null) return; if (pipe != null) return;
pipe = new Gst.Pipeline(null); pipe = new Gst.Pipeline(null);
@ -111,8 +79,8 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
#if WITH_VOICE_PROCESSOR #if WITH_VOICE_PROCESSOR
// Audio echo probe // Audio echo probe
echoprobe = create_echo_probe(); echoprobe = new EchoProbe();
pipe.add(echoprobe); if (echoprobe != null) pipe.add(echoprobe);
#endif #endif
// Pipeline // Pipeline
@ -237,23 +205,20 @@ public class Dino.Plugins.Rtp.Plugin : RootInterface, VideoCallPlugin, Object {
switch (message.type) { switch (message.type) {
case Gst.MessageType.DEVICE_ADDED: case Gst.MessageType.DEVICE_ADDED:
message.parse_device_added(out gst_device); message.parse_device_added(out gst_device);
if (is_skipped(gst_device)) return Source.CONTINUE; if (devices.any_match((it) => it.matches(gst_device))) return Source.CONTINUE;
device = new Device(this, gst_device); device = new Device(this, gst_device);
debug(@"(Notify) Add name=$(gst_device.name) device=$(gst_device.display_name)");
devices.add(device); devices.add(device);
break; break;
#if GST_1_16 #if GST_1_16
case Gst.MessageType.DEVICE_CHANGED: case Gst.MessageType.DEVICE_CHANGED:
message.parse_device_changed(out gst_device, out old_gst_device); message.parse_device_changed(out gst_device, out old_gst_device);
device = devices.first_match((it) => it.matches(old_gst_device)); device = devices.first_match((it) => it.matches(old_gst_device));
debug(@"(Notify) Change name=$(gst_device.name) device=$(gst_device.display_name)");
if (device != null) device.update(gst_device); if (device != null) device.update(gst_device);
break; break;
#endif #endif
case Gst.MessageType.DEVICE_REMOVED: case Gst.MessageType.DEVICE_REMOVED:
message.parse_device_removed(out gst_device); message.parse_device_removed(out gst_device);
device = devices.first_match((it) => it.matches(gst_device)); device = devices.first_match((it) => it.matches(gst_device));
debug(@"(Notify) Remove name=$(gst_device.name) device=$(gst_device.display_name)");
if (device != null) devices.remove(device); if (device != null) devices.remove(device);
break; break;
default: default:

View file

@ -0,0 +1,176 @@
using Gst;
namespace Dino.Plugins.Rtp {
public static extern Buffer adjust_to_running_time(Base.Transform transform, Buffer buf);
}
public class Dino.Plugins.Rtp.EchoProbe : Audio.Filter {
private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}};
private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}};
public Audio.Info audio_info { get; private set; }
public signal void on_new_buffer(Buffer buffer);
private uint period_samples;
private uint period_size;
private Base.Adapter adapter = new Base.Adapter();
static construct {
add_static_pad_template(sink_template);
add_static_pad_template(src_template);
set_static_metadata("Acoustic Echo Canceller probe", "Generic/Audio", "Gathers playback buffers for echo cancellation", "Dino Team <contact@dino.im>");
}
construct {
set_passthrough(true);
}
public override bool setup(Audio.Info info) {
audio_info = info;
period_samples = info.rate / 100; // 10ms buffers
period_size = period_samples * info.bpf;
return true;
}
public override FlowReturn transform_ip(Buffer buf) {
lock (adapter) {
adapter.push(adjust_to_running_time(this, buf));
while (adapter.available() > period_size) {
on_new_buffer(adapter.take_buffer(period_size));
}
}
return FlowReturn.OK;
}
public override bool stop() {
adapter.clear();
return true;
}
}
public class Dino.Plugins.Rtp.VoiceProcessor : Audio.Filter {
private static StaticPadTemplate sink_template = {"sink", PadDirection.SINK, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}};
private static StaticPadTemplate src_template = {"src", PadDirection.SRC, PadPresence.ALWAYS, {null, "audio/x-raw,rate=48000,channels=1,layout=interleaved,format=S16LE"}};
public Audio.Info audio_info { get; private set; }
private ulong process_outgoing_buffer_handler_id;
private uint adjust_delay_timeout_id;
private uint period_samples;
private uint period_size;
private Base.Adapter adapter = new Base.Adapter();
private EchoProbe? echo_probe;
private Audio.StreamVolume? stream_volume;
private ClockTime last_reverse;
private void* native;
static construct {
add_static_pad_template(sink_template);
add_static_pad_template(src_template);
set_static_metadata("Voice Processor (AGC, AEC, filters, etc.)", "Generic/Audio", "Pre-processes voice with WebRTC Audio Processing Library", "Dino Team <contact@dino.im>");
}
construct {
set_passthrough(false);
}
public VoiceProcessor(EchoProbe? echo_probe = null, Audio.StreamVolume? stream_volume = null) {
this.echo_probe = echo_probe;
this.stream_volume = stream_volume;
}
private static extern void* init_native(int stream_delay);
private static extern void setup_native(void* native);
private static extern void destroy_native(void* native);
private static extern void analyze_reverse_stream(void* native, Audio.Info info, Buffer buffer);
private static extern void process_stream(void* native, Audio.Info info, Buffer buffer);
private static extern void adjust_stream_delay(void* native);
private static extern void notify_gain_level(void* native, int gain_level);
private static extern int get_suggested_gain_level(void* native);
private static extern bool get_stream_has_voice(void* native);
public override bool setup(Audio.Info info) {
debug("VoiceProcessor.setup(%s)", info.to_caps().to_string());
audio_info = info;
period_samples = info.rate / 100; // 10ms buffers
period_size = period_samples * info.bpf;
adapter.clear();
setup_native(native);
return true;
}
public override bool start() {
native = init_native(150);
if (process_outgoing_buffer_handler_id == 0 && echo_probe != null) {
process_outgoing_buffer_handler_id = echo_probe.on_new_buffer.connect(process_outgoing_buffer);
}
if (stream_volume == null && sinkpad.get_peer() != null && sinkpad.get_peer().get_parent_element() is Audio.StreamVolume) {
stream_volume = sinkpad.get_peer().get_parent_element() as Audio.StreamVolume;
}
return true;
}
private bool adjust_delay() {
if (native != null) {
adjust_stream_delay(native);
return Source.CONTINUE;
} else {
adjust_delay_timeout_id = 0;
return Source.REMOVE;
}
}
private void process_outgoing_buffer(Buffer buffer) {
if (buffer.pts != uint64.MAX) {
last_reverse = buffer.pts;
}
analyze_reverse_stream(native, echo_probe.audio_info, buffer);
if (adjust_delay_timeout_id == 0 && echo_probe != null) {
adjust_delay_timeout_id = Timeout.add(1000, adjust_delay);
}
}
public override FlowReturn submit_input_buffer(bool is_discont, Buffer input) {
lock (adapter) {
if (is_discont) {
adapter.clear();
}
adapter.push(adjust_to_running_time(this, input));
}
return FlowReturn.OK;
}
public override FlowReturn generate_output(out Buffer output_buffer) {
lock (adapter) {
if (adapter.available() >= period_size) {
output_buffer = (Gst.Buffer) adapter.take_buffer(period_size).make_writable();
int old_gain_level = 0;
if (stream_volume != null) {
old_gain_level = (int) (stream_volume.get_volume(Audio.StreamVolumeFormat.LINEAR) * 255.0);
notify_gain_level(native, old_gain_level);
}
process_stream(native, audio_info, output_buffer);
if (stream_volume != null) {
int new_gain_level = get_suggested_gain_level(native);
if (old_gain_level != new_gain_level) {
debug("Gain: %i -> %i", old_gain_level, new_gain_level);
stream_volume.set_volume(Audio.StreamVolumeFormat.LINEAR, ((double)new_gain_level) / 255.0);
}
}
}
}
return FlowReturn.OK;
}
public override bool stop() {
if (process_outgoing_buffer_handler_id != 0) {
echo_probe.disconnect(process_outgoing_buffer_handler_id);
process_outgoing_buffer_handler_id = 0;
}
if (adjust_delay_timeout_id != 0) {
Source.remove(adjust_delay_timeout_id);
adjust_delay_timeout_id = 0;
}
adapter.clear();
destroy_native(native);
native = null;
return true;
}
}

View file

@ -0,0 +1,148 @@
#include <algorithm>
#include <gst/gst.h>
#include <gst/audio/audio.h>
#include <webrtc/modules/audio_processing/include/audio_processing.h>
#include <webrtc/modules/interface/module_common_types.h>
#include <webrtc/system_wrappers/include/trace.h>
#define SAMPLE_RATE 48000
#define SAMPLE_CHANNELS 1
struct _DinoPluginsRtpVoiceProcessorNative {
webrtc::AudioProcessing *apm;
gint stream_delay;
gint last_median;
gint last_poor_delays;
};
extern "C" void *dino_plugins_rtp_adjust_to_running_time(GstBaseTransform *transform, GstBuffer *buffer) {
GstBuffer *copy = gst_buffer_copy(buffer);
GST_BUFFER_PTS(copy) = gst_segment_to_running_time(&transform->segment, GST_FORMAT_TIME, GST_BUFFER_PTS(buffer));
return copy;
}
extern "C" void *dino_plugins_rtp_voice_processor_init_native(gint stream_delay) {
_DinoPluginsRtpVoiceProcessorNative *native = new _DinoPluginsRtpVoiceProcessorNative();
webrtc::Config config;
config.Set<webrtc::ExtendedFilter>(new webrtc::ExtendedFilter(true));
config.Set<webrtc::ExperimentalAgc>(new webrtc::ExperimentalAgc(true, 85));
native->apm = webrtc::AudioProcessing::Create(config);
native->stream_delay = stream_delay;
native->last_median = 0;
native->last_poor_delays = 0;
return native;
}
extern "C" void dino_plugins_rtp_voice_processor_setup_native(void *native_ptr) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::AudioProcessing *apm = native->apm;
webrtc::ProcessingConfig pconfig;
pconfig.streams[webrtc::ProcessingConfig::kInputStream] =
webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false);
pconfig.streams[webrtc::ProcessingConfig::kOutputStream] =
webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false);
pconfig.streams[webrtc::ProcessingConfig::kReverseInputStream] =
webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false);
pconfig.streams[webrtc::ProcessingConfig::kReverseOutputStream] =
webrtc::StreamConfig(SAMPLE_RATE, SAMPLE_CHANNELS, false);
apm->Initialize(pconfig);
apm->high_pass_filter()->Enable(true);
apm->echo_cancellation()->enable_drift_compensation(false);
apm->echo_cancellation()->set_suppression_level(webrtc::EchoCancellation::kModerateSuppression);
apm->echo_cancellation()->enable_delay_logging(true);
apm->echo_cancellation()->Enable(true);
apm->noise_suppression()->set_level(webrtc::NoiseSuppression::kModerate);
apm->noise_suppression()->Enable(true);
apm->gain_control()->set_analog_level_limits(0, 255);
apm->gain_control()->set_mode(webrtc::GainControl::kAdaptiveAnalog);
apm->gain_control()->set_target_level_dbfs(3);
apm->gain_control()->set_compression_gain_db(9);
apm->gain_control()->enable_limiter(true);
apm->gain_control()->Enable(true);
apm->voice_detection()->set_likelihood(webrtc::VoiceDetection::Likelihood::kLowLikelihood);
apm->voice_detection()->Enable(true);
}
extern "C" void
dino_plugins_rtp_voice_processor_analyze_reverse_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false);
webrtc::AudioProcessing *apm = native->apm;
GstMapInfo map;
gst_buffer_map(buffer, &map, GST_MAP_READ);
webrtc::AudioFrame frame;
frame.num_channels_ = info->channels;
frame.sample_rate_hz_ = info->rate;
frame.samples_per_channel_ = gst_buffer_get_size(buffer) / info->bpf;
memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf);
int err = apm->AnalyzeReverseStream(&frame);
if (err < 0) g_warning("voice_processor_native.cpp: ProcessReverseStream %i", err);
gst_buffer_unmap(buffer, &map);
}
extern "C" void dino_plugins_rtp_voice_processor_notify_gain_level(void *native_ptr, gint gain_level) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::AudioProcessing *apm = native->apm;
apm->gain_control()->set_stream_analog_level(gain_level);
}
extern "C" gint dino_plugins_rtp_voice_processor_get_suggested_gain_level(void *native_ptr) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::AudioProcessing *apm = native->apm;
return apm->gain_control()->stream_analog_level();
}
extern "C" bool dino_plugins_rtp_voice_processor_get_stream_has_voice(void *native_ptr) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::AudioProcessing *apm = native->apm;
return apm->voice_detection()->stream_has_voice();
}
extern "C" void dino_plugins_rtp_voice_processor_adjust_stream_delay(void *native_ptr) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::AudioProcessing *apm = native->apm;
int median, std, poor_delays;
float fraction_poor_delays;
apm->echo_cancellation()->GetDelayMetrics(&median, &std, &fraction_poor_delays);
poor_delays = (int)(fraction_poor_delays * 100.0);
if (fraction_poor_delays < 0 || (native->last_median == median && native->last_poor_delays == poor_delays)) return;
g_debug("voice_processor_native.cpp: Stream delay metrics: median=%i std=%i poor_delays=%i%%", median, std, poor_delays);
native->last_median = median;
native->last_poor_delays = poor_delays;
if (poor_delays > 90) {
native->stream_delay = std::min(std::max(0, native->stream_delay + std::min(48, std::max(median, -48))), 384);
g_debug("voice_processor_native.cpp: set stream_delay=%i", native->stream_delay);
}
}
extern "C" void
dino_plugins_rtp_voice_processor_process_stream(void *native_ptr, GstAudioInfo *info, GstBuffer *buffer) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
webrtc::StreamConfig config(SAMPLE_RATE, SAMPLE_CHANNELS, false);
webrtc::AudioProcessing *apm = native->apm;
GstMapInfo map;
gst_buffer_map(buffer, &map, GST_MAP_READWRITE);
webrtc::AudioFrame frame;
frame.num_channels_ = info->channels;
frame.sample_rate_hz_ = info->rate;
frame.samples_per_channel_ = info->rate / 100;
memcpy(frame.data_, map.data, frame.samples_per_channel_ * info->bpf);
apm->set_stream_delay_ms(native->stream_delay);
int err = apm->ProcessStream(&frame);
if (err >= 0) memcpy(map.data, frame.data_, frame.samples_per_channel_ * info->bpf);
if (err < 0) g_warning("voice_processor_native.cpp: ProcessStream %i", err);
gst_buffer_unmap(buffer, &map);
}
extern "C" void dino_plugins_rtp_voice_processor_destroy_native(void *native_ptr) {
_DinoPluginsRtpVoiceProcessorNative *native = (_DinoPluginsRtpVoiceProcessorNative *) native_ptr;
delete native;
}

View file

@ -37,5 +37,5 @@ if host_machine.system() == 'windows'
link_args += ['-Wl,--export-all-symbols'] link_args += ['-Wl,--export-all-symbols']
endif endif
libwin32_fonts = shared_library('win32-fonts', sources: sources, name_prefix:'', c_args: c_args, vala_args:vala_args, dependencies: dependencies, link_args: link_args, kwargs: install_options) libwin32_fonts = shared_library('win32-fonts', sources: sources, name_prefix:'', c_args: c_args, vala_args:vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir')/'dino/plugins', link_args: link_args)
dep_win32_fonts = declare_dependency(link_with: libwin32_fonts, include_directories: include_directories('.')) dep_win32_fonts = declare_dependency(link_with: libwin32_fonts, include_directories: include_directories('.'))

View file

@ -70,5 +70,5 @@ if host_machine.system() == 'windows'
link_args += ['-Wl,--export-all-symbols'] link_args += ['-Wl,--export-all-symbols']
endif endif
libwindows_notification = shared_library('windows-notification', sources: sources, include_directories: inc, name_prefix:'', c_args: c_args, vala_args:vala_args, dependencies: dependencies, link_args: link_args, cpp_args: cpp_args, kwargs: install_options) libwindows_notification = shared_library('windows-notification', sources: sources, include_directories: inc, name_prefix:'', c_args: c_args, vala_args:vala_args, dependencies: dependencies, install: true, install_dir: get_option('libdir')/'dino/plugins', link_args: link_args, cpp_args: cpp_args)
dep_windows_notification = declare_dependency(link_with: libwindows_notification, include_directories: include_directories('.')) dep_windows_notification = declare_dependency(link_with: libwindows_notification, include_directories: include_directories('.'))

View file

@ -1,6 +1,5 @@
Dino+, a modern XMPP/Jabber client software based on Dino Dino - Modern Jabber/XMPP Client using GTK+/Vala
Copyright (C) 2016-2023 Dino contributors Copyright (C) 2016-2020 Dino contributors
Copyright (C) 2024 Dino+ contributors
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View file

@ -3,22 +3,22 @@ Unicode True
RequestExecutionLevel user RequestExecutionLevel user
SetCompressor /SOLID lzma SetCompressor /SOLID lzma
!define MUI_PRODUCT "Dino+" !define MUI_PRODUCT "Dino"
!define MUI_PRODUCT_NAME ${MUI_PRODUCT} !define MUI_PRODUCT_NAME ${MUI_PRODUCT}
!define MUI_BRANDINGTEXT ${MUI_PRODUCT} !define MUI_BRANDINGTEXT ${MUI_PRODUCT}
!define PRODUCT_WEBSITE "https://github.com/mxlgv/dino" !define PRODUCT_WEBSITE "https://dino.im"
!define MUI_ICON "win64-dist/dino.ico" !define MUI_ICON "win64-dist/dino.ico"
!define ICON "win64-dist/dino.ico" !define ICON "win64-dist/dino.ico"
!define MUI_COMPONENTSPAGE_NODESC !define MUI_COMPONENTSPAGE_NODESC
# Modern Interface # Modern Interface
!include "MUI2.nsh" !include "MUI2.nsh"
!insertmacro MUI_PAGE_LICENSE "../LICENSE_SHORT" !insertmacro MUI_PAGE_LICENSE "LICENSE_SHORT"
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
!include "english.nsh" !include "english.nsh"
Name ${MUI_PRODUCT} Name ${MUI_PRODUCT}
BrandingText "Dino+, a modern XMPP/Jabber client" BrandingText "Communicating happiness"
# define installer name # define installer name
OutFile "dino-installer.exe" OutFile "dino-installer.exe"
@ -35,21 +35,15 @@ File /r win64-dist\*.*
# define uninstaller name # define uninstaller name
WriteUninstaller $INSTDIR\uninstaller.exe WriteUninstaller $INSTDIR\uninstaller.exe
# Create Dino shortcuts macro # Create a shortcut for startmenu
!macro CreateDinoShortcut Path
CreateShortCut "${Path}\Dino+.lnk" "$INSTDIR\bin\dino.exe" "" "$INSTDIR\dino.ico"
CreateShortCut "${Path}\Dino+ (debug).lnk" "$WINDIR\system32\cmd.exe" '/c set G_MESSAGES_DEBUG=all&&"$INSTDIR\bin\dino-with-console.exe"' "$INSTDIR\dino.ico"
!macroend
# Create shortcuts for startmenu
CreateDirectory "$SMPROGRAMS\Dino" CreateDirectory "$SMPROGRAMS\Dino"
!insertmacro CreateDinoShortcut "$SMPROGRAMS\Dino" CreateShortcut "$SMPROGRAMS\Dino\Dino.lnk" "$INSTDIR\bin\dino.exe" "" "$INSTDIR\dino.ico"
CreateShortcut "$SMPROGRAMS\Dino\Uninstaller.lnk" "$INSTDIR\uninstaller.exe" CreateShortcut "$SMPROGRAMS\Dino\Uninstaller.lnk" "$INSTDIR\uninstaller.exe"
CreateShortcut "$SMPROGRAMS\Dino\License.lnk" "notepad.exe" "$INSTDIR\LICENSE" CreateShortcut "$SMPROGRAMS\Dino\License.lnk" "notepad.exe" "$INSTDIR\LICENSE"
CreateShortcut "$SMPROGRAMS\Dino\Dino+ website.lnk" "https://github.com/mxlgv/dino" "" "$INSTDIR\dino.ico" CreateShortcut "$SMPROGRAMS\Dino\Dino website.lnk" "https://dino.im" "" "$INSTDIR\dino.ico"
# Create a shortcuts for desktop # Create a shortcut for desktop
!insertmacro CreateDinoShortcut "$DESKTOP" CreateShortCut "$DESKTOP\Dino.lnk" "$INSTDIR\bin\dino.exe" "" "$INSTDIR\dino.ico"
# set application ID # set application ID
# No "ApplicationID" plugin for NSIS MINGW64 # No "ApplicationID" plugin for NSIS MINGW64

View file

@ -177,7 +177,7 @@ install(TARGETS xmpp-vala ${TARGET_INSTALL})
install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.deps DESTINATION ${VAPI_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.deps DESTINATION ${VAPI_INSTALL_DIR})
install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.h DESTINATION ${INCLUDE_INSTALL_DIR}) install(FILES ${CMAKE_BINARY_DIR}/exports/xmpp-vala.h DESTINATION ${INCLUDE_INSTALL_DIR})
if(BUILD_TESTING) if(BUILD_TESTS)
vala_precompile(ENGINE_TEST_VALA_C vala_precompile(ENGINE_TEST_VALA_C
SOURCES SOURCES
"tests/common.vala" "tests/common.vala"
@ -198,5 +198,4 @@ if(BUILD_TESTING)
add_definitions(${VALA_CFLAGS}) add_definitions(${VALA_CFLAGS})
add_executable(xmpp-vala-test ${ENGINE_TEST_VALA_C}) add_executable(xmpp-vala-test ${ENGINE_TEST_VALA_C})
target_link_libraries(xmpp-vala-test xmpp-vala ${SIGNAL_PROTOCOL_PACKAGES}) target_link_libraries(xmpp-vala-test xmpp-vala ${SIGNAL_PROTOCOL_PACKAGES})
add_test(NAME xmpp-vala COMMAND xmpp-vala-test) endif(BUILD_TESTS)
endif(BUILD_TESTING)

View file

@ -136,14 +136,3 @@ lib_xmpp_vala = library('xmpp-vala', sources, c_args: c_args, vala_args: vala_ar
dep_xmpp_vala = declare_dependency(link_with: lib_xmpp_vala, include_directories: include_directories('.')) dep_xmpp_vala = declare_dependency(link_with: lib_xmpp_vala, include_directories: include_directories('.'))
install_data('xmpp-vala.deps', install_dir: get_option('datadir') / 'vala/vapi') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756 install_data('xmpp-vala.deps', install_dir: get_option('datadir') / 'vala/vapi') # TODO: workaround for https://github.com/mesonbuild/meson/issues/9756
sources = files(
'tests/common.vala',
'tests/testcase.vala',
'tests/jid.vala',
'tests/stanza.vala',
'tests/color.vala',
'tests/util.vala',
)
test_xmpp_vala = executable('test_xmpp_vala', sources, vala_args: vala_args, dependencies: dependencies + [dep_xmpp_vala])
test('xmpp-vala', test_xmpp_vala)

View file

@ -191,12 +191,4 @@ public abstract class Xmpp.XmppStream : Object {
} }
} }
} }
public static bool equals_func(XmppStream str1, XmppStream str2) {
return str1.remote_name.to_string() == str2.remote_name.to_string();
}
public static uint hash_func(XmppStream str) {
return str.remote_name.to_string().hash();
}
} }

View file

@ -25,9 +25,6 @@ namespace Xmpp.Xep.ExternalServiceDiscovery {
service.username = service_node.get_attribute("username", NS_URI); service.username = service_node.get_attribute("username", NS_URI);
service.password = service_node.get_attribute("password", NS_URI); service.password = service_node.get_attribute("password", NS_URI);
string? expires_str = service_node.get_attribute("expires", NS_URI);
if (expires_str != null) service.expires = DateTimeProfiles.parse_string(expires_str);
service.transport = service_node.get_attribute("transport", NS_URI); service.transport = service_node.get_attribute("transport", NS_URI);
service.name = service_node.get_attribute("name", NS_URI); service.name = service_node.get_attribute("name", NS_URI);
string? restricted_str = service_node.get_attribute("restricted", NS_URI); string? restricted_str = service_node.get_attribute("restricted", NS_URI);
@ -44,7 +41,6 @@ namespace Xmpp.Xep.ExternalServiceDiscovery {
public string username { get; set; } public string username { get; set; }
public string password { get; set; } public string password { get; set; }
public DateTime? expires { get; set; }
public string transport { get; set; } public string transport { get; set; }
public string name { get; set; } public string name { get; set; }

View file

@ -8,7 +8,6 @@ private const string NS_URI = "urn:xmpp:jingle:apps:file-transfer:5";
public class Module : Jingle.ContentType, XmppStreamModule { public class Module : Jingle.ContentType, XmppStreamModule {
public signal void transferred_bytes(size_t bytes);
public signal void file_incoming(XmppStream stream, FileTransfer file_transfer); public signal void file_incoming(XmppStream stream, FileTransfer file_transfer);
public static Xmpp.ModuleIdentity<Module> IDENTITY = new Xmpp.ModuleIdentity<Module>(NS_URI, "0234_jingle_file_transfer"); public static Xmpp.ModuleIdentity<Module> IDENTITY = new Xmpp.ModuleIdentity<Module>(NS_URI, "0234_jingle_file_transfer");
@ -43,10 +42,7 @@ public class Module : Jingle.ContentType, XmppStreamModule {
return yield stream.get_module(Jingle.Module.IDENTITY).is_available(stream, required_transport_type, required_components, full_jid); return yield stream.get_module(Jingle.Module.IDENTITY).is_available(stream, required_transport_type, required_components, full_jid);
} }
public async void offer_file_stream(XmppStream stream, Jid receiver_full_jid, public async void offer_file_stream(XmppStream stream, Jid receiver_full_jid, InputStream input_stream, string basename, int64 size, string? precondition_name = null, Object? precondition_options = null) throws Jingle.Error {
Cancellable cancellable, InputStream input_stream, string basename,
int64 size, string? precondition_name = null,
Object? precondition_options = null) throws Jingle.Error {
StanzaNode file_node; StanzaNode file_node;
StanzaNode description = new StanzaNode.build("description", NS_URI) StanzaNode description = new StanzaNode.build("description", NS_URI)
.add_self_xmlns() .add_self_xmlns()
@ -111,18 +107,7 @@ public class Module : Jingle.ContentType, XmppStreamModule {
} }
IOStream io_stream = yield connection.stream.wait_async(); IOStream io_stream = yield connection.stream.wait_async();
yield io_stream.input_stream.close_async(); yield io_stream.input_stream.close_async();
yield io_stream.output_stream.splice_async(input_stream, OutputStreamSpliceFlags.CLOSE_SOURCE|OutputStreamSpliceFlags.CLOSE_TARGET);
ssize_t read;
var buffer = new uint8[1024];
while ((read = yield input_stream.read_async(buffer, Priority.LOW, cancellable)) > 0) {
buffer.length = (int) read;
transferred_bytes((size_t)read);
yield io_stream.output_stream.write_async(buffer, Priority.LOW, cancellable);
buffer.length = 1024;
}
yield input_stream.close_async();
yield io_stream.output_stream.close_async();
yield connection.terminate(true); yield connection.terminate(true);
} catch (Error e) { } catch (Error e) {
if (session != null) { if (session != null) {