Feature: Screen Sharing

Avoid to use SCShareableContent API because of crash on release.
This commit is contained in:
Julien Wadel 2024-02-16 14:43:03 +01:00
parent 559b5b13ea
commit ca7278d51f
61 changed files with 2560 additions and 1413 deletions

View file

@ -18,7 +18,7 @@
echo $DEFAULT_MACOS_CMAKE_OPTIONS
echo $CMAKE_OPTIONS
echo $ADDITIONAL_BUILD_OPTIONS
cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.15 -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_MACOS_CMAKE_OPTIONS -DLINPHONE_BUILDER_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY" $XCODE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $RELEASE_FILE
cmake .. -G "$CMAKE_GENERATOR" -DCMAKE_OSX_DEPLOYMENT_TARGET=12.3 -DCMAKE_BUILD_TYPE=$CI_BUILD_TYPE $DEFAULT_MACOS_CMAKE_OPTIONS -DLINPHONE_BUILDER_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY" $XCODE_OPTIONS $CMAKE_OPTIONS $SCHEDULE_CMAKE_OPTIONS $RELEASE_FILE
cmake --build . --target install --config $CI_BUILD_TYPE $LBC_NODEBUG_OPTIONS -- $ADDITIONAL_BUILD_OPTIONS
ccache -s
@ -46,7 +46,7 @@ job-macosx-ninja:
- schedules
variables:
CMAKE_GENERATOR: Ninja
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=OFF
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
extends: .job-macosx-desktop
#################################################
@ -60,7 +60,7 @@ job-macosx-makefile:
- $DEPLOY_PLUGINS
variables:
CMAKE_GENERATOR: Unix Makefiles
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=OFF
CMAKE_OPTIONS: -DENABLE_PQCRYPTO=ON
ADDITIONAL_BUILD_OPTIONS: -j$MAKEFILE_JOBS
extends: .job-macosx-desktop
@ -69,7 +69,7 @@ job-macosx-ninja-novideo:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
variables:
CMAKE_OPTIONS: -DENABLE_VIDEO=OFF -DENABLE_PQCRYPTO=OFF
CMAKE_OPTIONS: -DENABLE_VIDEO=OFF -DENABLE_PQCRYPTO=ON
CMAKE_GENERATOR: Ninja
extends: .job-macosx-desktop
@ -99,7 +99,7 @@ job-macosx-makefile-package:
- $PACKAGE_MACOSX
- $DEPLOY_MACOSX
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=ON -DENABLE_G729=ON
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=ON -DENABLE_G729=ON -DENABLE_PQCRYPTO=ON
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER
extends: job-macosx-makefile
script:

View file

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- '[ui] logs_max_size' : option to set the max size of one log file.
- Screen Sharing
## 5.2.1 - 2024-02-01

View file

