mirror of
https://git.jami.net/savoirfairelinux/jami-client-qt.git
synced 2025-08-30 19:53:33 +02:00
misc: change the way of manipulating window's display screen
- avoid using screen number as display number on Linux - support for area selection over multiple screens on Linux - make getFrame null safe - make video-full-screen mode show in the correct screen - add the option of "share all screens" - use x11 api for unix system for sharing screen areas Gitlab: #160 Change-Id: Ibe47a4150b6a213950a0533d85e8cd7d5d159482
This commit is contained in:
parent
768ea9d601
commit
7f7e4b2202
21 changed files with 630 additions and 256 deletions
|
@ -103,10 +103,17 @@ unix {
|
||||||
|
|
||||||
LIBS += -L$${LRC}/lib -lringclient
|
LIBS += -L$${LRC}/lib -lringclient
|
||||||
LIBS += -lqrencode
|
LIBS += -lqrencode
|
||||||
|
LIBS += -lX11
|
||||||
|
|
||||||
isEmpty(PREFIX) { PREFIX = /tmp/$${TARGET}/bin }
|
isEmpty(PREFIX) { PREFIX = /tmp/$${TARGET}/bin }
|
||||||
target.path = $$PREFIX/bin
|
target.path = $$PREFIX/bin
|
||||||
INSTALLS += target
|
INSTALLS += target
|
||||||
|
|
||||||
|
# unix specific
|
||||||
|
HEADERS += \
|
||||||
|
src/xrectsel.h
|
||||||
|
SOURCES += \
|
||||||
|
src/xrectsel.c
|
||||||
}
|
}
|
||||||
|
|
||||||
# Input
|
# Input
|
||||||
|
|
|
@ -169,9 +169,12 @@ ApplicationWindow {
|
||||||
|
|
||||||
onClosing: root.close()
|
onClosing: root.close()
|
||||||
|
|
||||||
|
onScreenChanged: JamiQmlUtils.mainApplicationScreen = root.screen
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if(!startAccountMigration()){
|
if(!startAccountMigration()){
|
||||||
startClient()
|
startClient()
|
||||||
}
|
}
|
||||||
|
JamiQmlUtils.mainApplicationScreen = root.screen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,13 @@
|
||||||
#include "lrcinstance.h"
|
#include "lrcinstance.h"
|
||||||
#include "qtutils.h"
|
#include "qtutils.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include "xrectsel.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <QtConcurrent/QtConcurrent>
|
#include <QtConcurrent/QtConcurrent>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QPainter>
|
||||||
#include <QScreen>
|
#include <QScreen>
|
||||||
|
|
||||||
AvAdapter::AvAdapter(QObject* parent)
|
AvAdapter::AvAdapter(QObject* parent)
|
||||||
|
@ -66,37 +71,62 @@ AvAdapter::populateVideoDeviceContextMenuItem()
|
||||||
void
|
void
|
||||||
AvAdapter::onVideoContextMenuDeviceItemClicked(const QString& deviceName)
|
AvAdapter::onVideoContextMenuDeviceItemClicked(const QString& deviceName)
|
||||||
{
|
{
|
||||||
auto* convModel = LRCInstance::getCurrentConversationModel();
|
|
||||||
const auto conversation = convModel->getConversationForUID(LRCInstance::getCurrentConvUid());
|
|
||||||
auto call = LRCInstance::getCallInfoForConversation(conversation);
|
|
||||||
if (!call)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto deviceId = LRCInstance::avModel().getDeviceIdFromName(deviceName);
|
auto deviceId = LRCInstance::avModel().getDeviceIdFromName(deviceName);
|
||||||
if (deviceId.isEmpty()) {
|
if (deviceId.isEmpty()) {
|
||||||
qWarning() << "Couldn't find device: " << deviceName;
|
qWarning() << "Couldn't find device: " << deviceName;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LRCInstance::avModel().setCurrentVideoCaptureDevice(deviceId);
|
LRCInstance::avModel().setCurrentVideoCaptureDevice(deviceId);
|
||||||
LRCInstance::avModel().switchInputTo(deviceId, call->id);
|
LRCInstance::avModel().switchInputTo(deviceId, getCurrentCallId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AvAdapter::shareEntireScreen(int screenNumber)
|
AvAdapter::shareEntireScreen(int screenNumber)
|
||||||
{
|
{
|
||||||
QScreen* screen = qApp->screens().at(screenNumber);
|
QScreen* screen = QGuiApplication::screens().at(screenNumber);
|
||||||
if (!screen)
|
if (!screen)
|
||||||
return;
|
return;
|
||||||
QRect rect = screen ? screen->geometry() : qApp->primaryScreen()->geometry();
|
QRect rect = screen->geometry();
|
||||||
LRCInstance::avModel().setDisplay(screenNumber, rect.x(), rect.y(), rect.width(), rect.height());
|
|
||||||
|
int display = 0;
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
display = screenNumber;
|
||||||
|
#else
|
||||||
|
QString display_env {getenv("DISPLAY")};
|
||||||
|
if (!display_env.isEmpty()) {
|
||||||
|
auto list = display_env.split(":", Qt::SkipEmptyParts);
|
||||||
|
// Should only be one display, so get the first one
|
||||||
|
if (list.size() > 0) {
|
||||||
|
display = list.at(0).toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
LRCInstance::avModel()
|
||||||
|
.setDisplay(display, rect.x(), rect.y(), rect.width(), rect.height(), getCurrentCallId());
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString
|
void
|
||||||
|
AvAdapter::shareAllScreens()
|
||||||
|
{
|
||||||
|
auto screens = QGuiApplication::screens();
|
||||||
|
|
||||||
|
int width = 0, height = 0;
|
||||||
|
for (auto scr : screens) {
|
||||||
|
width += scr->geometry().width();
|
||||||
|
if (height < scr->geometry().height())
|
||||||
|
height = scr->geometry().height();
|
||||||
|
}
|
||||||
|
|
||||||
|
LRCInstance::avModel().setDisplay(0, 0, 0, width, height, getCurrentCallId());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
AvAdapter::captureScreen(int screenNumber)
|
AvAdapter::captureScreen(int screenNumber)
|
||||||
{
|
{
|
||||||
QScreen* screen = qApp->screens().at(screenNumber);
|
QtConcurrent::run([this, screenNumber]() {
|
||||||
|
QScreen* screen = QGuiApplication::screens().at(screenNumber);
|
||||||
if (!screen)
|
if (!screen)
|
||||||
return QString("");
|
return;
|
||||||
/*
|
/*
|
||||||
* The screen window id is always 0.
|
* The screen window id is always 0.
|
||||||
*/
|
*/
|
||||||
|
@ -105,32 +135,88 @@ AvAdapter::captureScreen(int screenNumber)
|
||||||
QBuffer buffer;
|
QBuffer buffer;
|
||||||
buffer.open(QIODevice::WriteOnly);
|
buffer.open(QIODevice::WriteOnly);
|
||||||
pixmap.save(&buffer, "PNG");
|
pixmap.save(&buffer, "PNG");
|
||||||
return Utils::byteArrayToBase64String(buffer.data());
|
|
||||||
|
emit screenCaptured(screenNumber, Utils::byteArrayToBase64String(buffer.data()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AvAdapter::captureAllScreens()
|
||||||
|
{
|
||||||
|
QtConcurrent::run([this]() {
|
||||||
|
auto screens = QGuiApplication::screens();
|
||||||
|
|
||||||
|
QList<QPixmap> scrs;
|
||||||
|
int width = 0, height = 0, currentPoint = 0;
|
||||||
|
|
||||||
|
foreach (auto scr, screens) {
|
||||||
|
QPixmap pix = scr->grabWindow(0);
|
||||||
|
width += pix.width();
|
||||||
|
if (height < pix.height())
|
||||||
|
height = pix.height();
|
||||||
|
scrs << pix;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPixmap final(width, height);
|
||||||
|
QPainter painter(&final);
|
||||||
|
final.fill(Qt::black);
|
||||||
|
|
||||||
|
foreach (auto scr, scrs) {
|
||||||
|
painter.drawPixmap(QPoint(currentPoint, 0), scr);
|
||||||
|
currentPoint += scr.width();
|
||||||
|
}
|
||||||
|
|
||||||
|
QBuffer buffer;
|
||||||
|
buffer.open(QIODevice::WriteOnly);
|
||||||
|
final.save(&buffer, "PNG");
|
||||||
|
emit screenCaptured(-1, Utils::byteArrayToBase64String(buffer.data()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AvAdapter::shareFile(const QString& filePath)
|
AvAdapter::shareFile(const QString& filePath)
|
||||||
{
|
{
|
||||||
LRCInstance::avModel().setInputFile(filePath);
|
LRCInstance::avModel().setInputFile(filePath, getCurrentCallId());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AvAdapter::shareScreenArea(int screenNumber, int x, int y, int width, int height)
|
AvAdapter::shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned height)
|
||||||
{
|
{
|
||||||
QScreen* screen = qApp->screens().at(screenNumber);
|
#ifdef Q_OS_LINUX
|
||||||
if (!screen)
|
int display;
|
||||||
return;
|
|
||||||
QRect rect = screen ? screen->geometry() : qApp->primaryScreen()->geometry();
|
|
||||||
|
|
||||||
/*
|
// Get display
|
||||||
* Provide minimum width, height.
|
QString display_env {getenv("DISPLAY")};
|
||||||
* Need to add screen x, y initial value to the setDisplay api call.
|
if (!display_env.isEmpty()) {
|
||||||
*/
|
auto list = display_env.split(":", Qt::SkipEmptyParts);
|
||||||
LRCInstance::avModel().setDisplay(screenNumber,
|
// Should only be one display, so get the first one
|
||||||
rect.x() + x,
|
if (list.size() > 0) {
|
||||||
rect.y() + y,
|
display = list.at(0).toInt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// xrectsel will freeze all displays too fast so that the call
|
||||||
|
// context menu will not be closed even closed signal is emitted
|
||||||
|
// use timer to wait until popup is closed
|
||||||
|
QTimer::singleShot(100, [=]() mutable {
|
||||||
|
x = y = width = height = 0;
|
||||||
|
xrectsel(&x, &y, &width, &height);
|
||||||
|
|
||||||
|
LRCInstance::avModel().setDisplay(0,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
width < 128 ? 128 : width,
|
width < 128 ? 128 : width,
|
||||||
height < 128 ? 128 : height);
|
height < 128 ? 128 : height,
|
||||||
|
getCurrentCallId());
|
||||||
|
});
|
||||||
|
#else
|
||||||
|
LRCInstance::avModel().setDisplay(0,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width < 128 ? 128 : width,
|
||||||
|
height < 128 ? 128 : height,
|
||||||
|
getCurrentCallId());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -145,19 +231,24 @@ AvAdapter::stopAudioMeter(bool async)
|
||||||
LRCInstance::stopAudioMeter(async);
|
LRCInstance::stopAudioMeter(async);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QString&
|
||||||
|
AvAdapter::getCurrentCallId()
|
||||||
|
{
|
||||||
|
auto* convModel = LRCInstance::getCurrentConversationModel();
|
||||||
|
const auto conversation = convModel->getConversationForUID(LRCInstance::getCurrentConvUid());
|
||||||
|
auto call = LRCInstance::getCallInfoForConversation(conversation);
|
||||||
|
if (!call)
|
||||||
|
return QString();
|
||||||
|
return call->id;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
AvAdapter::slotDeviceEvent()
|
AvAdapter::slotDeviceEvent()
|
||||||
{
|
{
|
||||||
auto& avModel = LRCInstance::avModel();
|
auto& avModel = LRCInstance::avModel();
|
||||||
auto defaultDevice = avModel.getDefaultDevice();
|
auto defaultDevice = avModel.getDefaultDevice();
|
||||||
auto currentCaptureDevice = avModel.getCurrentVideoCaptureDevice();
|
auto currentCaptureDevice = avModel.getCurrentVideoCaptureDevice();
|
||||||
QString callId {};
|
QString callId = getCurrentCallId();
|
||||||
|
|
||||||
auto* convModel = LRCInstance::getCurrentConversationModel();
|
|
||||||
const auto conversation = convModel->getConversationForUID(LRCInstance::getCurrentConvUid());
|
|
||||||
auto call = LRCInstance::getCallInfoForConversation(conversation);
|
|
||||||
if (call)
|
|
||||||
callId = call->id;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decide whether a device has plugged, unplugged, or nothing has changed.
|
* Decide whether a device has plugged, unplugged, or nothing has changed.
|
||||||
|
|
|
@ -39,6 +39,8 @@ signals:
|
||||||
*/
|
*/
|
||||||
void videoDeviceListChanged(bool listIsEmpty);
|
void videoDeviceListChanged(bool listIsEmpty);
|
||||||
|
|
||||||
|
void screenCaptured(int screenNumber, QString source);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void safeInit() override {};
|
void safeInit() override {};
|
||||||
|
|
||||||
|
@ -58,9 +60,19 @@ protected:
|
||||||
Q_INVOKABLE void shareEntireScreen(int screenNumber);
|
Q_INVOKABLE void shareEntireScreen(int screenNumber);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Take snap shot of the screen by returning base64 image string.
|
* Share the all screens connected.
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE const QString captureScreen(int screenNumber);
|
Q_INVOKABLE void shareAllScreens();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take snap shot of the screen and return emitting signal.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void captureScreen(int screenNumber);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take snap shot of the all screens and return by emitting signal.
|
||||||
|
*/
|
||||||
|
Q_INVOKABLE void captureAllScreens();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Share a media file.
|
* Share a media file.
|
||||||
|
@ -68,14 +80,19 @@ protected:
|
||||||
Q_INVOKABLE void shareFile(const QString& filePath);
|
Q_INVOKABLE void shareFile(const QString& filePath);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Select screen area to display.
|
* Select screen area to display (from all screens).
|
||||||
*/
|
*/
|
||||||
Q_INVOKABLE void shareScreenArea(int screenNumber, int x, int y, int width, int height);
|
Q_INVOKABLE void shareScreenArea(unsigned x, unsigned y, unsigned width, unsigned height);
|
||||||
|
|
||||||
Q_INVOKABLE void startAudioMeter(bool async);
|
Q_INVOKABLE void startAudioMeter(bool async);
|
||||||
Q_INVOKABLE void stopAudioMeter(bool async);
|
Q_INVOKABLE void stopAudioMeter(bool async);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/*
|
||||||
|
* Get current callId from current selected conv id.
|
||||||
|
*/
|
||||||
|
const QString& getCurrentCallId();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to classify capture device events.
|
* Used to classify capture device events.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -107,8 +107,16 @@ function addMenuItem(itemName,
|
||||||
console.log("Error loading component:",
|
console.log("Error loading component:",
|
||||||
menuItemComponent.errorString())
|
menuItemComponent.errorString())
|
||||||
if (menuItemObject !== null) {
|
if (menuItemObject !== null) {
|
||||||
menuItemObject.clicked.connect(function () {baseContextMenuObject.close()})
|
menuItemObject.clicked.connect(function () {
|
||||||
menuItemObject.clicked.connect(onClickedCallback)
|
var callback = function(){
|
||||||
|
onClickedCallback()
|
||||||
|
baseContextMenuObject.onVisibleChanged.disconnect(callback)
|
||||||
|
baseContextMenuObject.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
baseContextMenuObject.onVisibleChanged.connect(callback)
|
||||||
|
baseContextMenuObject.visible = false
|
||||||
|
})
|
||||||
menuItemObject.icon.color = "green"
|
menuItemObject.icon.color = "green"
|
||||||
|
|
||||||
baseContextMenuObject.addItem(menuItemObject)
|
baseContextMenuObject.addItem(menuItemObject)
|
||||||
|
|
|
@ -24,7 +24,9 @@ import QtQuick 2.14
|
||||||
Item {
|
Item {
|
||||||
readonly property string mainViewLoadPath: "qrc:/src/mainview/MainView.qml"
|
readonly property string mainViewLoadPath: "qrc:/src/mainview/MainView.qml"
|
||||||
readonly property string wizardViewLoadPath: "qrc:/src/wizardview/WizardView.qml"
|
readonly property string wizardViewLoadPath: "qrc:/src/wizardview/WizardView.qml"
|
||||||
|
readonly property string base64StringTitle: "data:image/png;base64,"
|
||||||
|
|
||||||
|
property var mainApplicationScreen: ""
|
||||||
property bool callIsFullscreen: false
|
property bool callIsFullscreen: false
|
||||||
|
|
||||||
TextMetrics {
|
TextMetrics {
|
||||||
|
|
|
@ -70,7 +70,7 @@ DistantRenderer::getScaledHeight() const
|
||||||
void
|
void
|
||||||
DistantRenderer::paint(QPainter* painter)
|
DistantRenderer::paint(QPainter* painter)
|
||||||
{
|
{
|
||||||
auto distantImage = LRCInstance::renderer()->getFrame(distantRenderId_);
|
LRCInstance::renderer()->drawFrame(distantRenderId_, [this, painter](QImage* distantImage) {
|
||||||
if (distantImage) {
|
if (distantImage) {
|
||||||
auto scaledDistant = distantImage->scaled(size().toSize(), Qt::KeepAspectRatio);
|
auto scaledDistant = distantImage->scaled(size().toSize(), Qt::KeepAspectRatio);
|
||||||
auto tempScaledWidth = static_cast<int>(scaledWidth_ * 1000);
|
auto tempScaledWidth = static_cast<int>(scaledWidth_ * 1000);
|
||||||
|
@ -88,7 +88,11 @@ DistantRenderer::paint(QPainter* painter)
|
||||||
or static_cast<int>(scaledHeight_ * 1000) != tempScaledHeight) {
|
or static_cast<int>(scaledHeight_ * 1000) != tempScaledHeight) {
|
||||||
emit offsetChanged();
|
emit offsetChanged();
|
||||||
}
|
}
|
||||||
painter->drawImage(QRect(xOffset_, yOffset_, scaledDistant.width(), scaledDistant.height()),
|
painter->drawImage(QRect(xOffset_,
|
||||||
|
yOffset_,
|
||||||
|
scaledDistant.width(),
|
||||||
|
scaledDistant.height()),
|
||||||
scaledDistant);
|
scaledDistant);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,13 +115,11 @@ Item {
|
||||||
ContextMenuGenerator.addMenuItem(JamiStrings.shareScreenArea,
|
ContextMenuGenerator.addMenuItem(JamiStrings.shareScreenArea,
|
||||||
"qrc:/images/icons/screen_share-24px.svg",
|
"qrc:/images/icons/screen_share-24px.svg",
|
||||||
function (){
|
function (){
|
||||||
if (Qt.application.screens.length === 1) {
|
if (Qt.platform.os !== "windows") {
|
||||||
ScreenRubberBandCreation.createScreenRubberBandWindowObject(
|
AvAdapter.shareScreenArea(0, 0, 0, 0)
|
||||||
null, 0)
|
|
||||||
ScreenRubberBandCreation.showScreenRubberBandWindow()
|
|
||||||
} else {
|
} else {
|
||||||
SelectScreenWindowCreation.createSelectScreenWindowObject(true)
|
ScreenRubberBandCreation.createScreenRubberBandWindowObject()
|
||||||
SelectScreenWindowCreation.showSelectScreenWindow()
|
ScreenRubberBandCreation.showScreenRubberBandWindow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
ContextMenuGenerator.addMenuItem(JamiStrings.shareFile,
|
ContextMenuGenerator.addMenuItem(JamiStrings.shareFile,
|
||||||
|
|
|
@ -51,7 +51,7 @@ Rectangle {
|
||||||
if (avatar === "") {
|
if (avatar === "") {
|
||||||
contactImage.source = ""
|
contactImage.source = ""
|
||||||
} else {
|
} else {
|
||||||
contactImage.source = "data:image/png;base64," + avatar
|
contactImage.source = JamiQmlUtils.base64StringTitle + avatar
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,16 +32,32 @@ import net.jami.Constants 1.0
|
||||||
Window {
|
Window {
|
||||||
id: screenRubberBandWindow
|
id: screenRubberBandWindow
|
||||||
|
|
||||||
property int screenNumber: 0
|
function setAllScreensGeo() {
|
||||||
|
var width = 0, height = 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
|
||||||
|
}
|
||||||
|
|
||||||
|
screenRubberBandWindow.width = width
|
||||||
|
screenRubberBandWindow.height = height
|
||||||
|
screenRubberBandWindow.x = 0
|
||||||
|
screenRubberBandWindow.y = 0
|
||||||
|
}
|
||||||
|
|
||||||
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
|
flags: Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
|
||||||
|
|
||||||
|
|
||||||
// Opacity with 0.7 window that will fill the entire screen,
|
// Opacity with 0.7 window that will fill the entire screen,
|
||||||
// provide the users to select the area that they
|
// provide the users to select the area that they
|
||||||
// want to share.
|
// want to share.
|
||||||
color: Qt.rgba(0, 0, 0, 0.7)
|
color: Qt.rgba(0, 0, 0, 0.7)
|
||||||
|
// +1 so that it does not fallback to the previous screen
|
||||||
|
x: screen.virtualX + 1
|
||||||
|
y: screen.virtualY + 1
|
||||||
|
|
||||||
|
screen: Qt.application.screens[0]
|
||||||
|
|
||||||
// Rect for selection.
|
// Rect for selection.
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
@ -67,7 +83,6 @@ Window {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.CrossCursor
|
cursorShape: Qt.CrossCursor
|
||||||
|
|
||||||
|
|
||||||
// Geo changing for user selection.
|
// Geo changing for user selection.
|
||||||
onPressed: {
|
onPressed: {
|
||||||
originalX = mouseX
|
originalX = mouseX
|
||||||
|
@ -97,7 +112,7 @@ Window {
|
||||||
|
|
||||||
onReleased: {
|
onReleased: {
|
||||||
recSelect.visible = false
|
recSelect.visible = false
|
||||||
AvAdapter.shareScreenArea(screenNumber, recSelect.x, recSelect.y,
|
AvAdapter.shareScreenArea(recSelect.x, recSelect.y,
|
||||||
recSelect.width, recSelect.height)
|
recSelect.width, recSelect.height)
|
||||||
screenRubberBandWindow.close()
|
screenRubberBandWindow.close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,7 @@ Window {
|
||||||
property int minHeight: 500
|
property int minHeight: 500
|
||||||
|
|
||||||
property int selectedScreenNumber: -1
|
property int selectedScreenNumber: -1
|
||||||
|
property bool selectAllScreens: false
|
||||||
// Decide whether to show screen area or entire screen.
|
|
||||||
property bool selectArea: false
|
|
||||||
|
|
||||||
// How many rows the ScrollView should have.
|
// How many rows the ScrollView should have.
|
||||||
function calculateRepeaterModel() {
|
function calculateRepeaterModel() {
|
||||||
|
@ -55,10 +53,10 @@ Window {
|
||||||
minimumWidth: minWidth
|
minimumWidth: minWidth
|
||||||
minimumHeight: minHeight
|
minimumHeight: minHeight
|
||||||
|
|
||||||
title: "Screen sharing"
|
width: minWidth
|
||||||
|
height: minHeight
|
||||||
|
|
||||||
// Note: Qt.application.screens[0] is the app's current existing screen.
|
screen: JamiQmlUtils.mainApplicationScreen
|
||||||
screen: Qt.application.screens[0]
|
|
||||||
|
|
||||||
modality: Qt.ApplicationModal
|
modality: Qt.ApplicationModal
|
||||||
|
|
||||||
|
@ -96,6 +94,7 @@ Window {
|
||||||
clip: true
|
clip: true
|
||||||
|
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||||
|
ScrollBar.vertical.policy: ScrollBar.AlwaysOn
|
||||||
|
|
||||||
// Column of rows repeater (two screen captures in a row).
|
// Column of rows repeater (two screen captures in a row).
|
||||||
Column {
|
Column {
|
||||||
|
@ -125,6 +124,21 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: AvAdapter
|
||||||
|
|
||||||
|
function onScreenCaptured(screenNumber, source) {
|
||||||
|
if (screenNumber === -1)
|
||||||
|
screenShotAll.source = JamiQmlUtils.base64StringTitle + source
|
||||||
|
if (screenNumber !== index && screenNumber !== index + 1)
|
||||||
|
return
|
||||||
|
if (screenNumber % 2 !== 1)
|
||||||
|
screenShotOdd.source = JamiQmlUtils.base64StringTitle + source
|
||||||
|
else
|
||||||
|
screenShotEven.source = JamiQmlUtils.base64StringTitle + source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// To make sure that two screen captures in one row,
|
// To make sure that two screen captures in one row,
|
||||||
// a repeater of two rect is needed, which one in charge
|
// a repeater of two rect is needed, which one in charge
|
||||||
// of odd number screen, one in charge of even number screen.
|
// of odd number screen, one in charge of even number screen.
|
||||||
|
@ -154,12 +168,9 @@ Window {
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
mipmap: true
|
mipmap: true
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: AvAdapter.captureScreen(
|
||||||
screenShotOdd.source = "data:image/png;base64,"
|
|
||||||
+ AvAdapter.captureScreen(
|
|
||||||
calculateScreenNumber(index, false) - 1)
|
calculateScreenNumber(index, false) - 1)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
id: screenNameOdd
|
id: screenNameOdd
|
||||||
|
@ -224,8 +235,7 @@ Window {
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (screenSelectionRectEven.visible)
|
if (screenSelectionRectEven.visible)
|
||||||
screenShotEven.source = "data:image/png;base64,"
|
AvAdapter.captureScreen(
|
||||||
+ AvAdapter.captureScreen(
|
|
||||||
calculateScreenNumber(index, true) - 1)
|
calculateScreenNumber(index, true) - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,6 +269,71 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: screenSelectionRectAll
|
||||||
|
|
||||||
|
property string borderColor: JamiTheme.tabbarBorderColor
|
||||||
|
|
||||||
|
anchors.horizontalCenter: screenSelectionScrollViewColumn.horizontalCenter
|
||||||
|
|
||||||
|
color: JamiTheme.secondaryBackgroundColor
|
||||||
|
|
||||||
|
height: screenSelectionScrollView.height
|
||||||
|
width: screenSelectionScrollView.width - 2 * JamiTheme.preferredMarginSize
|
||||||
|
|
||||||
|
border.color: borderColor
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: selectScreenWindow
|
||||||
|
|
||||||
|
function onSelectedScreenNumberChanged() {
|
||||||
|
// Recover from green state.
|
||||||
|
selectAllScreens = false
|
||||||
|
screenSelectionRectAll.borderColor = JamiTheme.tabbarBorderColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: screenShotAll
|
||||||
|
|
||||||
|
anchors.top: screenSelectionRectAll.top
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.horizontalCenter: screenSelectionRectAll.horizontalCenter
|
||||||
|
|
||||||
|
height: screenSelectionRectAll.height - 50
|
||||||
|
width: screenSelectionRectAll.width - 50
|
||||||
|
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
mipmap: true
|
||||||
|
|
||||||
|
Component.onCompleted: AvAdapter.captureAllScreens()
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: screenNameAll
|
||||||
|
|
||||||
|
anchors.top: screenShotAll.bottom
|
||||||
|
anchors.topMargin: 10
|
||||||
|
anchors.horizontalCenter: screenSelectionRectAll.horizontalCenter
|
||||||
|
|
||||||
|
font.pointSize: JamiTheme.textFontSize - 2
|
||||||
|
text: qsTr("All Screens")
|
||||||
|
color: JamiTheme.textColor
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
selectedScreenNumber = -1
|
||||||
|
selectAllScreens = true
|
||||||
|
screenSelectionRectAll.borderColor
|
||||||
|
= JamiTheme.screenSelectionBorderGreen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -273,7 +348,7 @@ Window {
|
||||||
width: 200
|
width: 200
|
||||||
height: 36
|
height: 36
|
||||||
|
|
||||||
visible: selectedScreenNumber != -1
|
visible: selectedScreenNumber != -1 || selectAllScreens
|
||||||
|
|
||||||
color: JamiTheme.buttonTintedBlack
|
color: JamiTheme.buttonTintedBlack
|
||||||
hoveredColor: JamiTheme.buttonTintedBlackHovered
|
hoveredColor: JamiTheme.buttonTintedBlackHovered
|
||||||
|
@ -284,21 +359,11 @@ Window {
|
||||||
text: JamiStrings.shareScreen
|
text: JamiStrings.shareScreen
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (selectArea) {
|
if (selectAllScreens)
|
||||||
selectScreenWindow.hide()
|
AvAdapter.shareAllScreens()
|
||||||
ScreenRubberBandCreation.createScreenRubberBandWindowObject(
|
else
|
||||||
selectScreenWindow, selectedScreenNumber - 1)
|
|
||||||
ScreenRubberBandCreation.showScreenRubberBandWindow()
|
|
||||||
|
|
||||||
|
|
||||||
// Destory selectScreenWindow once screenRubberBand is closed.
|
|
||||||
ScreenRubberBandCreation.connectOnClosingEvent(function () {
|
|
||||||
selectScreenWindow.close()
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
AvAdapter.shareEntireScreen(selectedScreenNumber - 1)
|
AvAdapter.shareEntireScreen(selectedScreenNumber - 1)
|
||||||
selectScreenWindow.close()
|
selectScreenWindow.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
import QtQuick 2.14
|
import QtQuick 2.14
|
||||||
import QtQuick.Window 2.14
|
import QtQuick.Window 2.14
|
||||||
|
import net.jami.Models 1.0
|
||||||
|
|
||||||
Window {
|
Window {
|
||||||
id: videoWindow
|
id: videoWindow
|
||||||
|
@ -28,7 +29,11 @@ Window {
|
||||||
|
|
||||||
flags: Qt.FramelessWindowHint
|
flags: Qt.FramelessWindowHint
|
||||||
|
|
||||||
screen: Qt.application.screens[0]
|
screen: JamiQmlUtils.mainApplicationScreen
|
||||||
|
|
||||||
|
// +1 so that it does not fallback to the previous screen
|
||||||
|
x: screen.virtualX + 1
|
||||||
|
y: screen.virtualY + 1
|
||||||
|
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ function finishCreation() {
|
||||||
|
|
||||||
// Signal connection.
|
// Signal connection.
|
||||||
callFullScreenWindowContainerObject.onClosing.connect(
|
callFullScreenWindowContainerObject.onClosing.connect(
|
||||||
destoryVideoCallFullScreenWindowContainer)
|
destroyVideoCallFullScreenWindowContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkIfVisible() {
|
function checkIfVisible() {
|
||||||
|
@ -57,7 +57,7 @@ function setAsContainerChild(obj) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Destroy and reset callFullScreenWindowContainerObject when window is closed.
|
// Destroy and reset callFullScreenWindowContainerObject when window is closed.
|
||||||
function destoryVideoCallFullScreenWindowContainer() {
|
function destroyVideoCallFullScreenWindowContainer() {
|
||||||
if (!callFullScreenWindowContainerObject)
|
if (!callFullScreenWindowContainerObject)
|
||||||
return
|
return
|
||||||
callFullScreenWindowContainerObject.destroy()
|
callFullScreenWindowContainerObject.destroy()
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 by Savoir-faire Linux
|
* Copyright (C) 2020 by Savoir-faire Linux
|
||||||
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||||
|
@ -17,64 +16,45 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Global screen rubber band window component, object variable for creation.
|
||||||
/*
|
|
||||||
* Global screen rubber band window component, object variable for creation.
|
|
||||||
*/
|
|
||||||
var screenRubberBandWindowComponent
|
var screenRubberBandWindowComponent
|
||||||
var screenRubberBandWindowObject
|
var screenRubberBandWindowObject
|
||||||
|
|
||||||
function createScreenRubberBandWindowObject(parent, screenNumber) {
|
var selectAllScreens = false
|
||||||
|
|
||||||
|
function createScreenRubberBandWindowObject() {
|
||||||
if (screenRubberBandWindowObject)
|
if (screenRubberBandWindowObject)
|
||||||
return
|
return
|
||||||
screenRubberBandWindowComponent = Qt.createComponent(
|
screenRubberBandWindowComponent = Qt.createComponent(
|
||||||
"../components/ScreenRubberBand.qml")
|
"../components/ScreenRubberBand.qml")
|
||||||
if (screenRubberBandWindowComponent.status === Component.Ready)
|
if (screenRubberBandWindowComponent.status === Component.Ready)
|
||||||
finishCreation(parent, screenNumber)
|
finishCreation()
|
||||||
else if (screenRubberBandWindowComponent.status === Component.Error)
|
else if (screenRubberBandWindowComponent.status === Component.Error)
|
||||||
console.log("Error loading component:",
|
console.log("Error loading component:",
|
||||||
screenRubberBandWindowComponent.errorString())
|
screenRubberBandWindowComponent.errorString())
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishCreation(parent, screenNumber) {
|
function finishCreation() {
|
||||||
screenRubberBandWindowObject = screenRubberBandWindowComponent.createObject(
|
screenRubberBandWindowObject = screenRubberBandWindowComponent.createObject()
|
||||||
parent)
|
|
||||||
if (screenRubberBandWindowObject === null) {
|
if (screenRubberBandWindowObject === null) {
|
||||||
|
// Error Handling.
|
||||||
|
|
||||||
/*
|
|
||||||
* Error Handling.
|
|
||||||
*/
|
|
||||||
console.log("Error creating screen rubber band object")
|
console.log("Error creating screen rubber band object")
|
||||||
}
|
}
|
||||||
|
|
||||||
screenRubberBandWindowObject.screenNumber = screenNumber
|
// Signal connection.
|
||||||
screenRubberBandWindowObject.screen = Qt.application.screens[screenNumber]
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Signal connection.
|
|
||||||
*/
|
|
||||||
screenRubberBandWindowObject.onClosing.connect(
|
screenRubberBandWindowObject.onClosing.connect(
|
||||||
destoryScreenRubberBandWindow)
|
destroyScreenRubberBandWindow)
|
||||||
}
|
}
|
||||||
|
|
||||||
function showScreenRubberBandWindow() {
|
function showScreenRubberBandWindow() {
|
||||||
screenRubberBandWindowObject.showFullScreen()
|
screenRubberBandWindowObject.show()
|
||||||
|
screenRubberBandWindowObject.setAllScreensGeo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy and reset screenRubberBandWindowObject when window is closed.
|
||||||
/*
|
function destroyScreenRubberBandWindow() {
|
||||||
* Destroy and reset screenRubberBandWindowObject when window is closed.
|
|
||||||
*/
|
|
||||||
function destoryScreenRubberBandWindow() {
|
|
||||||
if (!screenRubberBandWindowObject)
|
if (!screenRubberBandWindowObject)
|
||||||
return
|
return
|
||||||
screenRubberBandWindowObject.destroy()
|
screenRubberBandWindowObject.destroy()
|
||||||
screenRubberBandWindowObject = false
|
screenRubberBandWindowObject = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function connectOnClosingEvent(func) {
|
|
||||||
if (screenRubberBandWindowObject)
|
|
||||||
screenRubberBandWindowObject.onClosing.connect(func)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2020 by Savoir-faire Linux
|
* Copyright (C) 2020 by Savoir-faire Linux
|
||||||
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
|
||||||
|
@ -17,54 +16,45 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Global select screen window component, object variable for creation.
|
||||||
/*
|
|
||||||
* Global select screen window component, object variable for creation.
|
|
||||||
*/
|
|
||||||
var selectScreenWindowComponent
|
var selectScreenWindowComponent
|
||||||
var selectScreenWindowObject
|
var selectScreenWindowObject
|
||||||
|
|
||||||
function createSelectScreenWindowObject(selectArea = false) {
|
function createSelectScreenWindowObject() {
|
||||||
if (selectScreenWindowObject)
|
if (selectScreenWindowObject)
|
||||||
return
|
return
|
||||||
selectScreenWindowComponent = Qt.createComponent(
|
selectScreenWindowComponent = Qt.createComponent(
|
||||||
"../components/SelectScreen.qml")
|
"../components/SelectScreen.qml")
|
||||||
if (selectScreenWindowComponent.status === Component.Ready)
|
if (selectScreenWindowComponent.status === Component.Ready)
|
||||||
finishCreation(selectArea)
|
finishCreation()
|
||||||
else if (selectScreenWindowComponent.status === Component.Error)
|
else if (selectScreenWindowComponent.status === Component.Error)
|
||||||
console.log("Error loading component:",
|
console.log("Error loading component:",
|
||||||
selectScreenWindowComponent.errorString())
|
selectScreenWindowComponent.errorString())
|
||||||
}
|
}
|
||||||
|
|
||||||
function finishCreation(selectArea) {
|
function finishCreation() {
|
||||||
selectScreenWindowObject = selectScreenWindowComponent.createObject()
|
selectScreenWindowObject = selectScreenWindowComponent.createObject()
|
||||||
if (selectScreenWindowObject === null) {
|
if (selectScreenWindowObject === null) {
|
||||||
|
// Error Handling.
|
||||||
|
|
||||||
/*
|
|
||||||
* Error Handling.
|
|
||||||
*/
|
|
||||||
console.log("Error creating select screen object")
|
console.log("Error creating select screen object")
|
||||||
}
|
}
|
||||||
|
|
||||||
selectScreenWindowObject.selectArea = selectArea
|
// Signal connection.
|
||||||
|
selectScreenWindowObject.onClosing.connect(destroySelectScreenWindow)
|
||||||
|
|
||||||
/*
|
|
||||||
* Signal connection.
|
|
||||||
*/
|
|
||||||
selectScreenWindowObject.onClosing.connect(destorySelectScreenWindow)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSelectScreenWindow() {
|
function showSelectScreenWindow() {
|
||||||
selectScreenWindowObject.show()
|
selectScreenWindowObject.show()
|
||||||
|
|
||||||
|
var screen = selectScreenWindowObject.screen
|
||||||
|
selectScreenWindowObject.x = screen.virtualX +
|
||||||
|
(screen.width - selectScreenWindowObject.width) / 2
|
||||||
|
selectScreenWindowObject.y = screen.virtualY +
|
||||||
|
(screen.height - selectScreenWindowObject.height) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Destroy and reset selectScreenWindowObject when window is closed.
|
||||||
/*
|
function destroySelectScreenWindow() {
|
||||||
* Destroy and reset selectScreenWindowObject when window is closed.
|
|
||||||
*/
|
|
||||||
function destorySelectScreenWindow() {
|
|
||||||
if(!selectScreenWindowObject)
|
if(!selectScreenWindowObject)
|
||||||
return
|
return
|
||||||
selectScreenWindowObject.destroy()
|
selectScreenWindowObject.destroy()
|
||||||
|
|
|
@ -31,25 +31,23 @@ PreviewRenderer::PreviewRenderer(QQuickItem* parent)
|
||||||
|
|
||||||
previewFrameUpdatedConnection_ = connect(LRCInstance::renderer(),
|
previewFrameUpdatedConnection_ = connect(LRCInstance::renderer(),
|
||||||
&RenderManager::previewFrameUpdated,
|
&RenderManager::previewFrameUpdated,
|
||||||
[this]() { update(QRect(0, 0, width(), height())); });
|
[this]() {
|
||||||
|
if (isVisible())
|
||||||
previewRenderingStopped_ = connect(LRCInstance::renderer(),
|
update(QRect(0, 0, width(), height()));
|
||||||
&RenderManager::previewRenderingStopped,
|
});
|
||||||
[this]() { update(QRect(0, 0, width(), height())); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PreviewRenderer::~PreviewRenderer()
|
PreviewRenderer::~PreviewRenderer()
|
||||||
{
|
{
|
||||||
disconnect(previewFrameUpdatedConnection_);
|
disconnect(previewFrameUpdatedConnection_);
|
||||||
disconnect(previewRenderingStopped_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
PreviewRenderer::paint(QPainter* painter)
|
PreviewRenderer::paint(QPainter* painter)
|
||||||
{
|
{
|
||||||
auto previewImage = LRCInstance::renderer()->getPreviewFrame();
|
LRCInstance::renderer()
|
||||||
|
->drawFrame(lrc::api::video::PREVIEW_RENDERER_ID, [this, painter](QImage* previewImage) {
|
||||||
if (previewImage) {
|
if (previewImage) {
|
||||||
QImage scaledPreview;
|
|
||||||
auto aspectRatio = static_cast<qreal>(previewImage->width())
|
auto aspectRatio = static_cast<qreal>(previewImage->width())
|
||||||
/ static_cast<qreal>(previewImage->height());
|
/ static_cast<qreal>(previewImage->height());
|
||||||
auto previewHeight = height();
|
auto previewHeight = height();
|
||||||
|
@ -60,17 +58,21 @@ PreviewRenderer::paint(QPainter* painter)
|
||||||
* e.g.
|
* e.g.
|
||||||
* auto parent = qobject_cast<QWidget*>(this->parent());
|
* auto parent = qobject_cast<QWidget*>(this->parent());
|
||||||
* auto xPos = (parent->width() - previewWidth) / 2;
|
* auto xPos = (parent->width() - previewWidth) / 2;
|
||||||
* setGeometry(QRect(QPoint(xPos, this->pos().y()), QSize(previewWidth, previewHeight)));
|
* setGeometry(QRect(QPoint(xPos, this->pos().y()),
|
||||||
|
* QSize(previewWidth, previewHeight)));
|
||||||
*/
|
*/
|
||||||
setWidth(previewWidth);
|
setWidth(previewWidth);
|
||||||
setHeight(previewHeight);
|
setHeight(previewHeight);
|
||||||
|
|
||||||
|
// If the given size is empty, this function returns a null image.
|
||||||
|
QImage scaledPreview;
|
||||||
scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio);
|
scaledPreview = previewImage->scaled(size().toSize(), Qt::KeepAspectRatio);
|
||||||
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
||||||
scaledPreview);
|
scaledPreview);
|
||||||
} else {
|
} else {
|
||||||
paintBackground(painter);
|
paintBackground(painter);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -93,7 +95,8 @@ VideoCallPreviewRenderer::~VideoCallPreviewRenderer() {}
|
||||||
void
|
void
|
||||||
VideoCallPreviewRenderer::paint(QPainter* painter)
|
VideoCallPreviewRenderer::paint(QPainter* painter)
|
||||||
{
|
{
|
||||||
auto previewImage = LRCInstance::renderer()->getPreviewFrame();
|
LRCInstance::renderer()
|
||||||
|
->drawFrame(lrc::api::video::PREVIEW_RENDERER_ID, [this, painter](QImage* previewImage) {
|
||||||
if (previewImage) {
|
if (previewImage) {
|
||||||
auto scalingFactor = static_cast<qreal>(previewImage->height())
|
auto scalingFactor = static_cast<qreal>(previewImage->height())
|
||||||
/ static_cast<qreal>(previewImage->width());
|
/ static_cast<qreal>(previewImage->width());
|
||||||
|
@ -103,6 +106,7 @@ VideoCallPreviewRenderer::paint(QPainter* painter)
|
||||||
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
||||||
scaledPreview);
|
scaledPreview);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
PhotoboothPreviewRender::PhotoboothPreviewRender(QQuickItem* parent)
|
PhotoboothPreviewRender::PhotoboothPreviewRender(QQuickItem* parent)
|
||||||
|
@ -130,7 +134,8 @@ PhotoboothPreviewRender::paint(QPainter* painter)
|
||||||
{
|
{
|
||||||
painter->setRenderHint(QPainter::Antialiasing, true);
|
painter->setRenderHint(QPainter::Antialiasing, true);
|
||||||
|
|
||||||
auto previewImage = LRCInstance::renderer()->getPreviewFrame();
|
LRCInstance::renderer()
|
||||||
|
->drawFrame(lrc::api::video::PREVIEW_RENDERER_ID, [this, painter](QImage* previewImage) {
|
||||||
if (previewImage) {
|
if (previewImage) {
|
||||||
QImage scaledPreview;
|
QImage scaledPreview;
|
||||||
scaledPreview = Utils::getCirclePhoto(*previewImage,
|
scaledPreview = Utils::getCirclePhoto(*previewImage,
|
||||||
|
@ -138,4 +143,5 @@ PhotoboothPreviewRender::paint(QPainter* painter)
|
||||||
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
painter->drawImage(QRect(0, 0, scaledPreview.width(), scaledPreview.height()),
|
||||||
scaledPreview);
|
scaledPreview);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QMetaObject::Connection previewFrameUpdatedConnection_;
|
QMetaObject::Connection previewFrameUpdatedConnection_;
|
||||||
QMetaObject::Connection previewRenderingStopped_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class VideoCallPreviewRenderer : public PreviewRenderer
|
class VideoCallPreviewRenderer : public PreviewRenderer
|
||||||
|
|
|
@ -85,7 +85,10 @@ FrameWrapper::stopRendering()
|
||||||
QImage*
|
QImage*
|
||||||
FrameWrapper::getFrame()
|
FrameWrapper::getFrame()
|
||||||
{
|
{
|
||||||
return isRendering_ ? image_.get() : nullptr;
|
if (image_.get()) {
|
||||||
|
return isRendering_ ? (image_.get()->isNull() ? nullptr : image_.get()) : nullptr;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -94,6 +97,18 @@ FrameWrapper::isRendering()
|
||||||
return isRendering_;
|
return isRendering_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
FrameWrapper::frameMutexTryLock()
|
||||||
|
{
|
||||||
|
return mutex_.tryLock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FrameWrapper::frameMutexUnlock()
|
||||||
|
{
|
||||||
|
mutex_.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FrameWrapper::slotRenderingStarted(const QString& id)
|
FrameWrapper::slotRenderingStarted(const QString& id)
|
||||||
{
|
{
|
||||||
|
@ -127,23 +142,25 @@ FrameWrapper::slotFrameUpdated(const QString& id)
|
||||||
|
|
||||||
unsigned int width = renderer_->size().width();
|
unsigned int width = renderer_->size().width();
|
||||||
unsigned int height = renderer_->size().height();
|
unsigned int height = renderer_->size().height();
|
||||||
|
|
||||||
#ifndef Q_OS_LINUX
|
#ifndef Q_OS_LINUX
|
||||||
unsigned int size = frame_.storage.size();
|
unsigned int size = frame_.storage.size();
|
||||||
|
auto imageFormat = QImage::Format_ARGB32_Premultiplied;
|
||||||
|
#else
|
||||||
|
unsigned int size = frame_.size;
|
||||||
|
auto imageFormat = QImage::Format_ARGB32;
|
||||||
|
#endif
|
||||||
/*
|
/*
|
||||||
* If the frame is empty or not the expected size,
|
* If the frame is empty or not the expected size,
|
||||||
* do nothing and keep the last rendered QImage.
|
* do nothing and keep the last rendered QImage.
|
||||||
*/
|
*/
|
||||||
if (size != 0 && size == width * height * 4) {
|
if (size != 0 && size == width * height * 4) {
|
||||||
|
#ifndef Q_OS_LINUX
|
||||||
buffer_ = std::move(frame_.storage);
|
buffer_ = std::move(frame_.storage);
|
||||||
image_.reset(new QImage((uchar*) buffer_.data(),
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
QImage::Format_ARGB32_Premultiplied));
|
|
||||||
#else
|
#else
|
||||||
if (frame_.ptr) {
|
buffer_.reserve(size);
|
||||||
image_.reset(new QImage(frame_.ptr, width, height, QImage::Format_ARGB32));
|
std::move(frame_.ptr, frame_.ptr + size, buffer_.begin());
|
||||||
#endif
|
#endif
|
||||||
|
image_.reset(new QImage((uchar*) buffer_.data(), width, height, imageFormat));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit frameUpdated(id);
|
emit frameUpdated(id);
|
||||||
|
@ -161,7 +178,10 @@ FrameWrapper::slotRenderingStopped(const QString& id)
|
||||||
|
|
||||||
renderer_ = nullptr;
|
renderer_ = nullptr;
|
||||||
|
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&mutex_);
|
||||||
image_.reset();
|
image_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
emit renderingStopped(id);
|
emit renderingStopped(id);
|
||||||
}
|
}
|
||||||
|
@ -202,12 +222,6 @@ RenderManager::isPreviewing()
|
||||||
return previewFrameWrapper_->isRendering();
|
return previewFrameWrapper_->isRendering();
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage*
|
|
||||||
RenderManager::getPreviewFrame()
|
|
||||||
{
|
|
||||||
return previewFrameWrapper_->getFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RenderManager::stopPreviewing()
|
RenderManager::stopPreviewing()
|
||||||
{
|
{
|
||||||
|
@ -232,16 +246,6 @@ RenderManager::startPreviewing(bool force)
|
||||||
avModel_.startPreview();
|
avModel_.startPreview();
|
||||||
}
|
}
|
||||||
|
|
||||||
QImage*
|
|
||||||
RenderManager::getFrame(const QString& id)
|
|
||||||
{
|
|
||||||
auto dfwIt = distantFrameWrapperMap_.find(id);
|
|
||||||
if (dfwIt != distantFrameWrapperMap_.end()) {
|
|
||||||
return dfwIt->second->getFrame();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RenderManager::addDistantRenderer(const QString& id)
|
RenderManager::addDistantRenderer(const QString& id)
|
||||||
{
|
{
|
||||||
|
@ -305,3 +309,28 @@ RenderManager::removeDistantRenderer(const QString& id)
|
||||||
distantFrameWrapperMap_.erase(dfwIt);
|
distantFrameWrapperMap_.erase(dfwIt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RenderManager::drawFrame(const QString& id, DrawFrameCallback cb)
|
||||||
|
{
|
||||||
|
if (id == lrc::api::video::PREVIEW_RENDERER_ID) {
|
||||||
|
if (previewFrameWrapper_->frameMutexTryLock()) {
|
||||||
|
cb(previewFrameWrapper_->getFrame());
|
||||||
|
previewFrameWrapper_->frameMutexUnlock();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto dfwIt = distantFrameWrapperMap_.find(id);
|
||||||
|
if (dfwIt != distantFrameWrapperMap_.end()) {
|
||||||
|
if (dfwIt->second->frameMutexTryLock()) {
|
||||||
|
cb(dfwIt->second->getFrame());
|
||||||
|
dfwIt->second->frameMutexUnlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QImage*
|
||||||
|
RenderManager::getPreviewFrame()
|
||||||
|
{
|
||||||
|
return previewFrameWrapper_->getFrame();
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,10 @@ public:
|
||||||
*/
|
*/
|
||||||
bool isRendering();
|
bool isRendering();
|
||||||
|
|
||||||
|
bool frameMutexTryLock();
|
||||||
|
|
||||||
|
void frameMutexUnlock();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/*
|
/*
|
||||||
* Emitted each time a frame is ready to be displayed.
|
* Emitted each time a frame is ready to be displayed.
|
||||||
|
@ -168,15 +172,12 @@ public:
|
||||||
explicit RenderManager(AVModel& avModel);
|
explicit RenderManager(AVModel& avModel);
|
||||||
~RenderManager();
|
~RenderManager();
|
||||||
|
|
||||||
|
using DrawFrameCallback = std::function<void(QImage*)>;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if the preview is active.
|
* Check if the preview is active.
|
||||||
*/
|
*/
|
||||||
bool isPreviewing();
|
bool isPreviewing();
|
||||||
/*
|
|
||||||
* Get the most recently rendered preview frame as a QImage.
|
|
||||||
* @return the rendered preview image
|
|
||||||
*/
|
|
||||||
QImage* getPreviewFrame();
|
|
||||||
/*
|
/*
|
||||||
* Start capturing and rendering preview frames.
|
* Start capturing and rendering preview frames.
|
||||||
* @param force if the capture device should be started
|
* @param force if the capture device should be started
|
||||||
|
@ -186,13 +187,6 @@ public:
|
||||||
* Stop capturing.
|
* Stop capturing.
|
||||||
*/
|
*/
|
||||||
void stopPreviewing();
|
void stopPreviewing();
|
||||||
|
|
||||||
/*
|
|
||||||
* Get the most recently rendered distant frame for a given id
|
|
||||||
* as a QImage.
|
|
||||||
* @return the rendered preview image
|
|
||||||
*/
|
|
||||||
QImage* getFrame(const QString& id);
|
|
||||||
/*
|
/*
|
||||||
* Add and connect a distant renderer for a given id
|
* Add and connect a distant renderer for a given id
|
||||||
* to a FrameWrapper object
|
* to a FrameWrapper object
|
||||||
|
@ -205,6 +199,18 @@ public:
|
||||||
* @param id
|
* @param id
|
||||||
*/
|
*/
|
||||||
void removeDistantRenderer(const QString& id);
|
void removeDistantRenderer(const QString& id);
|
||||||
|
/*
|
||||||
|
* Frame will be provided in the callback thread safely
|
||||||
|
* @param id
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
void drawFrame(const QString& id, DrawFrameCallback cb);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the most recently rendered preview frame as a QImage (none thread safe).
|
||||||
|
* @return the rendered preview image
|
||||||
|
*/
|
||||||
|
QImage* getPreviewFrame();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
||||||
|
|
123
src/xrectsel.c
Normal file
123
src/xrectsel.c
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* This code is based and adapted from:
|
||||||
|
* https://github.com/lolilolicon/FFcast2/blob/master/xrectsel.c
|
||||||
|
*
|
||||||
|
* now located at:
|
||||||
|
* https://github.com/lolilolicon/xrectsel/blob/master/xrectsel.c
|
||||||
|
*
|
||||||
|
* xrectsel.c -- print the geometry of a rectangular screen region.
|
||||||
|
* Copyright (C) 2011-2014 lolilolicon <lolilolicon@gmail.com>
|
||||||
|
* 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 <X11/Xlib.h>
|
||||||
|
#include <X11/cursorfont.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
xrectsel(unsigned* x_sel, unsigned* y_sel, unsigned* w_sel, unsigned* h_sel)
|
||||||
|
{
|
||||||
|
Display* dpy = XOpenDisplay(NULL);
|
||||||
|
if (!dpy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Window root = DefaultRootWindow(dpy);
|
||||||
|
|
||||||
|
XEvent ev;
|
||||||
|
|
||||||
|
GC sel_gc;
|
||||||
|
XGCValues sel_gv;
|
||||||
|
|
||||||
|
int btn_pressed = 0;
|
||||||
|
int x = 0, y = 0;
|
||||||
|
unsigned int width = 0, height = 0;
|
||||||
|
int start_x = 0, start_y = 0;
|
||||||
|
|
||||||
|
Cursor cursor;
|
||||||
|
cursor = XCreateFontCursor(dpy, XC_crosshair);
|
||||||
|
|
||||||
|
/* Grab pointer for these events */
|
||||||
|
XGrabPointer(dpy,
|
||||||
|
root,
|
||||||
|
True,
|
||||||
|
PointerMotionMask | ButtonPressMask | ButtonReleaseMask,
|
||||||
|
GrabModeAsync,
|
||||||
|
GrabModeAsync,
|
||||||
|
None,
|
||||||
|
cursor,
|
||||||
|
CurrentTime);
|
||||||
|
|
||||||
|
sel_gv.function = GXinvert;
|
||||||
|
sel_gv.subwindow_mode = IncludeInferiors;
|
||||||
|
sel_gv.line_width = 1;
|
||||||
|
sel_gc = XCreateGC(dpy, root, GCFunction | GCSubwindowMode | GCLineWidth, &sel_gv);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
XNextEvent(dpy, &ev);
|
||||||
|
|
||||||
|
if (ev.type == ButtonPress) {
|
||||||
|
btn_pressed = 1;
|
||||||
|
x = start_x = ev.xbutton.x_root;
|
||||||
|
y = start_y = ev.xbutton.y_root;
|
||||||
|
width = height = 0;
|
||||||
|
|
||||||
|
} else if (ev.type == MotionNotify) {
|
||||||
|
if (!btn_pressed)
|
||||||
|
continue; /* Draw only if button is pressed */
|
||||||
|
|
||||||
|
/* Re-draw last Rectangle to clear it */
|
||||||
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
||||||
|
|
||||||
|
x = ev.xbutton.x_root;
|
||||||
|
y = ev.xbutton.y_root;
|
||||||
|
|
||||||
|
if (x > start_x) {
|
||||||
|
width = x - start_x;
|
||||||
|
x = start_x;
|
||||||
|
} else {
|
||||||
|
width = start_x - x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y > start_y) {
|
||||||
|
height = y - start_y;
|
||||||
|
y = start_y;
|
||||||
|
} else {
|
||||||
|
height = start_y - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Draw Rectangle */
|
||||||
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
||||||
|
XFlush(dpy);
|
||||||
|
|
||||||
|
} else if (ev.type == ButtonRelease)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Re-draw last Rectangle to clear it */
|
||||||
|
XDrawRectangle(dpy, root, sel_gc, x, y, width, height);
|
||||||
|
XFlush(dpy);
|
||||||
|
|
||||||
|
XUngrabPointer(dpy, CurrentTime);
|
||||||
|
XFreeCursor(dpy, cursor);
|
||||||
|
XFreeGC(dpy, sel_gc);
|
||||||
|
XSync(dpy, 1);
|
||||||
|
|
||||||
|
*x_sel = x;
|
||||||
|
*y_sel = y;
|
||||||
|
*w_sel = width;
|
||||||
|
*h_sel = height;
|
||||||
|
|
||||||
|
XCloseDisplay(dpy);
|
||||||
|
}
|
26
src/xrectsel.h
Normal file
26
src/xrectsel.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* This code is based and adapted from:
|
||||||
|
* https://github.com/lolilolicon/FFcast2/blob/master/xrectsel.c
|
||||||
|
*
|
||||||
|
* now located at:
|
||||||
|
* https://github.com/lolilolicon/xrectsel/blob/master/xrectsel.c
|
||||||
|
*
|
||||||
|
* xrectsel.c -- print the geometry of a rectangular screen region.
|
||||||
|
* Copyright (C) 2011-2014 lolilolicon <lolilolicon@gmail.com>
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void xrectsel(unsigned* x_sel, unsigned* y_sel, unsigned* w_sel, unsigned* h_sel);
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue