mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-04-21 21:52:03 +02:00
windows: enable window sharing
GitLab: https://git.jami.net/savoirfairelinux/jami-project/-/issues/1294 GitLab: #481 Change-Id: Iafdb542d37f9a1d59b35d83ba779c1c2f2f0ca0f
This commit is contained in:
parent
6862804a44
commit
a28d5c5c55
12 changed files with 168 additions and 37 deletions
|
@ -308,6 +308,8 @@ endif()
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
set(WINDOWS_SYS_LIBS
|
set(WINDOWS_SYS_LIBS
|
||||||
|
windowsapp.lib
|
||||||
|
dwmapi.lib
|
||||||
Shell32.lib
|
Shell32.lib
|
||||||
Ole32.lib
|
Ole32.lib
|
||||||
Advapi32.lib
|
Advapi32.lib
|
||||||
|
|
2
daemon
2
daemon
|
@ -1 +1 @@
|
||||||
Subproject commit e4fa5074cac04f7ba5f8e86b4ce371a3af6a0961
|
Subproject commit 9d76cf5cc767e33ab06054bfa40ee45f671002bd
|
|
@ -92,9 +92,15 @@ AvAdapter::shareEntireScreen(int screenNumber)
|
||||||
if (!screen)
|
if (!screen)
|
||||||
return;
|
return;
|
||||||
QRect rect = screen->geometry();
|
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()
|
auto resource = lrcInstance_->getCurrentCallModel()
|
||||||
->getDisplay(getScreenNumber(),
|
->getDisplay(getScreenNumber(screenNumber),
|
||||||
rect.x(),
|
rect.x(),
|
||||||
rect.y(),
|
rect.y(),
|
||||||
rect.width() * screen->devicePixelRatio(),
|
rect.width() * screen->devicePixelRatio(),
|
||||||
|
@ -229,6 +235,7 @@ AvAdapter::shareWindow(const QString& windowId)
|
||||||
->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING);
|
->addMedia(callId, resource, lrc::api::CallModel::MediaRequestType::SCREENSHARING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma optimize("", off)
|
||||||
QString
|
QString
|
||||||
AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "")
|
AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "")
|
||||||
{
|
{
|
||||||
|
@ -246,7 +253,14 @@ AvAdapter::getSharingResource(int screenId = -2, const QString& windowId = "")
|
||||||
return "";
|
return "";
|
||||||
QRect rect = screen->geometry();
|
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.x(),
|
||||||
rect.y(),
|
rect.y(),
|
||||||
rect.width()
|
rect.width()
|
||||||
|
@ -375,7 +389,7 @@ AvAdapter::hasCamera() const
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
AvAdapter::getScreenNumber() const
|
AvAdapter::getScreenNumber(int screenId) const
|
||||||
{
|
{
|
||||||
int display = 0;
|
int display = 0;
|
||||||
|
|
||||||
|
@ -389,6 +403,10 @@ AvAdapter::getScreenNumber() const
|
||||||
display = list.at(0).toInt();
|
display = list.at(0).toInt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#ifdef WIN32
|
||||||
|
display = screenId;
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return display;
|
return display;
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,5 +117,5 @@ private:
|
||||||
const QRect getAllScreensBoundingRect();
|
const QRect getAllScreensBoundingRect();
|
||||||
|
|
||||||
// Get the screen number
|
// Get the screen number
|
||||||
int getScreenNumber() const;
|
int getScreenNumber(int screenId = 0) const;
|
||||||
};
|
};
|
||||||
|
|
|
@ -121,12 +121,14 @@ Control {
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
shareModel.append({"Name": JamiStrings.shareScreen,
|
shareModel.append({"Name": JamiStrings.shareScreen,
|
||||||
"IconSource": JamiResources.laptop_black_24dp_svg})
|
"IconSource": JamiResources.laptop_black_24dp_svg})
|
||||||
if (Qt.platform.os == "linux") {
|
if (Qt.platform.os.toString() !== "osx") {
|
||||||
shareModel.append({"Name": JamiStrings.shareWindow,
|
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,
|
shareModel.append({"Name": JamiStrings.shareFile,
|
||||||
"IconSource" : JamiResources.file_black_24dp_svg})
|
"IconSource" : JamiResources.file_black_24dp_svg})
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,8 +130,7 @@ ContextMenuAutoLoader {
|
||||||
GeneralMenuItem {
|
GeneralMenuItem {
|
||||||
id: shareWindow
|
id: shareWindow
|
||||||
|
|
||||||
canTrigger: Qt.platform.os === "linux"
|
canTrigger: CurrentAccount.videoEnabled_Video
|
||||||
&& CurrentAccount.videoEnabled_Video
|
|
||||||
&& AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
|
&& AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
|
||||||
&& !CurrentCall.isSIP
|
&& !CurrentCall.isSIP
|
||||||
itemName: JamiStrings.shareWindow
|
itemName: JamiStrings.shareWindow
|
||||||
|
@ -150,6 +149,7 @@ ContextMenuAutoLoader {
|
||||||
canTrigger: CurrentAccount.videoEnabled_Video
|
canTrigger: CurrentAccount.videoEnabled_Video
|
||||||
&& AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
|
&& AvAdapter.currentRenderingDeviceType !== Video.DeviceType.DISPLAY
|
||||||
&& !CurrentCall.isSIP
|
&& !CurrentCall.isSIP
|
||||||
|
&& Qt.platform.os.toString() !== "windows" // temporarily disable for windows
|
||||||
itemName: JamiStrings.shareScreenArea
|
itemName: JamiStrings.shareScreenArea
|
||||||
iconSource: JamiResources.share_area_black_24dp_svg
|
iconSource: JamiResources.share_area_black_24dp_svg
|
||||||
onClicked: {
|
onClicked: {
|
||||||
|
|
|
@ -32,18 +32,40 @@ Window {
|
||||||
id: screenRubberBandWindow
|
id: screenRubberBandWindow
|
||||||
|
|
||||||
function setAllScreensGeo() {
|
function setAllScreensGeo() {
|
||||||
var width = 0, height = 0
|
var width = 0, height = 0, x = 0, y = 0
|
||||||
var screens = Qt.application.screens
|
var screens = Qt.application.screens
|
||||||
for (var i = 0; i < screens.length; ++i) {
|
for (var i = 0; i < screens.length; ++i) {
|
||||||
width += screens[i].width
|
var screenWidth = screens[i].width * screens[i].devicePixelRatio
|
||||||
if (height < screens[i].height)
|
var screenHeight = screens[i].height * screens[i].devicePixelRatio
|
||||||
height = screens[i].height
|
|
||||||
|
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.width = width
|
||||||
screenRubberBandWindow.height = height
|
screenRubberBandWindow.height = height
|
||||||
screenRubberBandWindow.x = 0
|
screenRubberBandWindow.x = x
|
||||||
screenRubberBandWindow.y = 0
|
screenRubberBandWindow.y = y
|
||||||
}
|
}
|
||||||
|
|
||||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
|
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
|
||||||
|
|
|
@ -65,7 +65,9 @@ Window {
|
||||||
return screens.length
|
return screens.length
|
||||||
}
|
}
|
||||||
|
|
||||||
onActiveChanged: {
|
onVisibleChanged: {
|
||||||
|
if (!visible)
|
||||||
|
return
|
||||||
if (!active) {
|
if (!active) {
|
||||||
selectedScreenNumber = -1
|
selectedScreenNumber = -1
|
||||||
selectAllScreens = false
|
selectAllScreens = false
|
||||||
|
@ -161,9 +163,7 @@ Window {
|
||||||
width: screenItem.width - 50
|
width: screenItem.width - 50
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
if (rendererId !== "" && rendererId !== currentPreview) {
|
VideoDevices.stopDevice(rendererId)
|
||||||
VideoDevices.stopDevice(rendererId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
@ -209,7 +209,7 @@ Window {
|
||||||
|
|
||||||
border.color: selectAllScreens ? JamiTheme.screenSelectionBorderColor : JamiTheme.tabbarBorderColor
|
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 {
|
Text {
|
||||||
id: screenNameAll
|
id: screenNameAll
|
||||||
|
@ -233,9 +233,7 @@ Window {
|
||||||
width: screenSelectionRectAll.width - 50
|
width: screenSelectionRectAll.width - 50
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
if (rendererId !== "" && rendererId !== currentPreview) {
|
VideoDevices.stopDevice(rendererId)
|
||||||
VideoDevices.stopDevice(rendererId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
@ -313,9 +311,7 @@ Window {
|
||||||
width: screenItem2.width - 50
|
width: screenItem2.width - 50
|
||||||
|
|
||||||
Component.onDestruction: {
|
Component.onDestruction: {
|
||||||
if (rendererId !== "" && rendererId !== currentPreview) {
|
VideoDevices.stopDevice(rendererId)
|
||||||
VideoDevices.stopDevice(rendererId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
|
|
|
@ -53,6 +53,11 @@
|
||||||
#if defined(Q_OS_UNIX) && !defined(__APPLE__)
|
#if defined(Q_OS_UNIX) && !defined(__APPLE__)
|
||||||
#include <xcb/xcb.h>
|
#include <xcb/xcb.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef WIN32
|
||||||
|
#include "Windows.h"
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace lrc {
|
namespace lrc {
|
||||||
|
|
||||||
|
@ -540,6 +545,71 @@ AVModel::stopPreview(const QString& resource)
|
||||||
VideoManager::instance().closeVideoInput(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__)
|
#if defined(Q_OS_UNIX) && !defined(__APPLE__)
|
||||||
static xcb_atom_t
|
static xcb_atom_t
|
||||||
getAtom(xcb_connection_t* c, const std::string& atomName)
|
getAtom(xcb_connection_t* c, const std::string& atomName)
|
||||||
|
@ -630,10 +700,19 @@ AVModel::getListWindows() const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
#else
|
|
||||||
return ret;
|
|
||||||
#endif
|
#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
|
void
|
||||||
|
|
|
@ -928,10 +928,20 @@ QString
|
||||||
CallModel::getDisplay(const QString& windowId)
|
CallModel::getDisplay(const QString& windowId)
|
||||||
{
|
{
|
||||||
QString sep = libjami::Media::VideoProtocolPrefix::SEPARATOR;
|
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(libjami::Media::VideoProtocolPrefix::DISPLAY)
|
||||||
.arg(sep)
|
.arg(sep)
|
||||||
.arg(windowId);
|
.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,
|
CallModelPimpl::CallModelPimpl(const CallModel& linked,
|
||||||
|
|
|
@ -30,15 +30,17 @@ VideoManagerInterface::VideoManagerInterface()
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
bool isMixer) {
|
bool isMixer) {
|
||||||
Q_EMIT decodingStarted(QString(id.c_str()),
|
Q_EMIT decodingStarted(QString::fromLatin1(QByteArray::fromStdString(id)),
|
||||||
QString(shmPath.c_str()),
|
QString::fromLatin1(QByteArray::fromStdString(shmPath)),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
isMixer);
|
isMixer);
|
||||||
}),
|
}),
|
||||||
exportable_callback<VideoSignal::DecodingStopped>(
|
exportable_callback<VideoSignal::DecodingStopped>(
|
||||||
[this](const std::string& id, const std::string& shmPath, bool isMixer) {
|
[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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ public Q_SLOTS: // METHODS
|
||||||
QString openVideoInput(const QString& resource)
|
QString openVideoInput(const QString& resource)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_VIDEO
|
#ifdef ENABLE_VIDEO
|
||||||
return libjami::openVideoInput(resource.toLatin1().toStdString()).c_str();
|
return QByteArray::fromStdString(libjami::openVideoInput(resource.toLatin1().toStdString()));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ public Q_SLOTS: // METHODS
|
||||||
bool registerSinkTarget(const QString& sinkID, const libjami::SinkTarget& target)
|
bool registerSinkTarget(const QString& sinkID, const libjami::SinkTarget& target)
|
||||||
{
|
{
|
||||||
#ifdef ENABLE_VIDEO
|
#ifdef ENABLE_VIDEO
|
||||||
return libjami::registerSinkTarget(sinkID.toStdString(), target);
|
return libjami::registerSinkTarget(sinkID.toLatin1().toStdString(), target);
|
||||||
#else
|
#else
|
||||||
Q_UNUSED(sinkID)
|
Q_UNUSED(sinkID)
|
||||||
Q_UNUSED(target)
|
Q_UNUSED(target)
|
||||||
|
|
Loading…
Add table
Reference in a new issue