Screen sharing : Display a preview when a Window has been selected.

This commit is contained in:
Julien Wadel 2024-03-05 14:04:02 +01:00
parent ae686db48c
commit d7a5f3898b
15 changed files with 176 additions and 18 deletions

View file

@ -833,9 +833,12 @@ endif()
target_link_libraries(${TARGET_NAME} ${APP_PLUGIN})
if(WIN32 AND ENABLE_LDAP)
find_package(OpenLDAP REQUIRED)
target_link_libraries(${TARGET_NAME} wsock32 ws2_32 ${OPENLDAP_LIBRARIES})
if(WIN32)
if(ENABLE_LDAP)
find_package(OpenLDAP REQUIRED)
target_link_libraries(${TARGET_NAME} wsock32 ws2_32 ${OPENLDAP_LIBRARIES})
endif()
target_link_libraries(${TARGET_NAME} Dwmapi)
endif()
if(NOT WIN32)

View file

@ -495,6 +495,7 @@ void App::initContentApp () {
mEngine->addImageProvider(QRCodeProvider::ProviderId, new QRCodeProvider());
mEngine->addImageProvider(ThumbnailProvider::ProviderId, new ThumbnailProvider());
mEngine->addImageProvider(ScreenProvider::ProviderId, new ScreenProvider());
mEngine->addImageProvider(WindowProvider::ProviderId, new WindowProvider());
mEngine->rootContext()->setContextProperty("applicationName", APPLICATION_NAME);
mEngine->rootContext()->setContextProperty("executableName", EXECUTABLE_NAME);

View file

@ -21,6 +21,9 @@
#include "ScreenProvider.hpp"
#include <QGuiApplication>
#include <QScreen>
#include <QThread>
#include <QWindow>
#include "components/other/desktop-tools/DesktopTools.hpp"
// =============================================================================
@ -31,13 +34,73 @@ ScreenProvider::ScreenProvider () : QQuickImageProvider(
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();
QImage ScreenProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) {
if(!requestedSize.isNull()) {
int index = id.toInt();
auto screens = QGuiApplication::screens();
if(index >= 0 && index < screens.size()){
auto screen = screens[index];
auto geometry = screen->geometry();
#if __APPLE__
auto image = screen->grabWindow(0, geometry.x(), geometry.y() ,geometry.width(), geometry.height()).scaled(requestedSize, Qt::KeepAspectRatio,Qt::SmoothTransformation);
#else
auto image = screen->grabWindow(0, 0, 0,geometry.width(), geometry.height()).scaled(requestedSize, Qt::KeepAspectRatio,Qt::SmoothTransformation);
#endif
*size = image.size();
qDebug() << "Screen(" << index << ") = " << screen->geometry() << " VG:" << screen->virtualGeometry() << " / " << screen->size() << " VS:" << screen->virtualSize() << " DeviceRatio: " << screen->devicePixelRatio();
return image.toImage();
}
}
QImage image(10,10, QImage::Format_Indexed8);
image.fill(Qt::gray);
*size = image.size();
return image;
}
// =============================================================================
const QString WindowProvider::ProviderId = "window";
WindowProvider::WindowProvider () : QQuickImageProvider(
QQmlImageProviderBase::Image,
QQmlImageProviderBase::ForceAsynchronousImageLoading
) {}
// Note: Using WId don't work with Window/Mac (For Mac, NSView cannot be used while we are working with CGWindowID)
// Linux seems to be ok but we want to use the same code.
QImage WindowProvider::requestImage (const QString &id, QSize *size, const QSize &requestedSize) {
if(!requestedSize.isNull()) {
auto winId = id.toLongLong();
if(winId > 0){
QRect area = DesktopTools::getWindowGeometry(reinterpret_cast<void*>(winId));
if(!area.isNull()) {
qDebug() << "Window (" << winId << ") = " << area;
auto screens = QGuiApplication::screens();
for(auto screen : screens){
auto geometry = screen->geometry();
auto devicePixelRatio = screen->devicePixelRatio();
// Warning: there are inconsistencies in geomtries from OS.
#if defined(__APPLE__)
devicePixelRatio = 1.0;// Doesn't apply device ratio.
// Change area to be absolute
area.setRect(area.x() + geometry.x(), area.y() + geometry.y(), area.width(), area.height());
#endif
geometry.setWidth(geometry.width() * devicePixelRatio);
geometry.setHeight(geometry.height() * devicePixelRatio);
if( geometry.contains(area.center())){
QThread::msleep(40);// Let OS some time to display properly the Window after a click.
qDebug() << "Grab (" << (area.x() - geometry.x())/devicePixelRatio<< ", " << (area.y() - geometry.y())/devicePixelRatio << ", " << area.width()/devicePixelRatio << ", " << area.height()/devicePixelRatio << ")";
auto image = screen->grabWindow(0, (area.x() - geometry.x())/devicePixelRatio, (area.y() - geometry.y())/devicePixelRatio, area.width()/devicePixelRatio, area.height()/devicePixelRatio).scaled(requestedSize, Qt::KeepAspectRatio,Qt::SmoothTransformation);
*size = image.size();
return image.toImage();
}
}
}
}
}
QImage image(10,10, QImage::Format_Indexed8);
image.fill(Qt::gray);
*size = image.size();
return image;
}

View file

@ -34,4 +34,13 @@ public:
static const QString ProviderId;
};
class WindowProvider : public QQuickImageProvider {
public:
WindowProvider ();
QImage requestImage (const QString &id, QSize *size, const QSize &requestedSize) override;
static const QString ProviderId;
};
#endif

View file

@ -120,3 +120,17 @@ void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
uintptr_t DesktopTools::getDisplayIndex(void* screenSharing){
return *(uintptr_t*)(&screenSharing);
}
QRect DesktopTools::getWindowGeometry(void* screenSharing) {
const char *displayStr = getenv("DISPLAY");
if (displayStr == NULL) displayStr = ":0";
Display *display = XOpenDisplay(displayStr);
if (display == NULL) {
qCritical() << "Can't open X display!";
return QRect();
}
Window windowId = (Window)screenSharing;
XWindowAttributes attributes;
XGetWindowAttributes(display, windowId, &attributes);
return QRect(attributes.x, attributes.y, attributes.width, attributes.height);
}

View file

@ -48,6 +48,7 @@ public:
Q_INVOKABLE void getWindowIdFromMouse(VideoSourceDescriptorModel *model);
static void *getDisplay(uintptr_t screenIndex){return reinterpret_cast<void*>(screenIndex);}
static uintptr_t getDisplayIndex(void* screenSharing);
static QRect getWindowGeometry(void* screenSharing);
signals:
void screenSaverStatusChanged(bool status);

View file

@ -43,6 +43,7 @@ public:
static void *getDisplay(int screenIndex);
static int getDisplayIndex(void *screenSharing);
Q_INVOKABLE void getWindowIdFromMouse(VideoSourceDescriptorModel *model);
static QRect getWindowGeometry(void* screenSharing);
signals:
void screenSaverStatusChanged(bool status);

View file

@ -2,6 +2,7 @@
#import <AVFoundation/AVFoundation.h>
#import <ScreenCaptureKit/ScreenCaptureKit.h>
#include <QDebug>
#include <QRect>
#include <QThread>
#include "components/videoSource/VideoSourceDescriptorModel.hpp"
@ -69,3 +70,25 @@ void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
return nil;
}];
}
QRect DesktopTools::getWindowGeometry(void* screenSharing) {
QRect result;
CGWindowID windowId = *(CGWindowID*)&screenSharing;
CFArrayRef descriptions = CGWindowListCopyWindowInfo(kCGWindowListOptionIncludingWindow, windowId);
if(CFArrayGetCount(descriptions) > 0) {
CFDictionaryRef description = (CFDictionaryRef)CFArrayGetValueAtIndex ((CFArrayRef)descriptions, 0);
if(CFDictionaryContainsKey(description, kCGWindowBounds)) {
CFDictionaryRef bounds = (CFDictionaryRef)CFDictionaryGetValue (description, kCGWindowBounds);
if(bounds) {
CGRect windowRect;
CGRectMakeWithDictionaryRepresentation(bounds, &windowRect);
result = QRect(windowRect.origin.x, windowRect.origin.y, windowRect.size.width, windowRect.size.height);
}else
qWarning() << "Bounds found be cannot be parsed for Window ID : " << windowId;
}else
qWarning() << "No bounds specified in Apple description for Window ID : " << windowId;
}else
qWarning() << "No description found for Window ID : " << windowId;
CFRelease(descriptions);
return result;
}

View file

@ -26,6 +26,7 @@
#include <QDebug>
#include <Windows.h>
#include <dwmapi.h>
// =============================================================================
DesktopTools::DesktopTools(QObject *parent) : QObject(parent) {
@ -79,3 +80,14 @@ void DesktopTools::getWindowIdFromMouse(VideoSourceDescriptorModel *model) {
uintptr_t DesktopTools::getDisplayIndex(void* screenSharing) {
return *(uintptr_t*)(&screenSharing);
}
QRect DesktopTools::getWindowGeometry(void* screenSharing) {
QRect result;
HWND windowId = *(HWND*)&screenSharing;
RECT area;
if (S_OK == DwmGetWindowAttribute(windowId, DWMWA_EXTENDED_FRAME_BOUNDS, &area, sizeof(RECT))) {
result = QRect(area.left + 1, area.top, area.right - area.left, area.bottom - area.top);// +1 for border
}else
qWarning() << "Cannot get attributes from HWND: " << windowId;
return result;
}

View file

@ -49,6 +49,7 @@ public:
return reinterpret_cast<void *>(screenIndex);
}
static uintptr_t getDisplayIndex(void *screenSharing);
static QRect getWindowGeometry(void* screenSharing);
HWND mWindowId = 0; // Window
VideoSourceDescriptorModel *mVideoSourceDescriptorModel = nullptr;

View file

@ -52,6 +52,14 @@ void * VideoSourceDescriptorModel::getScreenSharing() const{
return mDesc->getScreenSharing();
}
quint64 VideoSourceDescriptorModel::getWindowId() const{
if(!mDesc) return 0;
else{
void * temp = mDesc->getScreenSharing();
return *(quint64*)&temp;
}
}
bool VideoSourceDescriptorModel::isScreenSharing() const{
return mDesc && mDesc->getType() == linphone::VideoSourceType::ScreenSharing;
}

View file

@ -36,12 +36,14 @@ class VideoSourceDescriptorModel : public QObject{
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)
Q_PROPERTY(quint64 windowId READ getWindowId 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;
quint64 getWindowId() const;
bool isScreenSharing() const;
LinphoneEnums::VideoSourceScreenSharingType getVideoSourceType() const;

View file

@ -13,6 +13,7 @@ Item {
property color foregroundColor: '#00000000'
readonly property alias status: image.status
property int radius: width/2
property alias cache: image.cache
Rectangle {
id: backgroundArea

View file

@ -377,6 +377,7 @@ Rectangle{
backgroundColor: 'white'
source: 'image://screen/'+index
radius: 10
cache: false
}
Text{
Layout.fillWidth: true
@ -421,6 +422,18 @@ Rectangle{
mainItem.callModel.setVideoSourceDescriptorModel(screenSharingItem.desc)
}
}
RoundedImage{
visible: windowSharingRadioButton.checked
Layout.leftMargin: 15
Layout.preferredWidth: 114
Layout.preferredHeight: 64
backgroundColor: 'white'
source: windowSharingRadioButton.checked ? 'image://window/'+screenSharingItem.desc.windowId : ''
radius: 10
cache: false
}
Rectangle{
Layout.fillWidth: true
Layout.preferredHeight: IncallMenuStyle.list.border.width

View file

@ -675,12 +675,18 @@ Rectangle {
}
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()
onWindowIdSelectionStarted: {
mainItem.enabled = false
window.attachVirtualWindow(Utils.buildCommonDialogUri('ConfirmDialog'), {
descriptionText: "Click on the window that you want to share."
, showButtonOnly: 42
, buttonTexts : ['']
})
}
onWindowIdSelectionEnded: {
window.detachVirtualWindow()
mainItem.enabled = true
}
}
// ---------------------------------------------------------------------------