@ -67,7 +67,8 @@ if( APPLE )
set(CMAKE_INSTALL_DATAROOTDIR "${APPLICATION_NAME}.app/Contents/Resources/share")
if( NOT CMAKE_OSX_DEPLOYMENT_TARGET)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")#Qt: 'path' is unavailable: introduced in macOS 10.15
#set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")#Qt: 'path' is unavailable: introduced in macOS 10.15
set(CMAKE_OSX_DEPLOYMENT_TARGET "12.3")#ScreenSharing: 'SCStreamConfiguration' has been introduced in macOS 12.3
endif()
set(LINPHONESDK_MACOS_ARCHS ${LINPHONEAPP_MACOS_ARCHS})
set(CMAKE_OSX_ARCHITECTURES ${LINPHONESDK_MACOS_ARCHS} CACHE STRING "")
@ -151,6 +152,7 @@ add_option(OPTION_LIST ENABLE_QT_KEYCHAIN "Build QtKeychain to manage VFS from S
add_option(OPTION_LIST ENABLE_QRCODE "Enable QRCode support" OFF)#Experimental
add_option(OPTION_LIST ENABLE_RELATIVE_PREFIX "Set Internal packages relative to the binary" ON)
add_option(OPTION_LIST ENABLE_SANITIZER "Enable sanitizer." OFF)
add_option(OPTION_LIST ENABLE_SCREENSHARING "Enable screen sharing." ON)
add_option(OPTION_LIST ENABLE_STRICT "Build with strict compilator flags e.g. -Wall -Werror" OFF)
add_option(OPTION_LIST ENABLE_TESTS "Build with testing binaries of SDK" OFF)
add_option(OPTION_LIST ENABLE_TESTS_COMPONENTS "Build libbctoolbox-tester" OFF)

View file

@ -45,7 +45,7 @@ set(_MACOS_INSTALL_DIR "${APPLICATION_OUTPUT_DIR}/${_MACOS_INSTALL_RELATIVE_DIR}
#linphone_sdk_get_inherited_cmake_args(_CMAKE_CONFIGURE_ARGS _CMAKE_BUILD_ARGS)
#linphone_sdk_get_enable_cmake_args(_MACOS_CMAKE_ARGS)
set(_MACOS_CMAKE_ARGS ${CMAKE_OSX_DEPLOYMENT_TARGET})
set(_MACOS_CMAKE_ARGS "-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}")
set(_MACOS_TARGETS)
foreach(_MACOS_ARCH IN LISTS _MACOS_ARCHS)

View file

@ -259,6 +259,7 @@ set(SOURCES
src/app/providers/ExternalImageProvider.cpp
src/app/providers/QRCodeProvider.cpp
src/app/providers/ThumbnailProvider.cpp
src/app/providers/ScreenProvider.cpp
src/app/proxyModel/ProxyListModel.cpp
src/app/proxyModel/SortFilterProxyModel.cpp
#src/app/proxyModel/ProxyMapModel.cpp
@ -359,6 +360,8 @@ set(SOURCES
src/components/recorder/RecorderModel.cpp
src/components/recorder/RecordingListModel.cpp
src/components/recorder/RecordingProxyModel.cpp
src/components/screen/ScreenListModel.cpp
src/components/screen/ScreenProxyModel.cpp
src/components/search/SearchListener.cpp
src/components/search/SearchResultModel.cpp
src/components/search/SearchSipAddressesModel.cpp
@ -380,6 +383,7 @@ set(SOURCES
src/components/tunnel/TunnelConfigListModel.cpp
src/components/tunnel/TunnelConfigProxyModel.cpp
src/components/url-handlers/UrlHandlers.cpp
src/components/videoSource/VideoSourceDescriptorModel.cpp
src/utils/Constants.cpp
src/utils/LinphoneEnums.cpp
src/utils/MediastreamerUtils.cpp
@ -404,6 +408,7 @@ set(HEADERS
src/app/providers/ExternalImageProvider.hpp
src/app/providers/QRCodeProvider.hpp
src/app/providers/ThumbnailProvider.hpp
src/app/providers/ScreenProvider.hpp
src/app/proxyModel/ProxyAbstractListModel.hpp
src/app/proxyModel/ProxyAbstractMapModel.hpp
src/app/proxyModel/ProxyAbstractObject.hpp
@ -510,6 +515,8 @@ set(HEADERS
src/components/recorder/RecorderModel.hpp
src/components/recorder/RecordingListModel.hpp
src/components/recorder/RecordingProxyModel.hpp
src/components/screen/ScreenListModel.hpp
src/components/screen/ScreenProxyModel.hpp
src/components/search/SearchListener.hpp
src/components/search/SearchResultModel.hpp
src/components/search/SearchSipAddressesModel.hpp
@ -531,6 +538,7 @@ set(HEADERS
src/components/tunnel/TunnelConfigListModel.hpp
src/components/tunnel/TunnelConfigProxyModel.hpp
src/components/url-handlers/UrlHandlers.hpp
src/components/videoSource/VideoSourceDescriptorModel.hpp
src/utils/Constants.hpp
src/utils/LinphoneEnums.hpp
src/utils/MediastreamerUtils.hpp
@ -820,7 +828,7 @@ foreach(T ${APP_TARGETS})
endforeach()
if (APPLE)
target_link_libraries(${TARGET_NAME} "-framework Cocoa" "-framework IOKit" "-framework AVFoundation")
target_link_libraries(${TARGET_NAME} "-framework Cocoa" "-framework IOKit" "-framework AVFoundation" "-framework ScreenCaptureKit")
endif()
target_link_libraries(${TARGET_NAME} ${APP_PLUGIN})
@ -830,6 +838,13 @@ if(WIN32 AND ENABLE_LDAP)
target_link_libraries(${TARGET_NAME} wsock32 ws2_32 ${OPENLDAP_LIBRARIES})
endif()
if(NOT WIN32)
find_package(X11)
endif()
if(X11_FOUND)
target_link_libraries(${TARGET_NAME} X11::X11)
endif()
add_dependencies(${APP_LIBRARY} update_translations ${APP_PLUGIN})
# ------------------------------------------------------------------------------
# CPack settings & RPM.

View file

@ -432,7 +432,7 @@ if(${ENABLE_APP_PACKAGING})
message(FATAL_ERROR "Could not find the macdeployqt program. Make sure it is in the PATH.")
endif()
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cleanCpack.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cleanCpack.cmake" @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cleanCPack.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cleanCPack.cmake" @ONLY)
set(CPACK_PRE_BUILD_SCRIPTS "${CMAKE_CURRENT_BINARY_DIR}/cleanCPack.cmake")

View file

@ -48,6 +48,7 @@
#include "providers/ExternalImageProvider.hpp"
#include "providers/QRCodeProvider.hpp"
#include "providers/ThumbnailProvider.hpp"
#include "providers/ScreenProvider.hpp"
#include "translator/DefaultTranslator.hpp"
#include "utils/Utils.hpp"
#include "utils/Constants.hpp"
@ -57,6 +58,7 @@
#include "components/other/date/DateModel.hpp"
#include "components/other/spell-checker/SpellChecker.hpp"
#include "components/screen/ScreenProxyModel.hpp"
#include "components/settings/EmojisSettingsModel.hpp"
#include "components/timeline/TimelineModel.hpp"
#include "components/timeline/TimelineListModel.hpp"
@ -66,6 +68,8 @@
#include "components/participant/ParticipantListModel.hpp"
#include "components/participant/ParticipantProxyModel.hpp"
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
// =============================================================================
using namespace std;
@ -268,6 +272,7 @@ App::App (int &argc, char *argv[]) : SingleApplication(argc, argv, true, Mode::U
_putenv(("TZ="+QTimeZone::systemTimeZoneId().toStdString()).c_str());
_tzset();
#else
QString t = QTimeZone::systemTimeZoneId();
setenv("TZ", QTimeZone::systemTimeZoneId().toStdString().c_str(), 1);
tzset();
#endif
@ -489,6 +494,7 @@ void App::initContentApp () {
mEngine->addImageProvider(ExternalImageProvider::ProviderId, new ExternalImageProvider());
mEngine->addImageProvider(QRCodeProvider::ProviderId, new QRCodeProvider());
mEngine->addImageProvider(ThumbnailProvider::ProviderId, new ThumbnailProvider());
mEngine->addImageProvider(ScreenProvider::ProviderId, new ScreenProvider());
mEngine->rootContext()->setContextProperty("applicationName", APPLICATION_NAME);
mEngine->rootContext()->setContextProperty("executableName", EXECUTABLE_NAME);
@ -778,6 +784,7 @@ void App::registerTypes () {
registerType<SearchSipAddressesProxyModel>("SearchSipAddressesProxyModel");
registerType<TemporaryFile>("TemporaryFile");
registerType<TimeZoneProxyModel>("TimeZoneProxyModel");
registerType<VideoSourceDescriptorModel>("VideoSourceDescriptorModel");
registerType<CallHistoryProxyModel>("CallHistoryProxyModel");
registerType<ColorProxyModel>("ColorProxyModel");
@ -788,6 +795,7 @@ void App::registerTypes () {
registerType<ParticipantDeviceProxyModel>("ParticipantDeviceProxyModel");
registerType<SoundPlayer>("SoundPlayer");
registerType<TelephoneNumbersModel>("TelephoneNumbersModel");
registerType<ScreenProxyModel>("ScreenProxyModel");
registerType<SpellChecker>("SpellChecker");
registerSingletonType<AudioCodecsModel>("AudioCodecsModel");

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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 "ScreenProvider.hpp"
#include <QGuiApplication>
#include <QScreen>
// =============================================================================
const QString ScreenProvider::ProviderId = "screen";
ScreenProvider::ScreenProvider () : QQuickImageProvider(
QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading
) {}
QImage ScreenProvider::requestImage (const QString &id, QSize *size, const QSize &) {
int index = id.toInt();
auto screens = QGuiApplication::screens();
if(index >= 0 && index < screens.size()){
auto image = screens[index]->grabWindow(0);
*size = image.size();
return image.toImage();
}else
return QImage();
}

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#ifndef SCREEN_PROVIDER_H_
#define SCREEN_PROVIDER_H_
#include <QQuickImageProvider>
// =============================================================================
class ScreenProvider : public QQuickImageProvider {
public:
ScreenProvider ();
QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override;
static const QString ProviderId;
};
#endif

View file

@ -315,8 +315,8 @@ bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray
{
socket->write( msg );
socket->flush();
bool result = socket->waitForReadyRead( msecs < 0 ? -1 : msecs ); // await ack byte
bool result = socket->waitForReadyRead( msecs < 0 ? -1 : msecs ); // await ack byte
if (result) {
socket->read( 1 );
return true;

View file

@ -37,4 +37,4 @@ CallListener::CallListener(QObject* parent) : QObject(parent){
void CallListener::onRemoteRecording(const std::shared_ptr<linphone::Call> & call, bool recording){
qDebug() << "onRemoteRecording: " << recording;
emit remoteRecording(call, recording);
}
}

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2010-2020 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
@ -40,6 +40,7 @@
#include "components/settings/AccountSettingsModel.hpp"
#include "components/settings/SettingsModel.hpp"
#include "components/timeline/TimelineListModel.hpp"
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
#include "utils/MediastreamerUtils.hpp"
#include "utils/Utils.hpp"
@ -65,6 +66,7 @@ CallModel::CallModel (shared_ptr<linphone::Call> call){
connect(this, &CallModel::isPQZrtpChanged, this, &CallModel::securityUpdated);
connect(this, &CallModel::securityUpdated, this, &CallModel::onSecurityUpdated);
mCall = call;
if(mCall)
mCall->setData("call-model", *this);
@ -120,12 +122,14 @@ CallModel::CallModel (shared_ptr<linphone::Call> call){
if(mCall->getConference()) {
mConferenceModel = ConferenceModel::create(mCall->getConference());
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
connect(mConferenceModel.get(), &ConferenceModel::localScreenSharingChanged, this, &CallModel::onLocalScreenSharingChanged);
}
auto conferenceInfo = CoreManager::getInstance()->getCore()->findConferenceInformationFromUri(getConferenceAddress());
if( conferenceInfo ){
mConferenceInfoModel = ConferenceInfoModel::create(conferenceInfo);
}
mMagicSearch->getContactsListAsync(mRemoteAddress->getUsername(),mRemoteAddress->getDomain(), (int)linphone::MagicSearch::Source::LdapServers | (int)linphone::MagicSearch::Source::Friends, linphone::MagicSearch::Aggregation::Friend);
}
}
@ -248,6 +252,7 @@ QSharedPointer<ConferenceModel> CallModel::getConferenceSharedModel(){
if(mCall->getState() != linphone::Call::State::End && mCall->getConference() && !mConferenceModel){
mConferenceModel = ConferenceModel::create(mCall->getConference());
connect(mConferenceModel.get(), &ConferenceModel::participantAdminStatusChanged, this, &CallModel::onParticipantAdminStatusChanged);
connect(mConferenceModel.get(), &ConferenceModel::localScreenSharingChanged, this, &CallModel::onLocalScreenSharingChanged);
emit conferenceModelChanged();
}
return mConferenceModel;
@ -570,7 +575,6 @@ void CallModel::handleCallStateChanged (const shared_ptr<linphone::Call> &call,
case linphone::Call::State::PushIncomingReceived:
break;
}
emit statusChanged(getStatus());
}
@ -1031,6 +1035,12 @@ void CallModel::onSecurityUpdated(){
model->updateSecurityLevel();
}
void CallModel::onLocalScreenSharingChanged(bool enabled) {
qDebug() << "onLocalScreenSharingChanged" << enabled;
if(!enabled)
setVideoSource(nullptr);
}
void CallModel::setRemoteDisplayName(const std::string& name){
mRemoteAddress->setDisplayName(name);
if(mCall) {
@ -1081,6 +1091,9 @@ void CallModel::changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layo
shared_ptr<linphone::CallParams> params = coreManager->getCore()->createCallParams(mCall);
params->setConferenceVideoLayout(LinphoneEnums::toLinphone(layout));
params->enableVideo(layout != LinphoneEnums::ConferenceLayoutAudioOnly);
if (!params->videoEnabled() && params->screenSharingEnabled()) {
params->enableScreenSharing(false); // Deactivate screensharing if going to audio only.
}
mCall->update(params);
}
@ -1105,6 +1118,45 @@ void CallModel::updateConferenceVideoLayout(){
}
}
void CallModel::setVideoSource(std::shared_ptr<linphone::VideoSourceDescriptor> videoDesc){
//auto videoSource = mCall->getVideoSource();
//if(!videoDesc) {
// auto oldDesc = linphone::Factory::get()->createVideoSourceDescriptor();
// oldDesc->setCameraId(CoreManager::getInstance()->getCore()->getVideoDevice());
// mCall->setVideoSource(oldDesc);
// mCall->setVideoSource(videoDesc);
//}else
mCall->setVideoSource(videoDesc);
emit videoDescriptorChanged();
}
LinphoneEnums::VideoSourceScreenSharingType CallModel::getVideoSourceType() const{
auto videoSource = mCall->getVideoSource();
return LinphoneEnums::fromLinphone(videoSource ? videoSource->getScreenSharingType() : linphone::VideoSourceScreenSharingType::Display);
}
int CallModel::getScreenSharingIndex() const{
auto videoSource = mCall->getVideoSource();
if(videoSource && videoSource->getScreenSharingType() == linphone::VideoSourceScreenSharingType::Display) {
void * t = videoSource->getScreenSharing();
return *(int*)(&t);
}else
return -1;
}
VideoSourceDescriptorModel *CallModel::getVideoSourceDescriptorModel() const {
auto videoSource = mCall->getVideoSource();
return new VideoSourceDescriptorModel(videoSource ? videoSource->clone() : nullptr);
}
void CallModel::setVideoSourceDescriptorModel(VideoSourceDescriptorModel *model) {
if(model)
setVideoSource(model->mDesc);
else {
setVideoSource(nullptr);
}
}
// -----------------------------------------------------------------------------
CallModel::CallEncryption CallModel::getEncryption () const {

View file

@ -34,6 +34,7 @@ class ConferenceInfoModel;
class ConferenceModel;
class ContactModel;
class ChatRoomModel;
class VideoSourceDescriptorModel;
class CallModel : public QObject {
Q_OBJECT
@ -97,7 +98,9 @@ class CallModel : public QObject {
Q_PROPERTY(LinphoneEnums::ConferenceLayout conferenceVideoLayout READ getConferenceVideoLayout WRITE changeConferenceVideoLayout NOTIFY conferenceVideoLayoutChanged)
Q_PROPERTY(LinphoneEnums::VideoSourceScreenSharingType screenSharingType READ getVideoSourceType NOTIFY videoDescriptorChanged)
Q_PROPERTY(bool screenSharingIndex READ getScreenSharingIndex NOTIFY videoDescriptorChanged)
Q_PROPERTY(VideoSourceDescriptorModel *videoSourceDescriptorModel READ getVideoSourceDescriptorModel WRITE setVideoSourceDescriptorModel NOTIFY videoDescriptorChanged)
public:
enum CallStatus {
@ -194,6 +197,13 @@ public:
void changeConferenceVideoLayout(LinphoneEnums::ConferenceLayout layout); // Make a call request
void updateConferenceVideoLayout(); // Called from call state changed ater the new layout has been set.
void setVideoSource(std::shared_ptr<linphone::VideoSourceDescriptor> videoDesc);
LinphoneEnums::VideoSourceScreenSharingType getVideoSourceType() const;
int getScreenSharingIndex() const;
Q_INVOKABLE VideoSourceDescriptorModel *getVideoSourceDescriptorModel() const;
Q_INVOKABLE void setVideoSourceDescriptorModel(VideoSourceDescriptorModel *model = nullptr);
static constexpr int DtmfSoundDelay = 200;
std::shared_ptr<linphone::Call> mCall;
@ -210,6 +220,7 @@ public slots:
void onChatRoomInitialized(int state);
void onParticipantAdminStatusChanged(const std::shared_ptr<const linphone::Participant> & participant);
void onSecurityUpdated();
void onLocalScreenSharingChanged(bool enabled);
signals:
void meAdminChanged();
@ -241,8 +252,7 @@ signals:
void transferAddressChanged (const QString &transferAddress);
void conferenceVideoLayoutChanged();
void videoDescriptorChanged();
public:
void handleCallEncryptionChanged (const std::shared_ptr<linphone::Call> &call);
void handleCallStateChanged (const std::shared_ptr<linphone::Call> &call, linphone::Call::State state);

View file

@ -140,6 +140,7 @@ void Camera::updateWindowIdLocation(){
if(mIsPreview)
setWindowIdLocation( WindowIdLocation::CorePreview);
else{
if(mCallModel){
auto call = mCallModel->getCall();
if(call){
@ -180,6 +181,7 @@ void Camera::removeLinphonePlayer(){
QQuickFramebufferObject::Renderer *Camera::createRenderer () const {
QQuickFramebufferObject::Renderer * renderer = NULL;
resetWindowId();
if(mWindowIdLocation == CorePreview){
qInfo() << "[Camera] (" << mQmlName << ") Setting Camera to Preview";
renderer=(QQuickFramebufferObject::Renderer *)CoreManager::getInstance()->getCore()->createNativePreviewWindowId();
@ -334,8 +336,10 @@ void Camera::isNotReady(){
void Camera::activatePreview(){
mPreviewCounterMutex.lock();
if (++mPreviewCounter == 1)
if (++mPreviewCounter == 1) {
CoreManager::getInstance()->getCore()->enableVideoPreview(true);
CoreManager::getInstance()->getSettingsModel()->setCaptureWindowId();
}
mPreviewCounterMutex.unlock();
}
@ -355,4 +359,4 @@ void Camera::onCallStateChanged(){
disconnect(mCallModel, &CallModel::statusChanged, this, &Camera::onCallStateChanged);
mCallModel = nullptr;
}
}
}

View file

@ -46,7 +46,7 @@ void ConferenceListener::onActiveSpeakerParticipantDevice(const std::shared_ptr<
emit activeSpeakerParticipantDevice(participantDevice);
}
void ConferenceListener::onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant){
void ConferenceListener::onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<linphone::Participant> & participant){
qDebug() << "onParticipantAdded: " << participant->getAddress()->asString().c_str();
emit participantAdded(participant);
}
@ -54,7 +54,7 @@ void ConferenceListener::onParticipantRemoved(const std::shared_ptr<linphone::Co
qDebug() << "onParticipantRemoved";
emit participantRemoved(participant);
}
void ConferenceListener::onParticipantDeviceAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
void ConferenceListener::onParticipantDeviceAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<linphone::ParticipantDevice> & participantDevice){
qDebug() << "onParticipantDeviceAdded";
qDebug() << "Me devices : " << conference->getMe()->getDevices().size();
if( conference->getMe()->getDevices().size() > 1)
@ -87,6 +87,10 @@ void ConferenceListener::onParticipantDeviceIsSpeakingChanged(const std::shared_
//qDebug() << "onParticipantDeviceIsSpeakingChanged: " << participantDevice->getAddress()->asString().c_str() << ". Speaking:" << isSpeaking;
emit participantDeviceIsSpeakingChanged(participantDevice, isSpeaking);
}
void ConferenceListener::onParticipantDeviceScreenSharingChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled) {
qDebug() << "onParticipantDeviceScreenSharingChanged: " << device->getAddress()->asString().c_str() << ". Enabled:" << enabled;
emit participantDeviceScreenSharingChanged(device,enabled);
}
void ConferenceListener::onStateChanged(const std::shared_ptr<linphone::Conference> & conference, linphone::Conference::State newState){
qDebug() << "onStateChanged:" << (int)newState;
emit conferenceStateChanged(newState);

View file

@ -36,15 +36,16 @@ public:
// LINPHONE LISTENERS
virtual void onActiveSpeakerParticipantDevice(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) override;
virtual void onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
virtual void onParticipantAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<linphone::Participant> & participant) override;
virtual void onParticipantRemoved(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
virtual void onParticipantAdminStatusChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::Participant> & participant) override;
virtual void onParticipantDeviceAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) override;
virtual void onParticipantDeviceAdded(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<linphone::ParticipantDevice> & participantDevice) override;
virtual void onParticipantDeviceRemoved(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice) override;
virtual void onParticipantDeviceStateChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device, linphone::ParticipantDevice::State state) override;
virtual void onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device) override;
virtual void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device) override;
virtual void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking) override;
virtual void onParticipantDeviceScreenSharingChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled) override;
virtual void onStateChanged(const std::shared_ptr<linphone::Conference> & conference, linphone::Conference::State newState) override;
virtual void onSubjectChanged(const std::shared_ptr<linphone::Conference> & conference, const std::string & subject) override;
virtual void onAudioDeviceChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::AudioDevice> & audioDevice) override;
@ -61,7 +62,9 @@ signals:
void participantDeviceMediaCapabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceMediaAvailabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking);
void participantDeviceScreenSharingChanged( const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled);
void conferenceStateChanged(linphone::Conference::State newState);
void subjectChanged(const std::string & subject);
};

View file

@ -28,6 +28,8 @@
#include "app/App.hpp"
#include "components/participant/ParticipantListModel.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "utils/Utils.hpp"
@ -42,6 +44,7 @@ void ConferenceModel::connectTo(ConferenceListener * listener){
connect(listener, &ConferenceListener::participantDeviceMediaCapabilityChanged, this, &ConferenceModel::onParticipantDeviceMediaCapabilityChanged);
connect(listener, &ConferenceListener::participantDeviceMediaAvailabilityChanged, this, &ConferenceModel::onParticipantDeviceMediaAvailabilityChanged);
connect(listener, &ConferenceListener::participantDeviceIsSpeakingChanged, this, &ConferenceModel::onParticipantDeviceIsSpeakingChanged);
connect(listener, &ConferenceListener::participantDeviceScreenSharingChanged, this, &ConferenceModel::onParticipantDeviceScreenSharingChanged);
connect(listener, &ConferenceListener::conferenceStateChanged, this, &ConferenceModel::onConferenceStateChanged);
connect(listener, &ConferenceListener::subjectChanged, this, &ConferenceModel::onSubjectChanged);
}
@ -61,7 +64,9 @@ ConferenceModel::ConferenceModel (std::shared_ptr<linphone::Conference> conferen
mConference->addListener(mConferenceListener);
connect(this, &ConferenceModel::participantDeviceAdded, this, &ConferenceModel::participantDeviceCountChanged);
connect(this, &ConferenceModel::participantDeviceRemoved, this, &ConferenceModel::participantDeviceCountChanged);
connect(this, &ConferenceModel::isScreenSharingEnabledChanged, this, &ConferenceModel::onIsScreenSharingEnabledChanged);
connect(mParticipantListModel.get(), &ParticipantListModel::participantsChanged, this, &ConferenceModel::participantDeviceCountChanged);
connect(this, &ConferenceModel::participantDeviceCountChanged, this, &ConferenceModel::updateLayout);
onConferenceStateChanged(mConference->getState());// Is it already Created like for local conference?
}
@ -87,6 +92,42 @@ bool ConferenceModel::updateLocalParticipant(){
return changed;
}
void ConferenceModel::updateLayout(){
if(getParticipantDeviceCount() > CoreManager::getInstance()->getSettingsModel()->getConferenceMaxThumbnails()+1) {
auto call = mConference->getCall();
std::shared_ptr<linphone::CallParams> params = CoreManager::getInstance()->getCore()->createCallParams(call);
if( params->getConferenceVideoLayout() == linphone::Conference::Layout::Grid && params->videoEnabled()) {
params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker);
call->update(params);
}
}
}
void ConferenceModel::toggleScreenSharing() {
auto device = mConference->getScreenSharingParticipantDevice();
if(!device || Utils::isLocal(mConference, device)) {
bool enable = !device;
auto params = CoreManager::getInstance()->getCore()->createCallParams(mConference->getCall());
params->enableScreenSharing(enable);
if(enable) {
params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker);
params->enableVideo(true);
}
if(params->isValid())
mConference->getCall()->update(params);
}
}
bool ConferenceModel::isLocalScreenSharingEnabled() const{
auto device = mConference->getScreenSharingParticipantDevice();
return device && Utils::isLocal(mConference, device);
}
bool ConferenceModel::isScreenSharingEnabled() const{
return mConference && mConference->getScreenSharingParticipant();
}
std::shared_ptr<linphone::Conference> ConferenceModel::getConference()const{
return mConference;
}
@ -157,11 +198,14 @@ void ConferenceModel::onParticipantAdminStatusChanged(const std::shared_ptr<cons
void ConferenceModel::onParticipantDeviceAdded(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qDebug() << "Me devices : " << mConference->getMe()->getDevices().size();
updateLayout();
updateLocalParticipant();
emit participantDeviceAdded(participantDevice);
}
void ConferenceModel::onParticipantDeviceRemoved(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
qDebug() << "Me devices : " << mConference->getMe()->getDevices().size();
if( participantDevice->screenSharingEnabled())
emit isScreenSharingEnabledChanged();
emit participantDeviceRemoved(participantDevice);
}
@ -182,10 +226,21 @@ void ConferenceModel::onParticipantDeviceMediaAvailabilityChanged(const std::sha
void ConferenceModel::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking){
emit participantDeviceIsSpeakingChanged(participantDevice, isSpeaking);
}
void ConferenceModel::onParticipantDeviceScreenSharingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled){
emit participantDeviceScreenSharingChanged(device, enabled);
if(Utils::isLocal(mConference, device)){
emit localScreenSharingChanged(enabled);
}
emit isScreenSharingEnabledChanged();
}
void ConferenceModel::onConferenceStateChanged(linphone::Conference::State newState){
if(newState == linphone::Conference::State::Created){
setIsReady(true);
emit participantDeviceCountChanged();
if(mConference->getScreenSharingParticipant())
emit isScreenSharingEnabledChanged();
}
updateLocalParticipant();
emit conferenceStateChanged(newState);
@ -193,6 +248,13 @@ void ConferenceModel::onConferenceStateChanged(linphone::Conference::State newSt
void ConferenceModel::onSubjectChanged(const std::string& string){
emit subjectChanged();
}
void ConferenceModel::onIsScreenSharingEnabledChanged(){
auto call = mConference->getCall();
std::shared_ptr<linphone::CallParams> params = CoreManager::getInstance()->getCore()->createCallParams(call);
if(params->getConferenceVideoLayout() == linphone::Conference::Layout::Grid && params->videoEnabled()) {
params->setConferenceVideoLayout(linphone::Conference::Layout::ActiveSpeaker);
}
call->update(params);
}
//-----------------------------------------------------------------------------------------------------------------------

View file

@ -32,6 +32,7 @@
class ConferenceListener;
class ParticipantListModel;
class VideoSourceDescriptorModel;
class ConferenceModel : public QObject{
Q_OBJECT
@ -43,12 +44,18 @@ public:
Q_PROPERTY(ParticipantModel* localParticipant READ getLocalParticipant NOTIFY localParticipantChanged)
Q_PROPERTY(bool isReady MEMBER mIsReady WRITE setIsReady NOTIFY isReadyChanged)
Q_PROPERTY(int participantDeviceCount READ getParticipantDeviceCount NOTIFY participantDeviceCountChanged)
Q_PROPERTY(bool isLocalScreenSharingEnabled READ isLocalScreenSharingEnabled NOTIFY localScreenSharingChanged)
Q_PROPERTY(bool isScreenSharingEnabled READ isScreenSharingEnabled NOTIFY isScreenSharingEnabledChanged)
static QSharedPointer<ConferenceModel> create(std::shared_ptr<linphone::Conference> chatRoom, QObject *parent = Q_NULLPTR);
ConferenceModel(std::shared_ptr<linphone::Conference> content, QObject *parent = Q_NULLPTR);
virtual ~ConferenceModel();
bool updateLocalParticipant(); // true if changed
void updateLayout();
Q_INVOKABLE void toggleScreenSharing();
bool isLocalScreenSharingEnabled() const;
bool isScreenSharingEnabled() const;
std::shared_ptr<linphone::Conference> getConference()const;
@ -71,9 +78,12 @@ public:
virtual void onParticipantDeviceMediaCapabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device);
virtual void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device);
virtual void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, bool isSpeaking);
virtual void onParticipantDeviceScreenSharingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled);
virtual void onParticipantDeviceStateChanged(const std::shared_ptr<linphone::Conference> & conference, const std::shared_ptr<const linphone::ParticipantDevice> & device, linphone::ParticipantDevice::State state);
virtual void onConferenceStateChanged(linphone::Conference::State newState);
virtual void onSubjectChanged(const std::string& subject);
void onIsScreenSharingEnabledChanged();
//---------------------------------------------------------------------------
signals:
@ -87,11 +97,14 @@ signals:
void participantDeviceMediaCapabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceMediaAvailabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void participantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, bool isSpeaking);
void participantDeviceScreenSharingChanged( const std::shared_ptr<const linphone::ParticipantDevice> & device, bool enabled);
void participantDeviceStateChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, linphone::ParticipantDevice::State state);
void conferenceStateChanged(linphone::Conference::State newState);
void subjectChanged();
void isReadyChanged();
void participantDeviceCountChanged();
void localScreenSharingChanged(bool enabled);
void isScreenSharingEnabledChanged();
private:
void connectTo(ConferenceListener * listener);

View file

@ -42,7 +42,6 @@ ContactModel::ContactModel (VcardModel *vcardModel, QObject * parent) : QObject(
Q_CHECK_PTR(vcardModel);
Q_CHECK_PTR(vcardModel->mVcard);
Q_ASSERT(!vcardModel->mIsReadOnly);
mLinphoneFriend = CoreManager::getInstance()->getCore()->createFriendFromVcard(vcardModel->mVcard);
mLinphoneFriend->setData("contact-model", *this);
if(mLinphoneFriend)

View file

@ -115,4 +115,4 @@ void CallHistoryProxyModel::handleIsActiveChanged (QWindow *window) {
if (window->isActive() && getParentWindow(this) == window) {
CoreManager::getInstance()->resetMissedCallsCount();
}
}
}

View file

@ -20,23 +20,103 @@
#include "DesktopToolsLinux.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
#include <QDebug>
#include <QThread>
#include <X11/Xlib.h>
#include <fcntl.h>
#include <unistd.h>
// #include <X11/Xutil.h>
#include <X11/cursorfont.h>
// =============================================================================
DesktopTools::~DesktopTools () {
setScreenSaverStatus(true);
DesktopTools::~DesktopTools() {
setScreenSaverStatus(true);
}
bool DesktopTools::getScreenSaverStatus () const {
return mScreenSaverStatus;
bool DesktopTools::getScreenSaverStatus() const {
return mScreenSaverStatus;
}
void DesktopTools::setScreenSaverStatus (bool status) {
screenSaverDBus.setScreenSaverStatus(status);
screenSaverXdg.setScreenSaverStatus(status);
void DesktopTools::setScreenSaverStatus(bool status) {
screenSaverDBus.setScreenSaverStatus(status);
screenSaverXdg.setScreenSaverStatus(status);
bool newStatus = screenSaverDBus.getScreenSaverStatus() || screenSaverXdg.getScreenSaverStatus();
if (newStatus != mScreenSaverStatus) {
mScreenSaverStatus = newStatus;
emit screenSaverStatusChanged(mScreenSaverStatus);
}
bool newStatus = screenSaverDBus.getScreenSaverStatus() || screenSaverXdg.getScreenSaverStatus();
if (newStatus != mScreenSaverStatus) {
mScreenSaverStatus = newStatus;
emit screenSaverStatusChanged(mScreenSaverStatus);
}
}
class T : public QThread {
public:
Display *mDisplay;
Window mWindow;
VideoSourceDescriptorModel *mVideoSourceDescriptorModel = nullptr;
DesktopTools *mParent;
T() {
}
virtual void run() {
bool endLoop = false;
unsigned char data[3];
const char *pDevice = "/dev/input/event6";
int fd = open(pDevice, O_RDONLY), bytes;
XEvent event;
endLoop = (fd == -1);
int left, middle, right;
XEvent report;
XButtonEvent *xb = (XButtonEvent *)&report;
auto cursor = XCreateFontCursor(mDisplay, XC_crosshair);
auto i = XGrabPointer(mDisplay, mWindow, False, ButtonReleaseMask | ButtonPressMask | Button1MotionMask,
GrabModeSync, GrabModeAsync, mWindow, cursor, CurrentTime);
endLoop = (i != GrabSuccess);
if (endLoop) qCritical() << "Cannot open mouse events";
while (!endLoop) {
XAllowEvents(mDisplay, SyncPointer, CurrentTime);
XWindowEvent(mDisplay, mWindow, ButtonPressMask | ButtonReleaseMask, &report);
if (report.type == ButtonPress) {
printf("Press @ (%d, %d)\n", xb->x_root, xb->y_root);
XQueryPointer(mDisplay, mWindow, &event.xbutton.root, &event.xbutton.subwindow, &event.xbutton.x_root,
&event.xbutton.y_root, &event.xbutton.x, &event.xbutton.y, &event.xbutton.state);
auto id = event.xbutton.subwindow;
QMetaObject::invokeMethod(CoreManager::getInstance(), [id, this]() mutable {
mVideoSourceDescriptorModel->setScreenSharingWindow(reinterpret_cast<void*>(id));
});
endLoop = true;
}
}
XFlush(mDisplay);
XUngrabServer(mDisplay);
XCloseDisplay(mDisplay);
deleteLater();
}
};
void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
const char *displayStr = getenv("DISPLAY");
if (displayStr == NULL) displayStr = ":0";
Display *display = XOpenDisplay(displayStr); // QX11Info::display();
if (display == NULL) {
qCritical() << "Can't open X display!";
return;
}
emit windowIdSelectionStarted();
T *t = new T();
connect(t, &QThread::finished, this, &DesktopTools::windowIdSelectionEnded);
t->mVideoSourceDescriptorModel = model;
t->mDisplay = display;
auto screen = DefaultScreen(display); // QX11Info::appScreen();
t->mWindow = RootWindow(display, screen); // QX11Info::appRootWindow(m_x11_screen);
t->mParent = this;
t->start();
}
uintptr_t DesktopTools::getDisplayIndex(void* screenSharing){
return *(uintptr_t*)(&screenSharing);
}

View file

@ -25,30 +25,44 @@
#include "components/other/desktop-tools/screen-saver/ScreenSaverXdg.hpp"
// =============================================================================
class VideoSourceDescriptorModel;
class DesktopTools : public QObject {
Q_OBJECT;
Q_OBJECT
Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged);
Q_PROPERTY(
bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged)
public:
DesktopTools (QObject *parent = Q_NULLPTR) : QObject(parent) {}
~DesktopTools ();
DesktopTools(QObject *parent = Q_NULLPTR) : QObject(parent) {
}
~DesktopTools();
bool getScreenSaverStatus () const;
void setScreenSaverStatus (bool status);
bool getScreenSaverStatus() const;
void setScreenSaverStatus(bool status);
static void init(){}
static void applicationStateChanged(Qt::ApplicationState){};
static void init() {
}
static void applicationStateChanged(Qt::ApplicationState){};
Q_INVOKABLE void getWindowIdFromMouse(VideoSourceDescriptorModel *model);
static void *getDisplay(uintptr_t screenIndex){return reinterpret_cast<void*>(screenIndex);}
static uintptr_t getDisplayIndex(void* screenSharing);
signals:
void screenSaverStatusChanged (bool status);
void screenSaverStatusChanged(bool status);
void windowIdSelectionStarted();
void windowIdSelectionEnded();
private:
bool mScreenSaverStatus = true;
bool mScreenSaverStatus = true;
ScreenSaverDBus screenSaverDBus;
ScreenSaverXdg screenSaverXdg;
ScreenSaverDBus screenSaverDBus;
ScreenSaverXdg screenSaverXdg;
// X11 headers cannot be used in hpp. moc don't' compile.
void *mDisplay = nullptr; // Display
unsigned int mWindow = 0; // Window
};
#endif // DESKTOP_TOOLS_LINUX_H_

View file

@ -23,28 +23,34 @@
#include <QObject>
// =============================================================================
class VideoSourceDescriptorModel;
class DesktopTools : public QObject {
Q_OBJECT;
Q_OBJECT;
Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged);
Q_PROPERTY(
bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged);
public:
DesktopTools (QObject *parent = Q_NULLPTR);
~DesktopTools ();
DesktopTools(QObject *parent = Q_NULLPTR);
~DesktopTools();
bool getScreenSaverStatus () const;
void setScreenSaverStatus (bool status);
static void init(); // Do first initialization
static void applicationStateChanged(Qt::ApplicationState currentState);
bool getScreenSaverStatus() const;
void setScreenSaverStatus(bool status);
static void init(); // Do first initialization
static void applicationStateChanged(Qt::ApplicationState currentState);
static void *getDisplay(int screenIndex);
static int getDisplayIndex(void *screenSharing);
Q_INVOKABLE void getWindowIdFromMouse(VideoSourceDescriptorModel *model);
signals:
void screenSaverStatusChanged (bool status);
void screenSaverStatusChanged(bool status);
void windowIdSelectionStarted();
void windowIdSelectionEnded();
private:
bool mScreenSaverStatus = true;
bool mScreenSaverStatus = true;
};
#endif // DESKTOP_TOOLS_MAC_OS_H_

View file

@ -1,12 +1,71 @@
#include "DesktopToolsMacOs.hpp"
#import <AVFoundation/AVFoundation.h>
#import <ScreenCaptureKit/ScreenCaptureKit.h>
#include <QDebug>
#include <QThread>
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
void DesktopTools::init(){
// Request permissions
if( @available(macOS 10.14, *) ) {
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusNotDetermined)
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL) {}];
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] == AVAuthorizationStatusNotDetermined)
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL) {}];
}
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized) {
qDebug() << "Requesting Video permission";
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL) {}];
}
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio] != AVAuthorizationStatusAuthorized){
qDebug() << "Requesting Audio permission";
[AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL) {}];
}
}
void *DesktopTools::getDisplay(int screenIndex){
CGDirectDisplayID displays[screenIndex+1];
CGDisplayCount displayCount;
CGGetOnlineDisplayList(screenIndex+1, displays, &displayCount);
CGDirectDisplayID display = 0;
if(displayCount > screenIndex)
display = displays[screenIndex];
return reinterpret_cast<void*>(display);
}
int DesktopTools::getDisplayIndex(void* screenSharing){
CGDirectDisplayID displayId = *(CGDirectDisplayID*)&screenSharing;
int maxDisplayCount = 10;
CGDisplayCount displayCount;
do {
CGDirectDisplayID displays[maxDisplayCount];
CGGetOnlineDisplayList(maxDisplayCount, displays, &displayCount);
for(int i = 0 ; i < displayCount ; ++i)
if( displays[i] == displayId) {
return i;
}
maxDisplayCount *= 2;
}while(displayCount == maxDisplayCount/2);
return 0;
}
void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
__block id globalMonitorId;
__block id localMonitorId;
__block DesktopTools * tools = this;
emit windowIdSelectionStarted();
globalMonitorId = [NSEvent addGlobalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown
handler:^(NSEvent *event){
CGWindowID windowID = (CGWindowID)[event windowNumber];
if(event.type == NSEventTypeLeftMouseDown)
model->setScreenSharingWindow(reinterpret_cast<void *>(windowID));
[NSEvent removeMonitor:globalMonitorId];
[NSEvent removeMonitor:localMonitorId];
emit tools->windowIdSelectionEnded();
}];
localMonitorId = [NSEvent addLocalMonitorForEventsMatchingMask:NSEventMaskLeftMouseDown | NSEventMaskRightMouseDown
handler:^NSEvent *(NSEvent *event){
CGWindowID windowID = (CGWindowID)[event windowNumber];
if(event.type == NSEventTypeLeftMouseDown)
model->setScreenSharingWindow(reinterpret_cast<void *>(windowID));
[NSEvent removeMonitor:globalMonitorId];
[NSEvent removeMonitor:localMonitorId];
emit tools->windowIdSelectionEnded();
return nil;
}];
}

View file

@ -18,32 +18,64 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "DesktopToolsWindows.hpp"
#include "components/core/CoreManager.hpp"
#include "components/settings/SettingsModel.hpp"
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
#include <QDebug>
#include <Windows.h>
// =============================================================================
DesktopTools::DesktopTools (QObject *parent) : QObject(parent) {}
DesktopTools::~DesktopTools () {
setScreenSaverStatus(true);
DesktopTools::DesktopTools(QObject *parent) : QObject(parent) {
}
bool DesktopTools::getScreenSaverStatus () const {
return mScreenSaverStatus;
DesktopTools::~DesktopTools() {
setScreenSaverStatus(true);
}
void DesktopTools::setScreenSaverStatus (bool status) {
if (status == mScreenSaverStatus)
return;
if (status)
SetThreadExecutionState(ES_CONTINUOUS);
else
SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
mScreenSaverStatus = status;
emit screenSaverStatusChanged(status);
bool DesktopTools::getScreenSaverStatus() const {
return mScreenSaverStatus;
}
void DesktopTools::setScreenSaverStatus(bool status) {
if (status == mScreenSaverStatus) return;
if (status) SetThreadExecutionState(ES_CONTINUOUS);
else SetThreadExecutionState(ES_CONTINUOUS | ES_DISPLAY_REQUIRED);
mScreenSaverStatus = status;
emit screenSaverStatusChanged(status);
}
HHOOK hMouseHook;
DesktopTools *gTools = nullptr;
LRESULT CALLBACK mouseProc(int nCode, WPARAM wParam, LPARAM lParam) {
MOUSEHOOKSTRUCT *pMouseStruct = (MOUSEHOOKSTRUCT *)lParam;
if (pMouseStruct != NULL) {
if (wParam == WM_LBUTTONDOWN) {
printf("clicked");
auto id = WindowFromPoint(pMouseStruct->pt);
UnhookWindowsHookEx(hMouseHook);
QMetaObject::invokeMethod(CoreManager::getInstance(), [id]() {
gTools->mVideoSourceDescriptorModel->setScreenSharingWindow(reinterpret_cast<void *>(id));
});
emit gTools->windowIdSelectionEnded();
}
qDebug() << "Mouse position X = " << pMouseStruct->pt.x << " Mouse Position Y = " << pMouseStruct->pt.y;
}
return CallNextHookEx(hMouseHook, nCode, wParam, lParam);
}
void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
gTools = this;
gTools->mVideoSourceDescriptorModel = model;
emit windowIdSelectionStarted();
HINSTANCE hInstance = GetModuleHandle(NULL);
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, mouseProc, hInstance, NULL);
}
uintptr_t DesktopTools::getDisplayIndex(void* screenSharing) {
return *(uintptr_t*)(&screenSharing);
}

View file

@ -22,29 +22,43 @@
#define DESKTOP_TOOLS_WINDOWS_H_
#include <QObject>
#include <windows.h>
class VideoSourceDescriptorModel;
// =============================================================================
class DesktopTools : public QObject {
Q_OBJECT;
Q_OBJECT
Q_PROPERTY(bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged);
Q_PROPERTY(
bool screenSaverStatus READ getScreenSaverStatus WRITE setScreenSaverStatus NOTIFY screenSaverStatusChanged)
public:
DesktopTools (QObject *parent = Q_NULLPTR);
~DesktopTools ();
DesktopTools(QObject *parent = Q_NULLPTR);
~DesktopTools();
bool getScreenSaverStatus () const;
void setScreenSaverStatus (bool status);
bool getScreenSaverStatus() const;
void setScreenSaverStatus(bool status);
static void init(){}
static void applicationStateChanged(Qt::ApplicationState){};
static void init() {
}
static void applicationStateChanged(Qt::ApplicationState){};
Q_INVOKABLE void getWindowIdFromMouse(VideoSourceDescriptorModel *model);
static void *getDisplay(uintptr_t screenIndex) {
return reinterpret_cast<void *>(screenIndex);
}
static uintptr_t getDisplayIndex(void *screenSharing);
HWND mWindowId = 0; // Window
VideoSourceDescriptorModel *mVideoSourceDescriptorModel = nullptr;
signals:
void screenSaverStatusChanged (bool status);
void screenSaverStatusChanged(bool status);
void windowIdSelectionStarted();
void windowIdSelectionEnded();
private:
bool mScreenSaverStatus = true;
bool mScreenSaverStatus = true;
};
#endif // DESKTOP_TOOLS_WINDOWS_H_

View file

@ -70,7 +70,13 @@ void ParticipantDeviceListModel::initConferenceModel(){
connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaCapabilityChanged, this, &ParticipantDeviceListModel::onParticipantDeviceMediaCapabilityChanged);
connect(conferenceModel.get(), &ConferenceModel::participantDeviceMediaAvailabilityChanged, this, &ParticipantDeviceListModel::onParticipantDeviceMediaAvailabilityChanged);
connect(conferenceModel.get(), &ConferenceModel::participantDeviceIsSpeakingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged);
mActiveSpeaker = get(conferenceModel->getConference()->getActiveSpeakerParticipantDevice());
connect(conferenceModel.get(), &ConferenceModel::participantDeviceScreenSharingChanged, this, &ParticipantDeviceListModel::onParticipantDeviceScreenSharingChanged);
// TODO activeSpeaker
//auto activeSpeaker = conferenceModel->getConference()->getScreenSharingParticipantDevice();
//if(!activeSpeaker)
auto activeSpeaker = conferenceModel->getConference()->getActiveSpeakerParticipantDevice();
mActiveSpeaker = get(activeSpeaker);
mInitialized = true;
}
}
@ -102,6 +108,13 @@ void ParticipantDeviceListModel::updateDevices(const std::list<std::shared_ptr<l
}
}
void ParticipantDeviceListModel::setActiveSpeaker(QSharedPointer<ParticipantDeviceModel> activeSpeaker) {
if( mActiveSpeaker != activeSpeaker) {
mActiveSpeaker = activeSpeaker;
emit activeSpeakerChanged();
}
}
bool ParticipantDeviceListModel::add(std::shared_ptr<linphone::ParticipantDevice> deviceToAdd){
auto deviceToAddAddr = deviceToAdd->getAddress();
int row = 0;
@ -135,8 +148,14 @@ bool ParticipantDeviceListModel::add(std::shared_ptr<linphone::ParticipantDevice
if( addMe){
qDebug() << "Added a me device";
emit meChanged();
}else if(mList.size() == 1 || (mList.size() == 2 && isMe(mList.front().objectCast<ParticipantDeviceModel>()->getDevice()))){
mActiveSpeaker = mList.back().objectCast<ParticipantDeviceModel>();
}else{
// Todo ActiveSpeaker
//if(deviceToAdd->screenSharingEnabled())
// mActiveSpeaker = deviceModel;
//else
mActiveSpeaker = get(mCallModel->getConferenceSharedModel()->getConference()->getActiveSpeakerParticipantDevice());
if(!mActiveSpeaker && (mList.size() == 1 || (mList.size() == 2 && isMe(mList.front().objectCast<ParticipantDeviceModel>()->getDevice()))))
mActiveSpeaker = mList.back().objectCast<ParticipantDeviceModel>();
emit activeSpeakerChanged();
}
return true;
@ -239,6 +258,8 @@ void ParticipantDeviceListModel::onParticipantDeviceAdded(const std::shared_ptr<
qDebug() << "Adding new device : " << mList.count();
auto conferenceModel = mCallModel->getConferenceSharedModel();
std::list<std::shared_ptr<linphone::ParticipantDevice>> devices;
for(int i = 0 ; i < 2 ; ++i){
if( i == 0)
devices = conferenceModel->getConference()->getParticipantDeviceList();// Active devices.
@ -296,11 +317,12 @@ void ParticipantDeviceListModel::onParticipantDeviceMediaAvailabilityChanged(con
onParticipantDeviceAdded(participantDevice);
}
void ParticipantDeviceListModel::onActiveSpeakerParticipantDevice(const std::shared_ptr<const linphone::ParticipantDevice>& participantDevice){
auto device = get(participantDevice);
if( device){
mActiveSpeaker = device;
emit activeSpeakerChanged();
}
// TODO activeSpeaker
//auto activeSpeaker = get(mCallModel->getConferenceSharedModel()->getConference()->getScreenSharingParticipantDevice());
//if(!activeSpeaker)
auto activeSpeaker = get(participantDevice);
qDebug() << "onActiveSpeakerParticipantDevice " << participantDevice.get() << " == " << get(participantDevice) << " : " << (participantDevice ? participantDevice->getAddress()->asStringUriOnly().c_str() : "");
setActiveSpeaker(activeSpeaker);
}
void ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice, bool isSpeaking){
@ -308,6 +330,16 @@ void ParticipantDeviceListModel::onParticipantDeviceIsSpeakingChanged(const std:
if( device)
emit participantSpeaking(device.get());
}
void ParticipantDeviceListModel::onParticipantDeviceScreenSharingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice){
// TODO activeSpeaker
//auto activeSpeaker = mCallModel->getConferenceSharedModel()->getConference()->getScreenSharingParticipantDevice();
//if(!activeSpeaker)
auto activeSpeaker = mCallModel->getConferenceSharedModel()->getConference()->getActiveSpeakerParticipantDevice();
qDebug() << "onParticipantDeviceScreenSharingChanged " << participantDevice.get() << " == " << get(participantDevice) << " ; "
<< activeSpeaker.get() << " == " << get(activeSpeaker) << " : " << (activeSpeaker ? activeSpeaker->getAddress()->asStringUriOnly().c_str() : "")
<< ", ScreenShared:" << participantDevice->screenSharingEnabled();
setActiveSpeaker(get(activeSpeaker));
}
void ParticipantDeviceListModel::onParticipantDeviceSpeaking(){

View file

@ -42,6 +42,7 @@ public:
void initConferenceModel();
void updateDevices(std::shared_ptr<linphone::Participant> participant);
void updateDevices(const std::list<std::shared_ptr<linphone::ParticipantDevice>>& devices, const bool& isMe);
void setActiveSpeaker(QSharedPointer<ParticipantDeviceModel> activeSpeaker);
bool add(std::shared_ptr<linphone::ParticipantDevice> deviceToAdd);
bool remove(std::shared_ptr<const linphone::ParticipantDevice> deviceToAdd);
@ -65,6 +66,7 @@ public slots:
void onParticipantDeviceMediaAvailabilityChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
void onParticipantDeviceIsSpeakingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & device, bool isSpeaking);
void onParticipantDeviceSpeaking();
void onParticipantDeviceScreenSharingChanged(const std::shared_ptr<const linphone::ParticipantDevice> & participantDevice);
signals:
void activeSpeakerChanged();

View file

@ -38,6 +38,10 @@ void ParticipantDeviceListener::onIsMuted(const std::shared_ptr<linphone::Partic
emit isMuted(participantDevice, isMutedVar);
}
void ParticipantDeviceListener::onScreenSharingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isScreenSharing) {
emit isScreenSharingChanged(participantDevice, isScreenSharing);
}
void ParticipantDeviceListener::onStateChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::ParticipantDevice::State state){
qDebug() << "onStateChanged: " << participantDevice->getAddress()->asString().c_str() << " " << (int)state;
emit stateChanged(participantDevice, state);
@ -51,4 +55,4 @@ void ParticipantDeviceListener::onStreamCapabilityChanged(const std::shared_ptr<
void ParticipantDeviceListener::onStreamAvailabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool available, linphone::StreamType streamType) {
qDebug() << "onStreamAvailabilityChanged: " << participantDevice->getAddress()->asString().c_str() << " " << available<< " / " << (int)streamType;
emit streamAvailabilityChanged(participantDevice, available, streamType);
}
}

View file

@ -38,12 +38,14 @@ public:
virtual void onIsSpeakingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isSpeaking) override;
virtual void onIsMuted(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isMuted) override;
virtual void onScreenSharingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isScreenSharing) override;
virtual void onStateChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::ParticipantDevice::State state) override;
virtual void onStreamCapabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType) override;
virtual void onStreamAvailabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool available, linphone::StreamType streamType) override;
signals:
void isSpeakingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isSpeaking);
void isMuted(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isMuted);
void isScreenSharingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isScreenSharing);
void stateChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::ParticipantDevice::State state);
void streamCapabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::MediaDirection direction, linphone::StreamType streamType);
void streamAvailabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool available, linphone::StreamType streamType);

View file

@ -29,6 +29,7 @@
void ParticipantDeviceModel::connectTo(ParticipantDeviceListener * listener){
connect(listener, &ParticipantDeviceListener::isSpeakingChanged, this, &ParticipantDeviceModel::onIsSpeakingChanged);
connect(listener, &ParticipantDeviceListener::isMuted, this, &ParticipantDeviceModel::onIsMuted);
connect(listener, &ParticipantDeviceListener::isScreenSharingChanged, this, &ParticipantDeviceModel::onScreenSharingChanged);
connect(listener, &ParticipantDeviceListener::stateChanged, this, &ParticipantDeviceModel::onStateChanged);
connect(listener, &ParticipantDeviceListener::streamCapabilityChanged, this, &ParticipantDeviceModel::onStreamCapabilityChanged);
connect(listener, &ParticipantDeviceListener::streamAvailabilityChanged, this, &ParticipantDeviceModel::onStreamAvailabilityChanged);
@ -108,6 +109,10 @@ bool ParticipantDeviceModel::getIsMuted() const{
return mParticipantDevice ? mParticipantDevice->getIsMuted() : false;
}
bool ParticipantDeviceModel::getIsScreenSharingEnabled() const {
return mParticipantDevice ? mParticipantDevice->screenSharingEnabled() : false;
}
LinphoneEnums::ParticipantDeviceState ParticipantDeviceModel::getState() const{
return LinphoneEnums::fromLinphone(mState);
}
@ -120,6 +125,10 @@ bool ParticipantDeviceModel::isVideoEnabled() const{
return mIsVideoEnabled;
}
bool ParticipantDeviceModel::isThumbnailVideoEnabled() const{
return mIsThumbnailVideoEnabled;
}
void ParticipantDeviceModel::setPaused(bool paused){
if(mIsPaused != paused){
mIsPaused = paused;
@ -149,16 +158,51 @@ void ParticipantDeviceModel::setState(LinphoneEnums::ParticipantDeviceState stat
}
}
void ParticipantDeviceModel::updateVideoEnabled(){
bool enabled = (mParticipantDevice && mParticipantDevice->isInConference() && mParticipantDevice->getStreamAvailability(linphone::StreamType::Video) &&
( mParticipantDevice->getStreamCapability(linphone::StreamType::Video) == linphone::MediaDirection::SendRecv
|| mParticipantDevice->getStreamCapability(linphone::StreamType::Video) == linphone::MediaDirection::SendOnly
)
|| (isMe() && isLocal())) && !mIsPaused;
if( mIsVideoEnabled != enabled && mCall && mCall->getCall()->getState() == linphone::Call::State::StreamsRunning) {
qDebug() << "VideoEnabled: " << enabled << ", old=" << mIsVideoEnabled << (mParticipantDevice ? mParticipantDevice->getAddress()->asString().c_str() : "") << ", me=" << isMe() << ", isLocal=" << isLocal() << ", CallState=" << (mCall ? (int)mCall->getCall()->getState() : -1);
mIsVideoEnabled = enabled;
emit videoEnabledChanged();
void ParticipantDeviceModel::updateVideoEnabled() {
if (mCall && mCall->getCall()->getState() == linphone::Call::State::StreamsRunning) {
bool enabled = (mParticipantDevice && mParticipantDevice->isInConference()
&& mParticipantDevice->getStreamAvailability(linphone::StreamType::Video)
|| (isMe() && isLocal()))
&& !mIsPaused;
if (mIsVideoEnabled != enabled) {
qDebug() << "VideoEnabled: " << enabled << ", old=" << mIsVideoEnabled
<< (mParticipantDevice ? mParticipantDevice->getAddress()->asString().c_str()
: "")
<< ", me=" << isMe() << ", isLocal=" << isLocal() << ", inConf="
<< (mParticipantDevice ? mParticipantDevice->isInConference() : false)
<< ", CallState=" << (mCall ? (int) mCall->getCall()->getState() : -1)
<< ", StreamAvailability:"
<< (mParticipantDevice
? mParticipantDevice->getStreamAvailability(linphone::StreamType::Video)
: false)
<< ", StreamDir="
<< (mParticipantDevice ? (int) mParticipantDevice->getStreamCapability(
linphone::StreamType::Video)
: -1);
mIsVideoEnabled = enabled;
emit videoEnabledChanged();
}
enabled = (mParticipantDevice && mParticipantDevice->isInConference()
&& mParticipantDevice->getThumbnailStreamAvailability()
|| (isMe() && isLocal()))
&& !mIsPaused;
if (mIsThumbnailVideoEnabled != enabled) {
qDebug() << "ThumbnailVideoEnabled: " << enabled << ", old=" << mIsThumbnailVideoEnabled
<< (mParticipantDevice ? mParticipantDevice->getAddress()->asString().c_str()
: "")
<< ", me=" << isMe() << ", isLocal=" << isLocal() << ", inConf="
<< (mParticipantDevice ? mParticipantDevice->isInConference() : false)
<< ", CallState=" << (mCall ? (int) mCall->getCall()->getState() : -1)
<< ", StreamAvailability:"
<< (mParticipantDevice ? mParticipantDevice->getThumbnailStreamAvailability()
: false)
<< ", StreamDir="
<< (mParticipantDevice
? (int) mParticipantDevice->getThumbnailStreamCapability()
: -1);
mIsThumbnailVideoEnabled = enabled;
emit thumbnailVideoEnabledChanged();
}
}
}
@ -189,6 +233,9 @@ void ParticipantDeviceModel::onCallStatusChanged(){
}
//--------------------------------------------------------------------
void ParticipantDeviceModel::onScreenSharingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isScreenSharing) {
emit isScreenSharingEnabledChanged();
}
void ParticipantDeviceModel::onIsSpeakingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isSpeaking) {
setIsSpeaking(isSpeaking);
}
@ -216,4 +263,4 @@ void ParticipantDeviceModel::onStreamCapabilityChanged(const std::shared_ptr<lin
}
void ParticipantDeviceModel::onStreamAvailabilityChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool available, linphone::StreamType streamType) {
updateVideoEnabled();
}
}

