mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-10-30 07:53:33 +08:00
Compare commits
82 Commits
nightly/20
...
nightly/20
| Author | SHA1 | Date | |
|---|---|---|---|
| 7243b10e81 | |||
| 964c8e0553 | |||
| 96c00ff019 | |||
| c8716d1113 | |||
| 630e5e9fe4 | |||
| f447327c02 | |||
| 650f98636b | |||
| b92cd902b9 | |||
| b99c2674b4 | |||
| 1524ba0177 | |||
| 5503769023 | |||
| 89354a07e1 | |||
| 69400bee2a | |||
| 0e07f9cee7 | |||
| d2eba1d91e | |||
| 78389d8c28 | |||
| e6d820850a | |||
| 0a7f9349a9 | |||
| 99254f8d02 | |||
| 010a2c4eea | |||
| 61163037d4 | |||
| 3577982a93 | |||
| 3ad0b92dcd | |||
| 91475c3a3f | |||
| 9379af23ec | |||
| 406a251c85 | |||
| 20e2852e44 | |||
| 3225f90ce8 | |||
| df3e76a1cf | |||
| c5e15d26a0 | |||
| 77e019b02b | |||
| 89bed2bf85 | |||
| 519871e458 | |||
| 0a842042b0 | |||
| 9aeb2377dc | |||
| 6ad5f4b850 | |||
| f5c63d24fb | |||
| 5cb34bd31c | |||
| e56a966de1 | |||
| acc0c97234 | |||
| 665af7c0c3 | |||
| fa51e042e5 | |||
| 3b9fb0bfca | |||
| 3673b0646c | |||
| 2e2f6423f8 | |||
| 28c1cbbb34 | |||
| 87c215deb7 | |||
| 77eddcd962 | |||
| 043a715c59 | |||
| 5bd3ead22d | |||
| a9aa1cac80 | |||
| 409ba70258 | |||
| dc50f19815 | |||
| d83895dcc9 | |||
| e24a3d6a4d | |||
| 06de33e1be | |||
| c8fbcd8c6b | |||
| 31269fe8fc | |||
| a676ad395a | |||
| 04c71d02e0 | |||
| 1fe60b9c33 | |||
| 51ef7a83da | |||
| 588a8abdac | |||
| 8a149b6c4f | |||
| 6105f4f7ce | |||
| 92341b27b6 | |||
| f39afdac4c | |||
| 690f2dd85c | |||
| bd45d6a406 | |||
| 5b92e4708a | |||
| 63c01f1439 | |||
| 73aeb02ebd | |||
| 9d91317089 | |||
| 474bc5f6a4 | |||
| f5b64e955b | |||
| b88627d125 | |||
| 200978a044 | |||
| a673ff9890 | |||
| 7803dd0991 | |||
| a8a736bc8c | |||
| ff7acf9932 | |||
| afde816b23 |
13
.gitignore
vendored
13
.gitignore
vendored
@ -1,9 +1,18 @@
|
||||
*.user
|
||||
doc/Doxyfile
|
||||
|
||||
### VisualStudioCode ###
|
||||
.vscode/**/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
### VisualStudioCode Patch ###
|
||||
# Ignore all local history of files
|
||||
**/.history
|
||||
|
||||
GeneratedFiles/
|
||||
.vs/
|
||||
.vscode/
|
||||
x64/
|
||||
x86/
|
||||
[wW]in32/
|
||||
|
||||
49
.vscode/launch.json
vendored
Normal file
49
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Jami-Client-Debug",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"program": "",
|
||||
"linux":{
|
||||
"MIMode": "gdb",
|
||||
"program": "${workspaceFolder}/build/jami",
|
||||
"args": [
|
||||
"-d",
|
||||
],
|
||||
},
|
||||
"osx": {
|
||||
"MIMode": "lldb",
|
||||
"program": "${workspaceFolder}/build/Jami.app/Contents/MacOS/Jami",
|
||||
"environment": [
|
||||
{
|
||||
"name": "NO_COLOR",
|
||||
"value": "true",
|
||||
}
|
||||
],
|
||||
},
|
||||
"cwd": "${workspaceFolder}",
|
||||
"preLaunchTask": "cmake-build",
|
||||
"externalConsole": false, // A macOS dev may want to set this to true.
|
||||
},
|
||||
{
|
||||
// Using this configuration will require manually reconfiguring the project using
|
||||
// build.py --no-libwrap, otherwise the daemon executable will not be built and the
|
||||
// client will not be built with ENABLE_LIBWRAP=False.
|
||||
"name": "Jami-Daemon-Debug",
|
||||
"type": "cppdbg",
|
||||
"request": "launch",
|
||||
"linux": {
|
||||
"MIMode": "gdb",
|
||||
"program": "${workspaceFolder}/daemon/bin/dbus/jamid",
|
||||
},
|
||||
"program": "",
|
||||
"args": [
|
||||
"-cdp",
|
||||
],
|
||||
"cwd": "${workspaceFolder}",
|
||||
"preLaunchTask": "cmake-build",
|
||||
}
|
||||
]
|
||||
}
|
||||
15
.vscode/settings.json
vendored
Normal file
15
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"C_Cpp.default.includePath": [
|
||||
"${default}",
|
||||
"${workspaceFolder}/**",
|
||||
"/usr/lib/libqt-jami/include/**",
|
||||
"/usr/lib64/qt-jami/include/**",
|
||||
],
|
||||
"C_Cpp.default.cppStandard": "c++17",
|
||||
"C_Cpp.default.cStandard": "c11",
|
||||
"cmake.configureOnOpen": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "xaver.clang-format",
|
||||
"files.eol": "\n",
|
||||
"cSpell.enabled": false,
|
||||
}
|
||||
93
.vscode/tasks.json
vendored
Normal file
93
.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,93 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "run-tests",
|
||||
"type": "shell",
|
||||
"command": "ctest",
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/build/tests",
|
||||
"env": {
|
||||
"HOME": "/tmp"
|
||||
}
|
||||
},
|
||||
"args": [
|
||||
"-V",
|
||||
"-R"
|
||||
],
|
||||
"group": {
|
||||
"kind": "test",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"detail": "Run the tests using CTest."
|
||||
},
|
||||
{
|
||||
"label": "cmake-configure",
|
||||
"type": "shell",
|
||||
"command": "cmake",
|
||||
"args": [
|
||||
"-S", ".",
|
||||
"-B", "build",
|
||||
"-DCMAKE_BUILD_TYPE=Debug",
|
||||
"-DCMAKE_PREFIX_PATH=\"/usr/lib64/qt-jami;/usr/lib/libqt-jami\"",
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "Generate the build system files with CMake."
|
||||
},
|
||||
{
|
||||
"label": "cmake-configure-tests",
|
||||
"type": "shell",
|
||||
"command": "cmake",
|
||||
"args": [
|
||||
"-S", ".",
|
||||
"-B", "build",
|
||||
"-DBUILD_TESTING=${input:buildTestingInput}"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "Generate the build system files with CMake."
|
||||
},
|
||||
{
|
||||
"label": "cmake-build",
|
||||
"type": "shell",
|
||||
"command": "cmake",
|
||||
"args": [
|
||||
"--build", "build",
|
||||
"-j$(nproc)",
|
||||
],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": [
|
||||
"$gcc"
|
||||
],
|
||||
"detail": "Compile the project using the generated build system.",
|
||||
"dependsOn": [
|
||||
"cmake-configure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "cmake-clean",
|
||||
"type": "shell",
|
||||
"command": "rm",
|
||||
"args": [
|
||||
"-rf",
|
||||
"build"
|
||||
],
|
||||
"group": "build",
|
||||
"problemMatcher": [],
|
||||
"detail": "Clean the build directory."
|
||||
}
|
||||
],
|
||||
"inputs": [
|
||||
{
|
||||
"id": "buildTestingInput",
|
||||
"type": "pickString",
|
||||
"description": "Do you want to enable testing?",
|
||||
"options": ["True", "False"],
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -455,10 +455,12 @@ elseif (NOT APPLE)
|
||||
${APP_SRC_DIR}/xrectsel.c
|
||||
${APP_SRC_DIR}/connectivitymonitor.cpp
|
||||
${APP_SRC_DIR}/dbuserrorhandler.cpp
|
||||
${APP_SRC_DIR}/appversionmanager.cpp)
|
||||
${APP_SRC_DIR}/appversionmanager.cpp
|
||||
${APP_SRC_DIR}/screencastportal.cpp)
|
||||
list(APPEND COMMON_HEADERS
|
||||
${APP_SRC_DIR}/xrectsel.h
|
||||
${APP_SRC_DIR}/dbuserrorhandler.h)
|
||||
${APP_SRC_DIR}/dbuserrorhandler.h
|
||||
${APP_SRC_DIR}/screencastportal.h)
|
||||
list(APPEND QT_MODULES DBus)
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
@ -473,6 +475,11 @@ elseif (NOT APPLE)
|
||||
add_definitions(${GIO_CFLAGS})
|
||||
endif()
|
||||
|
||||
pkg_check_modules(GIOUNIX REQUIRED gio-unix-2.0)
|
||||
if(GIOUNIX_FOUND)
|
||||
add_definitions(${GIOUNIX_CFLAGS})
|
||||
endif()
|
||||
|
||||
pkg_check_modules(LIBNM libnm)
|
||||
if(LIBNM_FOUND)
|
||||
add_definitions(-DUSE_LIBNM)
|
||||
@ -584,6 +591,7 @@ include_directories(
|
||||
if(ENABLE_LIBWRAP)
|
||||
list(APPEND COMMON_HEADERS
|
||||
${LIBCLIENT_SRC_DIR}/qtwrapper/instancemanager_wrap.h)
|
||||
add_definitions(-DENABLE_LIBWRAP=true)
|
||||
endif()
|
||||
|
||||
# SFPM
|
||||
@ -811,12 +819,20 @@ else()
|
||||
"-framework Security"
|
||||
compression
|
||||
resolv
|
||||
)
|
||||
)
|
||||
|
||||
set(APP_CONTAINER "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app/Contents")
|
||||
|
||||
# ringtones. Copy the entire directory to the app bundle.
|
||||
# daemon/ringtones -> Jami.app/Contents/Resources/ringtones
|
||||
execute_process(
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${DAEMON_DIR}/ringtones
|
||||
${APP_CONTAINER}/Resources/ringtones
|
||||
)
|
||||
|
||||
# translations
|
||||
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND)
|
||||
set(APP_CONTAINER
|
||||
"${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app/Contents")
|
||||
file(GLOB TS_FILES ${PROJECT_SOURCE_DIR}/translations/*.ts)
|
||||
|
||||
# Generate lproj folders.
|
||||
@ -844,26 +860,26 @@ else()
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${JAMI_VERSION}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${JAMI_BUILD}"
|
||||
MACOSX_BUNDLE_COPYRIGHT "${PROJ_COPYRIGHT}")
|
||||
if(APPSTORE)
|
||||
message(STATUS "app store version")
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/appstore/Jami.entitlements")
|
||||
else()
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
SPARKLE_URL "${SPARKLE_URL}"
|
||||
SPARKLE_PUBLIC_KEY "${SPARKLE_PUBLIC_KEY}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
|
||||
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
|
||||
endif()
|
||||
if(DEPLOY)
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -DQML_SRC_DIR=${SRC_DIR}
|
||||
-DMAC_DEPLOY_QT_PATH=${CMAKE_PREFIX_PATH}/bin
|
||||
-DEXE_NAME="${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app"
|
||||
-DSPARKLE_PATH=${SPARKLE_FRAMEWORK}
|
||||
-DENABLE_SPARKLE=${ENABLE_SPARKLE}
|
||||
-P ${EXTRAS_DIR}/build/cmake/macos_qt_deploy.cmake)
|
||||
endif()
|
||||
if(APPSTORE)
|
||||
message(STATUS "app store version")
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/appstore/Jami.entitlements")
|
||||
else()
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
SPARKLE_URL "${SPARKLE_URL}"
|
||||
SPARKLE_PUBLIC_KEY "${SPARKLE_PUBLIC_KEY}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/resources/entitlements/Jami.entitlements"
|
||||
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME TRUE)
|
||||
endif()
|
||||
if(DEPLOY)
|
||||
execute_process(COMMAND
|
||||
"${CMAKE_PREFIX_PATH}/bin/macdeployqt"
|
||||
"${CMAKE_BINARY_DIR}/${PROJECT_NAME}.app"
|
||||
-qmldir=${QML_SRC_DIR})
|
||||
if(${ENABLE_SPARKLE} MATCHES true)
|
||||
file(COPY ${SPARKLE_FRAMEWORK} DESTINATION ${EXE_NAME}/Contents/Frameworks/)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${CLIENT_INCLUDE_DIRS})
|
||||
|
||||
@ -288,7 +288,7 @@ Once the build has finished, you should then be able to use the Visual Studio So
|
||||
|
||||
**Set up**
|
||||
|
||||
- macOS minimum version 10.15
|
||||
- macOS minimum version 11.0
|
||||
- install python3
|
||||
- download xcode
|
||||
- install Qt 6.6
|
||||
|
||||
6
build.py
6
build.py
@ -100,7 +100,7 @@ ZYPPER_DEPENDENCIES = [
|
||||
'speexdsp-devel', 'speex-devel', 'libdbus-c++-devel', 'jsoncpp-devel', 'yaml-cpp-devel',
|
||||
'yasm', 'libuuid-devel', 'libnettle-devel', 'libopus-devel', 'libexpat-devel',
|
||||
'libgnutls-devel', 'msgpack-c-devel', 'msgpack-cxx-devel', 'libavcodec-devel', 'libavdevice-devel', 'pcre-devel',
|
||||
'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel', 'libvdpau-devel',
|
||||
'alsa-devel', 'libpulse-devel', 'libudev-devel', 'libva-devel', 'libvdpau-devel', 'pipewire-devel',
|
||||
'libopenssl-devel', 'libavutil-devel',
|
||||
]
|
||||
|
||||
@ -130,7 +130,7 @@ DNF_DEPENDENCIES = [
|
||||
'gcc-c++', 'which', 'alsa-lib-devel', 'systemd-devel', 'libuuid-devel',
|
||||
'uuid-devel', 'gnutls-devel', 'nettle-devel', 'opus-devel', 'speexdsp-devel',
|
||||
'yaml-cpp-devel', 'swig', 'jsoncpp-devel',
|
||||
'patch', 'libva-devel', 'openssl-devel', 'libvdpau-devel', 'msgpack-devel',
|
||||
'patch', 'libva-devel', 'openssl-devel', 'libvdpau-devel', 'pipewire-devel', 'msgpack-devel',
|
||||
'sqlite-devel', 'openssl-static', 'pandoc', 'nasm',
|
||||
'bzip2'
|
||||
]
|
||||
@ -154,7 +154,7 @@ APT_DEPENDENCIES = [
|
||||
'libopus-dev', 'libpcre3-dev', 'libpulse-dev', 'libssl-dev',
|
||||
'libspeex-dev', 'libspeexdsp-dev', 'libswscale-dev', 'libtool',
|
||||
'libudev-dev', 'libyaml-cpp-dev', 'sip-tester', 'swig',
|
||||
'uuid-dev', 'yasm', 'libjsoncpp-dev', 'libva-dev', 'libvdpau-dev', 'libmsgpack-dev',
|
||||
'uuid-dev', 'yasm', 'libjsoncpp-dev', 'libva-dev', 'libvdpau-dev', 'libpipewire-0.3-dev', 'libmsgpack-dev',
|
||||
'pandoc', 'nasm', 'dpkg-dev', 'libsystemd-dev'
|
||||
]
|
||||
|
||||
|
||||
2
daemon
2
daemon
Submodule daemon updated: 8596d253a2...cbf8f0af6d
@ -1,7 +0,0 @@
|
||||
message("Qt deploying in dir " ${QML_SRC_DIR})
|
||||
execute_process(COMMAND "${MAC_DEPLOY_QT_PATH}/macdeployqt"
|
||||
${EXE_NAME}
|
||||
-qmldir=${QML_SRC_DIR})
|
||||
if(${ENABLE_SPARKLE} MATCHES true)
|
||||
file(COPY ${SPARKLE_PATH} DESTINATION ${EXE_NAME}/Contents/Frameworks/)
|
||||
endif()
|
||||
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:20.04
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV QT_QUICK_BACKEND software
|
||||
@ -10,7 +10,7 @@ RUN apt-get update && \
|
||||
|
||||
RUN apt install gnupg dirmngr ca-certificates curl --no-install-recommends
|
||||
RUN curl -s https://dl.jami.net/public-key.gpg | tee /usr/share/keyrings/jami-archive-keyring.gpg > /dev/null
|
||||
RUN sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/internal/ubuntu_20.04/ jami main' > /etc/apt/sources.list.d/jami.list"
|
||||
RUN sh -c "echo 'deb [signed-by=/usr/share/keyrings/jami-archive-keyring.gpg] https://dl.jami.net/internal/ubuntu_22.04/ jami main' > /etc/apt/sources.list.d/jami.list"
|
||||
RUN apt-get update && apt-get install libqt-jami -y
|
||||
|
||||
RUN apt-get install -y -o Acquire::Retries=10 \
|
||||
@ -51,6 +51,7 @@ RUN apt-get install -y -o Acquire::Retries=10 \
|
||||
libswscale-dev \
|
||||
libavdevice-dev \
|
||||
libopus-dev \
|
||||
libpipewire-0.3-dev \
|
||||
libudev-dev \
|
||||
libgsm1-dev \
|
||||
libjsoncpp-dev \
|
||||
@ -65,6 +66,7 @@ RUN apt-get install -y -o Acquire::Retries=10 \
|
||||
libvdpau-dev \
|
||||
libssl-dev
|
||||
RUN apt-get install -y pandoc \
|
||||
libcppunit-dev \
|
||||
googletest \
|
||||
libgtest-dev \
|
||||
wget
|
||||
|
||||
@ -49,7 +49,7 @@ QT_MAJOR := 6
|
||||
QT_MINOR := 6
|
||||
QT_PATCH := 1
|
||||
QT_TARBALL_CHECKSUM := dd3668f65645fe270bc615d748bd4dc048bd17b9dc297025106e6ecc419ab95d
|
||||
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-0
|
||||
DEBIAN_QT_VERSION := $(QT_MAJOR).$(QT_MINOR).$(QT_PATCH)-1
|
||||
DEBIAN_QT_DSC_FILENAME := libqt-jami_$(DEBIAN_QT_VERSION).dsc
|
||||
QT_JAMI_PREFIX := /usr/lib/libqt-jami
|
||||
|
||||
@ -166,11 +166,12 @@ DISTRIBUTIONS := \
|
||||
debian_unstable \
|
||||
ubuntu_20.04 \
|
||||
ubuntu_22.04 \
|
||||
ubuntu_23.04 \
|
||||
ubuntu_23.10 \
|
||||
ubuntu_24.04 \
|
||||
fedora_37 \
|
||||
fedora_38 \
|
||||
fedora_39 \
|
||||
fedora_40 \
|
||||
alma_9 \
|
||||
opensuse-leap_15.4 \
|
||||
opensuse-leap_15.5 \
|
||||
|
||||
@ -100,6 +100,7 @@ RUN dnf install -y \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3-html5lib \
|
||||
cups-devel
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -28,4 +28,10 @@ ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
RUN /opt/install-cmake.sh
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
|
||||
|
||||
# Setting this variable so that FFmpeg gets built without pipewiregrab
|
||||
# (see daemon/contrib/bootstrap and daemon/contrib/src/ffmpeg/rules.mak)
|
||||
# We rely on PipeWire for screen sharing on Wayland, but the version available on Debian 11 is too old.
|
||||
ENV DISABLE_PIPEWIRE=true
|
||||
|
||||
CMD ["/opt/build-package-debian.sh"]
|
||||
|
||||
@ -27,9 +27,7 @@ RUN /opt/prebuild-package-debian.sh qt-deps
|
||||
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
|
||||
RUN /opt/prebuild-package-debian.sh jami-deps
|
||||
|
||||
# Install CMake 3.21 for Qt 6
|
||||
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
RUN /opt/install-cmake.sh
|
||||
RUN apt-get remove -y libre2-dev libre2-11
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
|
||||
CMD ["/opt/build-package-debian.sh"]
|
||||
|
||||
@ -27,9 +27,7 @@ RUN /opt/prebuild-package-debian.sh qt-deps
|
||||
COPY extras/packaging/gnu-linux/rules/debian/control /tmp/builddeps/debian/control
|
||||
RUN /opt/prebuild-package-debian.sh jami-deps
|
||||
|
||||
# Install CMake 3.21 for Qt 6
|
||||
ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
RUN /opt/install-cmake.sh
|
||||
RUN apt-get remove -y libre2-dev libre2-11
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
|
||||
CMD ["/opt/build-package-debian.sh"]
|
||||
|
||||
@ -98,6 +98,7 @@ RUN dnf install -y \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
pipewire-devel \
|
||||
cups-devel #Chromium for Qt
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
@ -98,7 +98,8 @@ RUN dnf install -y \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3-html5lib \
|
||||
cups-devel
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
|
||||
@ -97,7 +97,8 @@ RUN dnf install -y \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
|
||||
105
extras/packaging/gnu-linux/docker/Dockerfile_fedora_40
Normal file
105
extras/packaging/gnu-linux/docker/Dockerfile_fedora_40
Normal file
@ -0,0 +1,105 @@
|
||||
FROM fedora:40
|
||||
|
||||
RUN dnf clean all
|
||||
RUN dnf update -y
|
||||
|
||||
RUN dnf install -y dnf-command\(builddep\) rpmdevtools && \
|
||||
dnf install -y mock
|
||||
|
||||
RUN dnf groupinstall -y "X Software Development"
|
||||
|
||||
RUN dnf install -y \
|
||||
git \
|
||||
rpm-build \
|
||||
tar \
|
||||
make \
|
||||
autoconf \
|
||||
automake \
|
||||
nasm \
|
||||
speexdsp-devel \
|
||||
pulseaudio-libs-devel \
|
||||
libcanberra-devel \
|
||||
libcurl-devel \
|
||||
libtool \
|
||||
mesa-libgbm-devel \
|
||||
mesa-dri-drivers \
|
||||
dbus-devel \
|
||||
expat-devel \
|
||||
pcre-devel \
|
||||
yaml-cpp-devel \
|
||||
libXext-devel \
|
||||
libXfixes-devel \
|
||||
yasm \
|
||||
speex-devel \
|
||||
gsm-devel \
|
||||
chrpath \
|
||||
check \
|
||||
astyle \
|
||||
uuid-c++-devel \
|
||||
gettext-devel \
|
||||
gcc-c++ \
|
||||
which \
|
||||
alsa-lib-devel \
|
||||
systemd-devel \
|
||||
libuuid-devel \
|
||||
uuid-devel \
|
||||
gnutls-devel \
|
||||
nettle-devel \
|
||||
opus-devel \
|
||||
patch \
|
||||
jsoncpp-devel \
|
||||
libnatpmp-devel \
|
||||
webkitgtk4-devel \
|
||||
cryptopp-devel \
|
||||
libva-devel \
|
||||
libvdpau-devel \
|
||||
msgpack-devel \
|
||||
NetworkManager-libnm-devel \
|
||||
openssl-devel \
|
||||
clutter-devel \
|
||||
clutter-gtk-devel \
|
||||
libappindicator-gtk3-devel \
|
||||
libnotify-devel \
|
||||
libupnp-devel \
|
||||
qrencode-devel \
|
||||
libargon2-devel \
|
||||
libsndfile-devel \
|
||||
libdrm \
|
||||
gperf \
|
||||
bison \
|
||||
clang \
|
||||
clang-devel \
|
||||
llvm-devel \
|
||||
nodejs \
|
||||
flex \
|
||||
gstreamer1 gstreamer1-devel \
|
||||
gstreamer1-plugins-base-devel \
|
||||
gstreamer1-plugins-good \
|
||||
gstreamer1-plugins-bad-free-devel \
|
||||
nss-devel \
|
||||
libxcb* \
|
||||
libxkb* \
|
||||
libX11-devel \
|
||||
vulkan-devel \
|
||||
libXrender-devel \
|
||||
xcb-util-* \
|
||||
xz \
|
||||
xkeyboard-config \
|
||||
libnotify \
|
||||
wget \
|
||||
libstdc++-static \
|
||||
sqlite-devel \
|
||||
perl-generators \
|
||||
perl-English \
|
||||
libxshmfence-devel \
|
||||
ninja-build \
|
||||
clang \
|
||||
cmake \
|
||||
fmt-devel \
|
||||
python3.10 \
|
||||
cups-devel \
|
||||
pipewire-devel
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-rpm.sh
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
@ -99,7 +99,8 @@ RUN zypper --non-interactive install -y \
|
||||
gstreamer-plugins-bad-devel \
|
||||
gstreamer-plugins-base-devel \
|
||||
cmake \
|
||||
wget
|
||||
wget \
|
||||
pipewire-devel
|
||||
|
||||
# openSUSE Leap 15.4 comes with Python 3.6 by default,
|
||||
# but we need at least 3.7 to compile Qt 6.6.1
|
||||
@ -112,4 +113,10 @@ ADD extras/packaging/gnu-linux/scripts/build-package-rpm.sh /opt/build-package-r
|
||||
|
||||
ENV CC=gcc
|
||||
ENV CXX=g++
|
||||
|
||||
# Setting this variable so that FFmpeg gets built without pipewiregrab
|
||||
# (see daemon/contrib/bootstrap and daemon/contrib/src/ffmpeg/rules.mak)
|
||||
# We rely on PipeWire for screen sharing on Wayland, but the version available on openSUSE Leap 15.4 is too old.
|
||||
ENV DISABLE_PIPEWIRE=true
|
||||
|
||||
CMD ["/opt/build-package-rpm.sh"]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
FROM opensuse/leap:15.5
|
||||
|
||||
RUN zypper refresh
|
||||
RUN zypper --gpg-auto-import-keys refresh
|
||||
|
||||
RUN zypper --non-interactive install -y \
|
||||
dnf \
|
||||
@ -100,7 +100,8 @@ RUN zypper --non-interactive install -y \
|
||||
gstreamer-plugins-bad-devel \
|
||||
gstreamer-plugins-base-devel \
|
||||
cmake \
|
||||
wget
|
||||
wget \
|
||||
pipewire-devel
|
||||
|
||||
# openSUSE Leap 15.5 comes with Python 3.6 by default,
|
||||
# but we need at least 3.7 to compile Qt 6.6.1
|
||||
|
||||
@ -33,4 +33,10 @@ ADD extras/packaging/gnu-linux/scripts/install-cmake.sh /opt/install-cmake.sh
|
||||
RUN /opt/install-cmake.sh
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/build-package-debian.sh /opt/build-package-debian.sh
|
||||
|
||||
# Setting this variable so that FFmpeg gets built without pipewiregrab
|
||||
# (see daemon/contrib/bootstrap and daemon/contrib/src/ffmpeg/rules.mak)
|
||||
# We rely on PipeWire for screen sharing on Wayland, but the version available on Ubuntu 20.04 is too old.
|
||||
ENV DISABLE_PIPEWIRE=true
|
||||
|
||||
CMD ["/opt/build-package-debian.sh"]
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
FROM ubuntu:23.04
|
||||
FROM ubuntu:24.04
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
@ -10,6 +10,9 @@ RUN apt-get update && \
|
||||
python-is-python3 \
|
||||
wget
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/install-gcc-debian.sh /opt/install-gcc-debian.sh
|
||||
RUN /opt/install-gcc-debian.sh 13
|
||||
|
||||
ADD extras/packaging/gnu-linux/scripts/prebuild-package-debian.sh /opt/prebuild-package-debian.sh
|
||||
|
||||
COPY extras/packaging/gnu-linux/rules/debian-qt/control /tmp/builddeps/debian/control
|
||||
@ -92,6 +92,7 @@ Build-Depends: debhelper (>= 9),
|
||||
libgl1-mesa-dri,
|
||||
# pkg-kde-tools (>= 0.15.17~),
|
||||
python3:any,
|
||||
python3-bs4,
|
||||
python3-html5lib,
|
||||
# qtbase5-private-dev (>= 5.15.2+dfsg~),
|
||||
xauth <!nocheck>,
|
||||
|
||||
@ -0,0 +1,348 @@
|
||||
From 24fb774485f719df1e84dda31605d3f69202d69f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Thu, 8 Aug 2024 14:59:17 -0400
|
||||
Subject: [PATCH] qtwebengine: enable building with Python 3.12
|
||||
|
||||
Replace the deprecated imp module by importlib:
|
||||
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/524014
|
||||
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/534568
|
||||
|
||||
Update six to fix html5lib import failure:
|
||||
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/535605
|
||||
https://issues.chromium.org/issues/40286977
|
||||
---
|
||||
.../protobufs/binary_proto_generator.py | 8 ++++++--
|
||||
.../mojo/public/tools/mojom/mojom/fileutil.py | 1 -
|
||||
.../tools/mojom/mojom/fileutil_unittest.py | 5 +----
|
||||
.../mojom/mojom/generate/generator_unittest.py | 7 ++-----
|
||||
.../mojom/mojom/generate/translate_unittest.py | 4 ----
|
||||
.../tools/mojom/mojom/parse/ast_unittest.py | 6 ------
|
||||
.../mojom/parse/conditional_features_unittest.py | 8 ++------
|
||||
.../mojo/public/tools/mojom/mojom/parse/lexer.py | 1 -
|
||||
.../tools/mojom/mojom/parse/lexer_unittest.py | 7 ++-----
|
||||
.../tools/mojom/mojom/parse/parser_unittest.py | 5 -----
|
||||
.../third_party/catapult/third_party/six/six.py | 16 ++++++++++++++++
|
||||
11 files changed, 29 insertions(+), 39 deletions(-)
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py b/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
|
||||
index 2a1802dccdc..8b9de65ed0b 100755
|
||||
--- a/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/components/resources/protobufs/binary_proto_generator.py
|
||||
@@ -9,7 +9,7 @@
|
||||
"""
|
||||
from __future__ import print_function
|
||||
import abc
|
||||
-import imp
|
||||
+from importlib import util as imp_util
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
@@ -68,7 +68,11 @@ class GoogleProtobufModuleImporter:
|
||||
raise ImportError(fullname)
|
||||
|
||||
filepath = self._fullname_to_filepath(fullname)
|
||||
- return imp.load_source(fullname, filepath)
|
||||
+ spec = imp_util.spec_from_file_location(fullname, filepath)
|
||||
+ loaded = imp_util.module_from_spec(spec)
|
||||
+ spec.loader.exec_module(loaded)
|
||||
+
|
||||
+ return loaded
|
||||
|
||||
class BinaryProtoGenerator:
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
|
||||
index 29daec367c5..124f12c134b 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil.py
|
||||
@@ -3,7 +3,6 @@
|
||||
# found in the LICENSE file.
|
||||
|
||||
import errno
|
||||
-import imp
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
|
||||
index 48eaf4eca94..c93d22898d2 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/fileutil_unittest.py
|
||||
@@ -2,19 +2,16 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
import os.path
|
||||
import shutil
|
||||
-import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
from mojom import fileutil
|
||||
|
||||
-
|
||||
class FileUtilTest(unittest.TestCase):
|
||||
def testEnsureDirectoryExists(self):
|
||||
- """Test that EnsureDirectoryExists fuctions correctly."""
|
||||
+ """Test that EnsureDirectoryExists functions correctly."""
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
try:
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
|
||||
index 76cda3981f3..7143e07c4d7 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/generator_unittest.py
|
||||
@@ -2,12 +2,11 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
+import importlib.util
|
||||
import os.path
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
-
|
||||
def _GetDirAbove(dirname):
|
||||
"""Returns the directory "above" this file containing |dirname| (which must
|
||||
also be "above" this file)."""
|
||||
@@ -20,12 +19,11 @@ def _GetDirAbove(dirname):
|
||||
|
||||
|
||||
try:
|
||||
- imp.find_module("mojom")
|
||||
+ importlib.util.find_spec("mojom")
|
||||
except ImportError:
|
||||
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
|
||||
from mojom.generate import generator
|
||||
|
||||
-
|
||||
class StringManipulationTest(unittest.TestCase):
|
||||
"""generator contains some string utilities, this tests only those."""
|
||||
|
||||
@@ -69,6 +67,5 @@ class StringManipulationTest(unittest.TestCase):
|
||||
self.assertEquals("SNAKE_D3D11_CASE",
|
||||
generator.ToUpperSnakeCase("snakeD3d11Case"))
|
||||
|
||||
-
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
|
||||
index 4259374513f..558e71e1193 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/generate/translate_unittest.py
|
||||
@@ -2,16 +2,12 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
-import os.path
|
||||
-import sys
|
||||
import unittest
|
||||
|
||||
from mojom.generate import module as mojom
|
||||
from mojom.generate import translate
|
||||
from mojom.parse import ast
|
||||
|
||||
-
|
||||
class TranslateTest(unittest.TestCase):
|
||||
"""Tests |parser.Parse()|."""
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
|
||||
index c36376712e7..b289f7b11f6 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/ast_unittest.py
|
||||
@@ -2,14 +2,10 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
-import os.path
|
||||
-import sys
|
||||
import unittest
|
||||
|
||||
from mojom.parse import ast
|
||||
|
||||
-
|
||||
class _TestNode(ast.NodeBase):
|
||||
"""Node type for tests."""
|
||||
|
||||
@@ -20,13 +16,11 @@ class _TestNode(ast.NodeBase):
|
||||
def __eq__(self, other):
|
||||
return super().__eq__(other) and self.value == other.value
|
||||
|
||||
-
|
||||
class _TestNodeList(ast.NodeListBase):
|
||||
"""Node list type for tests."""
|
||||
|
||||
_list_item_type = _TestNode
|
||||
|
||||
-
|
||||
class ASTTest(unittest.TestCase):
|
||||
"""Tests various AST classes."""
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
|
||||
index 5fc582025ee..2fa5d2be6ab 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/conditional_features_unittest.py
|
||||
@@ -2,12 +2,11 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
+import importlib.util
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
-
|
||||
def _GetDirAbove(dirname):
|
||||
"""Returns the directory "above" this file containing |dirname| (which must
|
||||
also be "above" this file)."""
|
||||
@@ -18,9 +17,8 @@ def _GetDirAbove(dirname):
|
||||
if tail == dirname:
|
||||
return path
|
||||
|
||||
-
|
||||
try:
|
||||
- imp.find_module('mojom')
|
||||
+ importlib.util.find_spec("mojom")
|
||||
except ImportError:
|
||||
sys.path.append(os.path.join(_GetDirAbove('pylib'), 'pylib'))
|
||||
import mojom.parse.ast as ast
|
||||
@@ -29,7 +27,6 @@ import mojom.parse.parser as parser
|
||||
|
||||
ENABLED_FEATURES = frozenset({'red', 'green', 'blue'})
|
||||
|
||||
-
|
||||
class ConditionalFeaturesTest(unittest.TestCase):
|
||||
"""Tests |mojom.parse.conditional_features|."""
|
||||
|
||||
@@ -356,6 +353,5 @@ class ConditionalFeaturesTest(unittest.TestCase):
|
||||
conditional_features.RemoveDisabledDefinitions,
|
||||
definition, ENABLED_FEATURES)
|
||||
|
||||
-
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
|
||||
index 73ca15df94c..1083a1af7bb 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer.py
|
||||
@@ -2,7 +2,6 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
|
||||
index ce376da66e0..bc9f8354316 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/lexer_unittest.py
|
||||
@@ -2,12 +2,11 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
+import importlib.util
|
||||
import os.path
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
-
|
||||
def _GetDirAbove(dirname):
|
||||
"""Returns the directory "above" this file containing |dirname| (which must
|
||||
also be "above" this file)."""
|
||||
@@ -18,17 +17,15 @@ def _GetDirAbove(dirname):
|
||||
if tail == dirname:
|
||||
return path
|
||||
|
||||
-
|
||||
sys.path.insert(1, os.path.join(_GetDirAbove("mojo"), "third_party"))
|
||||
from ply import lex
|
||||
|
||||
try:
|
||||
- imp.find_module("mojom")
|
||||
+ importlib.util.find_spec("mojom")
|
||||
except ImportError:
|
||||
sys.path.append(os.path.join(_GetDirAbove("pylib"), "pylib"))
|
||||
import mojom.parse.lexer
|
||||
|
||||
-
|
||||
# This (monkey-patching LexToken to make comparison value-based) is evil, but
|
||||
# we'll do it anyway. (I'm pretty sure ply's lexer never cares about comparing
|
||||
# for object identity.)
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
|
||||
index 0513343ec7e..0a26307b1a3 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/mojo/public/tools/mojom/mojom/parse/parser_unittest.py
|
||||
@@ -2,16 +2,12 @@
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
-import imp
|
||||
-import os.path
|
||||
-import sys
|
||||
import unittest
|
||||
|
||||
from mojom.parse import ast
|
||||
from mojom.parse import lexer
|
||||
from mojom.parse import parser
|
||||
|
||||
-
|
||||
class ParserTest(unittest.TestCase):
|
||||
"""Tests |parser.Parse()|."""
|
||||
|
||||
@@ -1375,6 +1371,5 @@ class ParserTest(unittest.TestCase):
|
||||
r" *associated\? MyInterface& a;$"):
|
||||
parser.Parse(source3, "my_file.mojom")
|
||||
|
||||
-
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py b/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
|
||||
index 83f69783d1a..5e7f0ce4437 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/third_party/catapult/third_party/six/six.py
|
||||
@@ -71,6 +71,11 @@ else:
|
||||
MAXSIZE = int((1 << 63) - 1)
|
||||
del X
|
||||
|
||||
+if PY34:
|
||||
+ from importlib.util import spec_from_loader
|
||||
+else:
|
||||
+ spec_from_loader = None
|
||||
+
|
||||
|
||||
def _add_doc(func, doc):
|
||||
"""Add documentation to a function."""
|
||||
@@ -186,6 +191,11 @@ class _SixMetaPathImporter(object):
|
||||
return self
|
||||
return None
|
||||
|
||||
+ def find_spec(self, fullname, path, target=None):
|
||||
+ if fullname in self.known_modules:
|
||||
+ return spec_from_loader(fullname, self)
|
||||
+ return None
|
||||
+
|
||||
def __get_module(self, fullname):
|
||||
try:
|
||||
return self.known_modules[fullname]
|
||||
@@ -223,6 +233,12 @@ class _SixMetaPathImporter(object):
|
||||
return None
|
||||
get_source = get_code # same as get_code
|
||||
|
||||
+ def create_module(self, spec):
|
||||
+ return self.load_module(spec.name)
|
||||
+
|
||||
+ def exec_module(self, module):
|
||||
+ pass
|
||||
+
|
||||
_importer = _SixMetaPathImporter(__name__)
|
||||
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
|
||||
index 3488120543..120e47a76f 100644
|
||||
--- a/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
|
||||
+++ b/qt3d/src/3rdparty/assimp/src/code/AssetLib/FBX/FBXBinaryTokenizer.cpp
|
||||
@@ -472,7 +472,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
|
||||
}
|
||||
catch (const DeadlyImportError& e)
|
||||
{
|
||||
- if (!is64bits && (length > std::numeric_limits<std::uint32_t>::max())) {
|
||||
+ if (!is64bits && (length > std::numeric_limits<uint32_t>::max())) {
|
||||
throw DeadlyImportError("The FBX file is invalid. This may be because the content is too big for this older version (", ai_to_string(version), ") of the FBX format. (", e.what(), ")");
|
||||
}
|
||||
throw;
|
||||
@ -0,0 +1,26 @@
|
||||
From cf208d11dc8a9a02160a57283596ec8bab964a09 Mon Sep 17 00:00:00 2001
|
||||
From: Sebastien Blin <sebastien.blin@savoirfairelinux.com>
|
||||
Date: Mon, 27 May 2024 16:01:21 -0400
|
||||
Subject: [PATCH] qtwayland: downgrade wl-seat to avoid high-resolution
|
||||
scrolling events
|
||||
|
||||
---
|
||||
qtwayland/src/client/qwaylandinputdevice.cpp | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/qtwayland/src/client/qwaylandinputdevice.cpp b/qtwayland/src/client/qwaylandinputdevice.cpp
|
||||
index a4f8757e3c..ad0aa7941c 100644
|
||||
--- a/qtwayland/src/client/qwaylandinputdevice.cpp
|
||||
+++ b/qtwayland/src/client/qwaylandinputdevice.cpp
|
||||
@@ -383,7 +383,7 @@ QWaylandInputDevice::Touch::~Touch()
|
||||
}
|
||||
|
||||
QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version, uint32_t id)
|
||||
- : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 9))
|
||||
+ : QtWayland::wl_seat(display->wl_registry(), id, qMin(version, 7))
|
||||
, mQDisplay(display)
|
||||
, mDisplay(display->wl_display())
|
||||
{
|
||||
--
|
||||
2.45.0
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
From 420b3e5ac2e91b7a99488ac34577e2798a84a68c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Tue, 6 Aug 2024 17:35:56 -0400
|
||||
Subject: [PATCH] qtbase: fix CMake error
|
||||
|
||||
For more information, see:
|
||||
https://github.com/qt/qtbase/commit/3411f2984a5325a35e3bed1f961e5973d8a565b9
|
||||
---
|
||||
qtbase/configure.cmake | 1 +
|
||||
qtbase/src/corelib/CMakeLists.txt | 1 -
|
||||
2 files changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/qtbase/configure.cmake b/qtbase/configure.cmake
|
||||
index 43de2aa026..37a82dcdb6 100644
|
||||
--- a/qtbase/configure.cmake
|
||||
+++ b/qtbase/configure.cmake
|
||||
@@ -18,6 +18,7 @@ if(TARGET ZLIB::ZLIB)
|
||||
set_property(TARGET ZLIB::ZLIB PROPERTY IMPORTED_GLOBAL TRUE)
|
||||
endif()
|
||||
|
||||
+qt_find_package(Threads PROVIDED_TARGETS Threads::Threads)
|
||||
qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders MODULE_NAME core)
|
||||
# openssl_headers
|
||||
# OPENSSL_VERSION_MAJOR is not defined for OpenSSL 1.1.1
|
||||
diff --git a/qtbase/src/corelib/CMakeLists.txt b/qtbase/src/corelib/CMakeLists.txt
|
||||
index 31b81734e8..b62e2f763b 100644
|
||||
--- a/qtbase/src/corelib/CMakeLists.txt
|
||||
+++ b/qtbase/src/corelib/CMakeLists.txt
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
-qt_find_package(Threads PROVIDED_TARGETS Threads::Threads)
|
||||
qt_find_package(WrapPCRE2 PROVIDED_TARGETS WrapPCRE2::WrapPCRE2)
|
||||
qt_find_package(WrapZLIB PROVIDED_TARGETS WrapZLIB::WrapZLIB)
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
From 4c7360faeb0fb7f1dfd995619fb8c596b4e15606 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Thu, 8 Aug 2024 10:29:43 -0400
|
||||
Subject: [PATCH] qtwebengine: add missing chromium dependencies
|
||||
|
||||
For more information, see:
|
||||
https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/555586
|
||||
---
|
||||
chromium/content/public/browser/BUILD.gn | 1 +
|
||||
chromium/extensions/browser/api/declarative_net_request/BUILD.gn | 1 +
|
||||
2 files changed, 2 insertions(+)
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
|
||||
index b25bf5764e7..dfbfb2ec77b 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/content/public/browser/BUILD.gn
|
||||
@@ -515,6 +515,7 @@ jumbo_source_set("browser_sources") {
|
||||
"//cc",
|
||||
"//components/services/storage/public/cpp",
|
||||
"//components/viz/host",
|
||||
+ "//components/spellcheck:buildflags",
|
||||
"//content/browser", # Must not be public_deps!
|
||||
"//device/fido",
|
||||
"//gpu",
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
|
||||
index 1fc492f5a0c..13a266e22f1 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/extensions/browser/api/declarative_net_request/BUILD.gn
|
||||
@@ -23,6 +23,7 @@ source_set("declarative_net_request") {
|
||||
"//extensions/common",
|
||||
"//extensions/common/api",
|
||||
"//services/preferences/public/cpp",
|
||||
+ "//components/web_cache/browser",
|
||||
]
|
||||
|
||||
public_deps = [ "//extensions/browser:browser_sources" ]
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -0,0 +1,49 @@
|
||||
From ab6d5bebaf68a9f4d00440b2adbaffe0e5b2ae6c Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Fran=C3=A7ois-Simon=20Fauteux-Chapleau?=
|
||||
<francois-simon.fauteux-chapleau@savoirfairelinux.com>
|
||||
Date: Thu, 8 Aug 2024 10:55:08 -0400
|
||||
Subject: [PATCH] qtwebengine: fix libxml2 build error
|
||||
|
||||
Version 2.12 of libxml2 introduced a change that broke chromium's build,
|
||||
see: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/523633
|
||||
---
|
||||
.../third_party/blink/renderer/core/xml/xslt_processor.h | 5 +++++
|
||||
.../blink/renderer/core/xml/xslt_processor_libxslt.cc | 4 ++++
|
||||
2 files changed, 9 insertions(+)
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
|
||||
index d53835e9675..72536e4fd7d 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor.h
|
||||
@@ -77,7 +77,12 @@ class XSLTProcessor final : public ScriptWrappable {
|
||||
|
||||
void reset();
|
||||
|
||||
+#if LIBXML_VERSION >= 21200
|
||||
+ static void ParseErrorFunc(void* user_data, const xmlError*);
|
||||
+#else
|
||||
static void ParseErrorFunc(void* user_data, xmlError*);
|
||||
+#endif
|
||||
+
|
||||
static void GenericErrorFunc(void* user_data, const char* msg, ...);
|
||||
|
||||
// Only for libXSLT callbacks
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
|
||||
index 133e0b3355d..e8e6a09f485 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/third_party/blink/renderer/core/xml/xslt_processor_libxslt.cc
|
||||
@@ -66,7 +66,11 @@ void XSLTProcessor::GenericErrorFunc(void*, const char*, ...) {
|
||||
// It would be nice to do something with this error message.
|
||||
}
|
||||
|
||||
+#if LIBXML_VERSION >= 21200
|
||||
+void XSLTProcessor::ParseErrorFunc(void* user_data, const xmlError* error) {
|
||||
+#else
|
||||
void XSLTProcessor::ParseErrorFunc(void* user_data, xmlError* error) {
|
||||
+#endif
|
||||
FrameConsole* console = static_cast<FrameConsole*>(user_data);
|
||||
if (!console)
|
||||
return;
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
0001-qtwebengine-enable-building-with-Python-3.12.patch
|
||||
0002-fix-binary-tokenizer.patch
|
||||
0003-qtwayland-downgrade-wl-seat-to-avoid-high-resolution.patch
|
||||
0004-qtbase-fix-CMake-error.patch
|
||||
0005-qtwebengine-add-missing-chromium-dependencies.patch
|
||||
0006-qtwebengine-fix-libxml2-build-error.patch
|
||||
@ -45,6 +45,8 @@ Build-Depends: debhelper (>= 9),
|
||||
libvdpau-dev,
|
||||
libssl-dev,
|
||||
libargon2-dev | libargon2-0-dev,
|
||||
# TODO: remove libpipewire-0.2-dev once we stop supporting Ubuntu 20.04
|
||||
libpipewire-0.3-dev | libpipewire-0.2-dev,
|
||||
# other
|
||||
nasm,
|
||||
yasm,
|
||||
|
||||
@ -99,10 +99,10 @@ if [ -f /etc/os-release ]; then
|
||||
ENDTAG="ubuntu_20.04"
|
||||
elif [ "${UBUNTU_CODENAME}" = "jammy" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_22.04" ]; then
|
||||
ENDTAG="ubuntu_22.04"
|
||||
elif [ "${UBUNTU_CODENAME}" = "lunar" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.04" ]; then
|
||||
ENDTAG="ubuntu_23.04"
|
||||
elif [ "${UBUNTU_CODENAME}" = "mantic" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_23.10" ]; then
|
||||
ENDTAG="ubuntu_23.10"
|
||||
elif [ "${UBUNTU_CODENAME}" = "noble" ] || [ "${ID}_${VERSION_ID}" = "ubuntu_24.04" ]; then
|
||||
ENDTAG="ubuntu_24.04"
|
||||
elif [ "${ID}" = "debian" ] && \
|
||||
[ "$(command -v lsb_release)" ] && \
|
||||
[ "$(lsb_release -rs)" = "testing" ]; then
|
||||
|
||||
@ -50,6 +50,7 @@ BuildRequires: libuuid-devel
|
||||
BuildRequires: libva-devel
|
||||
BuildRequires: libvdpau-devel
|
||||
BuildRequires: pcre-devel
|
||||
BuildRequires: pipewire-devel
|
||||
BuildRequires: uuid-devel
|
||||
BuildRequires: yaml-cpp-devel
|
||||
|
||||
|
||||
@ -16,6 +16,9 @@
|
||||
%define computed_job_count_ %(echo $(( %available_memory / %memory_required_per_core / %max_parallel_builds )))
|
||||
%define computed_job_count %max %computed_job_count_ 1
|
||||
%define job_count %min %cpu_count %computed_job_count
|
||||
# Exclude vendored Qt6 from dependency generator
|
||||
%define __provides_exclude_from ^%{_libdir}/qt-jami/.*$
|
||||
%define __requires_exclude ^libQt6.*$
|
||||
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
@ -26,6 +29,7 @@ License: GPLv3+
|
||||
Vendor: Savoir-faire Linux Inc.
|
||||
URL: https://jami.net/
|
||||
Source: jami-libqt-%{version}.tar.xz
|
||||
Patch0: 0001-fix-gcc14.patch
|
||||
|
||||
%global gst 0.10
|
||||
%if 0%{?fedora} || 0%{?rhel} > 7
|
||||
@ -61,6 +65,7 @@ This package contains Qt libraries for Jami.
|
||||
|
||||
%prep
|
||||
%setup -n qt-everywhere-src-%{version}
|
||||
%patch0 -p1
|
||||
|
||||
%build
|
||||
echo "Building Qt using %{job_count} parallel jobs"
|
||||
|
||||
@ -2,6 +2,9 @@
|
||||
%define version RELEASE_VERSION
|
||||
%define release 0
|
||||
|
||||
# Exclude vendored Qt6 from dependency generator
|
||||
%define __requires_exclude ^libQt6.*$
|
||||
|
||||
Name: %{name}
|
||||
Version: %{version}
|
||||
Release: %{release}%{?dist}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
From 9721082687c9529fe6ae3c5304dcf079158e8a77 Mon Sep 17 00:00:00 2001
|
||||
From: Sam James <sam@gentoo.org>
|
||||
Date: Sun, 04 Jun 2023 04:15:16 +0100
|
||||
Subject: [PATCH] heap: Add missing <algorithm> include for std::remove
|
||||
|
||||
GCC 14 changes some internal includes within libstdc++ so this transient
|
||||
include gets lost. Include <algorithm> explicitly for std::remove.
|
||||
|
||||
Change-Id: Iab8a2c751a0f9c9dc6a770d6296ad6de724ef3bb
|
||||
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4583222
|
||||
Reviewed-by: Michael Lippautz <mlippautz@chromium.org>
|
||||
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
|
||||
Cr-Commit-Position: refs/heads/main@{#88037}
|
||||
---
|
||||
|
||||
diff --git a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
|
||||
index 2cf728489d..d8414ae3c6 100644
|
||||
--- a/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
|
||||
+++ b/qtwebengine/src/3rdparty/chromium/v8/src/heap/cppgc/stats-collector.h
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
+#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
@ -138,6 +138,10 @@ apps:
|
||||
autostart: jami.desktop
|
||||
common-id: net.jami.Jami
|
||||
desktop: usr/share/applications/jami.desktop
|
||||
environment:
|
||||
PIPEWIRE_CONFIG_NAME: "$SNAP/usr/share/pipewire/pipewire.conf"
|
||||
PIPEWIRE_MODULE_DIR: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/pipewire-0.3"
|
||||
SPA_PLUGIN_DIR: "$SNAP/usr/lib/$SNAPCRAFT_ARCH_TRIPLET/spa-0.2"
|
||||
slots:
|
||||
- dbus-jami
|
||||
- dbus-ring
|
||||
@ -168,7 +172,7 @@ package-repositories:
|
||||
components: [main]
|
||||
suites: [jami]
|
||||
key-id: A295D773307D25A33AE72F2F64CD5FA175348F84
|
||||
url: https://dl.jami.net/nightly/ubuntu_20.04/
|
||||
url: https://dl.jami.net/internal/ubuntu_20.04/
|
||||
|
||||
parts:
|
||||
desktop-launch:
|
||||
@ -255,6 +259,8 @@ parts:
|
||||
sed -i -E 's|(tmpName) << (PACKAGE_NAME << "_shm_")|\1 << "snap.jami." << \2|' ./daemon/src/media/video/sinkclient.cpp
|
||||
sed -i -E 's|^Icon=.*|Icon=${SNAP}/usr/share/icons/hicolor/scalable/apps/jami.svg|' extras/data/jami.desktop
|
||||
override-build: |
|
||||
$SNAPCRAFT_PART_BUILD/extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
|
||||
|
||||
cd $SNAPCRAFT_PART_BUILD/daemon/contrib
|
||||
mkdir -p native
|
||||
cd native
|
||||
|
||||
@ -44,6 +44,23 @@ QUILT_REFRESH_ARGS="-p 1"
|
||||
|
||||
if [ ! -f "${qt_deb_path}" ] || [ "${FORCE_REBUILD_QT}" = "true" ]; then
|
||||
(
|
||||
|
||||
# HACK: For now on ubuntu 24.04 there is no python3.10 package
|
||||
# So create a PyEnv environment to install the required packages
|
||||
if cat /etc/os-release | grep -Eq "24.04"; then
|
||||
apt-get install git gcc make python3-pip libssl-dev curl libreadline-dev -y
|
||||
curl https://pyenv.run | bash
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
eval "$(pyenv init -)"
|
||||
pyenv install 3.10.0
|
||||
pyenv local 3.10.0
|
||||
|
||||
python -m pip install html5lib
|
||||
python -m pip install six
|
||||
fi
|
||||
|
||||
|
||||
flock 9 # block until the lock file is gone
|
||||
test -f "${qt_deb_path}" && exit 0 # check again
|
||||
|
||||
|
||||
@ -35,6 +35,7 @@ rpmdev-setuptree
|
||||
|
||||
# Copy the source tarball.
|
||||
cp --reflink=auto "/src/$RELEASE_TARBALL_FILENAME" /root/rpmbuild/SOURCES
|
||||
cp patches/*.patch /root/rpmbuild/SOURCES/
|
||||
|
||||
QT_JAMI_PREFIX="/usr/lib64/qt-jami"
|
||||
PATH="${QT_JAMI_PREFIX}/bin:${PATH}"
|
||||
@ -43,7 +44,7 @@ PKG_CONFIG_PATH="${QT_JAMI_PREFIX}/lib/pkgconfig:${PKG_CONFIG_PATH}"
|
||||
CMAKE_PREFIX_PATH="${QT_JAMI_PREFIX}/lib/cmake:${CMAKE_PREFIX_PATH}"
|
||||
QT_MAJOR=6
|
||||
QT_MINOR=6
|
||||
QT_PATCH=1
|
||||
QT_PATCH=3
|
||||
QT_RELEASE_PATCH=0
|
||||
|
||||
QT_MAJOR_MINOR=${QT_MAJOR}.${QT_MINOR}
|
||||
@ -52,7 +53,7 @@ QT_MAJOR_MINOR_PATCH=${QT_MAJOR}.${QT_MINOR}.${QT_PATCH}
|
||||
QT_TARBALL_URL=https://download.qt.io/archive/qt/$QT_MAJOR_MINOR/\
|
||||
$QT_MAJOR_MINOR_PATCH/single/qt-everywhere-src-$QT_MAJOR_MINOR_PATCH.tar.xz
|
||||
|
||||
QT_TARBALL_SHA256="dd3668f65645fe270bc615d748bd4dc048bd17b9dc297025106e6ecc419ab95d"
|
||||
QT_TARBALL_SHA256="69d0348fef415da98aa890a34651e9cfb232f1bffcee289b7b4e21386bf36104"
|
||||
QT_TARBALL_FILE_NAME=$(basename "$QT_TARBALL_URL")
|
||||
CACHED_QT_TARBALL=$TARBALLS/$QT_TARBALL_FILE_NAME
|
||||
|
||||
@ -111,6 +112,8 @@ if [ ! -f "${RPM_PATH}" ]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc38.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_39" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc39.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "fedora_40" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.fc40.x86_64.rpm "${RPM_PATH}"
|
||||
elif [[ "${DISTRIBUTION}" == "alma_9" ]]; then
|
||||
cp /root/rpmbuild/RPMS/x86_64/jami-libqt-$QT_MAJOR_MINOR_PATCH-*.el9.x86_64.rpm "${RPM_PATH}"
|
||||
else
|
||||
|
||||
38
extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
Executable file
38
extras/packaging/gnu-linux/scripts/install-pipewire-from-source.sh
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# The purpose of this script is to build PipeWire from source in a snap based on core20 / Ubuntu 20.04
|
||||
# It must be called in the "override-build" section of the relevant part in snapcraft.yaml
|
||||
|
||||
set -e
|
||||
|
||||
OLD_WD=$(pwd)
|
||||
cd /tmp
|
||||
|
||||
# Install PipeWire's build dependencies
|
||||
apt-get install --yes gcc git libasound2-dev libdbus-1-dev libglib2.0-dev ninja-build pkg-config
|
||||
|
||||
# Get a version of Meson that's recent enough to build PipeWire 1.0.5 (the one available via apt is too old)
|
||||
wget -q https://github.com/mesonbuild/meson/releases/download/0.61.1/meson-0.61.1.tar.gz
|
||||
echo "feb2cefb325b437dbf36146df7c6b87688ddff0b0205caa31dc64055c6da410c meson-0.61.1.tar.gz" | sha256sum --check
|
||||
tar xzf meson-0.61.1.tar.gz
|
||||
|
||||
# Build PipeWire 1.0.5 and install it in the /usr directory of the build environment
|
||||
wget -q https://gitlab.freedesktop.org/pipewire/pipewire/-/archive/1.0.5/pipewire-1.0.5.tar.gz
|
||||
echo "c5a5de26d684a1a84060ad7b6131654fb2835e03fccad85059be92f8e3ffe993 pipewire-1.0.5.tar.gz" | sha256sum --check
|
||||
tar xzf pipewire-1.0.5.tar.gz
|
||||
cd pipewire-1.0.5
|
||||
../meson-0.61.1/meson.py setup builddir -Dsession-managers=media-session -Dalsa=disabled -Dprefix=/usr
|
||||
../meson-0.61.1/meson.py compile -C builddir
|
||||
../meson-0.61.1/meson.py install -C builddir
|
||||
|
||||
# The files installed by the previous command are only for the "Build" step of the snap
|
||||
# creation process (https://snapcraft.io/docs/how-snapcraft-builds). In order to ensure
|
||||
# that PipeWire is installed in the final snap archive, we also need to copy all the
|
||||
# required files under the $SNAPCRAFT_PART_INSTALL directory.
|
||||
../meson-0.61.1/meson.py configure builddir -Dprefix=$SNAPCRAFT_PART_INSTALL/usr/
|
||||
../meson-0.61.1/meson.py install -C builddir
|
||||
|
||||
# Cleanup
|
||||
cd /tmp
|
||||
rm -rf meson-0.61.1 meson-0.61.1.tar.gz pipewire-1.0.5 pipewire-1.0.5.tar.gz
|
||||
cd $OLD_WD
|
||||
@ -34,7 +34,7 @@ cat << EOFILE > ${REPO_FOLDER}/${SPARKLE_FILE}
|
||||
<pubDate>$DATE_RFC2822</pubDate>
|
||||
<sparkle:version>${BUILD}</sparkle:version>
|
||||
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
|
||||
<sparkle:minimumSystemVersion>10.15.0</sparkle:minimumSystemVersion>
|
||||
<sparkle:minimumSystemVersion>11.0</sparkle:minimumSystemVersion>
|
||||
<enclosure url="${REPO_URL}/$(basename ${PACKAGE})" type="application/octet-stream" $(./sign_update ${PACKAGE}) />
|
||||
</item>
|
||||
$(echo -e "${ITEMS}")
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>10.15</string>
|
||||
<string>11.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
||||
@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M42 13.85V39q0 1.2-.9 2.1-.9.9-2.1.9H9q-1.2 0-2.1-.9Q6 40.2 6 39V9q0-1.2.9-2.1Q7.8 6 9 6h25.15Zm-3 1.35L32.8 9H9v30h30ZM24 35.75q2.15 0 3.675-1.525T29.2 30.55q0-2.15-1.525-3.675T24 25.35q-2.15 0-3.675 1.525T18.8 30.55q0 2.15 1.525 3.675T24 35.75ZM11.65 18.8h17.9v-7.15h-17.9ZM9 15.2V39 9Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M480-313 287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193ZM220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 369 B After Width: | Height: | Size: 268 B |
@ -1,5 +1,6 @@
|
||||
<h4 align="left"><span style="font-weight:600"> Created by</span></h4>
|
||||
<p>Adrien Béraud<br>
|
||||
<p>Abhishek Ojha<br>
|
||||
Adrien Béraud<br>
|
||||
Albert Babí<br>
|
||||
Alexandre Lision<br>
|
||||
Alexandr Sergheev<br>
|
||||
@ -25,6 +26,7 @@ Emma Falkiewitz<br>
|
||||
Emmanuel Lepage-Vallée<br>
|
||||
Fadi Shehadeh<br>
|
||||
Franck Laurent<br>
|
||||
François-Simon Fauteux-Chapleau<br>
|
||||
Frédéric Guimont<br>
|
||||
Guillaume Heller<br>
|
||||
Guillaume Roguez<br>
|
||||
@ -68,3 +70,4 @@ Yang Wang<br></p>
|
||||
<h4 align="left"><span style="font-weight:600"> Artwork by</span></h4>
|
||||
<p>Charlotte Hoffmann<br>
|
||||
Marianne Forget<br></p>
|
||||
<h4 align="left"><span style="font-weight:600"> And the volunteers who contribute to this project! </span></h4>
|
||||
|
||||
@ -145,6 +145,16 @@ ApplicationWindow {
|
||||
LRCInstance.selectConversation(convUid);
|
||||
}
|
||||
}
|
||||
ListElement {
|
||||
label: "Account ID"
|
||||
type: "combobox"
|
||||
getDataModel: () => AccountListModel
|
||||
displayRole: AccountList.Username
|
||||
onIndexChanged: function(model, index) {
|
||||
const accountId = JamiQmlUtils.getModelData(model, index, AccountList.ID);
|
||||
LRCInstance.currentAccountId = accountId;
|
||||
}
|
||||
}
|
||||
ListElement {
|
||||
label: "Force local preview"
|
||||
type: "checkbox"
|
||||
|
||||
@ -116,7 +116,8 @@ ApplicationWindow {
|
||||
function close(force = false) {
|
||||
// If we're in the onboarding wizard or 'MinimizeOnClose'
|
||||
// is set, then we can quit
|
||||
if (force || !UtilsAdapter.getAppValue(Settings.MinimizeOnClose) || !UtilsAdapter.getAccountListSize()) {
|
||||
var minimizeToTray = UtilsAdapter.getAppValue(Settings.MinimizeOnClose) && UtilsAdapter.isSystemTrayIconVisible();
|
||||
if (force || !minimizeToTray || !UtilsAdapter.getAccountListSize()) {
|
||||
Qt.quit();
|
||||
} else {
|
||||
layoutManager.closeToTray();
|
||||
|
||||
@ -29,6 +29,35 @@ AccountListModel::AccountListModel(LRCInstance* instance, QObject* parent)
|
||||
: AbstractListModelBase(parent)
|
||||
{
|
||||
lrcInstance_ = instance;
|
||||
|
||||
// Avoid resetting/redrawing the model when the account status changes.
|
||||
QObject::connect(&lrcInstance_->accountModel(),
|
||||
&AccountModel::accountStatusChanged,
|
||||
this,
|
||||
[&](const QString& accountId) {
|
||||
auto accountList = lrcInstance_->accountModel().getAccountList();
|
||||
auto index = accountList.indexOf(accountId);
|
||||
if (index != -1) {
|
||||
QModelIndex modelIndex = QAbstractListModel::index(index, 0);
|
||||
Q_EMIT dataChanged(modelIndex, modelIndex /*, ALL ROLES */);
|
||||
}
|
||||
});
|
||||
// If there's a reorder, it's reasonable to reset the model for simplicity, instead
|
||||
// of computing the difference. The same goes for accounts being added and removed.
|
||||
// These operations will only occur when the list is hidden, unless dbus is used while
|
||||
// the list is visible.
|
||||
QObject::connect(&lrcInstance_->accountModel(),
|
||||
&AccountModel::accountsReordered,
|
||||
this,
|
||||
&AccountListModel::reset);
|
||||
QObject::connect(&lrcInstance_->accountModel(),
|
||||
&AccountModel::accountAdded,
|
||||
this,
|
||||
&AccountListModel::reset);
|
||||
QObject::connect(&lrcInstance_->accountModel(),
|
||||
&AccountModel::accountRemoved,
|
||||
this,
|
||||
&AccountListModel::reset);
|
||||
}
|
||||
|
||||
int
|
||||
@ -91,6 +120,7 @@ AccountListModel::roleNames() const
|
||||
void
|
||||
AccountListModel::reset()
|
||||
{
|
||||
// Used to invalidate proxy models.
|
||||
beginResetModel();
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
|
||||
#include "appsettingsmanager.h"
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QLibraryInfo>
|
||||
|
||||
@ -101,7 +103,7 @@ AppSettingsManager::loadTranslations()
|
||||
installedTr_.clear();
|
||||
|
||||
QString locale_name = getLanguage();
|
||||
qDebug() << QString("Using locale: %1").arg(locale_name);
|
||||
C_INFO << QString("Using locale: %1").arg(locale_name);
|
||||
QString locale_lang = locale_name.split('_')[0];
|
||||
|
||||
QTranslator* qtTranslator_lang = new QTranslator(qApp);
|
||||
|
||||
@ -43,7 +43,7 @@ extern const QString defaultDownloadPath;
|
||||
|
||||
// Common key-value pairs for both APPSTORE and non-APPSTORE builds
|
||||
#define COMMON_KEYS \
|
||||
X(MinimizeOnClose, false) \
|
||||
X(MinimizeOnClose, true) \
|
||||
X(DownloadPath, defaultDownloadPath) \
|
||||
X(ScreenshotPath, {}) \
|
||||
X(EnableNotifications, true) \
|
||||
|
||||
@ -25,7 +25,11 @@
|
||||
#include "api/devicemodel.h"
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
#include "screencastportal.h"
|
||||
#include "xrectsel.h"
|
||||
#ifndef ENABLE_LIBWRAP
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <QtConcurrent/QtConcurrent>
|
||||
@ -58,6 +62,12 @@ AvAdapter::AvAdapter(LRCInstance* instance, QObject* parent)
|
||||
&lrc::api::AVModel::onRendererFpsChange,
|
||||
this,
|
||||
&AvAdapter::updateRenderersFPSInfo);
|
||||
#ifdef Q_OS_LINUX
|
||||
connect(&lrcInstance_->behaviorController(),
|
||||
&BehaviorController::callStatusChanged,
|
||||
this,
|
||||
&AvAdapter::onCallStatusChanged);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The top left corner of primary screen is (0, 0).
|
||||
@ -119,6 +129,93 @@ AvAdapter::shareEntireScreen(int screenNumber)
|
||||
->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
static std::map<QString, std::unique_ptr<ScreenCastPortal>> callPortal;
|
||||
|
||||
void
|
||||
AvAdapter::onCallStatusChanged(const QString& accountId, const QString& callId)
|
||||
{
|
||||
auto& accInfo = lrcInstance_->accountModel().getAccountInfo(accountId);
|
||||
auto& callModel = accInfo.callModel;
|
||||
const auto call = callModel->getCall(callId);
|
||||
|
||||
if (call.status == lrc::api::call::Status::ENDED) {
|
||||
closePortal(callId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AvAdapter::closePortal(const QString& callId)
|
||||
{
|
||||
if (callPortal.count(callId)) {
|
||||
lrcInstance_->avModel().stopPreview(callPortal[callId]->videoInputId);
|
||||
callPortal.erase(callId);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AvAdapter::shareWayland(bool entireScreen)
|
||||
{
|
||||
QString callId = lrcInstance_->getCurrentCallId();
|
||||
closePortal(callId);
|
||||
|
||||
PortalCaptureType captureType = entireScreen ? PortalCaptureType::SCREEN
|
||||
: PortalCaptureType::WINDOW;
|
||||
auto portal = std::make_unique<ScreenCastPortal>(captureType);
|
||||
|
||||
int err = portal->getPipewireFd();
|
||||
if (err == EACCES) {
|
||||
qInfo() << "Can't share screen: permission denied";
|
||||
return;
|
||||
} else if (err != 0) {
|
||||
qWarning() << "Failed to get PipeWire fd. Error code:" << err;
|
||||
return;
|
||||
}
|
||||
QString resource = QString("%1%2pipewire pid:%3 fd:%4 node:%5")
|
||||
.arg(libjami::Media::VideoProtocolPrefix::DISPLAY)
|
||||
.arg(libjami::Media::VideoProtocolPrefix::SEPARATOR)
|
||||
.arg(getpid())
|
||||
.arg(portal->pipewireFd)
|
||||
.arg(portal->pipewireNode);
|
||||
#ifndef ENABLE_LIBWRAP
|
||||
// If the daemon is running as a separate process, then it can't directly use the
|
||||
// PipeWire file descriptor opened by the client, so it will attempt to duplicate
|
||||
// it using the pidfd_getfd system call. This requires the daemon process to have
|
||||
// ptrace permission on the client process. On some systems, this will be true by
|
||||
// default (as long as the client and daemon processes have the same uid), but it
|
||||
// may not be if the Yama Linux Security Module is used. The call to prctl below
|
||||
// will grant permission if the Yama LSM is enabled and set to mode 1.
|
||||
//
|
||||
// References:
|
||||
// https://man7.org/linux/man-pages/man2/pidfd_getfd.2.html
|
||||
// https://man7.org/linux/man-pages/man2/prctl.2.html
|
||||
// https://github.com/torvalds/linux/blob/master/Documentation/admin-guide/LSM/Yama.rst
|
||||
prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
|
||||
#endif
|
||||
// We open the video input here (instead of letting the daemon do it) to ensure
|
||||
// that the daemon doesn't try to restart it while we still need it, since this
|
||||
// would require getting a new file descriptor for PipeWire.
|
||||
portal->videoInputId = lrcInstance_->avModel().startPreview(resource);
|
||||
|
||||
callPortal[callId] = std::move(portal);
|
||||
muteCamera_ = !isCapturing();
|
||||
lrcInstance_->getCurrentCallModel()
|
||||
->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING);
|
||||
}
|
||||
|
||||
void
|
||||
AvAdapter::shareEntireScreenWayland()
|
||||
{
|
||||
shareWayland(true);
|
||||
}
|
||||
|
||||
void
|
||||
AvAdapter::shareWindowWayland()
|
||||
{
|
||||
shareWayland(false);
|
||||
}
|
||||
#endif // Q_OS_LINUX
|
||||
|
||||
void
|
||||
AvAdapter::shareAllScreens()
|
||||
{
|
||||
@ -204,10 +301,14 @@ AvAdapter::shareFile(const QString& filePath)
|
||||
&lrc::api::AVModel::fileOpened,
|
||||
this,
|
||||
[this, callId, filePath, resource](bool hasAudio, bool hasVideo) {
|
||||
lrcInstance_->avModel().setAutoRestart(resource, true);
|
||||
lrcInstance_->getCurrentCallModel()
|
||||
->addMedia(callId, filePath, lrc::api::CallModel::MediaRequestType::FILESHARING, false, hasAudio);
|
||||
lrcInstance_->avModel().pausePlayer(resource, false);
|
||||
lrcInstance_->avModel().setAutoRestart(resource, true);
|
||||
lrcInstance_->getCurrentCallModel()
|
||||
->addMedia(callId,
|
||||
filePath,
|
||||
lrc::api::CallModel::MediaRequestType::FILESHARING,
|
||||
false,
|
||||
hasAudio);
|
||||
lrcInstance_->avModel().pausePlayer(resource, false);
|
||||
});
|
||||
|
||||
lrcInstance_->avModel().createMediaPlayer(resource);
|
||||
@ -307,6 +408,9 @@ void
|
||||
AvAdapter::stopSharing(const QString& source)
|
||||
{
|
||||
auto callId = lrcInstance_->getCurrentCallId();
|
||||
#ifdef Q_OS_LINUX
|
||||
closePortal(callId);
|
||||
#endif
|
||||
if (!source.isEmpty() && !callId.isEmpty()) {
|
||||
if (source.startsWith(libjami::Media::VideoProtocolPrefix::DISPLAY)) {
|
||||
qDebug() << "Stopping display: " << source;
|
||||
|
||||
@ -69,9 +69,18 @@ protected:
|
||||
*/
|
||||
Q_INVOKABLE bool hasCamera() const;
|
||||
|
||||
// Share the screen specificed by screen number.
|
||||
// Share the screen specificed by screen number (all platforms except Wayland).
|
||||
Q_INVOKABLE void shareEntireScreen(int screenNumber);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Share a screen on Wayland.
|
||||
// Sharing a screen on Wayland requires getting permission from the user. The logic for
|
||||
// this is handled by the ScreenCastPortal class using xdg-desktop-portal.
|
||||
// The choice of screen is also handled by xdg-desktop-portal, which is why we don't need
|
||||
// an argument for it (whereas we do on other platforms, cf. shareEntireScreen above).
|
||||
Q_INVOKABLE void shareEntireScreenWayland();
|
||||
#endif
|
||||
|
||||
// Share the all screens connected.
|
||||
Q_INVOKABLE void shareAllScreens();
|
||||
|
||||
@ -87,9 +96,18 @@ protected:
|
||||
// Select screen area to display (from all screens).
|
||||
Q_INVOKABLE void shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||
|
||||
// Select window to display.
|
||||
// Select window to display (all platforms except Wayland).
|
||||
Q_INVOKABLE void shareWindow(const QString& windowProcessId, const QString& windowId);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Share a window on Wayland.
|
||||
// Sharing a window on Wayland requires getting permission from the user. The logic for
|
||||
// this is handled by the ScreenCastPortal class using xdg-desktop-portal.
|
||||
// The choice of window is also handled by xdg-desktop-portal, which is why we don't need
|
||||
// arguments for it (whereas we do on other platforms, cf. shareWindow above).
|
||||
Q_INVOKABLE void shareWindowWayland();
|
||||
#endif
|
||||
|
||||
// Returns the screensharing resource
|
||||
Q_INVOKABLE QString getSharingResource(int screenId = -2,
|
||||
const QString& windowProcessId = "",
|
||||
@ -121,11 +139,25 @@ private Q_SLOTS:
|
||||
void onAudioDeviceEvent();
|
||||
void onRendererStarted(const QString& id, const QSize& size);
|
||||
void onRendererStopped(const QString& id);
|
||||
#ifdef Q_OS_LINUX
|
||||
// This function needs to be called whenever a screen/window share stops on Wayland.
|
||||
// Failure to do so can cause subsequent sharing attempts to fail.
|
||||
void closePortal(const QString& callId);
|
||||
|
||||
// On Wayland, we need to be informed of call status changes so that we can call
|
||||
// closePortal if a call ends while a screen/window share was in progress.
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId);
|
||||
#endif
|
||||
|
||||
private:
|
||||
// Get screens arrangement rect relative to primary screen.
|
||||
const QRect getAllScreensBoundingRect();
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Used internally by shareEntireScreenWayland and shareWindowWayland
|
||||
void shareWayland(bool entireScreen);
|
||||
#endif
|
||||
|
||||
// Get the screen number
|
||||
int getScreenNumber(int screenId = 0) const;
|
||||
|
||||
|
||||
@ -354,10 +354,11 @@ CallAdapter::onCallInfosChanged(const QString& accountId, const QString& callId)
|
||||
}
|
||||
|
||||
void
|
||||
CallAdapter::onCallAddedToConference(const QString& callId, const QString& confId)
|
||||
CallAdapter::onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId)
|
||||
{
|
||||
Q_UNUSED(callId)
|
||||
Q_UNUSED(confId)
|
||||
Q_UNUSED(conversationId)
|
||||
saveConferenceSubcalls();
|
||||
}
|
||||
|
||||
@ -389,16 +390,13 @@ CallAdapter::hangUpACall(const QString& accountId, const QString& convUid)
|
||||
}
|
||||
|
||||
void
|
||||
CallAdapter::setCallMedia(const QString& accountId, const QString& convUid, bool video)
|
||||
CallAdapter::setCallMedia(const QString& accountId, const QString& convUid, bool videoMuted)
|
||||
{
|
||||
const auto& convInfo = lrcInstance_->getConversationFromConvUid(convUid, accountId);
|
||||
if (convInfo.uid.isEmpty())
|
||||
return;
|
||||
try {
|
||||
lrcInstance_->getAccountInfo(accountId).callModel->updateCallMediaList(convInfo.callId,
|
||||
video);
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
lrcInstance_->getAccountInfo(accountId).callModel->setVideoMuted(convInfo.callId, videoMuted);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -67,7 +67,7 @@ public:
|
||||
Q_INVOKABLE void placeAudioOnlyCall();
|
||||
Q_INVOKABLE void placeCall();
|
||||
Q_INVOKABLE void hangUpACall(const QString& accountId, const QString& convUid);
|
||||
Q_INVOKABLE void setCallMedia(const QString& accountId, const QString& convUid, bool video);
|
||||
Q_INVOKABLE void setCallMedia(const QString& accountId, const QString& convUid, bool videoMuted);
|
||||
Q_INVOKABLE void acceptACall(const QString& accountId, const QString& convUid);
|
||||
|
||||
Q_INVOKABLE void connectCallModel(const QString& accountId);
|
||||
@ -122,7 +122,7 @@ public Q_SLOTS:
|
||||
void onAccountChanged();
|
||||
void onCallStatusChanged(const QString& accountId, const QString& callId);
|
||||
void onCallStatusChanged(const QString& callId, int code);
|
||||
void onCallAddedToConference(const QString& callId, const QString& confId);
|
||||
void onCallAddedToConference(const QString& callId, const QString& conversationId, const QString& confId);
|
||||
void onCallStarted(const QString& callId);
|
||||
void onCallEnded(const QString& callId);
|
||||
void onCallInfosChanged(const QString& accountId, const QString& callId);
|
||||
|
||||
@ -362,36 +362,32 @@ CallOverlayModel::clearControls()
|
||||
}
|
||||
|
||||
void
|
||||
CallOverlayModel::registerFilter(QObject* object, QQuickItem* item)
|
||||
CallOverlayModel::setEventFilterActive(QObject* object, QQuickItem* item, bool isActive)
|
||||
{
|
||||
QQuickWindow* window = qobject_cast<QQuickWindow*>(object);
|
||||
if (!window || !item) {
|
||||
C_WARN << "Attempting to register an invalid object or item" << object << item;
|
||||
C_WARN << "Attempting to" << (isActive ? "register" : "unregister")
|
||||
<< "an invalid object or item" << window << item;
|
||||
return;
|
||||
}
|
||||
if (watchedItems_.contains(item)) {
|
||||
C_DBG << "Item already registered" << item;
|
||||
}
|
||||
watchedItems_.push_back(item);
|
||||
if (watchedItems_.size() == 1) {
|
||||
window->installEventFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CallOverlayModel::unregisterFilter(QObject* object, QQuickItem* item)
|
||||
{
|
||||
QQuickWindow* window = qobject_cast<QQuickWindow*>(object);
|
||||
if (!window || !item) {
|
||||
C_WARN << "Attempting to unregister an invalid object or item" << object << item;
|
||||
return;
|
||||
}
|
||||
if (!watchedItems_.contains(item)) {
|
||||
C_DBG << "Item not registered" << item;
|
||||
}
|
||||
watchedItems_.removeOne(item);
|
||||
if (watchedItems_.size() == 0) {
|
||||
window->removeEventFilter(this);
|
||||
if (isActive) {
|
||||
if (watchedItems_.contains(item)) {
|
||||
C_DBG << "Item already registered" << item;
|
||||
} else {
|
||||
watchedItems_.push_back(item);
|
||||
if (watchedItems_.size() == 1) {
|
||||
window->installEventFilter(this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!watchedItems_.contains(item)) {
|
||||
C_DBG << "Item not registered" << item;
|
||||
} else {
|
||||
watchedItems_.removeOne(item);
|
||||
if (watchedItems_.size() == 0) {
|
||||
window->removeEventFilter(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -137,8 +137,7 @@ public:
|
||||
Q_INVOKABLE QVariant overflowHiddenModel();
|
||||
Q_INVOKABLE QVariant pendingConferenceesModel();
|
||||
|
||||
Q_INVOKABLE void registerFilter(QObject* object, QQuickItem* item);
|
||||
Q_INVOKABLE void unregisterFilter(QObject* object, QQuickItem* item);
|
||||
Q_INVOKABLE void setEventFilterActive(QObject* object, QQuickItem* item, bool isActive);
|
||||
bool eventFilter(QObject* object, QEvent* event) override;
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
@ -40,9 +40,18 @@ Loader {
|
||||
property int seq: MsgSeq.single
|
||||
property string author: Author
|
||||
property string body: Body
|
||||
property int transferStatus: Status
|
||||
property var tid: TID
|
||||
property int transferStatus: TransferStatus
|
||||
onTidChanged: {
|
||||
if (tid === "") {
|
||||
sourceComponent = deletedMsgComp
|
||||
}
|
||||
}
|
||||
onTransferStatusChanged: {
|
||||
if (transferStatus === Interaction.Status.TRANSFER_FINISHED) {
|
||||
if (tid === "") {
|
||||
sourceComponent = deletedMsgComp
|
||||
return;
|
||||
} else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
|
||||
mediaInfo = MessagesAdapter.getMediaInfo(root.body);
|
||||
if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
|
||||
sourceComponent = localMediaMsgComp;
|
||||
@ -58,6 +67,54 @@ Loader {
|
||||
Behavior on opacity { NumberAnimation { duration: 100 } }
|
||||
onLoaded: opacity = 1
|
||||
|
||||
Component {
|
||||
id: deletedMsgComp
|
||||
|
||||
SBSMessageBase {
|
||||
id: deletedItem
|
||||
|
||||
isOutgoing: Author === CurrentAccount.uri
|
||||
showTime: root.showTime
|
||||
seq: root.seq
|
||||
author: Author
|
||||
readers: Readers
|
||||
timestamp: root.timestamp
|
||||
formattedTime: root.formattedTime
|
||||
formattedDay: root.formattedTime
|
||||
extraHeight: 0
|
||||
textContentWidth: textEditId.width
|
||||
textContentHeight: textEditId.height
|
||||
innerContent.children: [
|
||||
TextEdit {
|
||||
id: textEditId
|
||||
|
||||
anchors.right: isOutgoing ? parent.right : undefined
|
||||
anchors.rightMargin: isOutgoing ? timeWidth : 0
|
||||
bottomPadding: 6
|
||||
topPadding: 6
|
||||
leftPadding: 10
|
||||
text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " " + JamiStrings.deletedMedia ;
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
width: Math.min((2 / 3) * parent.width, implicitWidth + 18, innerContent.width - senderMargin + 18)
|
||||
|
||||
font.pointSize: JamiTheme.smallFontSize
|
||||
font.hintingPreference: Font.PreferNoHinting
|
||||
renderType: Text.NativeRendering
|
||||
textFormat: Text.RichText
|
||||
clip: true
|
||||
readOnly: true
|
||||
color: getBaseColor()
|
||||
opacity: 0.5
|
||||
|
||||
function getBaseColor() {
|
||||
bubble.isDeleted = true
|
||||
return UtilsAdapter.luma(bubble.color) ? "white" : "dark"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: dataTransferMsgComp
|
||||
|
||||
@ -66,7 +123,7 @@ Loader {
|
||||
|
||||
transferId: Id
|
||||
property var transferStats: MessagesAdapter.getTransferStats(transferId, root.transferStatus)
|
||||
property bool canOpen: root.transferStatus === Interaction.Status.TRANSFER_FINISHED || isOutgoing
|
||||
property bool canOpen: root.transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED || isOutgoing
|
||||
property real maxMsgWidth: root.width - senderMargin -
|
||||
2 * hPadding - avatarBlockWidth
|
||||
- buttonsLoader.width - 24 - 6 - 24
|
||||
@ -112,18 +169,18 @@ Loader {
|
||||
|
||||
sourceComponent: {
|
||||
switch (root.transferStatus) {
|
||||
case Interaction.Status.TRANSFER_CREATED:
|
||||
case Interaction.Status.TRANSFER_FINISHED:
|
||||
case Interaction.TransferStatus.TRANSFER_CREATED:
|
||||
case Interaction.TransferStatus.TRANSFER_FINISHED:
|
||||
iconSource = JamiResources.link_black_24dp_svg
|
||||
return terminatedComp
|
||||
case Interaction.Status.TRANSFER_CANCELED:
|
||||
case Interaction.Status.TRANSFER_ERROR:
|
||||
case Interaction.Status.TRANSFER_UNJOINABLE_PEER:
|
||||
case Interaction.Status.TRANSFER_TIMEOUT_EXPIRED:
|
||||
case Interaction.Status.TRANSFER_AWAITING_HOST:
|
||||
case Interaction.TransferStatus.TRANSFER_CANCELED:
|
||||
case Interaction.TransferStatus.TRANSFER_ERROR:
|
||||
case Interaction.TransferStatus.TRANSFER_UNJOINABLE_PEER:
|
||||
case Interaction.TransferStatus.TRANSFER_TIMEOUT_EXPIRED:
|
||||
case Interaction.TransferStatus.TRANSFER_AWAITING_HOST:
|
||||
iconSource = JamiResources.download_black_24dp_svg
|
||||
return optionsComp
|
||||
case Interaction.Status.TRANSFER_ONGOING:
|
||||
case Interaction.TransferStatus.TRANSFER_ONGOING:
|
||||
iconSource = JamiResources.close_black_24dp_svg
|
||||
return optionsComp
|
||||
default:
|
||||
@ -158,7 +215,7 @@ Loader {
|
||||
normalColor: JamiTheme.chatviewBgColor
|
||||
imageColor: JamiTheme.chatviewButtonColor
|
||||
onClicked: {
|
||||
if (root.transferStatus === Interaction.Status.TRANSFER_ONGOING) {
|
||||
if (root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING) {
|
||||
return MessagesAdapter.cancelFile(transferId)
|
||||
} else {
|
||||
return MessagesAdapter.acceptFile(transferId)
|
||||
@ -191,7 +248,7 @@ Loader {
|
||||
onClicked: function (mouse) {
|
||||
if (canOpen) {
|
||||
dataTransferItem.hoveredLink = UtilsAdapter.urlFromLocalPath(location)
|
||||
Qt.openUrlExternally(new Url(dataTransferItem.hoveredLink))
|
||||
Qt.openUrlExternally(new URL(dataTransferItem.hoveredLink))
|
||||
} else {
|
||||
dataTransferItem.hoveredLink = ""
|
||||
}
|
||||
@ -223,11 +280,11 @@ Loader {
|
||||
: JamiTheme.chatviewTextColorDark
|
||||
}
|
||||
}
|
||||
}
|
||||
,ProgressBar {
|
||||
},
|
||||
ProgressBar {
|
||||
id: progressBar
|
||||
|
||||
visible: root.transferStatus === Interaction.Status.TRANSFER_ONGOING
|
||||
visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
|
||||
height: visible * implicitHeight
|
||||
value: transferStats.progress / transferStats.totalSize
|
||||
width: transferItem.width
|
||||
|
||||
@ -121,10 +121,7 @@ Item {
|
||||
font.pixelSize : text.length > 16 ? JamiTheme.jamiIdSmallFontSize : JamiTheme.bigFontSize
|
||||
property string registeredName: CurrentAccount.registeredName
|
||||
property string infohash: CurrentAccount.uri
|
||||
text: registeredName ? registeredName : infohash
|
||||
onRegisteredNameChanged: {
|
||||
text = registeredName ? registeredName : infohash
|
||||
}
|
||||
text: (btnId.clicked && registeredName) ? registeredName : infohash
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,11 +228,9 @@ Item {
|
||||
toolTipText: JamiStrings.identifierURI
|
||||
onClicked: {
|
||||
if (clicked) {
|
||||
usernameLabel.text = Qt.binding(function() {return CurrentAccount.uri} );
|
||||
usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.uri} );
|
||||
btnId.toolTipText = JamiStrings.identifierRegisterName;
|
||||
} else {
|
||||
usernameLabel.text = Qt.binding(function() {return CurrentAccount.registeredName} );
|
||||
usernameTextEdit.staticText = Qt.binding(function() {return CurrentAccount.registeredName} );
|
||||
btnId.toolTipText = JamiStrings.identifierURI;
|
||||
}
|
||||
|
||||
@ -1,83 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
// A SplitView that supports dynamic RTL and splitView state saving.
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
property bool isRTL: UtilsAdapter.isRTL
|
||||
property bool isSinglePane: false
|
||||
property bool isSwapped: false
|
||||
|
||||
onIsRTLChanged: {
|
||||
if (isRTL && isSinglePane && !isSwapped)
|
||||
return
|
||||
if ((isRTL && !isSwapped) || (!isRTL && isSwapped))
|
||||
swapItems()
|
||||
}
|
||||
onIsSinglePaneChanged: {
|
||||
if (isSwapped || isRTL)
|
||||
swapItems()
|
||||
}
|
||||
|
||||
property string splitViewStateKey: objectName
|
||||
property bool autoManageState: !(parent instanceof BaseView)
|
||||
|
||||
function saveSplitViewState() {
|
||||
UtilsAdapter.setAppValue("sv_" + splitViewStateKey, root.saveState());
|
||||
}
|
||||
|
||||
function restoreSplitViewState() {
|
||||
root.restoreState(UtilsAdapter.getAppValue("sv_" + splitViewStateKey));
|
||||
}
|
||||
|
||||
onResizingChanged: if (!resizing)
|
||||
saveSplitViewState()
|
||||
onVisibleChanged: {
|
||||
if (!autoManageState)
|
||||
return;
|
||||
visible ? restoreSplitViewState() : saveSplitViewState();
|
||||
}
|
||||
|
||||
function swapItems() {
|
||||
isSwapped = !isSwapped
|
||||
var qqci = children[0];
|
||||
if (qqci.children.length > 1) {
|
||||
// swap the children
|
||||
var tempPane = qqci.children[0];
|
||||
qqci.children[0] = qqci.children[1];
|
||||
qqci.children.push(tempPane);
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
visible: !isSinglePane
|
||||
implicitWidth: JamiTheme.splitViewHandlePreferredWidth
|
||||
implicitHeight: root.height
|
||||
color: JamiTheme.primaryBackgroundColor
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
implicitWidth: 1
|
||||
implicitHeight: root.height
|
||||
color: JamiTheme.tabbarBorderColor
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import net.jami.Adapters 1.1
|
||||
import net.jami.Constants 1.1
|
||||
|
||||
// A SplitView that supports dynamic RTL and splitView state saving.
|
||||
SplitView {
|
||||
id: control
|
||||
|
||||
property bool isRTL: UtilsAdapter.isRTL
|
||||
property bool isSinglePane: false
|
||||
property bool isSwapped: false
|
||||
property real handleSize: 1
|
||||
|
||||
onIsRTLChanged: {
|
||||
if (isRTL && isSinglePane && !isSwapped)
|
||||
return
|
||||
if ((isRTL && !isSwapped) || (!isRTL && isSwapped))
|
||||
swapItems()
|
||||
}
|
||||
onIsSinglePaneChanged: {
|
||||
if (isSwapped || isRTL)
|
||||
swapItems()
|
||||
}
|
||||
|
||||
property string splitViewStateKey: objectName
|
||||
property bool autoManageState: !(parent instanceof BaseView)
|
||||
|
||||
function saveSplitViewState() {
|
||||
UtilsAdapter.setAppValue("sv_" + splitViewStateKey, control.saveState());
|
||||
}
|
||||
|
||||
function restoreSplitViewState() {
|
||||
control.restoreState(UtilsAdapter.getAppValue("sv_" + splitViewStateKey));
|
||||
}
|
||||
|
||||
onResizingChanged: if (!resizing)
|
||||
saveSplitViewState()
|
||||
onVisibleChanged: {
|
||||
if (!autoManageState)
|
||||
return;
|
||||
visible ? restoreSplitViewState() : saveSplitViewState();
|
||||
}
|
||||
|
||||
function swapItems() {
|
||||
isSwapped = !isSwapped
|
||||
var qqci = children[0];
|
||||
if (qqci.children.length > 1) {
|
||||
// swap the children
|
||||
var tempPane = qqci.children[0];
|
||||
qqci.children[0] = qqci.children[1];
|
||||
qqci.children.push(tempPane);
|
||||
}
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
id: handleRoot
|
||||
|
||||
readonly property int defaultSize: control.handleSize
|
||||
|
||||
implicitWidth: control.orientation === Qt.Horizontal ? handleRoot.defaultSize : control.width
|
||||
implicitHeight: control.orientation === Qt.Horizontal ? control.height : handleRoot.defaultSize
|
||||
|
||||
color: JamiTheme.tabbarBorderColor
|
||||
|
||||
containmentMask: Item {
|
||||
// In the default configuration, the total handle size is the sum of the default size of the
|
||||
// handle and the extra handle size (4). If the layout is not right-to-left (RTL), the handle
|
||||
// is positioned at 0 on the X-axis, otherwise it's positioned to the left by the extra handle
|
||||
// size (4 pixels). This is done to make it easier to grab small scroll-view handles that are
|
||||
// adjacent to the SplitView handle. Note: vertically oriented handles are not offset.
|
||||
readonly property real extraHandleSize: 4
|
||||
readonly property real handleXPosition: !isRTL ? 0 : -extraHandleSize
|
||||
readonly property real handleSize: handleRoot.defaultSize + extraHandleSize
|
||||
|
||||
x: control.orientation === Qt.Horizontal ? handleXPosition : 0
|
||||
width: control.orientation === Qt.Horizontal ? handleSize : handleRoot.width
|
||||
height: control.orientation === Qt.Horizontal ? handleRoot.height : handleSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,12 +28,14 @@ VideoView {
|
||||
crop: true
|
||||
visible: isRendering && visibilityCondition
|
||||
|
||||
Component.onDestruction: VideoDevices.stopDevice(rendererId);
|
||||
|
||||
function startWithId(id, force = false) {
|
||||
if (id !== undefined && id.length === 0) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
const forceRestart = rendererId === id;
|
||||
const forceRestart = rendererId === id || force;
|
||||
if (!forceRestart) {
|
||||
// Stop previous device
|
||||
VideoDevices.stopDevice(rendererId);
|
||||
|
||||
@ -382,7 +382,11 @@ Control {
|
||||
property bool bubbleHovered
|
||||
property string imgSource
|
||||
|
||||
width: (root.type === Interaction.Type.TEXT ? root.textContentWidth + (IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth) : innerContent.childrenRect.width)
|
||||
width: (root.type === Interaction.Type.TEXT || isDeleted ?
|
||||
root.textContentWidth + (IsEmojiOnly || root.bigMsg ?
|
||||
0
|
||||
: root.timeWidth + root.editedWidth)
|
||||
: innerContent.childrenRect.width)
|
||||
height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0) + (root.bigMsg ? 15 : 0)
|
||||
|
||||
HoverHandler {
|
||||
@ -424,9 +428,9 @@ Control {
|
||||
id: editedRow
|
||||
anchors.left: root.bigMsg ? bubble.left : timestampItem.left
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: root.bigMsg || bubble.isDeleted ? 6 : 10
|
||||
anchors.bottomMargin: root.bigMsg ? 6 : 10
|
||||
anchors.leftMargin: root.bigMsg ? 10 : -timestampItem.width - 16
|
||||
visible: bubble.isEdited
|
||||
visible: bubble.isEdited && !bubble.isDeleted
|
||||
z: 1
|
||||
ResponsiveImage {
|
||||
id: editedImage
|
||||
|
||||
@ -153,7 +153,7 @@ BaseContextMenu {
|
||||
GeneralMenuItem {
|
||||
id: removeLocally
|
||||
|
||||
canTrigger: type === Interaction.Type.DATA_TRANSFER && Status === Interaction.Status.TRANSFER_FINISHED
|
||||
canTrigger: type === Interaction.Type.DATA_TRANSFER && TransferStatus === Interaction.TransferStatus.TRANSFER_FINISHED
|
||||
iconSource: JamiResources.trash_black_24dp_svg
|
||||
itemName: JamiStrings.removeLocally
|
||||
onClicked: {
|
||||
@ -175,7 +175,7 @@ BaseContextMenu {
|
||||
GeneralMenuItem {
|
||||
id: deleteMessage
|
||||
|
||||
canTrigger: root.isOutgoing && type === Interaction.Type.TEXT
|
||||
canTrigger: root.isOutgoing && (type === Interaction.Type.TEXT || type === Interaction.Type.DATA_TRANSFER)
|
||||
iconSource: JamiResources.delete_svg
|
||||
itemName: JamiStrings.deleteMessage
|
||||
onClicked: {
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <glib.h>
|
||||
#include <gio/gio.h>
|
||||
@ -171,10 +173,10 @@ static void
|
||||
logConnectionInfo(NMActiveConnection* connection)
|
||||
{
|
||||
if (connection) {
|
||||
qDebug() << "primary network connection:" << nm_active_connection_get_uuid(connection)
|
||||
<< "default: " << (nm_active_connection_get_default(connection) ? "yes" : "no");
|
||||
C_INFO << "primary network connection:" << nm_active_connection_get_uuid(connection)
|
||||
<< "default: " << (nm_active_connection_get_default(connection) ? "yes" : "no");
|
||||
} else {
|
||||
qWarning() << "no primary network connection detected, check network settings";
|
||||
C_WARN << "no primary network connection detected, check network settings";
|
||||
}
|
||||
}
|
||||
|
||||
@ -191,11 +193,10 @@ nmClientCallback(G_GNUC_UNUSED GObject* source_object, GAsyncResult* result, Con
|
||||
{
|
||||
GError* error = nullptr;
|
||||
if (auto nm_client = nm_client_new_finish(result, &error)) {
|
||||
qDebug() << "NetworkManager client initialized, version: "
|
||||
<< nm_client_get_version(nm_client)
|
||||
<< ", daemon running:" << (nm_client_get_nm_running(nm_client) ? "yes" : "no")
|
||||
<< ", networking enabled:"
|
||||
<< (nm_client_networking_get_enabled(nm_client) ? "yes" : "no");
|
||||
C_INFO << "NetworkManager client initialized, version: " << nm_client_get_version(nm_client)
|
||||
<< ", daemon running:" << (nm_client_get_nm_running(nm_client) ? "yes" : "no")
|
||||
<< ", networking enabled:"
|
||||
<< (nm_client_networking_get_enabled(nm_client) ? "yes" : "no");
|
||||
|
||||
auto connection = nm_client_get_primary_connection(nm_client);
|
||||
logConnectionInfo(connection);
|
||||
@ -205,7 +206,7 @@ nmClientCallback(G_GNUC_UNUSED GObject* source_object, GAsyncResult* result, Con
|
||||
cm);
|
||||
|
||||
} else {
|
||||
qWarning() << "error initializing NetworkManager client: " << error->message;
|
||||
C_WARN << "error initializing NetworkManager client: " << error->message;
|
||||
g_clear_error(&error);
|
||||
}
|
||||
}
|
||||
@ -222,7 +223,7 @@ ConnectivityMonitor::ConnectivityMonitor(QObject* parent)
|
||||
|
||||
ConnectivityMonitor::~ConnectivityMonitor()
|
||||
{
|
||||
qDebug() << "Destroying connectivity monitor";
|
||||
C_DBG << "Destroying connectivity monitor";
|
||||
}
|
||||
|
||||
bool
|
||||
|
||||
@ -125,7 +125,11 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
|
||||
if (interaction.type == interaction::Type::UPDATE_PROFILE) {
|
||||
lastInteractionBody = interaction::getProfileUpdatedString();
|
||||
} else if (interaction.type == interaction::Type::DATA_TRANSFER) {
|
||||
lastInteractionBody = interaction.commit.value("displayName");
|
||||
if (interaction.commit.value("tid").isEmpty()) {
|
||||
lastInteractionBody = tr("Deleted media");
|
||||
} else {
|
||||
lastInteractionBody = interaction.commit.value("displayName");
|
||||
}
|
||||
} else if (interaction.type == lrc::api::interaction::Type::CALL) {
|
||||
const auto isOutgoing = interaction.authorUri == accInfo.profileInfo.uri;
|
||||
lastInteractionBody = interaction::getCallInteractionString(isOutgoing, interaction);
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include "currentcall.h"
|
||||
|
||||
#include "callparticipantsmodel.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <api/callparticipantsmodel.h>
|
||||
#include <api/devicemodel.h>
|
||||
@ -223,43 +224,14 @@ CurrentCall::updateCallInfo()
|
||||
set_isGrid(callInfo.layout == call::Layout::GRID);
|
||||
set_isAudioOnly(callInfo.isAudioOnly);
|
||||
|
||||
bool isAudioMuted {};
|
||||
bool isVideoMuted {};
|
||||
bool isSharing {};
|
||||
QString sharingSource {};
|
||||
bool isCapturing {};
|
||||
QString previewId {};
|
||||
using namespace libjami::Media;
|
||||
if (callInfo.status != lrc::api::call::Status::ENDED) {
|
||||
for (const auto& media : callInfo.mediaList) {
|
||||
if (media[MediaAttributeKey::MEDIA_TYPE] == Details::MEDIA_TYPE_VIDEO) {
|
||||
if (media[MediaAttributeKey::SOURCE].startsWith(VideoProtocolPrefix::DISPLAY)
|
||||
|| media[MediaAttributeKey::SOURCE].startsWith(VideoProtocolPrefix::FILE)) {
|
||||
isSharing = true;
|
||||
sharingSource = media[MediaAttributeKey::SOURCE];
|
||||
}
|
||||
if (media[MediaAttributeKey::ENABLED] == TRUE_STR
|
||||
&& media[MediaAttributeKey::MUTED] == FALSE_STR && previewId.isEmpty()) {
|
||||
previewId = media[libjami::Media::MediaAttributeKey::SOURCE];
|
||||
}
|
||||
if (media[libjami::Media::MediaAttributeKey::SOURCE].startsWith(
|
||||
libjami::Media::VideoProtocolPrefix::CAMERA)) {
|
||||
isVideoMuted |= media[MediaAttributeKey::MUTED] == TRUE_STR;
|
||||
isCapturing = media[MediaAttributeKey::MUTED] == FALSE_STR;
|
||||
}
|
||||
} else if (media[MediaAttributeKey::MEDIA_TYPE] == Details::MEDIA_TYPE_AUDIO) {
|
||||
if (media[MediaAttributeKey::LABEL] == "audio_0") {
|
||||
isAudioMuted |= media[libjami::Media::MediaAttributeKey::MUTED] == TRUE_STR;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
set_previewId(previewId);
|
||||
set_isAudioMuted(isAudioMuted);
|
||||
set_isVideoMuted(isVideoMuted);
|
||||
set_isSharing(isSharing);
|
||||
set_sharingSource(sharingSource);
|
||||
set_isCapturing(isCapturing);
|
||||
auto callInfoEx = callInfo.getCallInfoEx();
|
||||
set_previewId(callInfoEx["preview_id"].toString());
|
||||
set_isAudioMuted(callInfoEx["is_audio_muted"].toBool());
|
||||
set_isVideoMuted(callInfoEx["is_video_muted"].toBool());
|
||||
set_isSharing(callInfoEx["is_sharing"].toBool());
|
||||
set_sharingSource(isSharing_ ? callInfoEx["preview_id"].toString() : QString());
|
||||
set_isCapturing(callInfoEx["is_capturing"].toBool());
|
||||
|
||||
set_isHandRaised(callModel->isHandRaised(id_));
|
||||
set_isModerator(callModel->isModerator(id_));
|
||||
|
||||
@ -377,7 +349,7 @@ CurrentCall::onCurrentAccountIdChanged()
|
||||
auto& accInfo = lrcInstance_->getCurrentAccountInfo();
|
||||
set_isSIP(accInfo.profileInfo.type == profile::Type::SIP);
|
||||
} catch (const std::exception& e) {
|
||||
qWarning() << "Can't update current call type" << e.what();
|
||||
C_DBG << "Can't update current call type" << e.what();
|
||||
}
|
||||
|
||||
connectModel();
|
||||
|
||||
@ -18,6 +18,8 @@
|
||||
|
||||
#include "currentconversation.h"
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#include <api/conversationmodel.h>
|
||||
#include <api/contact.h>
|
||||
|
||||
@ -264,51 +266,39 @@ void
|
||||
CurrentConversation::connectModel()
|
||||
{
|
||||
membersModel_->setMembers({}, {}, {});
|
||||
auto convModel = lrcInstance_->getCurrentConversationModel();
|
||||
if (!convModel)
|
||||
|
||||
auto currentConversationModel = lrcInstance_->getCurrentConversationModel();
|
||||
auto currentCallModel = lrcInstance_->getCurrentCallModel();
|
||||
if (!currentConversationModel || !currentCallModel) {
|
||||
C_DBG << "CurrentConversation: can't connect to unavailable models";
|
||||
return;
|
||||
}
|
||||
|
||||
auto connectObjectSignal = [this](auto obj, auto signal, auto slot) {
|
||||
connect(obj, signal, this, slot, Qt::UniqueConnection);
|
||||
};
|
||||
|
||||
connectObjectSignal(convModel,
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::conversationUpdated,
|
||||
&CurrentConversation::onConversationUpdated);
|
||||
connectObjectSignal(convModel,
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::profileUpdated,
|
||||
&CurrentConversation::updateProfile);
|
||||
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::profileUpdated,
|
||||
this,
|
||||
&CurrentConversation::updateProfile,
|
||||
Qt::UniqueConnection);
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::onConversationErrorsUpdated,
|
||||
this,
|
||||
&CurrentConversation::updateErrors,
|
||||
Qt::UniqueConnection);
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::activeCallsChanged,
|
||||
this,
|
||||
&CurrentConversation::updateActiveCalls,
|
||||
Qt::UniqueConnection);
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::conversationPreferencesUpdated,
|
||||
this,
|
||||
&CurrentConversation::updateConversationPreferences,
|
||||
Qt::UniqueConnection);
|
||||
connect(lrcInstance_->getCurrentConversationModel(),
|
||||
&ConversationModel::needsHost,
|
||||
this,
|
||||
&CurrentConversation::onNeedsHost,
|
||||
Qt::UniqueConnection);
|
||||
connect(lrcInstance_->getCurrentCallModel(),
|
||||
&CallModel::callStatusChanged,
|
||||
this,
|
||||
&CurrentConversation::onCallStatusChanged,
|
||||
Qt::UniqueConnection);
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::conversationErrorsUpdated,
|
||||
&CurrentConversation::updateErrors);
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::activeCallsChanged,
|
||||
&CurrentConversation::updateActiveCalls);
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::conversationPreferencesUpdated,
|
||||
&CurrentConversation::updateConversationPreferences);
|
||||
connectObjectSignal(currentConversationModel,
|
||||
&ConversationModel::needsHost,
|
||||
&CurrentConversation::onNeedsHost);
|
||||
connectObjectSignal(currentCallModel,
|
||||
&CallModel::callStatusChanged,
|
||||
&CurrentConversation::onCallStatusChanged);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -43,9 +43,8 @@ LRCInstance::LRCInstance(const QString& updateUrl,
|
||||
muteDaemon_ = muteDaemon;
|
||||
threadPool_->setMaxThreadCount(1);
|
||||
|
||||
connect(this, &LRCInstance::currentAccountIdChanged, [this] {
|
||||
// save to config, editing the accountlistmodel's underlying data
|
||||
accountModel().setTopAccount(currentAccountId_);
|
||||
// Update the current account when the account list changes.
|
||||
connect(&accountModel(), &AccountModel::accountsReordered, this, [this] {
|
||||
Q_EMIT accountListChanged();
|
||||
|
||||
profile::Info profileInfo;
|
||||
@ -62,6 +61,11 @@ LRCInstance::LRCInstance(const QString& updateUrl,
|
||||
set_currentAccountAvatarSet(!profileInfo.avatar.isEmpty());
|
||||
});
|
||||
|
||||
connect(this, &LRCInstance::currentAccountIdChanged, [this] {
|
||||
// This will trigger `AccountModel::accountsReordered`.
|
||||
accountModel().setTopAccount(currentAccountId_);
|
||||
});
|
||||
|
||||
connect(&accountModel(), &AccountModel::profileUpdated, this, [this](const QString& id) {
|
||||
if (id != currentAccountId_)
|
||||
return;
|
||||
|
||||
@ -162,6 +162,8 @@ MainApplication::MainApplication(int& argc, char** argv)
|
||||
"libclient.debug=false\n"
|
||||
"qt.*=false\n"
|
||||
"qml.debug=false\n"
|
||||
"default.debug=false\n"
|
||||
"client.debug=false\n"
|
||||
"\n");
|
||||
// These can be set in the environment as well.
|
||||
// e.g. QT_LOGGING_RULES="*.debug=false;qml.debug=true"
|
||||
@ -423,7 +425,8 @@ MainApplication::initQmlLayer()
|
||||
&screenInfo_,
|
||||
this);
|
||||
|
||||
QUrl url;
|
||||
QUrl url = u"qrc:/MainApplicationWindow.qml"_qs;
|
||||
#ifdef QT_DEBUG
|
||||
if (parser_.isSet("test")) {
|
||||
// List the QML files in the project source tree.
|
||||
const auto targetTestComponent = findResource(parser_.value("test"));
|
||||
@ -437,9 +440,8 @@ MainApplication::initQmlLayer()
|
||||
engine_->rootContext()->setContextProperty("testWidth", testWidth);
|
||||
engine_->rootContext()->setContextProperty("testHeight", testHeight);
|
||||
url = u"qrc:/ComponentTestWindow.qml"_qs;
|
||||
} else {
|
||||
url = u"qrc:/MainApplicationWindow.qml"_qs;
|
||||
}
|
||||
#endif
|
||||
QObject::connect(
|
||||
engine_.get(),
|
||||
&QQmlApplicationEngine::objectCreationFailed,
|
||||
@ -449,7 +451,7 @@ MainApplication::initQmlLayer()
|
||||
engine_->load(url);
|
||||
|
||||
// Report the render interface used.
|
||||
C_DBG << "Main window loaded using" << getRenderInterfaceString();
|
||||
C_INFO << "Main window loaded using" << getRenderInterfaceString();
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@ -48,10 +48,10 @@ ListSelectionView {
|
||||
leftPaneItem: viewCoordinator.getView("SidePanel", true)
|
||||
|
||||
rightPaneItem: StackLayout {
|
||||
id: conversationStackLayout
|
||||
objectName: "ConversationLayout"
|
||||
|
||||
currentIndex: !CurrentConversation.hasCall ? 0 : 1
|
||||
onCurrentIndexChanged: chatView.parent = currentIndex === 1 ? callStackView.chatViewContainer : chatViewContainer
|
||||
currentIndex: CurrentConversation.hasCall ? 1 : 0
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
@ -64,24 +64,35 @@ ListSelectionView {
|
||||
ChatView {
|
||||
id: chatView
|
||||
anchors.fill: parent
|
||||
inCallView: parent == callStackView.chatViewContainer
|
||||
|
||||
// Use callStackView.chatViewContainer only when hasCall is true
|
||||
// and callStackView.chatViewContainer not null.
|
||||
// Because after a swarm call ends, callStackView.chatViewContainer might not be null
|
||||
// due to a lack of call state change signals for the swarm call.
|
||||
readonly property bool hasCall: CurrentConversation.hasCall
|
||||
readonly property var inCallChatContainer: hasCall ? callStackView.chatViewContainer : null
|
||||
|
||||
// Parent the chat view to the call stack view when in call.
|
||||
parent: inCallChatContainer ? inCallChatContainer : chatViewContainer
|
||||
inCallView: parent === callStackView.chatViewContainer
|
||||
|
||||
readonly property string currentConvId: CurrentConversation.id
|
||||
onCurrentConvIdChanged: {
|
||||
if (!CurrentConversation.hasCall) {
|
||||
Qt.callLater(focusChatView);
|
||||
} else {
|
||||
dismiss();
|
||||
callStackView.contentView.forceActiveFocus();
|
||||
}
|
||||
Qt.callLater(function() {
|
||||
if (CurrentConversation.hasCall) {
|
||||
callStackView.contentView.forceActiveFocus();
|
||||
} else {
|
||||
focusChatView();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDismiss: {
|
||||
if (!inCallView) {
|
||||
viewNode.dismiss();
|
||||
} else {
|
||||
if (inCallView) {
|
||||
callStackView.chatViewContainer.visible = false;
|
||||
callStackView.contentView.forceActiveFocus();
|
||||
} else {
|
||||
viewNode.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -34,24 +34,6 @@ Label {
|
||||
|
||||
property bool inSettings: viewCoordinator.currentViewName === "SettingsView"
|
||||
|
||||
// TODO: remove these refresh hacks use QAbstractItemModels correctly
|
||||
Connections {
|
||||
target: AccountAdapter
|
||||
|
||||
function onAccountStatusChanged(accountId) {
|
||||
AccountListModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: LRCInstance
|
||||
|
||||
function onAccountListChanged() {
|
||||
root.update();
|
||||
AccountListModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
function togglePopup() {
|
||||
if (root.popup.opened) {
|
||||
root.popup.close();
|
||||
|
||||
@ -60,23 +60,6 @@ Popup {
|
||||
|
||||
property bool inSettings: viewCoordinator.currentViewName === "SettingsView"
|
||||
|
||||
// TODO: remove these refresh hacks use QAbstractItemModels correctly
|
||||
Connections {
|
||||
target: AccountAdapter
|
||||
|
||||
function onAccountStatusChanged(accountId) {
|
||||
AccountListModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: LRCInstance
|
||||
|
||||
function onAccountListChanged() {
|
||||
AccountListModel.reset();
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: mainLayout
|
||||
anchors.fill: parent
|
||||
@ -257,11 +240,6 @@ Popup {
|
||||
color: JamiTheme.smartListHoveredColor
|
||||
}
|
||||
|
||||
// fake footer item as workaround for Qt 5.15 bug
|
||||
// https://bugreports.qt.io/browse/QTBUG-85302
|
||||
// don't use the clip trick and footer item overlay
|
||||
// explained here https://stackoverflow.com/a/64625149
|
||||
// as it causes other complexities in handling the drop shadow
|
||||
ItemDelegate {
|
||||
id: addAccountItem
|
||||
|
||||
|
||||
@ -112,6 +112,7 @@ Control {
|
||||
},
|
||||
Action {
|
||||
id: shareMenuAction
|
||||
enabled: !CurrentCall.isSharing
|
||||
text: JamiStrings.selectShareMethod
|
||||
property int popupMode: CallActionBar.ActionPopupMode.ListElement
|
||||
property var listModel: ListModel {
|
||||
@ -123,7 +124,7 @@ Control {
|
||||
"Name": JamiStrings.shareScreen,
|
||||
"IconSource": JamiResources.laptop_black_24dp_svg
|
||||
});
|
||||
if (Qt.platform.os.toString() !== "osx" && !UtilsAdapter.isWayland()) {
|
||||
if (Qt.platform.os.toString() !== "osx") {
|
||||
shareModel.append({
|
||||
"Name": JamiStrings.shareWindow,
|
||||
"IconSource": JamiResources.window_black_24dp_svg
|
||||
@ -293,7 +294,24 @@ Control {
|
||||
},
|
||||
Action {
|
||||
id: muteVideoAction
|
||||
onTriggered: CallAdapter.muteCameraToggle()
|
||||
onTriggered: {
|
||||
if (CurrentCall.isSharing && UtilsAdapter.isWayland()) {
|
||||
// Unmuting the camera while a screen share is ongoing causes the daemon
|
||||
// to stop sharing. However, on Wayland, every share has an associated
|
||||
// ScreenCastPortal object which is managed by the client and needs to
|
||||
// be destroyed when the share ends. This is why we explicitly call the
|
||||
// stopSharing function below.
|
||||
//
|
||||
// The muteCamera variable is set whenever a share starts and is normally used
|
||||
// by the stopSharing function to restore the camera to its previous state
|
||||
// when a share ends. Here we know that the user wants to unmute the camera,
|
||||
// so we have to explicitly set muteCamera to false.
|
||||
AvAdapter.muteCamera = false;
|
||||
AvAdapter.stopSharing(CurrentCall.sharingSource);
|
||||
} else {
|
||||
CallAdapter.muteCameraToggle();
|
||||
}
|
||||
}
|
||||
checkable: true
|
||||
icon.source: checked ? JamiResources.videocam_off_24dp_svg : JamiResources.videocam_24dp_svg
|
||||
icon.color: checked ? "red" : "white"
|
||||
|
||||
@ -114,7 +114,9 @@ Item {
|
||||
}
|
||||
|
||||
function openShareScreen() {
|
||||
if (Qt.application.screens.length === 1) {
|
||||
if (UtilsAdapter.isWayland()) {
|
||||
AvAdapter.shareEntireScreenWayland();
|
||||
} else if (Qt.application.screens.length === 1) {
|
||||
AvAdapter.shareEntireScreen(0);
|
||||
} else {
|
||||
SelectScreenWindowCreation.presentSelectScreenWindow(appWindow, false);
|
||||
@ -122,6 +124,10 @@ Item {
|
||||
}
|
||||
|
||||
function openShareWindow() {
|
||||
if (UtilsAdapter.isWayland()) {
|
||||
AvAdapter.shareWindowWayland();
|
||||
return;
|
||||
}
|
||||
AvAdapter.getListWindows();
|
||||
if (AvAdapter.windowsNames.length >= 1) {
|
||||
SelectScreenWindowCreation.presentSelectScreenWindow(appWindow, true);
|
||||
|
||||
@ -25,7 +25,11 @@ import "../../commoncomponents"
|
||||
|
||||
Item {
|
||||
id: root
|
||||
property alias chatViewContainer: ongoingCallPage.chatViewContainer
|
||||
property var chatViewContainer: {
|
||||
if (callStackMainView.item instanceof OngoingCallPage)
|
||||
return callStackMainView.item.chatViewContainer;
|
||||
return undefined;
|
||||
}
|
||||
property alias contentView: callStackMainView
|
||||
|
||||
property var sipKeys: ["1", "2", "3", "A", "4", "5", "6", "B", "7", "8", "9", "C", "*", "0", "#", "D"]
|
||||
@ -61,44 +65,49 @@ Item {
|
||||
// TODO: this should all be done by listening to
|
||||
// parent visibility change or parent `Component.onDestruction`
|
||||
function needToCloseInCallConversationAndPotentialWindow() {
|
||||
ongoingCallPage.closeInCallConversation();
|
||||
ongoingCallPage.closeContextMenuAndRelatedWindows();
|
||||
if (callStackMainView.item instanceof OngoingCallPage) {
|
||||
callStackMainView.item.closeInCallConversation();
|
||||
callStackMainView.item.closeContextMenuAndRelatedWindows();
|
||||
}
|
||||
}
|
||||
|
||||
function toggleFullScreen() {
|
||||
if (!layoutManager.isCallFullscreen) {
|
||||
layoutManager.pushFullScreenItem(callStackMainView.currentItem, callStackMainView, null, null);
|
||||
layoutManager.pushFullScreenItem(callStackMainView.item, callStackMainView, null, null);
|
||||
} else {
|
||||
layoutManager.removeFullScreenItem(callStackMainView.currentItem);
|
||||
layoutManager.removeFullScreenItem(callStackMainView.item);
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
Loader {
|
||||
id: callStackMainView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
property Item currentItem: itemAt(currentIndex)
|
||||
|
||||
currentIndex: {
|
||||
sourceComponent: {
|
||||
switch (CurrentCall.status) {
|
||||
case Call.Status.IN_PROGRESS:
|
||||
case Call.Status.CONNECTED:
|
||||
case Call.Status.PAUSED:
|
||||
return 1;
|
||||
return ongoingCallPageComponent;
|
||||
case Call.Status.SEARCHING:
|
||||
case Call.Status.CONNECTING:
|
||||
case Call.Status.INCOMING_RINGING:
|
||||
case Call.Status.OUTGOING_RINGING:
|
||||
return initialCallPageComponent;
|
||||
default:
|
||||
return 0;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
InitialCallPage {
|
||||
Component {
|
||||
id: initialCallPageComponent
|
||||
InitialCallPage {}
|
||||
}
|
||||
OngoingCallPage {
|
||||
id: ongoingCallPage
|
||||
|
||||
Component {
|
||||
id: ongoingCallPageComponent
|
||||
OngoingCallPage {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ Rectangle {
|
||||
onExtrasPanelWidthChanged: {
|
||||
resolvePanes();
|
||||
// This range should ensure that the panel won't restore to maximized.
|
||||
if (extrasPanelWidth !== 0 && extrasPanelWidth !== width) {
|
||||
if (extrasPanelWidth !== 0 && extrasPanelWidth !== this.width) {
|
||||
console.debug("Saving previous extras panel width: %1".arg(extrasPanelWidth));
|
||||
previousExtrasPanelWidth = extrasPanelWidth;
|
||||
}
|
||||
|
||||
@ -88,7 +88,7 @@ Rectangle {
|
||||
mirror: UtilsAdapter.isRTL
|
||||
|
||||
source: JamiResources.back_24dp_svg
|
||||
toolTipText: CurrentConversation.inCall ? JamiStrings.backCall : JamiStrings.hideChat
|
||||
toolTipText: CurrentConversation.inCall ? JamiStrings.returnToCall : JamiStrings.hideChat
|
||||
|
||||
onClicked: root.backClicked()
|
||||
}
|
||||
@ -147,7 +147,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: startAAudioCallButton
|
||||
id: startAudioCallButton
|
||||
|
||||
visible: interactionButtonsVisibility &&
|
||||
(!addMemberVisibility || UtilsAdapter.getAppValue(Settings.EnableExperimentalSwarm))
|
||||
@ -158,7 +158,7 @@ Rectangle {
|
||||
}
|
||||
|
||||
JamiPushButton { QWKSetParentHitTestVisible {}
|
||||
id: startAVideoCallButton
|
||||
id: startVideoCallButton
|
||||
|
||||
visible: interactionButtonsVisibility &&
|
||||
CurrentAccount.videoEnabled_Video &&
|
||||
@ -230,8 +230,7 @@ Rectangle {
|
||||
|
||||
checkable: true
|
||||
checked: extrasPanel.isOpen(ChatView.SwarmDetailsPanel)
|
||||
visible: interactionButtonsVisibility &&
|
||||
(swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP) // TODO if SIP not a request
|
||||
visible: (swarmDetailsVisibility || LRCInstance.currentAccountType === Profile.Type.SIP)
|
||||
source: JamiResources.swarm_details_panel_svg
|
||||
toolTipText: JamiStrings.details
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ ContextMenuAutoLoader {
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
id: startVideoCallItem
|
||||
id: startVideoCall
|
||||
|
||||
canTrigger: CurrentAccount.videoEnabled_Video && !hasCall && !readOnly
|
||||
itemName: JamiStrings.startVideoCall
|
||||
|
||||
@ -60,7 +60,7 @@ JamiListView {
|
||||
|
||||
property var messageListModel: MessagesAdapter.mediaMessageListModel
|
||||
readonly property int documentType: Interaction.Type.DATA_TRANSFER
|
||||
readonly property int transferFinishedType: Interaction.Status.TRANSFER_FINISHED
|
||||
readonly property int transferFinishedType: Interaction.TransferStatus.TRANSFER_FINISHED
|
||||
readonly property int transferSuccesType: Interaction.Status.SUCCESS
|
||||
|
||||
onMessageListModelChanged: sourceModel = root.visible && messageListModel ? messageListModel : null
|
||||
|
||||
231
src/app/mainview/components/InCallLocalVideo.qml
Normal file
231
src/app/mainview/components/InCallLocalVideo.qml
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import net.jami.Enums 1.1
|
||||
import net.jami.Constants 1.1
|
||||
import net.jami.Adapters 1.1
|
||||
|
||||
import "../../commoncomponents"
|
||||
|
||||
// This component uses anchors and they are set within this component.
|
||||
LocalVideo {
|
||||
id: localPreview
|
||||
|
||||
required property var container
|
||||
required property real opacityModifier
|
||||
|
||||
readonly property int previewMargin: 15
|
||||
readonly property int previewMarginYTop: previewMargin + 42
|
||||
readonly property int previewMarginYBottom: previewMargin + 84
|
||||
|
||||
anchors.bottomMargin: previewMarginYBottom
|
||||
anchors.leftMargin: sideMargin
|
||||
anchors.rightMargin: sideMargin
|
||||
anchors.topMargin: previewMarginYTop
|
||||
|
||||
visibilityCondition: (CurrentCall.isSharing || !CurrentCall.isVideoMuted) &&
|
||||
!CurrentCall.isConference
|
||||
|
||||
// Keep the area of the preview a proportion of the screen size plus a
|
||||
// modifier to allow the user to scale it.
|
||||
readonly property real containerArea: container.width * container.height
|
||||
property real scalingFactor: 1
|
||||
width: Math.sqrt(containerArea / 16) * scalingFactor
|
||||
height: width * invAspectRatio
|
||||
|
||||
flip: CurrentCall.flipSelf && !CurrentCall.isSharing
|
||||
blurRadius: hidden ? 25 : 0
|
||||
|
||||
opacity: hidden ? opacityModifier : 1
|
||||
|
||||
// Allow hiding the preview (available when anchored)
|
||||
readonly property bool hovered: hoverHandler.hovered
|
||||
readonly property bool anchored: state !== "unanchored"
|
||||
property bool hidden: false
|
||||
readonly property real hiddenHandleSize: 32
|
||||
// Compute the margin as a function of the preview width in order to
|
||||
// apply a negative margin and expose a constant width handle.
|
||||
// If not hidden, return the previewMargin.
|
||||
property real sideMargin: !hidden ? previewMargin : -(width - hiddenHandleSize)
|
||||
// Animate the hiddenSize with a Behavior.
|
||||
Behavior on sideMargin { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
readonly property bool onLeft: state.indexOf("left") !== -1
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: !localPreview.hidden
|
||||
onWheel: function(event) {
|
||||
const delta = event.angleDelta.y / 120 * 0.1;
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
parent.opacity = JamiQmlUtils.clamp(parent.opacity + delta, 0.25, 1);
|
||||
} else {
|
||||
localPreview.scalingFactor = JamiQmlUtils.clamp(localPreview.scalingFactor + delta, 0.5, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PushButton {
|
||||
id: hidePreviewButton
|
||||
objectName: "hidePreviewButton"
|
||||
|
||||
width: localPreview.hiddenHandleSize
|
||||
state: localPreview.onLeft ?
|
||||
(localPreview.hidden ? "right" : "left") :
|
||||
(localPreview.hidden ? "left" : "right")
|
||||
states: [
|
||||
State {
|
||||
name: "left"
|
||||
AnchorChanges {
|
||||
target: hidePreviewButton
|
||||
anchors.left: parent.left
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "right"
|
||||
AnchorChanges {
|
||||
target: hidePreviewButton
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
]
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
opacity: (localPreview.anchored && localPreview.hovered) || localPreview.hidden
|
||||
Behavior on opacity { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
visible: opacity > 0
|
||||
background: Rectangle {
|
||||
readonly property color normalColor: JamiTheme.mediumGrey
|
||||
color: JamiTheme.mediumGrey
|
||||
opacity: hidePreviewButton.hovered ? 0.7 : 0.5
|
||||
Behavior on opacity { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
}
|
||||
normalImageSource: hidePreviewButton.state === "left" ?
|
||||
JamiResources.chevron_left_black_24dp_svg :
|
||||
JamiResources.chevron_right_black_24dp_svg
|
||||
imageColor: JamiTheme.darkGreyColor
|
||||
onClicked: localPreview.hidden = !localPreview.hidden
|
||||
toolTipText: localPreview.hidden ?
|
||||
JamiStrings.showLocalVideo :
|
||||
JamiStrings.hideLocalVideo
|
||||
}
|
||||
|
||||
state: "anchor_top_right"
|
||||
states: [
|
||||
State {
|
||||
name: "unanchored"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: undefined
|
||||
anchors.right: undefined
|
||||
anchors.bottom: undefined
|
||||
anchors.left: undefined
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_top_left"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: localPreview.container.top
|
||||
anchors.left: localPreview.container.left
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_top_right"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: localPreview.container.top
|
||||
anchors.right: localPreview.container.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_bottom_right"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.bottom: localPreview.container.bottom
|
||||
anchors.right: localPreview.container.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_bottom_left"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.bottom: localPreview.container.bottom
|
||||
anchors.left: localPreview.container.left
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutBack
|
||||
easing.overshoot: 1.5
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
readonly property var container: localPreview.container
|
||||
target: parent
|
||||
dragThreshold: 4
|
||||
enabled: !localPreview.hidden
|
||||
xAxis.maximum: container.width - parent.width - previewMargin
|
||||
xAxis.minimum: previewMargin
|
||||
yAxis.maximum: container.height - parent.height - previewMarginYBottom
|
||||
yAxis.minimum: previewMarginYTop
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
localPreview.state = "unanchored";
|
||||
} else {
|
||||
const center = Qt.point(target.x + target.width / 2,
|
||||
target.y + target.height / 2);
|
||||
const containerCenter = Qt.point(container.x + container.width / 2,
|
||||
container.y + container.height / 2);
|
||||
if (center.x >= containerCenter.x) {
|
||||
if (center.y >= containerCenter.y) {
|
||||
localPreview.state = "anchor_bottom_right";
|
||||
} else {
|
||||
localPreview.state = "anchor_top_right";
|
||||
}
|
||||
} else {
|
||||
if (center.y >= containerCenter.y) {
|
||||
localPreview.state = "anchor_bottom_left";
|
||||
} else {
|
||||
localPreview.state = "anchor_top_left";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: localPreview.width
|
||||
height: localPreview.height
|
||||
radius: JamiTheme.primaryRadius
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -213,12 +213,8 @@ Rectangle {
|
||||
|
||||
onClicked: {
|
||||
if (type === "cam" || type === "mic") {
|
||||
var acceptVideoMedia = true;
|
||||
if (type === "cam")
|
||||
acceptVideoMedia = true;
|
||||
else if (type === "mic")
|
||||
acceptVideoMedia = false;
|
||||
CallAdapter.setCallMedia(CurrentAccount.id, CurrentConversation.id, acceptVideoMedia);
|
||||
var muteVideo = (type === "mic");
|
||||
CallAdapter.setCallMedia(CurrentAccount.id, CurrentConversation.id, muteVideo);
|
||||
callAccepted();
|
||||
} else {
|
||||
callCanceled();
|
||||
|
||||
@ -82,11 +82,11 @@ Window {
|
||||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + C"
|
||||
description: qsTr("Start an audio call")
|
||||
description: qsTr("Start audio call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + X"
|
||||
description: qsTr("Start a video call")
|
||||
description: qsTr("Start video call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + L"
|
||||
@ -102,7 +102,7 @@ Window {
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + Delete"
|
||||
description: qsTr("Remove conversation")
|
||||
description: qsTr("Leave conversation")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + A"
|
||||
@ -117,41 +117,12 @@ Window {
|
||||
description: qsTr("Cancel message edition")
|
||||
}
|
||||
},
|
||||
ListModel {
|
||||
id: keyboardSettingsShortcutsModel
|
||||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + M"
|
||||
description: qsTr("Media settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + G"
|
||||
description: qsTr("General settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Alt + I"
|
||||
description: qsTr("Account settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + P"
|
||||
description: qsTr("Plugin settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + N"
|
||||
description: qsTr("Open account creation wizard")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "F10"
|
||||
shortcut2: ""
|
||||
description: qsTr("Open keyboard shortcut table")
|
||||
}
|
||||
},
|
||||
ListModel {
|
||||
id: keyboardCallsShortcutsModel
|
||||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Y"
|
||||
description: qsTr("Answer an incoming call")
|
||||
description: qsTr("Answer incoming call")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + D"
|
||||
@ -159,7 +130,7 @@ Window {
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + D"
|
||||
description: qsTr("Decline the call request")
|
||||
description: qsTr("Decline call request")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "M"
|
||||
@ -174,6 +145,35 @@ Window {
|
||||
description: qsTr("Take tile screenshot")
|
||||
}
|
||||
},
|
||||
ListModel {
|
||||
id: keyboardSettingsShortcutsModel
|
||||
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Alt + I"
|
||||
description: qsTr("Open account settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + G"
|
||||
description: qsTr("Open general settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + M"
|
||||
description: qsTr("Open media settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + E"
|
||||
description: qsTr("Open extensions settings")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Ctrl + Shift + N"
|
||||
description: qsTr("Open account creation wizard")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "F10"
|
||||
shortcut2: ""
|
||||
description: qsTr("View keyboard shortcuts")
|
||||
}
|
||||
},
|
||||
ListModel {
|
||||
id: keyboardMarkdownShortcutsModel
|
||||
|
||||
@ -215,11 +215,11 @@ Window {
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Shift + Alt + T"
|
||||
description: qsTr("Show formatting")
|
||||
description: qsTr("Show/hide formatting")
|
||||
}
|
||||
ListElement {
|
||||
shortcut: "Shift + Alt + P"
|
||||
description: qsTr("Show preview")
|
||||
description: qsTr("Show preview/Continue editing")
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@ -61,17 +61,9 @@ Item {
|
||||
|
||||
opacity: 0
|
||||
|
||||
// (un)subscribe to an app-wide mouse move event trap filtered
|
||||
// for the overlay's geometry
|
||||
function setupFilter() {
|
||||
if (visible) {
|
||||
CallOverlayModel.registerFilter(appWindow, this);
|
||||
} else {
|
||||
CallOverlayModel.unregisterFilter(appWindow, this);
|
||||
}
|
||||
}
|
||||
Component.onCompleted: setupFilter()
|
||||
onVisibleChanged: setupFilter()
|
||||
Component.onCompleted: CallOverlayModel.setEventFilterActive(appWindow, this, true)
|
||||
Component.onDestruction: CallOverlayModel.setEventFilterActive(appWindow, this, false)
|
||||
onVisibleChanged: CallOverlayModel.setEventFilterActive(appWindow, this, visible)
|
||||
|
||||
Connections {
|
||||
target: CallOverlayModel
|
||||
|
||||
@ -30,11 +30,6 @@ import "../../commoncomponents"
|
||||
Rectangle {
|
||||
id: root
|
||||
|
||||
// Constraints for the preview component.
|
||||
property int previewMargin: 15
|
||||
property int previewMarginYTop: previewMargin + 42
|
||||
property int previewMarginYBottom: previewMargin + 84
|
||||
|
||||
property alias chatViewContainer: chatViewContainer
|
||||
property string callPreviewId
|
||||
|
||||
@ -166,222 +161,15 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
LocalVideo {
|
||||
// Note: this component should not be used within a layout, as
|
||||
// it implements anchor management itself.
|
||||
InCallLocalVideo {
|
||||
id: localPreview
|
||||
objectName: "localPreview"
|
||||
|
||||
readonly property var container: parent
|
||||
readonly property string callPreviewId: root.callPreviewId
|
||||
|
||||
visibilityCondition: (CurrentCall.isSharing || !CurrentCall.isVideoMuted) &&
|
||||
!CurrentCall.isConference
|
||||
height: width * invAspectRatio
|
||||
|
||||
// Keep the area of the preview a proportion of the screen size plus a
|
||||
// modifier to allow the user to scale it.
|
||||
readonly property real containerArea: container.width * container.height
|
||||
property real scalingFactor: 1
|
||||
width: Math.sqrt(containerArea / 16) * scalingFactor
|
||||
flip: CurrentCall.flipSelf && !CurrentCall.isSharing
|
||||
blurRadius: hidden ? 25 : 0
|
||||
onCallPreviewIdChanged: startWithId(callPreviewId)
|
||||
onVisibleChanged: if (!visible) stop()
|
||||
|
||||
anchors.topMargin: previewMarginYTop
|
||||
anchors.leftMargin: sideMargin
|
||||
anchors.rightMargin: sideMargin
|
||||
anchors.bottomMargin: previewMarginYBottom
|
||||
|
||||
opacity: hidden ? callOverlay.mainOverlayOpacity : 1
|
||||
|
||||
// Allow hiding the preview (available when anchored)
|
||||
readonly property bool hovered: hoverHandler.hovered
|
||||
readonly property bool anchored: state !== "unanchored"
|
||||
property bool hidden: false
|
||||
readonly property real hiddenHandleSize: 32
|
||||
// Compute the margin as a function of the preview width in order to
|
||||
// apply a negative margin and expose a constant width handle.
|
||||
// If not hidden, return the previewMargin.
|
||||
property real sideMargin: !hidden ? previewMargin : -(width - hiddenHandleSize)
|
||||
// Animate the hiddenSize with a Behavior.
|
||||
Behavior on sideMargin { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
readonly property bool onLeft: state.indexOf("left") !== -1
|
||||
PushButton {
|
||||
id: hidePreviewButton
|
||||
objectName: "hidePreviewButton"
|
||||
|
||||
width: localPreview.hiddenHandleSize
|
||||
state: {
|
||||
if (!localPreview.anchored) {
|
||||
return "none";
|
||||
}
|
||||
return localPreview.onLeft ?
|
||||
(localPreview.hidden ? "right" : "left") :
|
||||
(localPreview.hidden ? "left" : "right")
|
||||
}
|
||||
states: [
|
||||
State {
|
||||
name: "none"
|
||||
// Override visible to false when the localPreview isn't anchored.
|
||||
PropertyChanges {
|
||||
target: hidePreviewButton
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "left"
|
||||
AnchorChanges {
|
||||
target: hidePreviewButton
|
||||
anchors.left: parent.left
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "right"
|
||||
AnchorChanges {
|
||||
target: hidePreviewButton
|
||||
anchors.right: parent.right
|
||||
}
|
||||
}
|
||||
]
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
opacity: (localPreview.anchored && localPreview.hovered) || localPreview.hidden
|
||||
Behavior on opacity { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
visible: opacity > 0
|
||||
background: Rectangle {
|
||||
readonly property color normalColor: JamiTheme.mediumGrey
|
||||
color: JamiTheme.mediumGrey
|
||||
opacity: hidePreviewButton.hovered ? 0.7 : 0.5
|
||||
Behavior on opacity { NumberAnimation { duration: 250; easing.type: Easing.OutExpo }}
|
||||
}
|
||||
normalImageSource: hidePreviewButton.state === "left" ?
|
||||
JamiResources.chevron_left_black_24dp_svg :
|
||||
JamiResources.chevron_right_black_24dp_svg
|
||||
imageColor: JamiTheme.darkGreyColor
|
||||
onClicked: localPreview.hidden = !localPreview.hidden
|
||||
toolTipText: localPreview.hidden ?
|
||||
JamiStrings.showLocalVideo :
|
||||
JamiStrings.hideLocalVideo
|
||||
}
|
||||
|
||||
state: "anchor_top_right"
|
||||
states: [
|
||||
State {
|
||||
name: "unanchored"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: undefined
|
||||
anchors.right: undefined
|
||||
anchors.bottom: undefined
|
||||
anchors.left: undefined
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_top_left"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: localPreview.container.top
|
||||
anchors.left: localPreview.container.left
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_top_right"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.top: localPreview.container.top
|
||||
anchors.right: localPreview.container.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_bottom_right"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.bottom: localPreview.container.bottom
|
||||
anchors.right: localPreview.container.right
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "anchor_bottom_left"
|
||||
AnchorChanges {
|
||||
target: localPreview
|
||||
anchors.bottom: localPreview.container.bottom
|
||||
anchors.left: localPreview.container.left
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition {
|
||||
AnchorAnimation {
|
||||
duration: 250
|
||||
easing.type: Easing.OutBack
|
||||
easing.overshoot: 1.5
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
onWheel: function(event) {
|
||||
const delta = event.angleDelta.y / 120 * 0.1;
|
||||
parent.opacity = JamiQmlUtils.clamp(parent.opacity + delta, 0.25, 1);
|
||||
}
|
||||
acceptedModifiers: Qt.CTRL
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
onWheel: function(event) {
|
||||
const delta = event.angleDelta.y / 120 * 0.1;
|
||||
localPreview.scalingFactor = JamiQmlUtils.clamp(localPreview.scalingFactor + delta, 0.5, 4);
|
||||
}
|
||||
acceptedModifiers: Qt.NoModifier
|
||||
enabled: !localPreview.hidden
|
||||
}
|
||||
|
||||
DragHandler {
|
||||
id: dragHandler
|
||||
readonly property var container: localPreview.container
|
||||
target: parent
|
||||
dragThreshold: 4
|
||||
enabled: !localPreview.hidden
|
||||
xAxis.maximum: container.width - parent.width - previewMargin
|
||||
xAxis.minimum: previewMargin
|
||||
yAxis.maximum: container.height - parent.height - previewMarginYBottom
|
||||
yAxis.minimum: previewMarginYTop
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
localPreview.state = "unanchored";
|
||||
} else {
|
||||
const center = Qt.point(target.x + target.width / 2,
|
||||
target.y + target.height / 2);
|
||||
const containerCenter = Qt.point(container.x + container.width / 2,
|
||||
container.y + container.height / 2);
|
||||
if (center.x >= containerCenter.x) {
|
||||
if (center.y >= containerCenter.y) {
|
||||
localPreview.state = "anchor_bottom_right";
|
||||
} else {
|
||||
localPreview.state = "anchor_top_right";
|
||||
}
|
||||
} else {
|
||||
if (center.y >= containerCenter.y) {
|
||||
localPreview.state = "anchor_bottom_left";
|
||||
} else {
|
||||
localPreview.state = "anchor_top_left";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
width: localPreview.width
|
||||
height: localPreview.height
|
||||
radius: JamiTheme.primaryRadius
|
||||
}
|
||||
}
|
||||
container: parent
|
||||
rendererId: CurrentCall.previewId
|
||||
opacityModifier: callOverlay.mainOverlayOpacity
|
||||
}
|
||||
|
||||
CallOverlay {
|
||||
|
||||
@ -38,9 +38,9 @@ ItemDelegate {
|
||||
|
||||
highlighted: ListView.isCurrentItem
|
||||
property bool interactive: true
|
||||
property string lastInteractionDate: LastInteractionTimeStamp === undefined ? "" : LastInteractionTimeStamp
|
||||
|
||||
property string lastInteractionFormattedDate: MessagesAdapter.getBestFormattedDate(lastInteractionDate)
|
||||
property int lastInteractionTimeStamp: LastInteractionTimeStamp
|
||||
property string lastInteractionFormattedDate: MessagesAdapter.getBestFormattedDate(lastInteractionTimeStamp)
|
||||
|
||||
property bool showSharePositionIndicator: PositionManager.isPositionSharedToConv(accountId, UID)
|
||||
property bool showSharedPositionIndicator: PositionManager.isConvSharingPosition(accountId, UID)
|
||||
@ -58,7 +58,7 @@ ItemDelegate {
|
||||
Connections {
|
||||
target: MessagesAdapter
|
||||
function onTimestampUpdated() {
|
||||
lastInteractionFormattedDate = MessagesAdapter.getBestFormattedDate(lastInteractionDate);
|
||||
lastInteractionFormattedDate = MessagesAdapter.getBestFormattedDate(lastInteractionTimeStamp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -130,7 +130,7 @@ ItemDelegate {
|
||||
color: JamiTheme.textColor
|
||||
}
|
||||
RowLayout {
|
||||
visible: ContactType !== Profile.Type.TEMPORARY && !IsBanned && lastInteractionFormattedDate !== undefined && interactive
|
||||
visible: ContactType !== Profile.Type.TEMPORARY && !IsBanned && lastInteractionTimeStamp > 0 && interactive
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: 20
|
||||
Layout.alignment: Qt.AlignTop
|
||||
@ -138,7 +138,7 @@ ItemDelegate {
|
||||
// last Interaction date
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
text: lastInteractionFormattedDate === undefined ? "" : lastInteractionFormattedDate
|
||||
text: lastInteractionFormattedDate
|
||||
textFormat: TextEdit.PlainText
|
||||
font.pointSize: JamiTheme.smallFontSize
|
||||
font.weight: UnreadMessagesCount ? Font.DemiBold : Font.Normal
|
||||
|
||||
@ -30,7 +30,7 @@ ContextMenuAutoLoader {
|
||||
|
||||
property list<GeneralMenuItem> menuItems: [
|
||||
GeneralMenuItem {
|
||||
id: startVideoCallItem
|
||||
id: startVideoCall
|
||||
itemName: JamiStrings.startVideoCall
|
||||
canTrigger: ConversationsAdapter.dialogId(participantUri) !== ""
|
||||
iconSource: JamiResources.videocam_24dp_svg
|
||||
|
||||
@ -335,40 +335,6 @@ MessagesAdapter::onPaste()
|
||||
}
|
||||
}
|
||||
|
||||
QString
|
||||
MessagesAdapter::getStatusString(int status)
|
||||
{
|
||||
switch (static_cast<interaction::Status>(status)) {
|
||||
case interaction::Status::SENDING:
|
||||
return QObject::tr("Sending");
|
||||
case interaction::Status::FAILURE:
|
||||
return QObject::tr("Failure");
|
||||
case interaction::Status::SUCCESS:
|
||||
return QObject::tr("Sent");
|
||||
case interaction::Status::TRANSFER_CREATED:
|
||||
return QObject::tr("Connecting");
|
||||
case interaction::Status::TRANSFER_ACCEPTED:
|
||||
return QObject::tr("Accept");
|
||||
case interaction::Status::TRANSFER_CANCELED:
|
||||
return QObject::tr("Canceled");
|
||||
case interaction::Status::TRANSFER_ERROR:
|
||||
case interaction::Status::TRANSFER_UNJOINABLE_PEER:
|
||||
return QObject::tr("Unable to make contact");
|
||||
case interaction::Status::TRANSFER_ONGOING:
|
||||
return QObject::tr("Ongoing");
|
||||
case interaction::Status::TRANSFER_AWAITING_PEER:
|
||||
return QObject::tr("Waiting for contact");
|
||||
case interaction::Status::TRANSFER_AWAITING_HOST:
|
||||
return QObject::tr("Incoming transfer");
|
||||
case interaction::Status::TRANSFER_TIMEOUT_EXPIRED:
|
||||
return QObject::tr("Timed out waiting for contact");
|
||||
case interaction::Status::TRANSFER_FINISHED:
|
||||
return QObject::tr("Finished");
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap
|
||||
MessagesAdapter::getTransferStats(const QString& msgId, int status)
|
||||
{
|
||||
|
||||
@ -159,7 +159,6 @@ public:
|
||||
const QColor& linkColor = QColor(0x06, 0x45, 0xad),
|
||||
const QColor& backgroundColor = QColor(0x0, 0x0, 0x0));
|
||||
Q_INVOKABLE void onPaste();
|
||||
Q_INVOKABLE QString getStatusString(int status);
|
||||
Q_INVOKABLE QVariantMap getTransferStats(const QString& messageId, int);
|
||||
Q_INVOKABLE QVariant dataForInteraction(const QString& interactionId,
|
||||
int role = Qt::DisplayRole) const;
|
||||
|
||||
@ -47,7 +47,7 @@ Item {
|
||||
// AboutPopUp
|
||||
property string version: qsTr("Version") + (AppVersionManager.isCurrentVersionBeta() ? " (Beta)" : "")
|
||||
property string declarationYear: "© 2015-2024"
|
||||
property string slogan: "Eleutheria"
|
||||
property string slogan: "Astarte"
|
||||
property string declaration: qsTr('Jami, a GNU package, is software for universal and distributed peer-to-peer communication that respects the freedom and privacy of its users. Visit <a href="https://jami.net" style="color: ' + JamiTheme.buttonTintedBlue + '">jami.net</a>' + ' to learn more.')
|
||||
property string noWarranty: qsTr('This program comes with absolutely no warranty. See the <a href="https://www.gnu.org/licenses/gpl-3.0.html" style="color: ' + JamiTheme.buttonTintedBlue + '">GNU General Public License</a>, version 3 or later for details.')
|
||||
property string contribute: qsTr('Contribute')
|
||||
@ -73,7 +73,7 @@ Item {
|
||||
property string authenticate: qsTr("Authenticate")
|
||||
property string deleteAccount: qsTr("Delete account")
|
||||
property string inProgress: qsTr("In progress…")
|
||||
property string authenticationFailed: qsTr("Authentication failed")
|
||||
property string authenticationFailed: qsTr("An error occurred while authenticating account.")
|
||||
property string password: qsTr("Password")
|
||||
property string username: qsTr("Username")
|
||||
property string alias: qsTr("Alias")
|
||||
@ -181,7 +181,7 @@ Item {
|
||||
property string callSettingsTitle: qsTr("Call settings")
|
||||
property string chatSettingsTitle: qsTr("Chat")
|
||||
property string advancedSettingsTitle: qsTr("Advanced settings")
|
||||
property string audioVideoSettingsTitle: qsTr("Audio and Video")
|
||||
property string audioVideoSettingsTitle: qsTr("Media")
|
||||
|
||||
// AudioSettings
|
||||
property string audio: qsTr("Audio")
|
||||
@ -259,13 +259,13 @@ Item {
|
||||
property string paste: qsTr("Paste")
|
||||
|
||||
// ConversationContextMenu
|
||||
property string startVideoCall: qsTr("Start video call")
|
||||
property string startAudioCall: qsTr("Start audio call")
|
||||
property string startVideoCall: qsTr("Start video call")
|
||||
property string clearConversation: qsTr("Clear conversation")
|
||||
property string confirmAction: qsTr("Confirm action")
|
||||
property string removeConversation: qsTr("Remove conversation")
|
||||
property string confirmRmConversation: qsTr("Would you really like to remove this conversation?")
|
||||
property string confirmBlockConversation: qsTr("Would you really like to block this conversation?")
|
||||
property string removeConversation: qsTr("Leave conversation")
|
||||
property string confirmRmConversation: qsTr("Do you really want to leave this conversation?")
|
||||
property string confirmBlockConversation: qsTr("Do you really want to block this conversation?")
|
||||
property string removeContact: qsTr("Remove contact")
|
||||
property string blockContact: qsTr("Block contact")
|
||||
property string convDetails: qsTr("Conversation details")
|
||||
@ -294,7 +294,7 @@ Item {
|
||||
property string screenshotTaken: qsTr("Screenshot saved to %1")
|
||||
property string fileSaved: qsTr("File saved to %1")
|
||||
|
||||
//advanced information
|
||||
// Advanced information
|
||||
property string renderersInformation: qsTr("Renderers information")
|
||||
property string callInformation: qsTr("Call information")
|
||||
property string peerNumber: qsTr("Peer number")
|
||||
@ -338,17 +338,18 @@ Item {
|
||||
|
||||
// Chatview header
|
||||
property string hideChat: qsTr("Hide chat")
|
||||
property string placeAudioCall: qsTr("Place audio call")
|
||||
property string placeVideoCall: qsTr("Place video call")
|
||||
property string placeAudioCall: qsTr("Start audio call")
|
||||
property string placeVideoCall: qsTr("Start video call")
|
||||
property string showPlugins: qsTr("Show available plugins")
|
||||
property string addToConversations: qsTr("Add to conversations")
|
||||
property string backendError: qsTr("This is the error from the backend: %0")
|
||||
property string backendError: qsTr("A backend system error occurred: %0")
|
||||
property string disabledAccount: qsTr("The account is disabled")
|
||||
property string noNetworkConnectivity: qsTr("No network connectivity")
|
||||
property string deletedMessage: qsTr("deleted a message")
|
||||
property string backCall: qsTr("Back to Call")
|
||||
property string deletedMedia: qsTr("deleted a media")
|
||||
property string returnToCall: qsTr("Return to call")
|
||||
|
||||
//MessagesResearch
|
||||
// MessagesResearch
|
||||
property string jumpTo: qsTr("Jump to")
|
||||
property string messages: qsTr("Messages")
|
||||
property string files: qsTr("Files")
|
||||
@ -379,7 +380,7 @@ Item {
|
||||
property string invalidUsername: qsTr("Invalid username")
|
||||
property string nameAlreadyTaken: qsTr("Name already taken")
|
||||
property string usernameAlreadyTaken: qsTr("Username already taken")
|
||||
property string joinJamiNoPassword: qsTr("Are you sure you would like to join Jami without a username?\nIf yes, only a randomly generated 40-character identifier will be assigned to this account.")
|
||||
property string joinJamiNoPassword: qsTr("Do you really want to join Jami without a username?\nIf yes, only a randomly generated 40-character identifier will be assigned to this account.")
|
||||
property string usernameToolTip: qsTr("- 32 characters maximum\n- Alphabetical characters (A to Z and a to z)\n- Numeric characters (0 to 9)\n- Special characters allowed: dash (-)")
|
||||
|
||||
// Good to know
|
||||
@ -416,11 +417,11 @@ Item {
|
||||
|
||||
// CurrentAccountSettings && AdvancedSettings
|
||||
property string backupSuccessful: qsTr("Backup successful")
|
||||
property string backupFailed: qsTr("Backup failed")
|
||||
property string backupFailed: qsTr("An error occurred while backing up account.")
|
||||
property string changePasswordSuccess: qsTr("Password changed successfully")
|
||||
property string changePasswordFailed: qsTr("Password change failed")
|
||||
property string changePasswordFailed: qsTr("An error occurred while changing account password.")
|
||||
property string setPasswordSuccess: qsTr("Password set successfully")
|
||||
property string setPasswordFailed: qsTr("Password set failed")
|
||||
property string setPasswordFailed: qsTr("An error occurred while setting account password.")
|
||||
property string changePassword: qsTr("Change password")
|
||||
property string setPassword: qsTr("Encrypt account")
|
||||
property string setAPassword: qsTr("Set a password")
|
||||
@ -451,16 +452,16 @@ Item {
|
||||
property string tipLinkNewDevice: qsTr("Link a new device to this account")
|
||||
property string linkDevice: qsTr("Exporting account…")
|
||||
property string removeDevice: qsTr("Remove Device")
|
||||
property string sureToRemoveDevice: qsTr("Are you sure you wish to remove this device?")
|
||||
property string sureToRemoveDevice: qsTr("Do you really want to unlink selected device? To continue, enter account password and click Unlink.")
|
||||
property string yourPinIs: qsTr("Your PIN is:")
|
||||
property string linkDeviceNetWorkError: qsTr("Error connecting to the network.\nPlease try again later.")
|
||||
property string linkDeviceNetWorkError: qsTr("A network error occurred while linking device.\nPlease try again later.")
|
||||
|
||||
// BannedContacts
|
||||
property string banned: qsTr("Banned")
|
||||
property string bannedContacts: qsTr("Banned contacts")
|
||||
|
||||
// DeleteAccountDialog
|
||||
property string confirmDeleteQuestion: qsTr("Would you really like to delete this account?")
|
||||
property string confirmDeleteQuestion: qsTr("Do you really want to delete this account? To continue, click Delete.")
|
||||
property string deleteAccountInfos: qsTr("If your account has not been backed up or added to another device, your account and registered username will be IRREVOCABLY LOST.")
|
||||
|
||||
// DeviceItemDelegate
|
||||
@ -498,14 +499,14 @@ Item {
|
||||
// File transfer settings
|
||||
property string fileTransfer: qsTr("File transfer")
|
||||
property string autoAcceptFiles: qsTr("Automatically accept incoming files")
|
||||
property string acceptTransferBelow: qsTr("Accept transfer limit (in Mb)")
|
||||
property string acceptTransferTooltip: qsTr("in MB, 0 = unlimited")
|
||||
property string acceptTransferBelow: qsTr("Accept transfer limit (Mb)")
|
||||
property string acceptTransferTooltip: qsTr("MB, 0 = unlimited")
|
||||
|
||||
// JamiUserIdentity settings
|
||||
property string register: qsTr("Register")
|
||||
property string incorrectPassword: qsTr("Incorrect password")
|
||||
property string networkError: qsTr("Network error")
|
||||
property string somethingWentWrong: qsTr("Something went wrong")
|
||||
property string somethingWentWrong: qsTr("An unexpected error occurred.")
|
||||
|
||||
// Context Menu
|
||||
property string saveFile: qsTr("Save file")
|
||||
@ -518,9 +519,9 @@ Item {
|
||||
property string enableAutoUpdates: qsTr("Enable/Disable automatic updates")
|
||||
property string updatesTitle: qsTr("Updates")
|
||||
property string updateDialogTitle: qsTr("Update")
|
||||
property string updateFound: qsTr("A new version of Jami was found\nWould you like to update now?")
|
||||
property string updateFound: qsTr("A new version of Jami was found.\nDo you want to update Jami now?\nTo continue, click Update.")
|
||||
property string updateNotFound: qsTr("No new version of Jami was found")
|
||||
property string updateCheckError: qsTr("An error occured when checking for a new version")
|
||||
property string updateCheckError: qsTr("An error occurred when checking for a new version.")
|
||||
property string updateNetworkError: qsTr("Network error")
|
||||
property string updateSSLError: qsTr("SSL error")
|
||||
property string updateDownloadCanceled: qsTr("Installer download canceled")
|
||||
@ -529,9 +530,9 @@ Item {
|
||||
property string networkDisconnected: qsTr("Network disconnected")
|
||||
property string accessError: qsTr("Content access error")
|
||||
property string contentNotFoundError: qsTr("Content not found")
|
||||
property string genericError: qsTr("Something went wrong")
|
||||
property string genericError: qsTr("An unexpected error occurred.")
|
||||
|
||||
//Troubleshoot Settings
|
||||
// Troubleshoot Settings
|
||||
property string troubleshootTitle: qsTr("Troubleshoot")
|
||||
property string troubleshootButton: qsTr("Open logs")
|
||||
property string troubleshootText: qsTr("Get logs")
|
||||
@ -546,9 +547,9 @@ Item {
|
||||
property string callRecording: qsTr("Call recording")
|
||||
property string alwaysRecordCalls: qsTr("Always record calls")
|
||||
|
||||
// KeyboardShortCutTable
|
||||
property string keyboardShortcutTableWindowTitle: qsTr("Keyboard Shortcut Table")
|
||||
property string keyboardShortcuts: qsTr("Keyboard Shortcuts")
|
||||
// Keyboard shortcuts
|
||||
property string keyboardShortcutTableWindowTitle: qsTr("Keyboard shortcuts")
|
||||
property string keyboardShortcuts: qsTr("Keyboard shortcuts")
|
||||
property string conversationKeyboardShortcuts: qsTr("Conversation")
|
||||
property string callKeyboardShortcuts: qsTr("Call")
|
||||
property string settings: qsTr("Settings")
|
||||
@ -557,11 +558,11 @@ Item {
|
||||
// View Logs
|
||||
property string logsViewTitle: qsTr("Debug")
|
||||
property string logsViewCopy: qsTr("Copy")
|
||||
property string logsViewReport: qsTr("Report Bug")
|
||||
property string logsViewReport: qsTr("Submit issue")
|
||||
property string logsViewClear: qsTr("Clear")
|
||||
property string cancel: qsTr("Cancel")
|
||||
property string logsViewCopied: qsTr("Copied to clipboard!")
|
||||
property string logsViewDisplay: qsTr("Receive Logs")
|
||||
property string logsViewDisplay: qsTr("View logs")
|
||||
|
||||
// ImportFromBackupPage
|
||||
property string archive: qsTr("Archive")
|
||||
@ -618,7 +619,7 @@ Item {
|
||||
property string takePhoto: qsTr("Take photo")
|
||||
property string imageFiles: qsTr("Image Files (*.jpeg *.jpg *.png *.JPEG* .JPG *.PNG)")
|
||||
|
||||
// Plugins
|
||||
// Extensions
|
||||
property string autoUpdate: qsTr("Auto update")
|
||||
property string disableAll: qsTr("Disable all")
|
||||
property string installed: qsTr("Installed")
|
||||
@ -627,16 +628,16 @@ Item {
|
||||
property string installManually: qsTr("Install manually")
|
||||
property string installMannuallyDescription: qsTr("Install an extension directly from your device.")
|
||||
property string pluginStoreTitle: qsTr("Available")
|
||||
property string pluginStoreNotAvailable: qsTr("Plugins store is not available")
|
||||
property string storeNotSupportedPlatform: qsTr("The Jami Extension Store currently has no extension available for the platform in use. Check again later!")
|
||||
property string pluginStoreNotAvailable: qsTr("The Jami Extension Store is not currently available. Please try again later.")
|
||||
property string storeNotSupportedPlatform: qsTr("There are no extensions currently available in the Jami Extension Store for the platform in use. Please check again later.")
|
||||
property string pluginPreferences: qsTr("Preferences")
|
||||
property string installationFailed: qsTr("Installation failed")
|
||||
property string pluginInstallationFailed: qsTr("The installation of the plugin failed")
|
||||
property string installationFailed: qsTr("Installation error")
|
||||
property string pluginInstallationFailed: qsTr("An error occurred while installing the extension.")
|
||||
property string reset: qsTr("Reset")
|
||||
property string uninstall: qsTr("Uninstall")
|
||||
property string resetPreferences: qsTr("Reset Preferences")
|
||||
property string selectPluginInstall: qsTr("Select a plugin to install")
|
||||
property string uninstallPlugin: qsTr("Uninstall plugin")
|
||||
property string selectPluginInstall: qsTr("Select extension to install")
|
||||
property string uninstallPlugin: qsTr("Uninstall extension")
|
||||
property string pluginResetConfirmation: qsTr("Are you sure you wish to reset %1 preferences?")
|
||||
property string pluginUninstallConfirmation: qsTr("Are you sure you wish to uninstall %1?")
|
||||
property string goBackToPluginsList: qsTr("Go back to plugins list")
|
||||
@ -653,6 +654,7 @@ Item {
|
||||
property string lastUpdate: qsTr("Last update %1")
|
||||
property string by: qsTr("By %1")
|
||||
property string proposedBy: qsTr("Proposed by %1")
|
||||
|
||||
// ProfilePage
|
||||
property string information: qsTr("Information")
|
||||
property string moreInformation: qsTr("More information")
|
||||
@ -662,9 +664,9 @@ Item {
|
||||
property string confirmRemovalRequest: qsTr("Enter the account password to confirm the removal of this device")
|
||||
|
||||
// SelectScreen
|
||||
property string selectScreen: qsTr("Select a screen to share")
|
||||
property string selectWindow: qsTr("Select a window to share")
|
||||
property string allScreens: qsTr("All Screens")
|
||||
property string selectScreen: qsTr("Select screen to share")
|
||||
property string selectWindow: qsTr("Select window to share")
|
||||
property string allScreens: qsTr("All screens")
|
||||
property string screens: qsTr("Screens")
|
||||
property string windows: qsTr("Windows")
|
||||
property string screen: qsTr("Screen %1")
|
||||
@ -682,7 +684,7 @@ Item {
|
||||
property string connectJAMSServer: qsTr("Connect to a JAMS server")
|
||||
property string createFromJAMS: qsTr("Create account from Jami Account Management Server (JAMS)")
|
||||
property string addSIPAccount: qsTr("Configure a SIP account")
|
||||
property string errorCreateAccount: qsTr("Error while creating your account. Check your credentials.")
|
||||
property string errorCreateAccount: qsTr("An error occurred while creating the account. Check check credentials and try again.")
|
||||
property string createNewRV: qsTr("Create a rendezvous point")
|
||||
property string joinJami: qsTr("Join Jami")
|
||||
property string createNewJamiAccount: qsTr("Create new Jami account")
|
||||
@ -694,9 +696,9 @@ Item {
|
||||
property string welcomeToJami: qsTr("Welcome to Jami")
|
||||
|
||||
// SmartList
|
||||
property string clearText: qsTr("Clear Text")
|
||||
property string clearText: qsTr("Clear text")
|
||||
property string conversations: qsTr("Conversations")
|
||||
property string searchResults: qsTr("Search Results")
|
||||
property string searchResults: qsTr("Search results")
|
||||
|
||||
// SmartList context menu
|
||||
property string declineContactRequest: qsTr("Decline contact request")
|
||||
@ -706,13 +708,14 @@ Item {
|
||||
property string update: qsTr("Automatically check for updates")
|
||||
|
||||
// Generic dialog options
|
||||
property string optionOk: qsTr("Ok")
|
||||
property string optionOk: qsTr("OK")
|
||||
property string optionSave: qsTr("Save")
|
||||
property string optionCancel: qsTr("Cancel")
|
||||
property string optionUpgrade: qsTr("Upgrade")
|
||||
property string optionLater: qsTr("Later")
|
||||
property string optionDelete: qsTr("Delete")
|
||||
property string optionRemove: qsTr("Remove")
|
||||
property string optionLeave: qsTr("Leave")
|
||||
property string optionBlock: qsTr("Block")
|
||||
property string optionUnban: qsTr("Unban")
|
||||
|
||||
@ -735,8 +738,8 @@ Item {
|
||||
property string removeDefaultModerator: qsTr("Remove default moderator")
|
||||
|
||||
// Daemon reconnection
|
||||
property string reconnectDaemon: qsTr("Trying to reconnect to the Jami daemon (jamid)…")
|
||||
property string reconnectionFailed: qsTr("Could not re-connect to the Jami daemon (jamid).\nJami will now quit.")
|
||||
property string reconnectDaemon: qsTr("Attempting to reconnect to the Jami daemon (jamid)…")
|
||||
property string reconnectionFailed: qsTr("An error occurred while reconnecting to the Jami daemon (jamid).\nThe application will now exit.")
|
||||
|
||||
// Message view
|
||||
property string addEmoji: qsTr("Add emoji")
|
||||
@ -791,23 +794,23 @@ Item {
|
||||
|
||||
// Invitation View
|
||||
property string invitationViewSentRequest: qsTr("%1 has sent you a request for a conversation.")
|
||||
property string invitationViewJoinConversation: qsTr("Hello,\nWould you like to join the conversation?")
|
||||
property string invitationViewJoinConversation: qsTr("Hello,\nDo you want to join this conversation?")
|
||||
property string invitationViewAcceptedConversation: qsTr("You have accepted\nthe conversation request")
|
||||
property string invitationViewWaitingForSync: qsTr("Waiting until %1\nconnects to synchronize the conversation.")
|
||||
|
||||
// SwarmDetailsPanel
|
||||
property string members: qsTr("%1 Members")
|
||||
property string member: qsTr("Member")
|
||||
property string swarmName: qsTr("Swarm's name")
|
||||
property string contactName: qsTr("Contact's name")
|
||||
property string swarmName: qsTr("Swarm name")
|
||||
property string contactName: qsTr("Contact name")
|
||||
property string addADescription: qsTr("Add a description")
|
||||
|
||||
property string muteConversation: qsTr("Mute conversation")
|
||||
property string ignoreNotificationsTooltip: qsTr("Ignore all notifications from this conversation")
|
||||
property string chooseAColor: qsTr("Choose a color")
|
||||
property string chooseAColor: qsTr("Color")
|
||||
property string defaultCallHost: qsTr("Default host (calls)")
|
||||
property string leaveConversation: qsTr("Leave conversation")
|
||||
property string typeOfSwarm: qsTr("Type of swarm")
|
||||
property string typeOfSwarm: qsTr("Type")
|
||||
property string none: qsTr("None")
|
||||
|
||||
// NewSwarmPage
|
||||
@ -864,20 +867,20 @@ Item {
|
||||
property string theme: qsTr("Theme")
|
||||
property string zoomLevel: qsTr("Text zoom level")
|
||||
|
||||
//Donation campaign
|
||||
// Donation campaign
|
||||
property string donationTipBoxText: qsTr("Free and private sharing. <a href=\"https://jami.net/whydonate/\">Donate</a> to expand it.")
|
||||
property string donation: qsTr("Donate")
|
||||
property string donationText: qsTr("If you enjoy using Jami and believe in our mission, would you make a donation?")
|
||||
property string notNow: qsTr("Not now")
|
||||
property string enableDonation: qsTr("Enable donation campaign")
|
||||
|
||||
//Chat setting page
|
||||
// Chat setting page
|
||||
property string enter: qsTr("Enter")
|
||||
property string shiftEnter: qsTr("Shift+Enter")
|
||||
property string textFormattingDescription: qsTr("Enter or Shift+Enter to insert a new line")
|
||||
property string textFormatting: qsTr("Text formatting")
|
||||
|
||||
//Connection monitoring
|
||||
// Connection monitoring
|
||||
property string connected: qsTr("Connected")
|
||||
property string connectingTLS: qsTr("Connecting TLS")
|
||||
property string connectingICE: qsTr("Connecting ICE")
|
||||
|
||||
520
src/app/screencastportal.cpp
Normal file
520
src/app/screencastportal.cpp
Normal file
@ -0,0 +1,520 @@
|
||||
/*!
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "screencastportal.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <unistd.h>
|
||||
|
||||
#define REQUEST_PATH "/org/freedesktop/portal/desktop/request/%s/%s"
|
||||
|
||||
/*
|
||||
* PipeWire supported cursor modes
|
||||
*/
|
||||
enum PortalCursorMode {
|
||||
PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
|
||||
PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
|
||||
PORTAL_CURSOR_MODE_METADATA = 1 << 2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function to allow getPipewireFd to stop and return an error
|
||||
* code if a DBus operation/callback fails.
|
||||
*/
|
||||
void
|
||||
ScreenCastPortal::abort(int error, const char* message)
|
||||
{
|
||||
portal_error = error;
|
||||
qWarning() << "Aborting:" << message;
|
||||
|
||||
if (glib_main_loop && g_main_loop_is_running(glib_main_loop)) {
|
||||
g_main_loop_quit(glib_main_loop);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Callback to free a DbusCallData object's memory and unsubscribe from the
|
||||
* associated dbus signal.
|
||||
*/
|
||||
void
|
||||
ScreenCastPortal::dbusCallDataFree(DbusCallData* ptr_dbus_call_data)
|
||||
{
|
||||
if (!ptr_dbus_call_data)
|
||||
return;
|
||||
|
||||
if (ptr_dbus_call_data->signal_id)
|
||||
g_dbus_connection_signal_unsubscribe(ptr_dbus_call_data->portal->connection,
|
||||
ptr_dbus_call_data->signal_id);
|
||||
|
||||
g_clear_pointer(&ptr_dbus_call_data->request_path, g_free);
|
||||
}
|
||||
|
||||
DbusCallData*
|
||||
ScreenCastPortal::subscribeToSignal(const char* path, GDBusSignalCallback callback)
|
||||
{
|
||||
DbusCallData* ptr_dbus_call_data = new DbusCallData;
|
||||
|
||||
ptr_dbus_call_data->portal = this;
|
||||
ptr_dbus_call_data->request_path = g_strdup(path);
|
||||
ptr_dbus_call_data->signal_id
|
||||
= g_dbus_connection_signal_subscribe(connection,
|
||||
"org.freedesktop.portal.Desktop" /*sender*/,
|
||||
"org.freedesktop.portal.Request" /*interface_name*/,
|
||||
"Response" /*member: dbus signal name*/,
|
||||
ptr_dbus_call_data->request_path /*object_path*/,
|
||||
NULL,
|
||||
G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
|
||||
callback,
|
||||
ptr_dbus_call_data,
|
||||
NULL);
|
||||
return ptr_dbus_call_data;
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::openPipewireRemote()
|
||||
{
|
||||
GUnixFDList* fd_list = NULL;
|
||||
GVariant* result = NULL;
|
||||
GError* error = NULL;
|
||||
int fd_index;
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
|
||||
result = g_dbus_proxy_call_with_unix_fd_list_sync(proxy,
|
||||
"OpenPipeWireRemote",
|
||||
g_variant_new("(oa{sv})",
|
||||
session_handle,
|
||||
&builder),
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&fd_list,
|
||||
NULL,
|
||||
&error);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
g_variant_get(result, "(h)", &fd_index);
|
||||
g_variant_unref(result);
|
||||
|
||||
pipewireFd = g_unix_fd_list_get(fd_list, fd_index, &error);
|
||||
g_object_unref(fd_list);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
g_main_loop_quit(glib_main_loop);
|
||||
return;
|
||||
|
||||
fail:
|
||||
qWarning() << "Error retrieving PipeWire fd:" << error->message;
|
||||
g_error_free(error);
|
||||
abort(EIO, "Failed to open PipeWire remote");
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::onStartResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
GVariant* stream_properties = NULL;
|
||||
GVariant* streams = NULL;
|
||||
GVariant* result = NULL;
|
||||
GVariantIter iter;
|
||||
uint32_t response;
|
||||
|
||||
DbusCallData* ptr_dbus_call_data = (DbusCallData*) user_data;
|
||||
ScreenCastPortal* portal = ptr_dbus_call_data->portal;
|
||||
|
||||
g_clear_pointer(&ptr_dbus_call_data, dbusCallDataFree);
|
||||
|
||||
g_variant_get(parameters, "(u@a{sv})", &response, &result);
|
||||
|
||||
if (response) {
|
||||
g_variant_unref(result);
|
||||
portal->abort(EACCES, "Failed to start screencast, denied or cancelled by user");
|
||||
return;
|
||||
}
|
||||
|
||||
streams = g_variant_lookup_value(result, "streams", G_VARIANT_TYPE_ARRAY);
|
||||
|
||||
g_variant_iter_init(&iter, streams);
|
||||
|
||||
g_variant_iter_loop(&iter, "(u@a{sv})", &portal->pipewireNode, &stream_properties);
|
||||
|
||||
qInfo() << "Monitor selected, setting up screencast\n";
|
||||
|
||||
g_variant_unref(result);
|
||||
g_variant_unref(streams);
|
||||
g_variant_unref(stream_properties);
|
||||
|
||||
portal->openPipewireRemote();
|
||||
}
|
||||
|
||||
int
|
||||
ScreenCastPortal::callDBusMethod(const gchar* method_name, GVariant* parameters)
|
||||
{
|
||||
GVariant* result;
|
||||
GError* error = NULL;
|
||||
|
||||
result = g_dbus_proxy_call_sync(proxy,
|
||||
method_name,
|
||||
parameters,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
&error);
|
||||
if (error) {
|
||||
qWarning() << "Call to DBus method" << method_name << "failed:" << error->message;
|
||||
g_error_free(error);
|
||||
return EIO;
|
||||
}
|
||||
g_variant_unref(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::start()
|
||||
{
|
||||
int ret;
|
||||
const char* request_token;
|
||||
g_autofree char* request_path;
|
||||
GVariantBuilder builder;
|
||||
GVariant* parameters;
|
||||
struct DbusCallData* ptr_dbus_call_data;
|
||||
|
||||
request_token = "pipewiregrabStart";
|
||||
request_path = g_strdup_printf(REQUEST_PATH, sender_name, request_token);
|
||||
|
||||
qInfo() << "Asking for monitor...";
|
||||
|
||||
ptr_dbus_call_data = subscribeToSignal(request_path, onStartResponseReceivedCallback);
|
||||
if (!ptr_dbus_call_data) {
|
||||
abort(ENOMEM, "Failed to allocate DBus call data");
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(request_token));
|
||||
parameters = g_variant_new("(osa{sv})", session_handle, "", &builder);
|
||||
|
||||
ret = callDBusMethod("Start", parameters);
|
||||
if (ret != 0)
|
||||
abort(ret, "Failed to start screen cast session");
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::onSelectSourcesResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
GVariant* ret = NULL;
|
||||
uint32_t response;
|
||||
struct DbusCallData* ptr_dbus_call_data = (DbusCallData*) user_data;
|
||||
ScreenCastPortal* portal = ptr_dbus_call_data->portal;
|
||||
|
||||
g_clear_pointer(&ptr_dbus_call_data, dbusCallDataFree);
|
||||
|
||||
g_variant_get(parameters, "(u@a{sv})", &response, &ret);
|
||||
g_variant_unref(ret);
|
||||
if (response) {
|
||||
portal->abort(EACCES, "Failed to select screencast sources, denied or cancelled by user");
|
||||
return;
|
||||
}
|
||||
|
||||
portal->start();
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::selectSources()
|
||||
{
|
||||
int ret;
|
||||
const char* request_token;
|
||||
g_autofree char* request_path;
|
||||
GVariantBuilder builder;
|
||||
GVariant* parameters;
|
||||
struct DbusCallData* ptr_dbus_call_data;
|
||||
|
||||
request_token = "pipewiregrabSelectSources";
|
||||
request_path = g_strdup_printf(REQUEST_PATH, sender_name, request_token);
|
||||
|
||||
ptr_dbus_call_data = subscribeToSignal(request_path, onSelectSourcesResponseReceivedCallback);
|
||||
if (!ptr_dbus_call_data) {
|
||||
abort(ENOMEM, "Failed to allocate DBus call data");
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add(&builder, "{sv}", "types", g_variant_new_uint32(capture_type));
|
||||
g_variant_builder_add(&builder, "{sv}", "multiple", g_variant_new_boolean(FALSE));
|
||||
g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(request_token));
|
||||
|
||||
if ((available_cursor_modes & PORTAL_CURSOR_MODE_EMBEDDED) && draw_mouse)
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
"cursor_mode",
|
||||
g_variant_new_uint32(PORTAL_CURSOR_MODE_EMBEDDED));
|
||||
else
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
"cursor_mode",
|
||||
g_variant_new_uint32(PORTAL_CURSOR_MODE_HIDDEN));
|
||||
parameters = g_variant_new("(oa{sv})", session_handle, &builder);
|
||||
|
||||
ret = callDBusMethod("SelectSources", parameters);
|
||||
if (ret != 0)
|
||||
abort(ret, "Failed to select sources for screen cast session");
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::onCreateSessionResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data)
|
||||
{
|
||||
uint32_t response;
|
||||
GVariant* result = NULL;
|
||||
DbusCallData* ptr_dbus_call_data = (DbusCallData*) user_data;
|
||||
ScreenCastPortal* portal = ptr_dbus_call_data->portal;
|
||||
|
||||
g_clear_pointer(&ptr_dbus_call_data, dbusCallDataFree);
|
||||
|
||||
g_variant_get(parameters, "(u@a{sv})", &response, &result);
|
||||
|
||||
if (response != 0) {
|
||||
g_variant_unref(result);
|
||||
portal->abort(EACCES, "Failed to create screencast session, denied or cancelled by user");
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Screencast session created";
|
||||
|
||||
g_variant_lookup(result, "session_handle", "s", &portal->session_handle);
|
||||
g_variant_unref(result);
|
||||
|
||||
portal->selectSources();
|
||||
}
|
||||
|
||||
void
|
||||
ScreenCastPortal::createSession()
|
||||
{
|
||||
int ret;
|
||||
GVariantBuilder builder;
|
||||
GVariant* parameters;
|
||||
const char* request_token;
|
||||
g_autofree char* request_path;
|
||||
DbusCallData* ptr_dbus_call_data;
|
||||
|
||||
request_token = "pipewiregrabCreateSession";
|
||||
request_path = g_strdup_printf(REQUEST_PATH, sender_name, request_token);
|
||||
|
||||
ptr_dbus_call_data = subscribeToSignal(request_path, onCreateSessionResponseReceivedCallback);
|
||||
if (!ptr_dbus_call_data) {
|
||||
abort(ENOMEM, "Failed to allocate DBus call data");
|
||||
return;
|
||||
}
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
|
||||
g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(request_token));
|
||||
g_variant_builder_add(&builder,
|
||||
"{sv}",
|
||||
"session_handle_token",
|
||||
g_variant_new_string("pipewiregrab"));
|
||||
parameters = g_variant_new("(a{sv})", &builder);
|
||||
|
||||
ret = callDBusMethod("CreateSession", parameters);
|
||||
if (ret != 0)
|
||||
abort(ret, "Failed to create screen cast session");
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function: get available cursor modes and update the
|
||||
* PipewireGrabContext accordingly
|
||||
*/
|
||||
void
|
||||
ScreenCastPortal::updateAvailableCursorModes()
|
||||
{
|
||||
GVariant* cached_cursor_modes = NULL;
|
||||
|
||||
cached_cursor_modes = g_dbus_proxy_get_cached_property(proxy, "AvailableCursorModes");
|
||||
available_cursor_modes = cached_cursor_modes ? g_variant_get_uint32(cached_cursor_modes) : 0;
|
||||
|
||||
// Only use embedded or hidden mode for now
|
||||
available_cursor_modes &= PORTAL_CURSOR_MODE_EMBEDDED | PORTAL_CURSOR_MODE_HIDDEN;
|
||||
|
||||
g_variant_unref(cached_cursor_modes);
|
||||
}
|
||||
|
||||
int
|
||||
ScreenCastPortal::createDBusProxy()
|
||||
{
|
||||
GError* error = NULL;
|
||||
|
||||
proxy = g_dbus_proxy_new_sync(connection,
|
||||
G_DBUS_PROXY_FLAGS_NONE,
|
||||
NULL,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
"/org/freedesktop/portal/desktop",
|
||||
"org.freedesktop.portal.ScreenCast",
|
||||
NULL,
|
||||
&error);
|
||||
if (error) {
|
||||
qWarning() << "Error creating proxy:" << error->message;
|
||||
g_error_free(error);
|
||||
return EPERM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create DBus connection and related objects
|
||||
*/
|
||||
int
|
||||
ScreenCastPortal::createDBusConnection()
|
||||
{
|
||||
char* aux;
|
||||
GError* error = NULL;
|
||||
|
||||
connection = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
|
||||
if (error) {
|
||||
qWarning() << "Error getting session bus:" << error->message;
|
||||
g_error_free(error);
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
sender_name = g_strdup(g_dbus_connection_get_unique_name(connection) + 1);
|
||||
while ((aux = g_strstr_len(sender_name, -1, ".")) != NULL)
|
||||
*aux = '_';
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Use XDG Desktop Portal's ScreenCast interface to open a file descriptor that
|
||||
* can be used by PipeWire to access the screen cast streams.
|
||||
* (https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.ScreenCast.html)
|
||||
*/
|
||||
int
|
||||
ScreenCastPortal::getPipewireFd()
|
||||
{
|
||||
int ret = 0;
|
||||
GMainContext* glib_main_context;
|
||||
|
||||
// Create a new GLib context and set it as the default for the current thread.
|
||||
// This ensures that the callbacks from DBus operations started in this thread are
|
||||
// handled by the GLib main loop defined below, even if pipewiregrab_init was
|
||||
// called by a program which also uses GLib and already had its own main loop running.
|
||||
glib_main_context = g_main_context_new();
|
||||
g_main_context_push_thread_default(glib_main_context);
|
||||
glib_main_loop = g_main_loop_new(glib_main_context, FALSE);
|
||||
if (!glib_main_loop) {
|
||||
qWarning() << "g_main_loop_new failed!";
|
||||
ret = ENOMEM;
|
||||
}
|
||||
|
||||
ret = createDBusConnection();
|
||||
if (ret != 0)
|
||||
goto exit_glib_loop;
|
||||
|
||||
ret = createDBusProxy();
|
||||
if (ret != 0)
|
||||
goto exit_glib_loop;
|
||||
|
||||
updateAvailableCursorModes();
|
||||
createSession();
|
||||
if (portal_error) {
|
||||
ret = portal_error;
|
||||
goto exit_glib_loop;
|
||||
}
|
||||
|
||||
g_main_loop_run(glib_main_loop);
|
||||
// The main loop will run until it's stopped by openPipewireRemote (if
|
||||
// all DBus method calls were successfully), abort (in case of error) or
|
||||
// on_cancelled_callback (if a DBus request is cancelled).
|
||||
// In the latter two cases, pw_ctx->portal_error gets set to a nonzero value.
|
||||
if (portal_error)
|
||||
ret = portal_error;
|
||||
|
||||
exit_glib_loop:
|
||||
g_main_loop_unref(glib_main_loop);
|
||||
glib_main_loop = NULL;
|
||||
g_main_context_pop_thread_default(glib_main_context);
|
||||
g_main_context_unref(glib_main_context);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
ScreenCastPortal::ScreenCastPortal(PortalCaptureType captureType)
|
||||
: draw_mouse(true)
|
||||
, pipewireFd(0)
|
||||
{
|
||||
switch (captureType) {
|
||||
case PortalCaptureType::SCREEN:
|
||||
capture_type = 1;
|
||||
break;
|
||||
case PortalCaptureType::WINDOW:
|
||||
capture_type = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ScreenCastPortal::~ScreenCastPortal()
|
||||
{
|
||||
if (session_handle) {
|
||||
g_dbus_connection_call(connection,
|
||||
"org.freedesktop.portal.Desktop",
|
||||
session_handle,
|
||||
"org.freedesktop.portal.Session",
|
||||
"Close",
|
||||
NULL,
|
||||
NULL,
|
||||
G_DBUS_CALL_FLAGS_NONE,
|
||||
-1,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
g_clear_pointer(&session_handle, g_free);
|
||||
}
|
||||
g_clear_object(&connection);
|
||||
g_clear_object(&proxy);
|
||||
g_clear_pointer(&sender_name, g_free);
|
||||
|
||||
#ifndef ENABLE_LIBWRAP
|
||||
// If the daemon is running as a separate process, then it can't directly use the
|
||||
// PipeWire file descriptor opened by the client, so it will have to duplicate it.
|
||||
// The duplicated file descriptor will be closed by the daemon, but the original
|
||||
// file descriptor needs to be closed by the client.
|
||||
if (close(pipewireFd) != 0) {
|
||||
int err = errno;
|
||||
qWarning() << "Error while attempting to close PipeWire file descriptor: errno =" << err;
|
||||
} else {
|
||||
qInfo() << "Successfully closed PipeWire file descriptor";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
102
src/app/screencastportal.h
Normal file
102
src/app/screencastportal.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*!
|
||||
* Copyright (C) 2024 Savoir-faire Linux Inc.
|
||||
*
|
||||
* 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
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <cstdint>
|
||||
#include <gio/gio.h>
|
||||
#include <gio/gunixfdlist.h>
|
||||
|
||||
enum class PortalCaptureType {
|
||||
SCREEN = 1,
|
||||
WINDOW = 2,
|
||||
};
|
||||
|
||||
struct DbusCallData;
|
||||
|
||||
class ScreenCastPortal
|
||||
{
|
||||
public:
|
||||
ScreenCastPortal(PortalCaptureType captureType);
|
||||
~ScreenCastPortal();
|
||||
int getPipewireFd();
|
||||
int pipewireFd;
|
||||
uint32_t pipewireNode = 0;
|
||||
QString videoInputId;
|
||||
|
||||
private:
|
||||
void createSession();
|
||||
void selectSources();
|
||||
void start();
|
||||
void openPipewireRemote();
|
||||
void abort(int error, const char* message);
|
||||
|
||||
static void onCreateSessionResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data);
|
||||
static void onSelectSourcesResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data);
|
||||
static void onStartResponseReceivedCallback(GDBusConnection* connection,
|
||||
const char* sender_name,
|
||||
const char* object_path,
|
||||
const char* interface_name,
|
||||
const char* signal_name,
|
||||
GVariant* parameters,
|
||||
gpointer user_data);
|
||||
|
||||
int callDBusMethod(const gchar* method_name, GVariant* parameters);
|
||||
int createDBusProxy();
|
||||
int createDBusConnection();
|
||||
void updateAvailableCursorModes();
|
||||
DbusCallData* subscribeToSignal(const char* path, GDBusSignalCallback callback);
|
||||
static void dbusCallDataFree(DbusCallData* ptr_dbus_call_data);
|
||||
|
||||
GDBusConnection* connection = nullptr;
|
||||
GDBusProxy* proxy = nullptr;
|
||||
|
||||
char* sender_name = nullptr;
|
||||
char* session_handle = nullptr;
|
||||
|
||||
uint32_t available_cursor_modes = 0;
|
||||
|
||||
GMainLoop* glib_main_loop = nullptr;
|
||||
struct pw_thread_loop* thread_loop = nullptr;
|
||||
struct pw_context* context = nullptr;
|
||||
|
||||
guint32 capture_type;
|
||||
|
||||
bool draw_mouse;
|
||||
|
||||
int portal_error = 0;
|
||||
};
|
||||
|
||||
struct DbusCallData
|
||||
{
|
||||
ScreenCastPortal* portal;
|
||||
char* request_path;
|
||||
guint signal_id;
|
||||
};
|
||||
@ -18,6 +18,8 @@
|
||||
|
||||
#include "screensaver.h"
|
||||
|
||||
#include "global.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
ScreenSaver::ScreenSaver(QObject* parent)
|
||||
@ -31,8 +33,7 @@ ScreenSaver::ScreenSaver(QObject* parent)
|
||||
}
|
||||
#else
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
{}
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
@ -50,7 +51,7 @@ ScreenSaver::createInterface(void)
|
||||
services_[i],
|
||||
sessionBus_);
|
||||
if (screenSaverInterface_ && screenSaverInterface_->isValid()) {
|
||||
qDebug() << "Screen saver dbus interface: " << services_[i];
|
||||
C_INFO << "Screen saver dbus interface: " << services_[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ BaseModalDialog {
|
||||
pinRectangle.visible = true
|
||||
exportedPIN.text = pin;
|
||||
} else {
|
||||
pinRectangle.success = false;
|
||||
infoLabel.success = false;
|
||||
infoLabel.visible = true;
|
||||
switch (status) {
|
||||
case NameDirectory.ExportOnRingStatus.WRONG_PASSWORD:
|
||||
@ -274,7 +274,7 @@ BaseModalDialog {
|
||||
height: qrImage.height + 4
|
||||
anchors.centerIn: parent
|
||||
radius: 5
|
||||
color: JamiTheme.darkTheme ? JamiTheme.whiteColor : JamiTheme.jamiButtonBorderColor
|
||||
color: JamiTheme.whiteColor
|
||||
Image {
|
||||
id: qrImage
|
||||
anchors.centerIn: parent
|
||||
|
||||
@ -101,8 +101,7 @@ SettingsPageBase {
|
||||
id: closeOrMinCheckBox
|
||||
Layout.fillWidth: true
|
||||
|
||||
visible: UtilsAdapter.isSystemTrayIconVisible()
|
||||
checked: UtilsAdapter.getAppValue(Settings.MinimizeOnClose) && UtilsAdapter.isSystemTrayIconVisible()
|
||||
checked: UtilsAdapter.getAppValue(Settings.MinimizeOnClose)
|
||||
labelText: JamiStrings.keepMinimized
|
||||
onSwitchToggled: UtilsAdapter.setAppValue(Settings.Key.MinimizeOnClose, checked)
|
||||
}
|
||||
|
||||
@ -80,13 +80,10 @@ SettingsPageBase {
|
||||
Component.onCompleted: {
|
||||
flipControl.checked = UtilsAdapter.getAppValue(Settings.FlipSelf);
|
||||
hardwareAccelControl.checked = AvAdapter.getHardwareAcceleration();
|
||||
if (previewWidget.visible)
|
||||
startPreviewing(true);
|
||||
startPreviewing(true);
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
previewWidget.startWithId("");
|
||||
}
|
||||
Component.onDestruction: previewWidget.stop()
|
||||
|
||||
// video Preview
|
||||
Rectangle {
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "systemtray.h"
|
||||
|
||||
#include "appsettingsmanager.h"
|
||||
#include "global.h"
|
||||
|
||||
#ifdef USE_LIBNOTIFY
|
||||
#include <libnotify/notification.h>
|
||||
@ -106,8 +107,8 @@ SystemTray::SystemTray(AppSettingsManager* settingsManager, QObject* parent)
|
||||
char* spec = nullptr;
|
||||
|
||||
if (notify_get_server_info(&name, &vendor, &version, &spec)) {
|
||||
qDebug() << QString("notify server name: %1, vendor: %2, version: %3, spec: %4")
|
||||
.arg(name, vendor, version, spec);
|
||||
C_INFO << QString("notify server name: %1, vendor: %2, version: %3, spec: %4")
|
||||
.arg(name, vendor, version, spec);
|
||||
}
|
||||
|
||||
// check notify server capabilities
|
||||
@ -167,7 +168,7 @@ SystemTray::hideNotification(const QString& id)
|
||||
// Close
|
||||
GError* error = nullptr;
|
||||
if (!notify_notification_close(notification->second.nn.get(), &error)) {
|
||||
qWarning("could not close notification: %s", error->message);
|
||||
C_WARN << QString("could not close notification: %1").arg(error->message);
|
||||
g_clear_error(&error);
|
||||
return false;
|
||||
}
|
||||
@ -235,7 +236,7 @@ SystemTray::showNotification(const QString& id,
|
||||
GError* error = nullptr;
|
||||
notify_notification_show(notification.get(), &error);
|
||||
if (error) {
|
||||
qWarning("failed to show notification: %s", error->message);
|
||||
C_WARN << QString("failed to show notification: %1").arg(error->message);
|
||||
g_clear_error(&error);
|
||||
}
|
||||
#else
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
|
||||
#include "jamiavatartheme.h"
|
||||
#include "lrcinstance.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <api/contact.h>
|
||||
|
||||
@ -419,8 +420,14 @@ Utils::contactPhoto(LRCInstance* instance,
|
||||
try {
|
||||
auto& accInfo = instance->accountModel().getAccountInfo(
|
||||
accountId.isEmpty() ? instance->get_currentAccountId() : accountId);
|
||||
auto contactInfo = accInfo.contactModel->getContact(contactUri);
|
||||
auto contactPhoto = accInfo.contactModel->avatar(contactUri);
|
||||
if (!contactPhoto.isEmpty()) {
|
||||
photo = imageFromBase64String(contactPhoto);
|
||||
if (!photo.isNull())
|
||||
return Utils::scaleAndFrame(photo, size);
|
||||
}
|
||||
// If no avatar is found, generate one
|
||||
auto contactInfo = accInfo.contactModel->getContact(contactUri);
|
||||
auto bestName = accInfo.contactModel->bestNameForContact(contactUri);
|
||||
if (accInfo.profileInfo.type == profile::Type::SIP
|
||||
&& contactInfo.profileInfo.type == profile::Type::TEMPORARY) {
|
||||
@ -428,12 +435,6 @@ Utils::contactPhoto(LRCInstance* instance,
|
||||
} else if (contactInfo.profileInfo.type == profile::Type::TEMPORARY
|
||||
&& contactInfo.profileInfo.uri.isEmpty()) {
|
||||
photo = Utils::fallbackAvatar(QString(), QString());
|
||||
} else if (!contactPhoto.isEmpty()) {
|
||||
photo = imageFromBase64String(contactPhoto);
|
||||
if (photo.isNull()) {
|
||||
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
||||
photo = Utils::fallbackAvatar("jami:" + contactInfo.profileInfo.uri, avatarName);
|
||||
}
|
||||
} else {
|
||||
auto avatarName = contactInfo.profileInfo.uri == bestName ? QString() : bestName;
|
||||
photo = Utils::fallbackAvatar("jami:" + contactInfo.profileInfo.uri, avatarName);
|
||||
@ -488,7 +489,7 @@ Utils::conversationAvatar(LRCInstance* instance,
|
||||
painter.drawImage(avatar.rect(), peerAAvatar);
|
||||
painter.drawImage(avatar.rect(), peerBAvatar);
|
||||
} catch (const std::exception& e) {
|
||||
qDebug() << Q_FUNC_INFO << e.what();
|
||||
C_DBG << e.what();
|
||||
}
|
||||
return avatar;
|
||||
}
|
||||
@ -539,13 +540,20 @@ Utils::getCirclePhoto(const QImage original, int sizePhoto)
|
||||
Qt::KeepAspectRatioByExpanding,
|
||||
Qt::SmoothTransformation)
|
||||
.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
int margin = 0;
|
||||
int marginX = 0;
|
||||
int marginY = 0;
|
||||
|
||||
if (scaledPhoto.width() > sizePhoto) {
|
||||
margin = (scaledPhoto.width() - sizePhoto) / 2;
|
||||
marginX = (scaledPhoto.width() - sizePhoto) / 2;
|
||||
}
|
||||
if (scaledPhoto.height() > sizePhoto) {
|
||||
marginY = (scaledPhoto.height() - sizePhoto) / 2;
|
||||
}
|
||||
|
||||
painter.drawEllipse(0, 0, sizePhoto, sizePhoto);
|
||||
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
||||
painter.drawImage(0, 0, scaledPhoto, margin, 0);
|
||||
painter.drawImage(0, 0, scaledPhoto, marginX, marginY);
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
#include "systemtray.h"
|
||||
#include "utils.h"
|
||||
#include "version.h"
|
||||
#include "global.h"
|
||||
|
||||
#include <api/datatransfermodel.h>
|
||||
#include <api/contact.h>
|
||||
@ -229,7 +230,7 @@ UtilsAdapter::getConvIdForUri(const QString& accountId, const QString& uri)
|
||||
return {};
|
||||
return convInfo->get().uid;
|
||||
} catch (const std::out_of_range& e) {
|
||||
qDebug() << e.what();
|
||||
C_DBG << e.what();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -242,7 +243,7 @@ UtilsAdapter::getPeerUri(const QString& accountId, const QString& uid)
|
||||
const auto& convInfo = convModel->getConversationForUid(uid).value();
|
||||
return convInfo.get().participants.front().uri;
|
||||
} catch (const std::out_of_range& e) {
|
||||
qDebug() << e.what();
|
||||
C_DBG << e.what();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -745,7 +746,7 @@ UtilsAdapter::isSystemThemeDark()
|
||||
#endif
|
||||
return readAppsUseLightThemeRegistry(true);
|
||||
#else
|
||||
qWarning("System theme detection is not implemented or is not supported");
|
||||
C_WARN << "System theme detection is not implemented or is not supported";
|
||||
return false;
|
||||
#endif // WIN32
|
||||
#endif // __has_include(<gio/gio.h>)
|
||||
@ -827,13 +828,7 @@ UtilsAdapter::isRTL()
|
||||
bool
|
||||
UtilsAdapter::isSystemTrayIconVisible()
|
||||
{
|
||||
if (!systemTray_)
|
||||
return false;
|
||||
// https://bugreports.qt.io/browse/QTBUG-118656
|
||||
#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)
|
||||
return true;
|
||||
#endif
|
||||
return systemTray_->geometry() != QRect();
|
||||
return QSystemTrayIcon::isSystemTrayAvailable() && systemTray_;
|
||||
}
|
||||
|
||||
QString
|
||||
@ -891,7 +886,7 @@ UtilsAdapter::createDummyImage() const
|
||||
qInfo() << "Dummy image created" << QDir::tempPath() + "/dummy.png";
|
||||
return QDir::tempPath() + "/dummy.png";
|
||||
} else {
|
||||
qWarning() << "Could not create dummy image";
|
||||
C_WARN << "Could not create dummy image";
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
|
||||
#include "videodevices.h"
|
||||
|
||||
#include "api/devicemodel.h"
|
||||
#include "global.h"
|
||||
|
||||
VideoInputDeviceModel::VideoInputDeviceModel(LRCInstance* lrcInstance,
|
||||
VideoDevices* videoDeviceInstance)
|
||||
@ -256,13 +256,14 @@ VideoDevices::startDevice(const QString& id, bool force)
|
||||
void
|
||||
VideoDevices::stopDevice(const QString& id)
|
||||
{
|
||||
if (!id.isEmpty()) {
|
||||
qInfo() << "Stopping device" << id;
|
||||
if (lrcInstance_->avModel().stopPreview(id)) {
|
||||
deviceOpen_ = false;
|
||||
} else {
|
||||
qWarning() << "Failed to stop device" << id;
|
||||
}
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (lrcInstance_->avModel().stopPreview(id)) {
|
||||
deviceOpen_ = false;
|
||||
} else {
|
||||
C_DBG << "Failed to stop device" << id;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -149,6 +149,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# Remove warnings related to `import`.
|
||||
add_definitions(-Wno-import-preprocessor-directive-pedantic)
|
||||
set(CMAKE_MACOSX_RPATH on)
|
||||
set(CMAKE_SKIP_BUILD_RPATH false)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH false)
|
||||
@ -273,6 +275,7 @@ set(LIBCLIENT_SOURCES
|
||||
# other
|
||||
avmodel.cpp
|
||||
pluginmodel.cpp
|
||||
interaction.cpp
|
||||
namedirectory.cpp
|
||||
renderer.cpp)
|
||||
|
||||
|
||||
@ -166,7 +166,6 @@ public Q_SLOTS:
|
||||
* @param ok
|
||||
*/
|
||||
void slotMigrationEnded(const QString& accountId, bool ok);
|
||||
|
||||
/**
|
||||
* Emit accountProfileReceived
|
||||
* @param accountId
|
||||
@ -176,7 +175,6 @@ public Q_SLOTS:
|
||||
void slotAccountProfileReceived(const QString& accountId,
|
||||
const QString& displayName,
|
||||
const QString& userPhoto);
|
||||
|
||||
/**
|
||||
* Emit new position
|
||||
* @param accountId
|
||||
@ -415,10 +413,6 @@ AccountModelPimpl::AccountModelPimpl(AccountModel& linked,
|
||||
&CallbacksHandler::migrationEnded,
|
||||
this,
|
||||
&AccountModelPimpl::slotMigrationEnded);
|
||||
connect(&callbacksHandler,
|
||||
&CallbacksHandler::accountProfileReceived,
|
||||
this,
|
||||
&AccountModelPimpl::slotAccountProfileReceived);
|
||||
connect(&callbacksHandler,
|
||||
&CallbacksHandler::newPosition,
|
||||
this,
|
||||
@ -434,8 +428,9 @@ AccountModelPimpl::updateAccounts()
|
||||
const auto previousAccountIdListSize = accountIdList.size();
|
||||
accountIdList = configurationManager.getAccountList();
|
||||
|
||||
// If this is just a reordering of the accounts, don't do anything
|
||||
// If this is just a reordering of the accounts, just notify the view.
|
||||
if (accountIdList.size() == previousAccountIdListSize) {
|
||||
Q_EMIT linked.accountsReordered();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -531,11 +526,12 @@ AccountModelPimpl::slotAccountStatusChanged(const QString& accountID,
|
||||
if (status != api::account::Status::INITIALIZING
|
||||
&& accountInfo.status == api::account::Status::INITIALIZING) {
|
||||
// Detect when a new account is generated (keys are ready). During
|
||||
// the generation, a Ring account got the "INITIALIZING" status.
|
||||
// the generation, an account got the "INITIALIZING" status.
|
||||
// When keys are generated, the status will change.
|
||||
// The account is already added and initialized. Just update details from daemon
|
||||
updateAccountDetails(accountInfo);
|
||||
// This will load swarms as the account was not loaded before.
|
||||
accountInfo.contactModel->initContacts();
|
||||
accountInfo.conversationModel->initConversations();
|
||||
Q_EMIT linked.accountAdded(accountID);
|
||||
} else if (!accountInfo.profileInfo.uri.isEmpty()) {
|
||||
@ -690,25 +686,6 @@ AccountModelPimpl::slotMigrationEnded(const QString& accountId, bool ok)
|
||||
Q_EMIT linked.migrationEnded(accountId, ok);
|
||||
}
|
||||
|
||||
void
|
||||
AccountModelPimpl::slotAccountProfileReceived(const QString& accountId,
|
||||
const QString& displayName,
|
||||
const QString& userPhoto)
|
||||
{
|
||||
LC_WARN << accountId << displayName;
|
||||
|
||||
auto account = accounts.find(accountId);
|
||||
if (account == accounts.end())
|
||||
return;
|
||||
auto& accountInfo = account->second.first;
|
||||
accountInfo.profileInfo.avatar = userPhoto;
|
||||
accountInfo.profileInfo.alias = displayName;
|
||||
|
||||
storage::vcard::setProfile(accountInfo.id, accountInfo.profileInfo);
|
||||
|
||||
Q_EMIT linked.profileUpdated(accountId);
|
||||
}
|
||||
|
||||
void
|
||||
AccountModelPimpl::slotNewPosition(const QString& accountId,
|
||||
const QString& peerId,
|
||||
@ -719,6 +696,24 @@ AccountModelPimpl::slotNewPosition(const QString& accountId,
|
||||
Q_EMIT linked.newPosition(accountId, peerId, body, timestamp, daemonId);
|
||||
}
|
||||
|
||||
void
|
||||
AccountModelPimpl::slotAccountProfileReceived(const QString& accountId,
|
||||
const QString& displayName,
|
||||
const QString& userPhoto)
|
||||
{
|
||||
LC_WARN << accountId << displayName;
|
||||
auto account = accounts.find(accountId);
|
||||
if (account == accounts.end() || userPhoto.isEmpty())
|
||||
return;
|
||||
// NOTE: This signal is still used for JAMS account where the avatar is
|
||||
// retrieven from the server. In this case we MUST save it.
|
||||
auto& accountInfo = account->second.first;
|
||||
accountInfo.profileInfo.avatar = userPhoto;
|
||||
accountInfo.profileInfo.alias = displayName;
|
||||
storage::vcard::setProfile(accountInfo.id, accountInfo.profileInfo);
|
||||
Q_EMIT linked.profileUpdated(accountId);
|
||||
}
|
||||
|
||||
void
|
||||
AccountModelPimpl::addToAccounts(const QString& accountId)
|
||||
{
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user