From a28d5c5c5572126c133aebc5f3c33a5833f1d43a Mon Sep 17 00:00:00 2001 From: Aline Gondim Santos <aline.gondimsantos@savoirfairelinux.com> Date: Fri, 2 Sep 2022 09:31:55 -0300 Subject: [PATCH] windows: enable window sharing GitLab: https://git.jami.net/savoirfairelinux/jami-project/-/issues/1294 GitLab: #481 Change-Id: Iafdb542d37f9a1d59b35d83ba779c1c2f2f0ca0f --- CMakeLists.txt | 2 + daemon | 2 +- src/app/avadapter.cpp | 24 +++++- src/app/avadapter.h | 2 +- src/app/mainview/components/CallActionBar.qml | 10 ++- .../components/CallViewContextMenu.qml | 4 +- .../mainview/components/ScreenRubberBand.qml | 34 ++++++-- src/app/mainview/components/SelectScreen.qml | 18 ++-- src/libclient/avmodel.cpp | 85 ++++++++++++++++++- src/libclient/callmodel.cpp | 12 ++- src/libclient/qtwrapper/videomanager_wrap.cpp | 8 +- src/libclient/qtwrapper/videomanager_wrap.h | 4 +- 12 files changed, 168 insertions(+), 37 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 17af0ff44..92d4d65fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,6 +308,8 @@ endif() if(MSVC) set(WINDOWS_SYS_LIBS + windowsapp.lib + dwmapi.lib Shell32.lib Ole32.lib Advapi32.lib diff --git a/daemon b/daemon index e4fa5074c..9d76cf5cc 160000 --- a/daemon +++ b/daemon @@ -1 +1 @@ -Subproject commit e4fa5074cac04f7ba5f8e86b4ce371a3af6a0961 +Subproject commit 9d76cf5cc767e33ab06054bfa40ee45f671002bd diff --git a/src/app/avadapter.cpp b/src/app/avadapter.cpp index 35697e787..8e072d57a 100644 --- a/src/app/avadapter.cpp +++ b/src/app/avadapter.cpp @@ -92,9 +92,15 @@ AvAdapter::shareEntireScreen(int screenNumber) if (!screen) return; QRect rect = screen->geometry(); +#ifdef WIN32 + rect.moveRight(rect.right() - rect.left() + 1); + rect.moveLeft(0); + rect.moveBottom(rect.bottom() - rect.top() + 1); + rect.moveTop(0); +#endif auto resource = lrcInstance_->getCurrentCallModel() - ->getDisplay(getScreenNumber(), + ->getDisplay(getScreenNumber(screenNumber), rect.x(), rect.y(), rect.width() * screen->devicePixelRatio(), @@ -229,6 +235,7 @@ AvAdapter::shareWindow(const QString& windowId) ->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING); } +#pragma optimize("", off) QString AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "") { @@ -246,7 +253,14 @@ AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "") return ""; QRect rect = screen->geometry(); - return lrcInstance_->getCurrentCallModel()->getDisplay(getScreenNumber(), +#ifdef WIN32 + rect.moveRight(rect.right() - rect.left() + 1); + rect.moveLeft(0); + rect.moveBottom(rect.bottom() - rect.top() + 1); + rect.moveTop(0); +#endif + + return lrcInstance_->getCurrentCallModel()->getDisplay(screenId, rect.x(), rect.y(), rect.width() @@ -375,7 +389,7 @@ AvAdapter::hasCamera() const } int -AvAdapter::getScreenNumber() const +AvAdapter::getScreenNumber(int screenId) const { int display = 0; @@ -389,6 +403,10 @@ AvAdapter::getScreenNumber() const display = list.at(0).toInt(); } } +#else +#ifdef WIN32 + display = screenId; +#endif #endif return display; } diff --git a/src/app/avadapter.h b/src/app/avadapter.h index 2f115cf8a..c5ed95761 100644 --- a/src/app/avadapter.h +++ b/src/app/avadapter.h @@ -117,5 +117,5 @@ private: const QRect getAllScreensBoundingRect(); // Get the screen number - int getScreenNumber() const; + int getScreenNumber(int screenId = 0) const; }; diff --git a/src/app/mainview/components/CallActionBar.qml b/src/app/mainview/components/CallActionBar.qml index 62fff410d..3f9ed4d59 100644 --- a/src/app/mainview/components/CallActionBar.qml +++ b/src/app/mainview/components/CallActionBar.qml @@ -121,12 +121,14 @@ Control { Component.onCompleted: { shareModel.append({"Name": JamiStrings.shareScreen, "IconSource": JamiResources.laptop_black_24dp_svg}) - if (Qt.platform.os == "linux") { + if (Qt.platform.os.toString() !== "osx") { shareModel.append({"Name": JamiStrings.shareWindow, - "IconSource" : JamiResources.window_black_24dp_svg}) + "IconSource" : JamiResources.window_black_24dp_svg}) + } + if (Qt.platform.os.toString() !== "windows") { // temporarily disable for windows + shareModel.append({"Name": JamiStrings.shareScreenArea, + "IconSource" : JamiResources.share_area_black_24dp_svg}) } - shareModel.append({"Name": JamiStrings.shareScreenArea, - "IconSource" : JamiResources.share_area_black_24dp_svg}) shareModel.append({"Name": JamiStrings.shareFile, "IconSource" : JamiResources.file_black_24dp_svg}) } diff --git a/src/app/mainview/components/CallViewContextMenu.qml b/src/app/mainview/components/CallViewContextMenu.qml index 733178729..179df492f 100644 --- a/src/app/mainview/components/CallViewContextMenu.qml +++ b/src/app/mainview/components/CallViewContextMenu.qml @@ -130,8 +130,7 @@ ContextMenuAutoLoader { GeneralMenuItem { id: shareWindow - canTrigger: Qt.platform.os === "linux" - && CurrentAccount.videoEnabled_Video + canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY && !CurrentCall.isSIP itemName: JamiStrings.shareWindow @@ -150,6 +149,7 @@ ContextMenuAutoLoader { canTrigger: CurrentAccount.videoEnabled_Video && AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY && !CurrentCall.isSIP + && Qt.platform.os.toString() !== "windows" // temporarily disable for windows itemName: JamiStrings.shareScreenArea iconSource: JamiResources.share_area_black_24dp_svg onClicked: { diff --git a/src/app/mainview/components/ScreenRubberBand.qml b/src/app/mainview/components/ScreenRubberBand.qml index 1cebb882a..700f368c5 100644 --- a/src/app/mainview/components/ScreenRubberBand.qml +++ b/src/app/mainview/components/ScreenRubberBand.qml @@ -32,18 +32,40 @@ Window { id: screenRubberBandWindow function setAllScreensGeo() { - var width = 0, height = 0 + var width = 0, height = 0, x = 0, y = 0 var screens = Qt.application.screens for (var i = 0; i < screens.length; ++i) { - width += screens[i].width - if (height < screens[i].height) - height = screens[i].height + var screenWidth = screens[i].width * screens[i].devicePixelRatio + var screenHeight = screens[i].height * screens[i].devicePixelRatio + + if (screens[i].virtualX >= x + width) + width += screenWidth + else if (screens[i].virtualX + screenWidth <= x) + width += screenWidth + else if (screens[i].virtualX < x && screens[i].virtualX + screenWidth > x) + width += (x - screens[i].virtualX) * screens[i].devicePixelRatio + else if (screens[i].virtualX > x && screens[i].virtualX + screenWidth > x + width) + width += (screens[i].virtualX + screenWidth - x - width) * screens[i].devicePixelRatio + + if (screens[i].virtualY >= y + height) + height += screenHeight + else if (screens[i].virtualY + screenHeight <= y) + height += screenHeight + else if (screens[i].virtualY < y && screens[i].virtualY + screenHeight > y) + height += (y - screens[i].virtualY) * screens[i].devicePixelRatio + else if (screens[i].virtualY > y && screens[i].virtualY + screenHeight > y + height) + height += (screens[i].virtualY + screenWidth - y - height) * screens[i].devicePixelRatio + + if (screens[i].virtualY < y) + y = screens[i].virtualY + if (screens[i].virtualX < x) + x = screens[i].virtualX } screenRubberBandWindow.width = width screenRubberBandWindow.height = height - screenRubberBandWindow.x = 0 - screenRubberBandWindow.y = 0 + screenRubberBandWindow.x = x + screenRubberBandWindow.y = y } flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground diff --git a/src/app/mainview/components/SelectScreen.qml b/src/app/mainview/components/SelectScreen.qml index c3427120a..6488e3276 100644 --- a/src/app/mainview/components/SelectScreen.qml +++ b/src/app/mainview/components/SelectScreen.qml @@ -65,7 +65,9 @@ Window { return screens.length } - onActiveChanged: { + onVisibleChanged: { + if (!visible) + return if (!active) { selectedScreenNumber = -1 selectAllScreens = false @@ -161,9 +163,7 @@ Window { width: screenItem.width - 50 Component.onDestruction: { - if (rendererId !== "" && rendererId !== currentPreview) { - VideoDevices.stopDevice(rendererId) - } + VideoDevices.stopDevice(rendererId) } Component.onCompleted: { if (visible) { @@ -209,7 +209,7 @@ Window { border.color: selectAllScreens ? JamiTheme.screenSelectionBorderColor : JamiTheme.tabbarBorderColor - visible: !root.window && Qt.application.screens.length > 1 + visible: !root.window && Qt.application.screens.length > 1 && Qt.platform.os.toString() !== "windows" Text { id: screenNameAll @@ -233,9 +233,7 @@ Window { width: screenSelectionRectAll.width - 50 Component.onDestruction: { - if (rendererId !== "" && rendererId !== currentPreview) { - VideoDevices.stopDevice(rendererId) - } + VideoDevices.stopDevice(rendererId) } Component.onCompleted: { if (visible) { @@ -313,9 +311,7 @@ Window { width: screenItem2.width - 50 Component.onDestruction: { - if (rendererId !== "" && rendererId !== currentPreview) { - VideoDevices.stopDevice(rendererId) - } + VideoDevices.stopDevice(rendererId) } Component.onCompleted: { if (visible) { diff --git a/src/libclient/avmodel.cpp b/src/libclient/avmodel.cpp index 67ade3482..63aa950ce 100644 --- a/src/libclient/avmodel.cpp +++ b/src/libclient/avmodel.cpp @@ -53,6 +53,11 @@ #if defined(Q_OS_UNIX) && !defined(__APPLE__) #include <xcb/xcb.h> #endif +#ifdef WIN32 +#include "Windows.h" +#include <tchar.h> +#include <dwmapi.h> +#endif namespace lrc { @@ -540,6 +545,71 @@ AVModel::stopPreview(const QString& resource) VideoManager::instance().closeVideoInput(resource); } +#ifdef WIN32 +BOOL +IsAltTabWindow(HWND hwnd) +{ + LONG style = GetWindowLong(hwnd, GWL_STYLE); + if (!((style & WS_DISABLED) != WS_DISABLED)) { + return false; + } + + DWORD cloaked = FALSE; + HRESULT hrTemp = DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, &cloaked, sizeof(cloaked)); + if (SUCCEEDED(hrTemp) && cloaked == DWM_CLOAKED_SHELL) { + return false; + } + + // Start at the root owner + HWND hwndWalk = GetAncestor(hwnd, GA_ROOTOWNER); + + // See if we are the last active visible popup + HWND hwndTry; + while ((hwndTry = GetLastActivePopup(hwndWalk)) != hwndTry) { + if (IsWindowVisible(hwndTry)) + break; + hwndWalk = hwndTry; + } + return hwndWalk == hwnd; +} + +BOOL CALLBACK +CbEnumAltTab(HWND hwnd, LPARAM lParam) +{ + // Do not show invisible windows + if (!IsWindowVisible(hwnd)) + return TRUE; + + // Alt-tab test as described by Raymond Chen + if (!IsAltTabWindow(hwnd)) + return TRUE; + + const size_t MAX_WINDOW_NAME = 256; + TCHAR windowName[MAX_WINDOW_NAME]; + if (hwnd == GetShellWindow()) + return TRUE; + else + GetWindowText(hwnd, windowName, MAX_WINDOW_NAME); + + // Do not show windows that has no caption + if (0 == windowName[0]) + return TRUE; + + std::wstring msg = std::wstring(windowName); + auto name = QString::fromStdWString(msg); + + QMap<QString, QVariant>* windowList = reinterpret_cast<QMap<QString, QVariant>*>(lParam); + auto keys = windowList->keys(); + if (keys.indexOf(name) > 0) { + return FALSE; + } else { + windowList->insert(name, name); + } + + return TRUE; +} +#endif + #if defined(Q_OS_UNIX) && !defined(__APPLE__) static xcb_atom_t getAtom(xcb_connection_t* c, const std::string& atomName) @@ -630,10 +700,19 @@ AVModel::getListWindows() const } } } - return ret; -#else - return ret; #endif +#ifdef WIN32 + try { + auto newWindow = true; + LPARAM lParam = reinterpret_cast<LPARAM>(&ret); + while (newWindow) { + newWindow = EnumWindows(CbEnumAltTab, lParam); + } + auto finishedloop = true; + } catch (...) { + } + #endif + return ret; } void diff --git a/src/libclient/callmodel.cpp b/src/libclient/callmodel.cpp index c4e454ca4..17bbded66 100644 --- a/src/libclient/callmodel.cpp +++ b/src/libclient/callmodel.cpp @@ -928,10 +928,20 @@ QString CallModel::getDisplay(const QString& windowId) { QString sep = libjami::Media::VideoProtocolPrefix::SEPARATOR; - return QString("%1%2:+0,0 window-id:%3") + QString ret{}; +#if (defined(Q_OS_UNIX) && !defined(__APPLE__)) + ret = QString("%1%2:+0,0 window-id:%3") .arg(libjami::Media::VideoProtocolPrefix::DISPLAY) .arg(sep) .arg(windowId); +#endif +#ifdef WIN32 + ret = QString("%1%2:+0,0 window-id:title=%3") + .arg(libjami::Media::VideoProtocolPrefix::DISPLAY) + .arg(sep) + .arg(windowId); +#endif + return ret; } CallModelPimpl::CallModelPimpl(const CallModel& linked, diff --git a/src/libclient/qtwrapper/videomanager_wrap.cpp b/src/libclient/qtwrapper/videomanager_wrap.cpp index dd4ac4b67..f7889782c 100644 --- a/src/libclient/qtwrapper/videomanager_wrap.cpp +++ b/src/libclient/qtwrapper/videomanager_wrap.cpp @@ -30,15 +30,17 @@ VideoManagerInterface::VideoManagerInterface() int width, int height, bool isMixer) { - Q_EMIT decodingStarted(QString(id.c_str()), - QString(shmPath.c_str()), + Q_EMIT decodingStarted(QString::fromLatin1(QByteArray::fromStdString(id)), + QString::fromLatin1(QByteArray::fromStdString(shmPath)), width, height, isMixer); }), exportable_callback<VideoSignal::DecodingStopped>( [this](const std::string& id, const std::string& shmPath, bool isMixer) { - Q_EMIT decodingStopped(QString(id.c_str()), QString(shmPath.c_str()), isMixer); + Q_EMIT decodingStopped(QString::fromLatin1(QByteArray::fromStdString(id)), + QString::fromLatin1(QByteArray::fromStdString(shmPath)), + isMixer); })}; #endif } diff --git a/src/libclient/qtwrapper/videomanager_wrap.h b/src/libclient/qtwrapper/videomanager_wrap.h index 24402596e..4a2b68f4c 100644 --- a/src/libclient/qtwrapper/videomanager_wrap.h +++ b/src/libclient/qtwrapper/videomanager_wrap.h @@ -126,7 +126,7 @@ public Q_SLOTS: // METHODS QString openVideoInput(const QString& resource) { #ifdef ENABLE_VIDEO - return libjami::openVideoInput(resource.toLatin1().toStdString()).c_str(); + return QByteArray::fromStdString(libjami::openVideoInput(resource.toLatin1().toStdString())); #endif } @@ -150,7 +150,7 @@ public Q_SLOTS: // METHODS bool registerSinkTarget(const QString& sinkID, const libjami::SinkTarget& target) { #ifdef ENABLE_VIDEO - return libjami::registerSinkTarget(sinkID.toStdString(), target); + return libjami::registerSinkTarget(sinkID.toLatin1().toStdString(), target); #else Q_UNUSED(sinkID) Q_UNUSED(target) -- GitLab