View file

@ -50,11 +50,13 @@ public:
Q_PROPERTY(int securityLevel READ getSecurityLevel NOTIFY securityLevelChanged)
Q_PROPERTY(time_t timeOfJoining READ getTimeOfJoining CONSTANT)
Q_PROPERTY(bool videoEnabled READ isVideoEnabled NOTIFY videoEnabledChanged)
Q_PROPERTY(bool thumbnailVideoEnabled READ isThumbnailVideoEnabled NOTIFY thumbnailVideoEnabledChanged)
Q_PROPERTY(bool isMe READ isMe CONSTANT)
Q_PROPERTY(bool isLocal READ isLocal WRITE setIsLocal NOTIFY isLocalChanged)// Can change on call update. Not really used but it just in case as Object can be initialized with empty call/device.
Q_PROPERTY(bool isPaused READ getPaused WRITE setPaused NOTIFY isPausedChanged)
Q_PROPERTY(bool isSpeaking READ getIsSpeaking WRITE setIsSpeaking NOTIFY isSpeakingChanged)
Q_PROPERTY(bool isMuted READ getIsMuted NOTIFY isMutedChanged)
Q_PROPERTY(bool isScreenSharingEnabled READ getIsScreenSharingEnabled NOTIFY isScreenSharingEnabledChanged)
Q_PROPERTY(LinphoneEnums::ParticipantDeviceState state READ getState WRITE setState NOTIFY stateChanged)
QString getName() const;
@ -63,11 +65,13 @@ public:
int getSecurityLevel() const;
time_t getTimeOfJoining() const;
bool isVideoEnabled() const;
bool isThumbnailVideoEnabled() const;
bool isMe() const;
bool isLocal()const;
bool getPaused() const;
bool getIsSpeaking() const;
bool getIsMuted() const;
bool getIsScreenSharingEnabled() const;
LinphoneEnums::ParticipantDeviceState getState() const;
std::shared_ptr<linphone::ParticipantDevice> getDevice();
@ -77,6 +81,7 @@ public:
void setIsLocal(bool local);
void setState(LinphoneEnums::ParticipantDeviceState state);
virtual void onScreenSharingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isScreenSharing);
virtual void onIsSpeakingChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isSpeaking);
virtual void onIsMuted(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, bool isMuted);
virtual void onStateChanged(const std::shared_ptr<linphone::ParticipantDevice> & participantDevice, linphone::ParticipantDevice::State state);
@ -93,9 +98,11 @@ public slots:
signals:
void securityLevelChanged();
void videoEnabledChanged();
void thumbnailVideoEnabledChanged();
void isPausedChanged();
void isSpeakingChanged();
void isMutedChanged();
void isScreenSharingEnabledChanged();
void isLocalChanged();
void stateChanged();
@ -103,7 +110,8 @@ private:
bool mIsMe = false;
bool mIsLocal = false;
bool mIsVideoEnabled;
bool mIsVideoEnabled = false;
bool mIsThumbnailVideoEnabled = false;
bool mIsPaused = false;
bool mIsSpeaking = false;
linphone::ParticipantDevice::State mState;

View file

@ -100,6 +100,7 @@ void ParticipantDeviceProxyModel::setCallModel(CallModel * callModel){
sort(0);
emit countChanged();
emit meChanged();
emit activeSpeakerChanged();
}
void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant){
@ -112,6 +113,7 @@ void ParticipantDeviceProxyModel::setParticipant(ParticipantModel * participant)
sort(0);
emit countChanged();
emit meChanged();
emit activeSpeakerChanged();
}
void ParticipantDeviceProxyModel::setShowMe(const bool& show){

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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 "ScreenListModel.hpp"
#include <QGuiApplication>
#include <QPixmap>
#include <QScreen>
ScreenListModel::ScreenListModel(QObject *parent) : ProxyAbstractListModel(parent) {
mList = QGuiApplication::screens();
}
ScreenListModel::~ScreenListModel() {
}
QVariant ScreenListModel::data(const QModelIndex &index, int role) const {
int row = index.row();
if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant();
if (role == Qt::DisplayRole) {
auto screen = mList[row];
QVariantMap data;
data["name"] = screen->name();
data["screenshot"] = QVariant::fromValue(screen->grabWindow(0));
return data;
}
return QVariant();
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#ifndef SCREEN_LIST_MODEL_H_
#define SCREEN_LIST_MODEL_H_
#include "app/proxyModel/ProxyListModel.hpp"
#include <QScreen>
class ScreenListModel : public ProxyAbstractListModel<QScreen*> {
Q_OBJECT
public:
ScreenListModel(QObject *parent = Q_NULLPTR);
virtual ~ScreenListModel();
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
};
#endif

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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 <QQuickWindow>
#include "ScreenListModel.hpp"
#include "ScreenProxyModel.hpp"
// =============================================================================
ScreenProxyModel::ScreenProxyModel(QObject *parent) : SortFilterProxyModel(parent) {
setSourceModel(new ScreenListModel(this));
sort(0);
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#ifndef SCREEN_PROXY_MODEL_H_
#define SCREEN_PROXY_MODEL_H_
#include "app/proxyModel/SortFilterProxyModel.hpp"
// =============================================================================
class QWindow;
class ScreenProxyModel : public SortFilterProxyModel {
class ScreenModelFilter;
Q_OBJECT
public:
ScreenProxyModel(QObject *parent = Q_NULLPTR);
};
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,69 @@
/*
* Copyright (c) 2021 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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 "VideoSourceDescriptorModel.hpp"
#include "components/other/desktop-tools/DesktopTools.hpp"
// =============================================================================
VideoSourceDescriptorModel::VideoSourceDescriptorModel() {
}
VideoSourceDescriptorModel::VideoSourceDescriptorModel(std::shared_ptr<linphone::VideoSourceDescriptor> desc) {
mDesc = desc;
if(mDesc && mDesc->getScreenSharingType() == linphone::VideoSourceScreenSharingType::Display)
mScreenIndex = DesktopTools::getDisplayIndex(mDesc->getScreenSharing());
}
void VideoSourceDescriptorModel::setScreenSharingDisplay(int index) {
if(!mDesc) mDesc = linphone::Factory::get()->createVideoSourceDescriptor();
mScreenIndex = index;
mDesc->setScreenSharing(linphone::VideoSourceScreenSharingType::Display, DesktopTools::getDisplay(index));
emit videoDescriptorChanged();
}
void VideoSourceDescriptorModel::setScreenSharingWindow(void *window){ // Get data from DesktopTools.
if(!mDesc) mDesc = linphone::Factory::get()->createVideoSourceDescriptor();
else if(getVideoSourceType() == LinphoneEnums::VideoSourceScreenSharingTypeWindow && window == getScreenSharing())
return;
mDesc->setScreenSharing(linphone::VideoSourceScreenSharingType::Window, window);
emit videoDescriptorChanged();
}
void * VideoSourceDescriptorModel::getScreenSharing() const{
if(!mDesc)
return nullptr;
else
return mDesc->getScreenSharing();
}
bool VideoSourceDescriptorModel::isScreenSharing() const{
return mDesc && mDesc->getType() == linphone::VideoSourceType::ScreenSharing;
}
LinphoneEnums::VideoSourceScreenSharingType VideoSourceDescriptorModel::getVideoSourceType() const{
return mDesc ? LinphoneEnums::fromLinphone(mDesc->getScreenSharingType()) : LinphoneEnums::VideoSourceScreenSharingType::VideoSourceScreenSharingTypeDisplay;
}
int VideoSourceDescriptorModel::getScreenSharingIndex() const{
if(mDesc && mDesc->getScreenSharingType() == linphone::VideoSourceScreenSharingType::Display) {
return mScreenIndex;
}else
return -1;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (c) 2021 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* 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/>.
*/
#ifndef VIDEO_SOURCE_DESCRIPTOR_MODEL_H_
#define VIDEO_SOURCE_DESCRIPTOR_MODEL_H_
#include <linphone++/linphone.hh>
// =============================================================================
#include <QObject>
#include <QDateTime>
#include <QString>
#include <QSharedPointer>
#include "utils/LinphoneEnums.hpp"
class VideoSourceDescriptorModel : public QObject{
Q_OBJECT
Q_PROPERTY(bool isScreenSharing READ isScreenSharing NOTIFY videoDescriptorChanged)
Q_PROPERTY(LinphoneEnums::VideoSourceScreenSharingType screenSharingType READ getVideoSourceType NOTIFY videoDescriptorChanged)
Q_PROPERTY(int screenSharingIndex READ getScreenSharingIndex WRITE setScreenSharingDisplay NOTIFY videoDescriptorChanged)
public:
VideoSourceDescriptorModel();
VideoSourceDescriptorModel(std::shared_ptr<linphone::VideoSourceDescriptor> desc);
void setScreenSharingDisplay(int index);
void setScreenSharingWindow(void *window); // Get data from DesktopTools.
void *getScreenSharing() const;
bool isScreenSharing() const;
LinphoneEnums::VideoSourceScreenSharingType getVideoSourceType() const;
int getScreenSharingIndex() const;
std::shared_ptr<linphone::VideoSourceDescriptor> mDesc;
int mScreenIndex = 0;
signals:
void videoDescriptorChanged();
};
#endif

View file

@ -38,6 +38,7 @@ void LinphoneEnums::registerMetaTypes(){
qRegisterMetaType<LinphoneEnums::RecorderState>();
qRegisterMetaType<LinphoneEnums::TunnelMode>();
qRegisterMetaType<LinphoneEnums::TransportType>();
qRegisterMetaType<LinphoneEnums::VideoSourceScreenSharingType>();
qRegisterMetaType<std::shared_ptr<linphone::Call>>();
qRegisterMetaType<linphone::Call::State>();
@ -171,3 +172,11 @@ void LinphoneEnums::fromString(const QString& transportType, LinphoneEnums::Tran
else
*transport = TransportTypeDtls;
}
linphone::VideoSourceScreenSharingType LinphoneEnums::toLinphone(const LinphoneEnums::VideoSourceScreenSharingType& type){
return static_cast<linphone::VideoSourceScreenSharingType>(type);
}
LinphoneEnums::VideoSourceScreenSharingType LinphoneEnums::fromLinphone(const linphone::VideoSourceScreenSharingType& type){
return static_cast<LinphoneEnums::VideoSourceScreenSharingType>(type);
}

View file

@ -214,6 +214,17 @@ linphone::TransportType toLinphone(const LinphoneEnums::TransportType& type);
LinphoneEnums::TransportType fromLinphone(const linphone::TransportType& type);
QString toString(const LinphoneEnums::TransportType& type);
void fromString(const QString& transportType, LinphoneEnums::TransportType *transport);
enum VideoSourceScreenSharingType{
VideoSourceScreenSharingTypeArea = int(linphone::VideoSourceScreenSharingType::Area),
VideoSourceScreenSharingTypeDisplay = int(linphone::VideoSourceScreenSharingType::Display),
VideoSourceScreenSharingTypeWindow = int(linphone::VideoSourceScreenSharingType::Window)
};
Q_ENUM_NS(VideoSourceScreenSharingType)
linphone::VideoSourceScreenSharingType toLinphone(const LinphoneEnums::VideoSourceScreenSharingType& type);
LinphoneEnums::VideoSourceScreenSharingType fromLinphone(const linphone::VideoSourceScreenSharingType& type);
}
@ -230,6 +241,7 @@ Q_DECLARE_METATYPE(LinphoneEnums::ParticipantDeviceState)
Q_DECLARE_METATYPE(LinphoneEnums::RecorderState)
Q_DECLARE_METATYPE(LinphoneEnums::TunnelMode)
Q_DECLARE_METATYPE(LinphoneEnums::TransportType)
Q_DECLARE_METATYPE(LinphoneEnums::VideoSourceScreenSharingType)
Q_DECLARE_METATYPE(std::shared_ptr<linphone::Call>)
Q_DECLARE_METATYPE(linphone::Call::State)

View file

@ -680,6 +680,12 @@ bool Utils::isMe(const std::shared_ptr<const linphone::Address>& address){
}else
return address ? CoreManager::getInstance()->getAccountSettingsModel()->getUsedSipAddress()->weakEqual(address) : false;
}
bool Utils::isLocal(const std::shared_ptr<linphone::Conference>& conference, const std::shared_ptr<const linphone::ParticipantDevice>& device) {
auto deviceAddress = device->getAddress();
auto callAddress = conference->getMe()->getAddress();
auto gruuAddress = CoreManager::getInstance()->getAccountSettingsModel()->findAccount(callAddress)->getContactAddress();
return deviceAddress->equal(gruuAddress);
}
bool Utils::isAnimatedImage(const QString& path){
if(path.isEmpty()) return false;

View file

@ -195,6 +195,8 @@ public:
static QString computeUserAgent(const std::shared_ptr<linphone::Config>& config);
static bool isMe(const std::shared_ptr<const linphone::Address>& address);
static bool isLocal(const std::shared_ptr<linphone::Conference>& conference, const std::shared_ptr<const linphone::ParticipantDevice>& device);
static void deleteAllUserData();
static void deleteAllUserDataOffline();// When we are out of all events and core is not running (aka in main())

View file

@ -26,10 +26,12 @@ Item {
property color borderColorPressed
property alias text: button.text
property alias textColor: button.textColor
property bool enabled: true
property bool showBorder : false
property alias toggled : button.checked
property alias button: button
property alias radius: button.radius
property alias capitalization : button.capitalization
//Additional size around text
@ -81,16 +83,18 @@ Item {
Button {
id: button
property int capitalization
property color textColor: _getTextColor()
property int radius: AbstractTextButtonStyle.background.radius
background: Rectangle {
color: _getBackgroundColor()
radius: AbstractTextButtonStyle.background.radius
radius: button.radius
border.color: _getBorderColor()
border.width: (showBorder ? 1 : 0)
}
contentItem: Text {
color: _getTextColor()
color: button.textColor
font {
bold: true
pointSize: AbstractTextButtonStyle.text.pointSize

View file

@ -16,7 +16,10 @@ Control.RadioButton{
font.weight: checked ? RadioButtonStyle.selectedWeight : RadioButtonStyle.weight
font.pointSize: RadioButtonStyle.pointSize
spacing: 10
FontMetrics{id: fontMetrics}
FontMetrics{
id: fontMetrics
font: radio.font
}
MouseArea{
anchors.fill:parent
@ -29,7 +32,8 @@ Control.RadioButton{
height: fontMetrics.height - 5
width: height
x: parent.leftPadding
y: parent.height / 2 - height / 2
//y: parent.height / 2 - (textItem.lineCount > 1 ? height / 2 : 0)
y: height / 2
radius: width/2
border.color: RadioButtonStyle.colorModel.color
property bool checked: parent.checked
@ -44,14 +48,15 @@ Control.RadioButton{
}
}
contentItem: Text{
id: textItem
text: parent.text
font: parent.font
width: parent.width - (parent.indicator.width + parent.spacing)
height: implicitHeight
y:0
//height: implicitHeight
//y:0
// Override unwanted auto changes
onYChanged: y = 0
onHeightChanged: height=implicitHeight
//onYChanged: y = 0
//onHeightChanged: height=implicitHeight
//---------------------------------------
color: RadioButtonStyle.colorModel.color
verticalAlignment: Text.AlignVCenter
@ -59,4 +64,4 @@ Control.RadioButton{
wrapMode: Text.WordWrap
elide: Text.ElideRight
}
}
}

View file

@ -12,12 +12,13 @@ Item {
property color backgroundColor: '#00000000'
property color foregroundColor: '#00000000'
readonly property alias status: image.status
property int radius: width/2
Rectangle {
id: backgroundArea
anchors.fill: parent
color: item.backgroundColor
radius: width/2
radius: item.radius
}
Image {
id: image
@ -36,6 +37,6 @@ Item {
anchors.fill: parent
visible: color != 'transparent'
color: item.foregroundColor
radius: width/2
radius: item.radius
}
}

View file

@ -25,13 +25,15 @@ Item {
property bool hideCamera: false
property bool isPaused: false
property bool deactivateCamera: true
property bool isThumbnail: false
property bool isDeviceVideoEnabled: container.currentDevice && (isThumbnail ? container.currentDevice.thumbnailVideoEnabled : container.currentDevice.videoEnabled)
property bool isVideoEnabled: !deactivateCamera && (!callModel || callModel.videoEnabled)
&& (!container.currentDevice || ( callModel && container.currentDevice &&
( (! (container.currentDevice.isMe && container.currentDevice.isLocal) && container.currentDevice.videoEnabled)
( (! (container.currentDevice.isMe && container.currentDevice.isLocal) && isDeviceVideoEnabled)
|| (container.currentDevice.isMe && container.currentDevice.isLocal && callModel.cameraEnabled))))
property bool a : callModel && callModel.videoEnabled
property bool b: container.currentDevice && container.currentDevice.videoEnabled
property bool b: isDeviceVideoEnabled
property bool c: container.currentDevice && container.currentDevice.isMe && container.currentDevice.isLocal
property bool d : callModel && callModel.cameraEnabled
property bool isReady: cameraLoader.item && cameraLoader.item.isReady

View file

@ -21,6 +21,7 @@ Item{
property alias isPreview: camera.isPreview
property alias isFullscreen: camera.isFullscreen
property alias isCameraFromDevice: camera.isCameraFromDevice
property alias isThumbnail: camera.isThumbnail
property alias qmlName: camera.qmlName
property bool showCloseButton: false
property bool showActiveSpeakerOverlay: true

View file

@ -11,6 +11,7 @@ import Linphone.Styles 1.0
import LinphoneEnums 1.0
import UtilsCpp 1.0
import DesktopTools 1.0
import App.Styles 1.0
@ -23,6 +24,8 @@ Rectangle{
property ParticipantModel me: conferenceModel ? conferenceModel.localParticipant : null
property bool isMeAdmin: me && me.adminStatus
property bool isParticipantsMenu: false
property bool isScreenSharingMenu: false
property bool screenSharingAvailable: conferenceModel && (!conferenceModel.isScreenSharingEnabled || conferenceModel.isLocalScreenSharingEnabled)
signal close()
signal layoutChanging(int layoutMode)
@ -40,13 +43,18 @@ Rectangle{
//: 'Invite participants' : Menu title to invite participants in admin mode.
mainItem.isMeAdmin ? qsTr('incallMenuInvite')
//: 'Participants list' : Menu title to show participants in non-admin mode.
: qsTr('incallMenuParticipants')
: qsTr('incallMenuParticipants'),
"Partage d'écran"
]
function showParticipantsMenu(){
contentsStack.push(participantsMenu, {title:Qt.binding(function() { return mainItem.menuTitles[2]})})
visible = true
}
function showScreenSharingMenu(){
contentsStack.push(screenSharingMenu, {title:Qt.binding(function() { return mainItem.menuTitles[3]})})
visible = true
}
onVisibleChanged: if(!visible && contentsStack.nViews > 1) {
contentsStack.pop()
}
@ -59,6 +67,7 @@ Rectangle{
}
}
ButtonGroup{id: modeGroup}
ButtonGroup{id: screenSharingGroup}
ColumnLayout{
anchors.fill: parent
// HEADER
@ -134,7 +143,12 @@ Rectangle{
{ titleIndex: 2
, icon: IncallMenuStyle.settingsIcons.participantsIcon
, nextPage:participantsMenu
, visible: mainItem.callModel && mainItem.callModel.isConference}
, visible: mainItem.callModel && mainItem.callModel.isConference},
{ titleIndex: 3
, icon: IncallMenuStyle.settingsIcons.screenSharingIcon
, nextPage: screenSharingMenu
, visible: mainItem.screenSharingAvailable && SettingsModel.videoAvailable}
]
delegate:
Borders{
@ -226,11 +240,12 @@ Rectangle{
Layout.fillWidth: true
Repeater{
//: 'Mosaic mode' : Grid layout for video conference.
model: [{text: qsTr('incallMenuGridLayout'), icon: IncallMenuStyle.modeIcons.gridIcon, value:LinphoneEnums.ConferenceLayoutGrid}
model: [{text: qsTr('incallMenuGridLayout'), icon: IncallMenuStyle.modeIcons.gridIcon, value:LinphoneEnums.ConferenceLayoutGrid, enabled: (!mainItem.conferenceModel
|| (mainItem.conferenceModel.participantDeviceCount <= SettingsModel.conferenceMaxThumbnails+1 && !mainItem.conferenceModel.isScreenSharingEnabled))}
//: 'Active speaker mode' : Active speaker layout for video conference.
, {text: qsTr('incallMenuActiveSpeakerLayout'), icon: IncallMenuStyle.modeIcons.activeSpeakerIcon, value:LinphoneEnums.ConferenceLayoutActiveSpeaker}
, {text: qsTr('incallMenuActiveSpeakerLayout'), icon: IncallMenuStyle.modeIcons.activeSpeakerIcon, value:LinphoneEnums.ConferenceLayoutActiveSpeaker, enabled: true}
//: 'Audio only mode' : Audio only layout for video conference.
, {text: qsTr('incallMenuAudioLayout'), icon: IncallMenuStyle.modeIcons.audioOnlyIcon, value:LinphoneEnums.ConferenceLayoutAudioOnly}
, {text: qsTr('incallMenuAudioLayout'), icon: IncallMenuStyle.modeIcons.audioOnlyIcon, value:LinphoneEnums.ConferenceLayoutAudioOnly, enabled: true}
]
delegate:
Borders{
@ -238,7 +253,8 @@ Rectangle{
bottomWidth: IncallMenuStyle.list.border.width
Layout.preferredHeight: Math.max(layoutIcon.height, radio.contentItem.implicitHeight) + 20
Layout.fillWidth: true
enabled: mainItem.callModel && !mainItem.callModel.updating
enabled: mainItem.callModel && !mainItem.callModel.updating && modelData.enabled
opacity: enabled ? 1.0 : 0.5
MouseArea{
anchors.fill: parent
onClicked: radio.clicked()
@ -310,5 +326,130 @@ Rectangle{
Component.onDestruction: mainItem.isParticipantsMenu = false
}
}
//-----------------------------------------------------------------------------------------------------------------------------
Component{
id: screenSharingMenu
ColumnLayout{
id: screenSharingItem
property VideoSourceDescriptorModel desc: mainItem.callModel.videoSourceDescriptorModel
property string title
Layout.fillHeight: true
Layout.fillWidth: true
RadioButton{
id: displayRadioButton
Layout.fillWidth: true
Layout.leftMargin: 15
text: "Partager l'intégralité de l'écran"
font.pointSize: IncallMenuStyle.list.pointSize
ButtonGroup.group: screenSharingGroup
//checked: screenList.selectedIndex >= 0
checked: screenSharingItem.desc && screenSharingItem.desc.isScreenSharing && screenSharingItem.desc.screenSharingType == LinphoneEnums.VideoSourceScreenSharingTypeDisplay
onClicked: {
screenSharingItem.desc.screenSharingIndex = 0
if( mainItem.conferenceModel.isLocalScreenSharingEnabled)
mainItem.callModel.setVideoSourceDescriptorModel(screenSharingItem.desc)
}
}
ListView{
id: screenList
property int selectedIndex: displayRadioButton.checked ? screenSharingItem.desc.screenSharingIndex : -1
Layout.fillWidth: true
Layout.leftMargin: 15
Layout.preferredWidth: parent.width
Layout.preferredHeight: 100
orientation: ListView.Horizontal
model: ScreenProxyModel{}
spacing: 10
delegate:Rectangle{
width: 114 + 10
height: 100
border.color: 'red'
border.width: index == screenList.selectedIndex ? 1 : 0
radius: 10
ColumnLayout{
anchors.fill: parent
anchors.margins: 5
RoundedImage{
Layout.preferredWidth: 114
Layout.preferredHeight: 64
backgroundColor: 'white'
source: 'image://screen/'+index
radius: 10
}
Text{
Layout.fillWidth: true
text: modelData.name
font.pointSize: IncallMenuStyle.list.pointSize
color: IncallMenuStyle.list.colorModel.color
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
}
MouseArea{
anchors.fill: parent
onClicked: {
screenSharingItem.desc.screenSharingIndex = index
if( mainItem.conferenceModel.isLocalScreenSharingEnabled)
mainItem.callModel.setVideoSourceDescriptorModel(screenSharingItem.desc)
}
}
}
}
Rectangle{
Layout.fillWidth: true
Layout.preferredHeight: IncallMenuStyle.list.border.width
color: IncallMenuStyle.list.border.colorModel.color
}
RadioButton{
id: windowSharingRadioButton
Layout.fillWidth: true
Layout.leftMargin: 15
text: "Partager une fenêtre"
font.pointSize: IncallMenuStyle.list.pointSize
ButtonGroup.group: screenSharingGroup
checked: screenSharingItem.desc && screenSharingItem.desc.isScreenSharing && screenSharingItem.desc.screenSharingType == LinphoneEnums.VideoSourceScreenSharingTypeWindow//screenList.selectedIndex < 0
onClicked: DesktopTools.getWindowIdFromMouse(screenSharingItem.desc)
Connections{
target: DesktopTools
onWindowIdSelectionEnded: if( mainItem.conferenceModel.isLocalScreenSharingEnabled)
mainItem.callModel.setVideoSourceDescriptorModel(screenSharingItem.desc)
}
}
Rectangle{
Layout.fillWidth: true
Layout.preferredHeight: IncallMenuStyle.list.border.width
color: IncallMenuStyle.list.border.colorModel.color
}
Item{// Spacer
Layout.fillWidth: true
Layout.fillHeight: true
}
Item{// Item encapsulation because of a bug on width update when changing text
Layout.fillWidth: true
Layout.preferredHeight: screenSharingButton.fitHeight
Layout.margins: 20
TextButtonB{
id: screenSharingButton
anchors.fill: parent
visible: mainItem.screenSharingAvailable
enabled: displayRadioButton.checked || windowSharingRadioButton.checked
text: mainItem.conferenceModel && mainItem.conferenceModel.isLocalScreenSharingEnabled
//: 'Stop' : Text button to stop the screen sharing.
? qsTr('incallMenuScreenSharingStop')
//: 'Share' : Text button to start the screen sharing.
: qsTr('incallMenuScreenSharingStart')
capitalization: Font.AllUppercase
onClicked: mainItem.conferenceModel.toggleScreenSharing()
Connections{
target: mainItem.conferenceModel
onLocalScreenSharingChanged: (enabled) => {if(enabled) mainItem.callModel.setVideoSourceDescriptorModel(screenSharingItem.desc) }
}
}
}
Component.onCompleted: mainItem.isScreenSharingMenu = true
Component.onDestruction: mainItem.isScreenSharingMenu = false
}
}
}
}

View file

@ -23,6 +23,7 @@ DecorationSticker{
property alias isCameraFromDevice: camera.isCameraFromDevice
property alias isReady: camera.isReady
property alias isVideoEnabled: camera.isVideoEnabled
property alias isThumbnail: camera.isThumbnail
property alias cameraQmlName: camera.qmlName
property bool showCloseButton: false
property bool showActiveSpeakerOverlay: true

View file

@ -37,6 +37,7 @@ Item{
property alias deactivateCamera: camera.deactivateCamera
readonly property alias isVideoEnabled: camera.isVideoEnabled
property alias cameraQmlName: camera.cameraQmlName
property alias isThumbnail: camera.isThumbnail
property alias image: avatar.image
property alias avatarBackgroundColor: avatar.avatarBackgroundColor

View file

@ -45,6 +45,8 @@ QtObject {
property string audioOnlyIcon: 'conference_audio_only_custom'
property string mediaIcon: 'micro_on_custom'
property string participantsIcon: 'participants_custom'
property string screenSharingIcon: 'screen_sharing_custom'
property int width: 40
property int height: 40
}

View file

@ -9,6 +9,7 @@ import Linphone 1.0
import LinphoneEnums 1.0
import UtilsCpp 1.0
import DesktopTools 1.0
import App.Styles 1.0
@ -28,6 +29,8 @@ Rectangle {
property bool previewIsReady : false
property bool isFullScreen: false // Use this variable to test if we are in fullscreen. Do not test _fullscreen : we need to clean memory before having the window (see .js file)
property bool layoutChanging: false
property bool isLocalScreenSharingEnabled: conferenceModel && conferenceModel.isLocalScreenSharingEnabled
property bool isScreenSharingEnabled: conferenceModel && conferenceModel.isScreenSharingEnabled
property var _fullscreen: null
on_FullscreenChanged: if( !_fullscreen) isFullScreen = false
@ -251,12 +254,41 @@ Rectangle {
}
}
// Mode buttons
TextButtonB{
id: screenSharingButton
visible: mainItem.isScreenSharingEnabled
Layout.preferredWidth: fitWidth
Icon{
id: screenSharingIcon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
icon: IncallStyle.buttons.screenSharing.icon
iconSize: IncallStyle.buttons.screenSharing.iconSize
overwriteColor: screenSharingButton.textColor
}
button.leftPadding: screenSharingIcon.width
addHeight: 15
addWidth: 75
radius: height/4
text: mainItem.isLocalScreenSharingEnabled ? "Arrêter la présentation" : "Présentation en cours"
onClicked: if(mainItem.isLocalScreenSharingEnabled) conferenceModel.toggleScreenSharing()
}
ActionButton{
visible: !screenSharingButton.visible && callModel && mainItem.conferenceModel && callModel.videoEnabled
isCustom: true
backgroundRadius: width/2
colorSet: IncallStyle.buttons.screenSharing
visible: false //TODO
toggled: rightMenu.visible && rightMenu.isScreenSharingMenu
onClicked: {
if(toggled)
rightMenu.visible = false
else
rightMenu.showScreenSharingMenu()
}
}
ActionButton {
id: recordingSwitch
isCustom: true
@ -637,10 +669,20 @@ Rectangle {
onClicked: rightMenu.visible = !rightMenu.visible
}
}
Connections{
target: DesktopTools
onWindowIdSelectionStarted: window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), {
descriptionText: "Click on the window that you want to share."
, showButtonOnly: 42
, buttonTexts : ['']
})
onWindowIdSelectionEnded: window.detachVirtualWindow()
}
// ---------------------------------------------------------------------------
// TelKeypad.
// ---------------------------------------------------------------------------
CallStatistics {
id: callStatistics

View file

@ -27,14 +27,15 @@ Item {
property bool cameraEnabled: true
property bool isConference: callModel && callModel.isConference
property bool isConferenceReady: isConference && callModel.conferenceModel && callModel.conferenceModel.isReady
property ConferenceModel conferenceModel: callModel && callModel.conferenceModel
property bool isScreenSharingEnabled: conferenceModel && conferenceModel.isScreenSharingEnabled
property bool isLocalScreenSharingEnabled: conferenceModel && conferenceModel.isLocalScreenSharingEnabled
property int participantCount: isConference ? allDevices.count + 1 : 2 // +me. allDevices==0 if !conference
property ParticipantDeviceProxyModel participantDevices : ParticipantDeviceProxyModel {
id: allDevices
callModel: mainItem.callModel
showMe: false
showMe: false
onConferenceCreated: cameraView.resetCamera()
}
@ -59,7 +60,7 @@ Item {
|| !mainItem.isConferenceReady
: (callModel && (callModel.pausedByUser || callModel.status === CallModel.CallStatusPaused || !callModel.videoEnabled) )
|| currentDevice && !currentDevice.videoEnabled
isPreview: !preview.visible && mainItem.participantCount == 1
isPreview: !preview.visible && mainItem.participantCount == 1 && !isScreenSharingEnabled
onIsPreviewChanged: {cameraView.resetCamera() }
isCameraFromDevice: isPreview
isPaused: isPreview && callModel.pausedByUser
@ -86,7 +87,7 @@ Item {
height: visible ? miniViews.cellHeight : 0
width: 16 * height / 9
visible: mainItem.isConferenceReady && allDevices.count >= 1
visible: mainItem.isConferenceReady && (allDevices.count >= 1 || mainItem.isLocalScreenSharingEnabled)
|| (!mainItem.isConference && mainItem.callModel && mainItem.callModel.cameraEnabled)// use videoEnabled if we want to show the preview sticker
Loader{
@ -152,7 +153,7 @@ Item {
id: miniViews
property int cellHeight: 150
anchors.fill: parent
model : mainItem.isConference && mainItem.participantDevices.count > 1 ? mainItem.participantDevices : []
model : /*mainItem.isConference && mainItem.participantDevices.count > 1 ? */mainItem.participantDevices //: []
spacing: 0
verticalLayoutDirection: ListView.BottomToTop
fitCacheToContent: false
@ -162,7 +163,7 @@ Item {
delegate:Item{
height: visible ? miniViews.cellHeight + 15 : 0
width: visible ? miniViews.width : 0
visible: cameraView.currentDevice != modelData
visible: cameraView.currentDevice != modelData || mainItem.isScreenSharingEnabled
clip:false
Sticker{
id: miniView
@ -172,8 +173,9 @@ Item {
anchors.rightMargin: 3
anchors.bottomMargin: 18
cameraQmlName: 'S_'+index
isThumbnail: true
deactivateCamera: (!mainItem.isConferenceReady || !mainItem.isConference)
&& (index <0 || !mainItem.cameraEnabled || (!modelData.videoEnabled) || (callModel && callModel.pausedByUser) )
&& (index <0 || !mainItem.cameraEnabled || (!modelData.thumbnailVideoEnabled) || (callModel && callModel.pausedByUser) )
currentDevice: modelData.isPreview ? null : modelData
callModel: modelData.isPreview ? null : mainItem.callModel
isCameraFromDevice: mainItem.isConference

View file

@ -38,6 +38,8 @@ Window {
console.info('[QML] Call becomes not ready : exit fullscreen')
window.exit()
}
property bool isLocalScreenSharingEnabled: conferenceModel && conferenceModel.isLocalScreenSharingEnabled
property bool isScreenSharingEnabled: conferenceModel && conferenceModel.isScreenSharingEnabled
// ---------------------------------------------------------------------------
function exit (cb) {
@ -223,11 +225,39 @@ Window {
font.pointSize: IncallStyle.title.pointSize
}
// Mode buttons
TextButtonB{
id: screenSharingButton
visible: window.isScreenSharingEnabled
Layout.preferredWidth: fitWidth
Icon{
id: screenSharingIcon
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: 10
icon: IncallStyle.buttons.screenSharing.icon
iconSize: IncallStyle.buttons.screenSharing.iconSize
overwriteColor: screenSharingButton.textColor
}
button.leftPadding: screenSharingIcon.width
addHeight: 15
addWidth: 75
radius: height/4
text: window.isLocalScreenSharingEnabled ? "Arrêter la présentation" : "Présentation en cours"
onClicked: if(window.isLocalScreenSharingEnabled) conferenceModel.toggleScreenSharing()
}
ActionButton{
visible: !screenSharingButton.visible && callModel && window.conferenceModel && callModel.videoEnabled
isCustom: true
backgroundRadius: width/2
colorSet: IncallStyle.buttons.screenSharing
visible: false //TODO
toggled: rightMenu.visible && rightMenu.isScreenSharingMenu
onClicked: {
if(toggled)
rightMenu.visible = false
else
rightMenu.showScreenSharingMenu()
}
}
ActionButton {
id: recordingSwitch

View file

@ -3,6 +3,7 @@ import QtGraphicalEffects 1.12
import Common 1.0
import Linphone 1.0
import DesktopTools 1.0
import App.Styles 1.0
@ -11,6 +12,8 @@ import App.Styles 1.0
DialogPlus {
id: dialog
buttons: [
TextButtonB {
text: qsTr('confirm')
@ -26,8 +29,9 @@ DialogPlus {
width: SettingsVideoPreviewStyle.width
// ---------------------------------------------------------------------------
Item{
MouseArea{
anchors.fill: parent
onClicked: DesktopTools.getWindowIdFromMouse()
CameraView{
id: previewLoader
anchors.centerIn: parent

View file

@ -5,6 +5,8 @@ import Common 1.0
import Linphone 1.0
import LinphoneEnums 1.0
import Utils 1.0
import Units 1.0
import DesktopTools 1.0
import App.Styles 1.0
@ -288,6 +290,7 @@ TabContainer {
}
}
FormLine {
width: parent.width
FormGroup {
//: 'Default video layout' : Label to choose the default layout in video conference.
label: qsTr('videoLayout')
@ -303,6 +306,17 @@ TabContainer {
onActivated: SettingsModel.videoConferenceLayout = model[index].value
}
}
FormGroup {
label: 'Maximum thumbnails'
NumericField {
maxValue: 60
minValue: 0
text: SettingsModel.conferenceMaxThumbnails
onEditingFinished: SettingsModel.conferenceMaxThumbnails = text
}
}
}
}
}

View file

@ -226,9 +226,12 @@ QtObject {
property var backgroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_n', icon, 's_n_b_bg')
property var backgroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_h', icon, 's_h_b_bg')
property var backgroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_p', icon, 's_p_b_bg')
property var backgroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_bg_c', icon, 's_p_b_bg')
property var foregroundNormalColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_n', icon, 's_n_b_fg')
property var foregroundHoveredColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_h', icon, 's_h_b_fg')
property var foregroundPressedColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_p', icon, 's_p_b_fg')
property var foregroundUpdatingColor : ColorsList.addImageColor(sectionName+'_'+name+'_fg_c', icon, 's_p_b_fg')
}
property QtObject record: QtObject {
property int iconSize: 40

@ -1 +1 @@
Subproject commit e310a060639474dd333239810d86d14db5f9c83d
Subproject commit 119c43076dbfc48ee25642c3bff52b975b08194e