mirror of
https://github.com/barry-ran/QtScrcpy.git
synced 2025-08-03 22:28:39 +00:00
feat: Add minimal QML support, add simple demo QML
app
This commit is contained in:
parent
4274042673
commit
9fa1933f46
10 changed files with 1668 additions and 5 deletions
|
@ -18,6 +18,8 @@ set(QC_PROJECT_VERSION ${QC_FILE_VERSION})
|
||||||
project(${QC_PROJECT_NAME} VERSION ${QC_PROJECT_VERSION} LANGUAGES CXX)
|
project(${QC_PROJECT_NAME} VERSION ${QC_PROJECT_VERSION} LANGUAGES CXX)
|
||||||
message(STATUS "[${PROJECT_NAME}] Project ${PROJECT_NAME} ${PROJECT_VERSION}")
|
message(STATUS "[${PROJECT_NAME}] Project ${PROJECT_NAME} ${PROJECT_VERSION}")
|
||||||
|
|
||||||
|
option(BUILD_QML_DEMO "Build demo QML app" ON)
|
||||||
|
|
||||||
# QC define
|
# QC define
|
||||||
|
|
||||||
# check arch
|
# check arch
|
||||||
|
@ -68,7 +70,12 @@ if (NOT MSVC)
|
||||||
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||||
|
|
||||||
# disable some warning
|
# disable some warning
|
||||||
add_compile_options(-Wno-nested-anon-types -Wno-c++17-extensions -Wno-overloaded-virtual)
|
set(QSC_COMPILE_DEFINITIONS -Wno-nested-anon-types -Wno-c++17-extensions -Wno-overloaded-virtual)
|
||||||
|
if(BUILD_QML_DEMO)
|
||||||
|
set(QSC_COMPILE_DEFINITIONS ${QSC_COMPILE_DEFINITIONS} -Wno-gnu-zero-variadic-macro-arguments)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_options(${QSC_COMPILE_DEFINITIONS})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -79,8 +86,8 @@ set(CMAKE_AUTOUIC ON)
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
|
|
||||||
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
|
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia OpenGL OpenGLWidgets Quick REQUIRED)
|
||||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
|
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Multimedia OpenGL OpenGLWidgets Quick REQUIRED)
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
find_package(X11 REQUIRED)
|
find_package(X11 REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
@ -297,6 +304,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework AppKit")
|
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework AppKit")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
qt_add_library(QtScrcpyQml STATIC)
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
|
get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
|
||||||
|
@ -313,7 +322,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
# qx11
|
# qx11
|
||||||
Qt${QT_VERSION_MAJOR}::CorePrivate
|
|
||||||
# xcb https://doc.qt.io/qt-5/linux-requirements.html
|
# xcb https://doc.qt.io/qt-5/linux-requirements.html
|
||||||
xcb
|
xcb
|
||||||
# pthread
|
# pthread
|
||||||
|
@ -339,3 +347,26 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
|
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
|
||||||
QtScrcpyCore
|
QtScrcpyCore
|
||||||
)
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(QtScrcpyQml
|
||||||
|
SOURCES
|
||||||
|
URI "QtScrcpy"
|
||||||
|
VERSION 1.0
|
||||||
|
SOURCES
|
||||||
|
qml/scrcpyitem.h qml/scrcpyitem.cpp
|
||||||
|
qml/scrcpymanager.h qml/scrcpymanager.cpp
|
||||||
|
util/config.h
|
||||||
|
util/config.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(QtScrcpyQml PRIVATE qml)
|
||||||
|
|
||||||
|
target_link_libraries(QtScrcpyQml PRIVATE
|
||||||
|
Qt${QT_VERSION_MAJOR}::OpenGL
|
||||||
|
Qt${QT_VERSION_MAJOR}::Quick
|
||||||
|
QtScrcpyCore
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_QML_DEMO)
|
||||||
|
add_subdirectory(TestQml)
|
||||||
|
endif()
|
||||||
|
|
46
QtScrcpy/TestQml/CMakeLists.txt
Normal file
46
QtScrcpy/TestQml/CMakeLists.txt
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
qt_add_executable(appTestQml
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(appTestQml
|
||||||
|
URI TestQml
|
||||||
|
VERSION 1.0
|
||||||
|
QML_FILES
|
||||||
|
Main.qml
|
||||||
|
)
|
||||||
|
|
||||||
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
|
# If you are developing for iOS or macOS you should consider setting an
|
||||||
|
# explicit, fixed bundle identifier manually though.
|
||||||
|
set_target_properties(appTestQml PROPERTIES
|
||||||
|
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appTestQml
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
WIN32_EXECUTABLE TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(appTestQml
|
||||||
|
PRIVATE Qt6::Quick QtScrcpyQmlplugin
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
install(TARGETS appTestQml
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
set(QC_CPU_ARCH x64)
|
||||||
|
else()
|
||||||
|
set(QC_CPU_ARCH x86)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set_target_properties(appTestQml PROPERTIES
|
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../output/${QC_CPU_ARCH}/${CMAKE_BUILD_TYPE}/$<0:>"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(TARGET appTestQml POST_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/../../config/config.ini" "${CMAKE_CURRENT_SOURCE_DIR}/../../output/${QC_CPU_ARCH}/${CMAKE_BUILD_TYPE}/config/config.ini"
|
||||||
|
)
|
213
QtScrcpy/TestQml/Main.qml
Normal file
213
QtScrcpy/TestQml/Main.qml
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import QtScrcpy
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
width: 800
|
||||||
|
height: 600
|
||||||
|
visible: true
|
||||||
|
title: qsTr("Scrcpy Manager")
|
||||||
|
|
||||||
|
color: "black"
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: mainLayout
|
||||||
|
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: _list
|
||||||
|
|
||||||
|
Layout.preferredWidth: parent.width * 0.5
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
|
||||||
|
visible: !_scrcpyItem.visible
|
||||||
|
enabled: !ScrcpyManager.currentlyConnecting
|
||||||
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
model: ScrcpyManager.devicesList
|
||||||
|
delegate: Frame {
|
||||||
|
width: parent.width
|
||||||
|
height: 80
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "lightgray"
|
||||||
|
border.color: "black"
|
||||||
|
radius: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 10
|
||||||
|
|
||||||
|
Text {
|
||||||
|
text: modelData
|
||||||
|
font.pixelSize: 20
|
||||||
|
color: "black"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Connect"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
onClicked: {
|
||||||
|
ScrcpyManager.connectToDevice(_scrcpyItem,
|
||||||
|
modelData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: settingsContainer
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.preferredWidth: parent.width * 0.5
|
||||||
|
|
||||||
|
spacing: 10
|
||||||
|
visible: !_scrcpyItem.visible
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Label {
|
||||||
|
text: "Size"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
ComboBox {
|
||||||
|
id: maxSizeComboBox
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ScrcpyManager.maxSizeArray
|
||||||
|
currentIndex: ScrcpyManager.maxSizeIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
ScrcpyManager.maxSizeIndex = currentIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Label {
|
||||||
|
text: "Rate"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
ComboBox {
|
||||||
|
id: bitRateUnitsComboBox
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ScrcpyManager.availableBitRatesUnits
|
||||||
|
currentIndex: ScrcpyManager.bitRateUnits === "Mbps" ? 0 : 1
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
ScrcpyManager.bitRateUnits = (currentIndex === 0 ? "Mbps" : "Kbps")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Label {
|
||||||
|
text: "Bits"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
TextField {
|
||||||
|
id: bitRateField
|
||||||
|
Layout.fillWidth: true
|
||||||
|
placeholderText: "Bit Rate"
|
||||||
|
text: ScrcpyManager.bitRateNumeric.toString()
|
||||||
|
onTextChanged: {
|
||||||
|
ScrcpyManager.bitRateNumeric = parseInt(text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
Label {
|
||||||
|
text: "Orientation"
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
ComboBox {
|
||||||
|
id: lockOrientationComboBox
|
||||||
|
Layout.fillWidth: true
|
||||||
|
model: ScrcpyManager.availableOrientations
|
||||||
|
currentIndex: ScrcpyManager.lockOrientationIndex
|
||||||
|
onCurrentIndexChanged: {
|
||||||
|
ScrcpyManager.lockOrientationIndex = currentIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckBox {
|
||||||
|
id: autoUpdateDeviceCheck
|
||||||
|
text: "Auto Update Device"
|
||||||
|
checked: ScrcpyManager.autoUpdateDevice
|
||||||
|
onToggled: {
|
||||||
|
ScrcpyManager.autoUpdateDevice = checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: _controlsContainer
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
spacing: 10
|
||||||
|
visible: _scrcpyItem.visible
|
||||||
|
|
||||||
|
Button {
|
||||||
|
text: "Disconnect"
|
||||||
|
onClicked: {
|
||||||
|
ScrcpyManager.disconnectFromDevice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrcpyItem {
|
||||||
|
id: _scrcpyItem
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: _controlsContainer.top
|
||||||
|
}
|
||||||
|
visible: false
|
||||||
|
|
||||||
|
onFrameSizeChanged: {
|
||||||
|
if (visible) {
|
||||||
|
root.width = Math.max(frameSize.width, 800)
|
||||||
|
root.height = frameSize.height + _controlsContainer.height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: ScrcpyManager
|
||||||
|
|
||||||
|
onDeviceConnected: {
|
||||||
|
_scrcpyItem.visible = true
|
||||||
|
_scrcpyItem.focus = true
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeviceDisconnected: {
|
||||||
|
_scrcpyItem.visible = false
|
||||||
|
_scrcpyItem.focus = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
ScrcpyManager.listDevices()
|
||||||
|
}
|
||||||
|
}
|
13
QtScrcpy/TestQml/main.cpp
Normal file
13
QtScrcpy/TestQml/main.cpp
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app, []() { QCoreApplication::exit(-1); }, Qt::QueuedConnection);
|
||||||
|
engine.loadFromModule("TestQml", "Main");
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
524
QtScrcpy/qml/scrcpyitem.cpp
Normal file
524
QtScrcpy/qml/scrcpyitem.cpp
Normal file
|
@ -0,0 +1,524 @@
|
||||||
|
#include "scrcpyitem.h"
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <cstring>
|
||||||
|
#include <QtMath>
|
||||||
|
|
||||||
|
#include "util/config.h"
|
||||||
|
|
||||||
|
ScrcpyItem::ScrcpyItem(QQuickItem *parent)
|
||||||
|
: QQuickFramebufferObject(parent)
|
||||||
|
{
|
||||||
|
setMirrorVertically(false);
|
||||||
|
setTextureFollowsItemSize(true);
|
||||||
|
initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickFramebufferObject::Renderer* ScrcpyItem::createRenderer() const
|
||||||
|
{
|
||||||
|
return new ScrcpyItemRenderer(const_cast<ScrcpyItem*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::initUI()
|
||||||
|
{
|
||||||
|
setAcceptTouchEvents(true);
|
||||||
|
setAcceptedMouseButtons(Qt::AllButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::setFrameSize(const QSize &fs)
|
||||||
|
{
|
||||||
|
if (m_frameSize != fs) {
|
||||||
|
m_frameSize = fs;
|
||||||
|
emit frameSizeChanged();
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::updateTextures(quint8* dataY,
|
||||||
|
quint8* dataU,
|
||||||
|
quint8* dataV,
|
||||||
|
quint32 linesizeY,
|
||||||
|
quint32 linesizeU,
|
||||||
|
quint32 linesizeV)
|
||||||
|
{
|
||||||
|
m_dataY = dataY;
|
||||||
|
m_dataU = dataU;
|
||||||
|
m_dataV = dataV;
|
||||||
|
m_linesizeY = linesizeY;
|
||||||
|
m_linesizeU = linesizeU;
|
||||||
|
m_linesizeV = linesizeV;
|
||||||
|
|
||||||
|
m_newFrameAvailable = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::updateRender(int width, int height,
|
||||||
|
uint8_t *dataY, uint8_t *dataU, uint8_t *dataV,
|
||||||
|
int linesizeY, int linesizeU, int linesizeV)
|
||||||
|
{
|
||||||
|
setFrameSize(QSize(width, height));
|
||||||
|
updateTextures(dataY, dataU, dataV, linesizeY, linesizeU, linesizeV);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::setSerial(const QString &serial)
|
||||||
|
{
|
||||||
|
m_serial = serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScrcpyItem::serial() const
|
||||||
|
{
|
||||||
|
return m_serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::onFrame(int width, int height,
|
||||||
|
uint8_t* dataY, uint8_t* dataU, uint8_t* dataV,
|
||||||
|
int linesizeY, int linesizeU, int linesizeV)
|
||||||
|
{
|
||||||
|
updateRender(width, height, dataY, dataU, dataV, linesizeY, linesizeU, linesizeV);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::mousePressEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (event->button() == Qt::MiddleButton && device && !device->isCurrentCustomKeymap()) {
|
||||||
|
device->postGoHome();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->button() == Qt::RightButton && device && !device->isCurrentCustomKeymap()) {
|
||||||
|
device->postGoBack();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundingRect().contains(event->position())) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF mappedPos = event->position();
|
||||||
|
emit device->mouseEvent(new QMouseEvent(event->type(),
|
||||||
|
mappedPos,
|
||||||
|
event->globalPosition(),
|
||||||
|
event->button(),
|
||||||
|
event->buttons(),
|
||||||
|
event->modifiers()),
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
qreal x = mappedPos.x() / width();
|
||||||
|
qreal y = mappedPos.y() / height();
|
||||||
|
qInfo() << QString(R"("pos": {"x": %1, "y": %2})").arg(x).arg(y);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (event->button() == Qt::LeftButton) {
|
||||||
|
event->accept();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::mouseReleaseEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPointF local = event->position();
|
||||||
|
|
||||||
|
if (local.x() < 0) local.setX(0);
|
||||||
|
if (local.x() > width()) local.setX(width());
|
||||||
|
if (local.y() < 0) local.setY(0);
|
||||||
|
if (local.y() > height()) local.setY(height());
|
||||||
|
|
||||||
|
emit device->mouseEvent(new QMouseEvent(event->type(),
|
||||||
|
local,
|
||||||
|
event->globalPosition(),
|
||||||
|
event->button(),
|
||||||
|
event->buttons(),
|
||||||
|
event->modifiers()),
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::mouseMoveEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (boundingRect().contains(event->position())) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF mappedPos = event->position();
|
||||||
|
emit device->mouseEvent(new QMouseEvent(event->type(),
|
||||||
|
mappedPos,
|
||||||
|
event->globalPosition(),
|
||||||
|
event->button(),
|
||||||
|
event->buttons(),
|
||||||
|
event->modifiers()),
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::mouseDoubleClickEvent(QMouseEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (event->button() == Qt::RightButton && device && !device->isCurrentCustomKeymap()) {
|
||||||
|
emit device->postBackOrScreenOn(event->type() == QEvent::MouseButtonPress);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundingRect().contains(event->position())) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF mappedPos = event->position();
|
||||||
|
emit device->mouseEvent(new QMouseEvent(event->type(),
|
||||||
|
mappedPos,
|
||||||
|
event->globalPosition(),
|
||||||
|
event->button(),
|
||||||
|
event->buttons(),
|
||||||
|
event->modifiers()),
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::wheelEvent(QWheelEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (boundingRect().contains(event->position())) {
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QPointF pos = event->position();
|
||||||
|
|
||||||
|
QWheelEvent adjustedEvent(
|
||||||
|
pos,
|
||||||
|
event->globalPosition(),
|
||||||
|
event->pixelDelta(),
|
||||||
|
event->angleDelta(),
|
||||||
|
event->buttons(),
|
||||||
|
event->modifiers(),
|
||||||
|
event->phase(),
|
||||||
|
event->inverted()
|
||||||
|
);
|
||||||
|
emit device->wheelEvent(&adjustedEvent,
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::keyPressEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit device->keyEvent(event,
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::keyReleaseEvent(QKeyEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit device->keyEvent(event,
|
||||||
|
QSize(frameSize().width(), frameSize().height()),
|
||||||
|
size().toSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItem::dropEvent(QDropEvent *event)
|
||||||
|
{
|
||||||
|
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QMimeData *qm = event->mimeData();
|
||||||
|
QList<QUrl> urls = qm->urls();
|
||||||
|
|
||||||
|
for (const QUrl &url : urls) {
|
||||||
|
QString file = url.toLocalFile();
|
||||||
|
QFileInfo fileInfo(file);
|
||||||
|
if (!fileInfo.exists()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fileInfo.isFile() && fileInfo.suffix() == "apk") {
|
||||||
|
emit device->installApkRequest(file);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
emit device->pushFileRequest(file, Config::getInstance().getPushFilePath() + fileInfo.fileName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const GLfloat s_coords[20] = {
|
||||||
|
-1.0f, -1.0f, 0.0f,
|
||||||
|
1.0f, -1.0f, 0.0f,
|
||||||
|
-1.0f, 1.0f, 0.0f,
|
||||||
|
1.0f, 1.0f, 0.0f,
|
||||||
|
|
||||||
|
0.0f, 0.0f,
|
||||||
|
1.0f, 0.0f,
|
||||||
|
0.0f, 1.0f,
|
||||||
|
1.0f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
const GLfloat ScrcpyItemRenderer::s_coordinates[20] = {
|
||||||
|
s_coords[0], s_coords[1], s_coords[2], s_coords[3], s_coords[4],
|
||||||
|
s_coords[5], s_coords[6], s_coords[7], s_coords[8], s_coords[9],
|
||||||
|
s_coords[10], s_coords[11], s_coords[12], s_coords[13], s_coords[14],
|
||||||
|
s_coords[15], s_coords[16], s_coords[17], s_coords[18], s_coords[19]
|
||||||
|
};
|
||||||
|
|
||||||
|
const char* ScrcpyItemRenderer::s_vertShaderSrc = R"(
|
||||||
|
attribute vec3 vertexIn;
|
||||||
|
attribute vec2 textureIn;
|
||||||
|
varying vec2 textureOut;
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
gl_Position = vec4(vertexIn, 1.0);
|
||||||
|
textureOut = textureIn;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
const char* ScrcpyItemRenderer::s_fragShaderSrc = R"(
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
varying vec2 textureOut;
|
||||||
|
uniform sampler2D textureY;
|
||||||
|
uniform sampler2D textureU;
|
||||||
|
uniform sampler2D textureV;
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
vec3 yuv;
|
||||||
|
vec3 rgb;
|
||||||
|
|
||||||
|
const vec3 Rcoeff = vec3(1.1644, 0.0000, 1.7927);
|
||||||
|
const vec3 Gcoeff = vec3(1.1644, -0.2132, -0.5329);
|
||||||
|
const vec3 Bcoeff = vec3(1.1644, 2.1124, 0.0000);
|
||||||
|
|
||||||
|
yuv.x = texture2D(textureY, textureOut).r - 0.0625;
|
||||||
|
yuv.y = texture2D(textureU, textureOut).r - 0.5;
|
||||||
|
yuv.z = texture2D(textureV, textureOut).r - 0.5;
|
||||||
|
|
||||||
|
rgb.r = dot(yuv, Rcoeff);
|
||||||
|
rgb.g = dot(yuv, Gcoeff);
|
||||||
|
rgb.b = dot(yuv, Bcoeff);
|
||||||
|
|
||||||
|
gl_FragColor = vec4(rgb, 1.0);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
ScrcpyItemRenderer::ScrcpyItemRenderer(ScrcpyItem* item)
|
||||||
|
: m_item(item)
|
||||||
|
{
|
||||||
|
initializeOpenGLFunctions();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrcpyItemRenderer::~ScrcpyItemRenderer()
|
||||||
|
{
|
||||||
|
releaseGLResources();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::synchronize(QQuickFramebufferObject* qItem)
|
||||||
|
{
|
||||||
|
auto item = static_cast<ScrcpyItem*>(qItem);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (item->m_newFrameAvailable) {
|
||||||
|
m_dataY = item->m_dataY;
|
||||||
|
m_dataU = item->m_dataU;
|
||||||
|
m_dataV = item->m_dataV;
|
||||||
|
m_linesizeY = item->m_linesizeY;
|
||||||
|
m_linesizeU = item->m_linesizeU;
|
||||||
|
m_linesizeV = item->m_linesizeV;
|
||||||
|
m_needUpdateTextures = true;
|
||||||
|
item->m_newFrameAvailable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_localFrameSize != item->m_frameSize) {
|
||||||
|
m_localFrameSize = item->m_frameSize;
|
||||||
|
deInitTextures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::render()
|
||||||
|
{
|
||||||
|
if (!m_shaderInited) {
|
||||||
|
initShader();
|
||||||
|
m_shaderInited = true;
|
||||||
|
}
|
||||||
|
if (!m_vboInited) {
|
||||||
|
m_vbo.create();
|
||||||
|
m_vbo.bind();
|
||||||
|
m_vbo.allocate(s_coordinates, sizeof(s_coordinates));
|
||||||
|
m_vboInited = true;
|
||||||
|
}
|
||||||
|
if (!m_texturesInited && m_localFrameSize.isValid()) {
|
||||||
|
initTextures();
|
||||||
|
m_texturesInited = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glViewport(0, 0, size().width(), size().height());
|
||||||
|
glClearColor(0, 0, 0, 1);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
|
if (m_needUpdateTextures && m_texturesInited) {
|
||||||
|
if (m_dataY) updateTexture(m_textures[0], 0, m_dataY, m_linesizeY);
|
||||||
|
if (m_dataU) updateTexture(m_textures[1], 1, m_dataU, m_linesizeU);
|
||||||
|
if (m_dataV) updateTexture(m_textures[2], 2, m_dataV, m_linesizeV);
|
||||||
|
m_needUpdateTextures = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_program.bind();
|
||||||
|
m_vbo.bind();
|
||||||
|
|
||||||
|
m_program.enableAttributeArray("vertexIn");
|
||||||
|
m_program.setAttributeBuffer("vertexIn",
|
||||||
|
GL_FLOAT,
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
3 * sizeof(GLfloat));
|
||||||
|
m_program.enableAttributeArray("textureIn");
|
||||||
|
m_program.setAttributeBuffer("textureIn",
|
||||||
|
GL_FLOAT,
|
||||||
|
4 * 3 * sizeof(GLfloat),
|
||||||
|
2,
|
||||||
|
2 * sizeof(GLfloat));
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[0]);
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[1]);
|
||||||
|
glActiveTexture(GL_TEXTURE2);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[2]);
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||||
|
|
||||||
|
m_program.release();
|
||||||
|
m_vbo.release();
|
||||||
|
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QSize ScrcpyItemRenderer::size() const
|
||||||
|
{
|
||||||
|
return m_item->size().toSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::releaseGLResources()
|
||||||
|
{
|
||||||
|
if (m_vboInited) {
|
||||||
|
m_vbo.destroy();
|
||||||
|
m_vboInited = false;
|
||||||
|
}
|
||||||
|
if (m_texturesInited) {
|
||||||
|
deInitTextures();
|
||||||
|
}
|
||||||
|
if (m_shaderInited) {
|
||||||
|
m_program.removeAllShaders();
|
||||||
|
m_shaderInited = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::initShader()
|
||||||
|
{
|
||||||
|
m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, s_vertShaderSrc);
|
||||||
|
m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, s_fragShaderSrc);
|
||||||
|
m_program.link();
|
||||||
|
m_program.bind();
|
||||||
|
|
||||||
|
m_program.setUniformValue("textureY", 0);
|
||||||
|
m_program.setUniformValue("textureU", 1);
|
||||||
|
m_program.setUniformValue("textureV", 2);
|
||||||
|
|
||||||
|
m_program.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::initTextures()
|
||||||
|
{
|
||||||
|
glGenTextures(3, m_textures);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[0]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
m_localFrameSize.width(),
|
||||||
|
m_localFrameSize.height(),
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[1]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
m_localFrameSize.width() / 2,
|
||||||
|
m_localFrameSize.height() / 2,
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_textures[2]);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
m_localFrameSize.width() / 2,
|
||||||
|
m_localFrameSize.height() / 2,
|
||||||
|
0,
|
||||||
|
GL_LUMINANCE,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::deInitTextures()
|
||||||
|
{
|
||||||
|
glDeleteTextures(3, m_textures);
|
||||||
|
std::memset(m_textures, 0, sizeof(m_textures));
|
||||||
|
m_texturesInited = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyItemRenderer::updateTexture(GLuint texture, int textureType,
|
||||||
|
quint8* pixels, quint32 stride)
|
||||||
|
{
|
||||||
|
if (!pixels)
|
||||||
|
return;
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
|
||||||
|
QSize planeSize = (textureType == 0) ? m_localFrameSize : (m_localFrameSize / 2);
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
|
||||||
|
glTexSubImage2D(GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
0, 0,
|
||||||
|
planeSize.width(),
|
||||||
|
planeSize.height(),
|
||||||
|
GL_LUMINANCE,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
pixels);
|
||||||
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
||||||
|
}
|
133
QtScrcpy/qml/scrcpyitem.h
Normal file
133
QtScrcpy/qml/scrcpyitem.h
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
#ifndef SCRCPYITEM_H
|
||||||
|
#define SCRCPYITEM_H
|
||||||
|
|
||||||
|
#include <QQuickFramebufferObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QMargins>
|
||||||
|
#include <QSize>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QWheelEvent>
|
||||||
|
#include <QDragEnterEvent>
|
||||||
|
#include <QDragMoveEvent>
|
||||||
|
#include <QDragLeaveEvent>
|
||||||
|
#include <QDropEvent>
|
||||||
|
#include <QOpenGLFunctions>
|
||||||
|
#include <QOpenGLShaderProgram>
|
||||||
|
#include <QOpenGLBuffer>
|
||||||
|
#include "QtScrcpyCore/include/QtScrcpyCore.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ScrcpyItemRenderer;
|
||||||
|
|
||||||
|
class ScrcpyItem : public QQuickFramebufferObject, public qsc::DeviceObserver
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
|
||||||
|
Q_PROPERTY(QSize frameSize READ frameSize WRITE setFrameSize NOTIFY frameSizeChanged)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ScrcpyItem(QQuickItem *parent = nullptr);
|
||||||
|
|
||||||
|
Renderer* createRenderer() const override;
|
||||||
|
|
||||||
|
QSize frameSize() const { return m_frameSize; }
|
||||||
|
void setFrameSize(const QSize &fs);
|
||||||
|
|
||||||
|
Q_INVOKABLE void updateTextures(quint8* dataY,
|
||||||
|
quint8* dataU,
|
||||||
|
quint8* dataV,
|
||||||
|
quint32 linesizeY,
|
||||||
|
quint32 linesizeU,
|
||||||
|
quint32 linesizeV);
|
||||||
|
|
||||||
|
void updateRender(int width, int height,
|
||||||
|
uint8_t *dataY, uint8_t *dataU, uint8_t *dataV,
|
||||||
|
int linesizeY, int linesizeU, int linesizeV);
|
||||||
|
void setSerial(const QString &serial);
|
||||||
|
|
||||||
|
QString serial() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void frameSizeChanged();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void onFrame(int width, int height,
|
||||||
|
uint8_t* dataY, uint8_t* dataU, uint8_t* dataV,
|
||||||
|
int linesizeY, int linesizeU, int linesizeV) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *event) override;
|
||||||
|
void mouseDoubleClickEvent(QMouseEvent *event) override;
|
||||||
|
void wheelEvent(QWheelEvent *event) override;
|
||||||
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
void keyReleaseEvent(QKeyEvent *event) override;
|
||||||
|
|
||||||
|
void dropEvent(QDropEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void initUI();
|
||||||
|
|
||||||
|
QSize m_frameSize;
|
||||||
|
bool m_newFrameAvailable = false;
|
||||||
|
|
||||||
|
quint8* m_dataY = nullptr;
|
||||||
|
quint8* m_dataU = nullptr;
|
||||||
|
quint8* m_dataV = nullptr;
|
||||||
|
quint32 m_linesizeY = 0;
|
||||||
|
quint32 m_linesizeU = 0;
|
||||||
|
quint32 m_linesizeV = 0;
|
||||||
|
|
||||||
|
QString m_serial;
|
||||||
|
|
||||||
|
friend class ScrcpyItemRenderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ScrcpyItemRenderer : public QQuickFramebufferObject::Renderer,
|
||||||
|
protected QOpenGLFunctions
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScrcpyItemRenderer(ScrcpyItem* item);
|
||||||
|
~ScrcpyItemRenderer() override;
|
||||||
|
|
||||||
|
void render() override;
|
||||||
|
void synchronize(QQuickFramebufferObject* item) override;
|
||||||
|
QSize size() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void releaseGLResources();
|
||||||
|
void initShader();
|
||||||
|
void initTextures();
|
||||||
|
void deInitTextures();
|
||||||
|
void updateTexture(GLuint texture, int textureType, quint8* pixels, quint32 stride);
|
||||||
|
|
||||||
|
ScrcpyItem* m_item = nullptr;
|
||||||
|
|
||||||
|
bool m_texturesInited = false;
|
||||||
|
bool m_shaderInited = false;
|
||||||
|
bool m_vboInited = false;
|
||||||
|
|
||||||
|
GLuint m_textures[3] = {0};
|
||||||
|
QOpenGLShaderProgram m_program;
|
||||||
|
QOpenGLBuffer m_vbo;
|
||||||
|
|
||||||
|
quint8* m_dataY = nullptr;
|
||||||
|
quint8* m_dataU = nullptr;
|
||||||
|
quint8* m_dataV = nullptr;
|
||||||
|
quint32 m_linesizeY = 0;
|
||||||
|
quint32 m_linesizeU = 0;
|
||||||
|
quint32 m_linesizeV = 0;
|
||||||
|
bool m_needUpdateTextures = false;
|
||||||
|
|
||||||
|
QSize m_localFrameSize;
|
||||||
|
|
||||||
|
static const GLfloat s_coordinates[20];
|
||||||
|
static const char* s_vertShaderSrc;
|
||||||
|
static const char* s_fragShaderSrc;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
520
QtScrcpy/qml/scrcpymanager.cpp
Normal file
520
QtScrcpy/qml/scrcpymanager.cpp
Normal file
|
@ -0,0 +1,520 @@
|
||||||
|
#include "scrcpymanager.h"
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QRandomGenerator>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include "QtScrcpyCore/include/QtScrcpyCore.h"
|
||||||
|
#include "util/config.h"
|
||||||
|
|
||||||
|
ScrcpyManager::ScrcpyManager(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
{
|
||||||
|
connect(&m_adb, &qsc::AdbProcess::adbProcessResult,
|
||||||
|
this, &ScrcpyManager::handleAdbResult);
|
||||||
|
|
||||||
|
connect(&qsc::IDeviceManage::getInstance(), &qsc::IDeviceManage::deviceConnected,
|
||||||
|
this, &ScrcpyManager::onDeviceConnected);
|
||||||
|
connect(&qsc::IDeviceManage::getInstance(), &qsc::IDeviceManage::deviceDisconnected,
|
||||||
|
this, &ScrcpyManager::onDeviceDisconnected);
|
||||||
|
|
||||||
|
connect(&m_autoUpdateTimer, &QTimer::timeout, this, &ScrcpyManager::listDevices);
|
||||||
|
|
||||||
|
m_autoUpdateTimer.setInterval(1000);
|
||||||
|
m_autoUpdateTimer.setSingleShot(false);
|
||||||
|
|
||||||
|
updateBootConfig(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrcpyManager::~ScrcpyManager()
|
||||||
|
{
|
||||||
|
updateBootConfig(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::updateBootConfig(bool toView)
|
||||||
|
{
|
||||||
|
if (toView) {
|
||||||
|
UserBootConfig config = Config::getInstance().getUserBootConfig();
|
||||||
|
|
||||||
|
if (config.bitRate == 0) {
|
||||||
|
setBitRateUnits("Mbps");
|
||||||
|
} else if (config.bitRate % 1000000 == 0) {
|
||||||
|
setBitRateNumeric(config.bitRate / 1000000);
|
||||||
|
setBitRateUnits("Mbps");
|
||||||
|
} else {
|
||||||
|
setBitRateNumeric(config.bitRate / 1000);
|
||||||
|
setBitRateUnits("Kbps");
|
||||||
|
}
|
||||||
|
|
||||||
|
setMaxSizeIndex(config.maxSizeIndex);
|
||||||
|
setRecordFormatIndex(config.recordFormatIndex);
|
||||||
|
setRecordPath(config.recordPath);
|
||||||
|
setLockOrientationIndex(config.lockOrientationIndex);
|
||||||
|
setFramelessWindow(config.framelessWindow);
|
||||||
|
setRecordScreen(config.recordScreen);
|
||||||
|
setRecordBackground(config.recordBackground);
|
||||||
|
setReverseConnect(config.reverseConnect);
|
||||||
|
setShowFPS(config.showFPS);
|
||||||
|
setWindowOnTop(config.windowOnTop);
|
||||||
|
setAutoOffScreen(config.autoOffScreen);
|
||||||
|
setKeepAlive(config.keepAlive);
|
||||||
|
setSimpleMode(config.simpleMode);
|
||||||
|
setAutoUpdateDevice(config.autoUpdateDevice);
|
||||||
|
setShowToolbar(config.showToolbar);
|
||||||
|
} else {
|
||||||
|
UserBootConfig config;
|
||||||
|
|
||||||
|
config.bitRate = (bitRateUnits() == "Mbps")
|
||||||
|
? bitRateNumeric() * 1000000
|
||||||
|
: bitRateNumeric() * 1000;
|
||||||
|
|
||||||
|
config.maxSizeIndex = maxSizeIndex();
|
||||||
|
config.recordFormatIndex = recordFormatIndex();
|
||||||
|
config.recordPath = recordPath();
|
||||||
|
config.lockOrientationIndex = lockOrientationIndex();
|
||||||
|
config.framelessWindow = framelessWindow();
|
||||||
|
config.recordScreen = recordScreen();
|
||||||
|
config.recordBackground = recordBackground();
|
||||||
|
config.reverseConnect = reverseConnect();
|
||||||
|
config.showFPS = showFPS();
|
||||||
|
config.windowOnTop = windowOnTop();
|
||||||
|
config.autoOffScreen = autoOffScreen();
|
||||||
|
config.keepAlive = keepAlive();
|
||||||
|
config.simpleMode = simpleMode();
|
||||||
|
config.autoUpdateDevice = autoUpdateDevice();
|
||||||
|
config.showToolbar = showToolbar();
|
||||||
|
|
||||||
|
Config::getInstance().setUserBootConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ScrcpyManager::devicesList()
|
||||||
|
{
|
||||||
|
return m_deviceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrcpyItem* ScrcpyManager::currentDevice() const
|
||||||
|
{
|
||||||
|
return m_currentItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::currentlyConnecting() const
|
||||||
|
{
|
||||||
|
return m_currentlyConnecting;
|
||||||
|
}
|
||||||
|
|
||||||
|
quint32 ScrcpyManager::bitRateNumeric() const
|
||||||
|
{
|
||||||
|
return m_bitRateNumeric;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScrcpyManager::bitRateUnits() const
|
||||||
|
{
|
||||||
|
return m_bitRateUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScrcpyManager::maxSizeIndex() const
|
||||||
|
{
|
||||||
|
return m_maxSizeIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScrcpyManager::recordFormatIndex() const
|
||||||
|
{
|
||||||
|
return m_recordFormatIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScrcpyManager::recordPath() const
|
||||||
|
{
|
||||||
|
return m_recordPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ScrcpyManager::lockOrientationIndex() const
|
||||||
|
{
|
||||||
|
return m_lockOrientationIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::framelessWindow() const
|
||||||
|
{
|
||||||
|
return m_framelessWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::recordScreen() const
|
||||||
|
{
|
||||||
|
return m_recordScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::recordBackground() const
|
||||||
|
{
|
||||||
|
return m_recordBackground;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::reverseConnect() const
|
||||||
|
{
|
||||||
|
return m_reverseConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::showFPS() const
|
||||||
|
{
|
||||||
|
return m_showFPS;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::windowOnTop() const
|
||||||
|
{
|
||||||
|
return m_windowOnTop;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::autoOffScreen() const
|
||||||
|
{
|
||||||
|
return m_autoOffScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::keepAlive() const
|
||||||
|
{
|
||||||
|
return m_keepAlive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::simpleMode() const
|
||||||
|
{
|
||||||
|
return m_simpleMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::autoUpdateDevice() const
|
||||||
|
{
|
||||||
|
return m_autoUpdateDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::showToolbar() const
|
||||||
|
{
|
||||||
|
return m_showToolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::listDevices()
|
||||||
|
{
|
||||||
|
if (isAdbBusy()) {
|
||||||
|
qWarning() << "[ScrcpyManager] ADB is busy, cannot list devices yet.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_adb.execute(QString(), {"devices"});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::connectToDevice(ScrcpyItem* item, const QString &serial)
|
||||||
|
{
|
||||||
|
if (!item) {
|
||||||
|
qWarning() << "[ScrcpyManager] Invalid ScrcpyItem pointer!";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_currentItem) {
|
||||||
|
disconnectFromDevice(m_currentItem->serial());
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentlyConnecting(true);
|
||||||
|
|
||||||
|
qsc::DeviceParams params;
|
||||||
|
params.serial = serial;
|
||||||
|
|
||||||
|
QString videoSizeStr;
|
||||||
|
if (m_maxSizeIndex < m_maxSizeArray.size() - 1) {
|
||||||
|
videoSizeStr = m_maxSizeArray.value(m_maxSizeIndex, "640");
|
||||||
|
} else {
|
||||||
|
videoSizeStr = "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
quint16 videoSize = static_cast<quint16>(videoSizeStr.toUShort());
|
||||||
|
params.maxSize = videoSize;
|
||||||
|
|
||||||
|
if (bitRateUnits() == "Mbps") {
|
||||||
|
params.bitRate = bitRateNumeric() * 1000000;
|
||||||
|
} else {
|
||||||
|
params.bitRate = bitRateNumeric() * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.maxFps = static_cast<quint32>(Config::getInstance().getMaxFps());
|
||||||
|
params.closeScreen = m_autoOffScreen;
|
||||||
|
params.useReverse = m_reverseConnect;
|
||||||
|
params.display = !m_recordBackground;
|
||||||
|
params.renderExpiredFrames = Config::getInstance().getRenderExpiredFrames();
|
||||||
|
|
||||||
|
if (m_lockOrientationIndex > 0) {
|
||||||
|
params.captureOrientationLock = 1;
|
||||||
|
params.captureOrientation = (m_lockOrientationIndex - 1) * 90;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.stayAwake = m_keepAlive;
|
||||||
|
params.recordFile = m_recordScreen;
|
||||||
|
params.recordPath = m_recordPath;
|
||||||
|
|
||||||
|
params.serverLocalPath = getServerPath();
|
||||||
|
params.serverRemotePath = Config::getInstance().getServerPath();
|
||||||
|
params.pushFilePath = Config::getInstance().getPushFilePath();
|
||||||
|
params.serverVersion = Config::getInstance().getServerVersion();
|
||||||
|
params.logLevel = Config::getInstance().getLogLevel();
|
||||||
|
params.codecOptions = Config::getInstance().getCodecOptions();
|
||||||
|
params.codecName = Config::getInstance().getCodecName();
|
||||||
|
params.gameScript = QString();
|
||||||
|
params.scid = QRandomGenerator::global()->bounded(1, 10000) & 0x7FFFFFFF;
|
||||||
|
|
||||||
|
qsc::IDeviceManage::getInstance().connectDevice(params);
|
||||||
|
m_currentItem = item;
|
||||||
|
emit currentDeviceChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::disconnectFromDevice(const QString &serial)
|
||||||
|
{
|
||||||
|
bool disconnected = qsc::IDeviceManage::getInstance().disconnectDevice(serial);
|
||||||
|
if (disconnected) {
|
||||||
|
qDebug() << "[ScrcpyManager] Disconnect requested for" << serial;
|
||||||
|
} else {
|
||||||
|
qWarning() << "[ScrcpyManager] No device was disconnected (maybe not found?).";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentItem && m_currentItem->serial() == serial) {
|
||||||
|
m_currentItem.clear();
|
||||||
|
emit currentDeviceChanged();
|
||||||
|
}
|
||||||
|
setCurrentlyConnecting(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::disconnectFromDevice()
|
||||||
|
{
|
||||||
|
if (m_currentItem) {
|
||||||
|
disconnectFromDevice(m_currentItem->serial());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::onDeviceConnected(bool success, const QString &serial,
|
||||||
|
const QString &deviceName, const QSize &size)
|
||||||
|
{
|
||||||
|
Q_UNUSED(deviceName)
|
||||||
|
Q_UNUSED(size)
|
||||||
|
|
||||||
|
setCurrentlyConnecting(false);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
qWarning() << "[ScrcpyManager] Device" << serial << "failed to connect.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_currentItem) {
|
||||||
|
auto dev = qsc::IDeviceManage::getInstance().getDevice(serial);
|
||||||
|
if (dev) {
|
||||||
|
dev->setUserData(static_cast<void*>(m_currentItem.data()));
|
||||||
|
dev->registerDeviceObserver(m_currentItem.data());
|
||||||
|
m_currentItem->setSerial(serial);
|
||||||
|
qDebug() << "[ScrcpyManager] Device connected:" << serial;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emit deviceConnected(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::onDeviceDisconnected(const QString &serial)
|
||||||
|
{
|
||||||
|
qDebug() << "[ScrcpyManager] Device disconnected:" << serial;
|
||||||
|
if (m_currentItem && m_currentItem->serial() == serial) {
|
||||||
|
m_currentItem.clear();
|
||||||
|
emit currentDeviceChanged();
|
||||||
|
}
|
||||||
|
emit deviceDisconnected(serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrcpyManager::isAdbBusy() { return m_adb.isRuning(); }
|
||||||
|
|
||||||
|
void ScrcpyManager::handleAdbResult(qsc::AdbProcess::ADB_EXEC_RESULT processResult)
|
||||||
|
{
|
||||||
|
switch (processResult) {
|
||||||
|
case qsc::AdbProcess::AER_ERROR_START:
|
||||||
|
case qsc::AdbProcess::AER_ERROR_EXEC:
|
||||||
|
qWarning() << "[ScrcpyManager] ADB command error.";
|
||||||
|
setCurrentlyConnecting(false);
|
||||||
|
break;
|
||||||
|
case qsc::AdbProcess::AER_ERROR_MISSING_BINARY:
|
||||||
|
qWarning() << "[ScrcpyManager] ADB not found!";
|
||||||
|
setCurrentlyConnecting(false);
|
||||||
|
break;
|
||||||
|
case qsc::AdbProcess::AER_SUCCESS_START:
|
||||||
|
break;
|
||||||
|
case qsc::AdbProcess::AER_SUCCESS_EXEC:
|
||||||
|
if (m_adb.arguments().contains("devices")) {
|
||||||
|
m_deviceList = m_adb.getDevicesSerialFromStdOut();
|
||||||
|
emit devicesListChanged();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &ScrcpyManager::getServerPath()
|
||||||
|
{
|
||||||
|
static QString serverPath;
|
||||||
|
if (serverPath.isEmpty()) {
|
||||||
|
serverPath = QString::fromLocal8Bit(qgetenv("QTSCRCPY_SERVER_PATH"));
|
||||||
|
QFileInfo fileInfo(serverPath);
|
||||||
|
if (serverPath.isEmpty() || !fileInfo.isFile()) {
|
||||||
|
serverPath = QCoreApplication::applicationDirPath() + "/scrcpy-server";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return serverPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ScrcpyManager::availableOrientations() const
|
||||||
|
{
|
||||||
|
return m_availableOrientations;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ScrcpyManager::availableBitRatesUnits() const
|
||||||
|
{
|
||||||
|
return m_availableBitRatesUnits;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList ScrcpyManager::maxSizeArray() const
|
||||||
|
{
|
||||||
|
return m_maxSizeArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setCurrentlyConnecting(bool val)
|
||||||
|
{
|
||||||
|
if (m_currentlyConnecting == val)
|
||||||
|
return;
|
||||||
|
m_currentlyConnecting = val;
|
||||||
|
emit currentlyConnectingChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setBitRateNumeric(quint32 val)
|
||||||
|
{
|
||||||
|
if (m_bitRateNumeric == val)
|
||||||
|
return;
|
||||||
|
m_bitRateNumeric = val;
|
||||||
|
emit bitRateNumericChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setBitRateUnits(const QString &val)
|
||||||
|
{
|
||||||
|
if (m_bitRateUnits == val)
|
||||||
|
return;
|
||||||
|
m_bitRateUnits = val;
|
||||||
|
emit bitRateUnitsChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setMaxSizeIndex(int val)
|
||||||
|
{
|
||||||
|
if (m_maxSizeIndex == val)
|
||||||
|
return;
|
||||||
|
m_maxSizeIndex = val;
|
||||||
|
emit maxSizeIndexChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setRecordFormatIndex(int val)
|
||||||
|
{
|
||||||
|
if (m_recordFormatIndex == val)
|
||||||
|
return;
|
||||||
|
m_recordFormatIndex = val;
|
||||||
|
emit recordFormatIndexChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setRecordPath(const QString &val)
|
||||||
|
{
|
||||||
|
if (m_recordPath == val)
|
||||||
|
return;
|
||||||
|
m_recordPath = val;
|
||||||
|
emit recordPathChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setLockOrientationIndex(int val)
|
||||||
|
{
|
||||||
|
if (m_lockOrientationIndex == val)
|
||||||
|
return;
|
||||||
|
m_lockOrientationIndex = val;
|
||||||
|
emit lockOrientationIndexChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setFramelessWindow(bool val)
|
||||||
|
{
|
||||||
|
if (m_framelessWindow == val)
|
||||||
|
return;
|
||||||
|
m_framelessWindow = val;
|
||||||
|
emit framelessWindowChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setRecordScreen(bool val)
|
||||||
|
{
|
||||||
|
if (m_recordScreen == val)
|
||||||
|
return;
|
||||||
|
m_recordScreen = val;
|
||||||
|
emit recordScreenChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setRecordBackground(bool val)
|
||||||
|
{
|
||||||
|
if (m_recordBackground == val)
|
||||||
|
return;
|
||||||
|
m_recordBackground = val;
|
||||||
|
emit recordBackgroundChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setReverseConnect(bool val)
|
||||||
|
{
|
||||||
|
if (m_reverseConnect == val)
|
||||||
|
return;
|
||||||
|
m_reverseConnect = val;
|
||||||
|
emit reverseConnectChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setShowFPS(bool val)
|
||||||
|
{
|
||||||
|
if (m_showFPS == val)
|
||||||
|
return;
|
||||||
|
m_showFPS = val;
|
||||||
|
emit showFPSChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setWindowOnTop(bool val)
|
||||||
|
{
|
||||||
|
if (m_windowOnTop == val)
|
||||||
|
return;
|
||||||
|
m_windowOnTop = val;
|
||||||
|
emit windowOnTopChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setAutoOffScreen(bool val)
|
||||||
|
{
|
||||||
|
if (m_autoOffScreen == val)
|
||||||
|
return;
|
||||||
|
m_autoOffScreen = val;
|
||||||
|
emit autoOffScreenChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setKeepAlive(bool val)
|
||||||
|
{
|
||||||
|
if (m_keepAlive == val)
|
||||||
|
return;
|
||||||
|
m_keepAlive = val;
|
||||||
|
emit keepAliveChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setSimpleMode(bool val)
|
||||||
|
{
|
||||||
|
if (m_simpleMode == val)
|
||||||
|
return;
|
||||||
|
m_simpleMode = val;
|
||||||
|
emit simpleModeChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setAutoUpdateDevice(bool val)
|
||||||
|
{
|
||||||
|
if (m_autoUpdateDevice == val)
|
||||||
|
return;
|
||||||
|
m_autoUpdateDevice = val;
|
||||||
|
emit autoUpdateDeviceChanged();
|
||||||
|
|
||||||
|
if (m_autoUpdateDevice) {
|
||||||
|
m_autoUpdateTimer.start();
|
||||||
|
} else {
|
||||||
|
m_autoUpdateTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrcpyManager::setShowToolbar(bool val)
|
||||||
|
{
|
||||||
|
if (m_showToolbar == val)
|
||||||
|
return;
|
||||||
|
m_showToolbar = val;
|
||||||
|
emit showToolbarChanged();
|
||||||
|
}
|
178
QtScrcpy/qml/scrcpymanager.h
Normal file
178
QtScrcpy/qml/scrcpymanager.h
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QJSEngine>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <qtimer.h>
|
||||||
|
#include "adbprocess.h"
|
||||||
|
#include "scrcpyitem.h"
|
||||||
|
|
||||||
|
class ScrcpyManager : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
QML_SINGLETON
|
||||||
|
|
||||||
|
Q_PROPERTY(QStringList maxSizeArray READ maxSizeArray CONSTANT FINAL)
|
||||||
|
Q_PROPERTY(QStringList devicesList READ devicesList NOTIFY devicesListChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(ScrcpyItem* currentDevice READ currentDevice NOTIFY currentDeviceChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool currentlyConnecting READ currentlyConnecting WRITE setCurrentlyConnecting NOTIFY currentlyConnectingChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(quint32 bitRateNumeric READ bitRateNumeric WRITE setBitRateNumeric NOTIFY bitRateNumericChanged)
|
||||||
|
Q_PROPERTY(QString bitRateUnits READ bitRateUnits WRITE setBitRateUnits NOTIFY bitRateUnitsChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(int maxSizeIndex READ maxSizeIndex WRITE setMaxSizeIndex NOTIFY maxSizeIndexChanged)
|
||||||
|
Q_PROPERTY(int recordFormatIndex READ recordFormatIndex WRITE setRecordFormatIndex NOTIFY recordFormatIndexChanged)
|
||||||
|
Q_PROPERTY(QString recordPath READ recordPath WRITE setRecordPath NOTIFY recordPathChanged)
|
||||||
|
Q_PROPERTY(int lockOrientationIndex READ lockOrientationIndex WRITE setLockOrientationIndex NOTIFY lockOrientationIndexChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(bool framelessWindow READ framelessWindow WRITE setFramelessWindow NOTIFY framelessWindowChanged)
|
||||||
|
Q_PROPERTY(bool recordScreen READ recordScreen WRITE setRecordScreen NOTIFY recordScreenChanged)
|
||||||
|
Q_PROPERTY(bool recordBackground READ recordBackground WRITE setRecordBackground NOTIFY recordBackgroundChanged)
|
||||||
|
Q_PROPERTY(bool reverseConnect READ reverseConnect WRITE setReverseConnect NOTIFY reverseConnectChanged)
|
||||||
|
Q_PROPERTY(bool showFPS READ showFPS WRITE setShowFPS NOTIFY showFPSChanged)
|
||||||
|
Q_PROPERTY(bool windowOnTop READ windowOnTop WRITE setWindowOnTop NOTIFY windowOnTopChanged)
|
||||||
|
Q_PROPERTY(bool autoOffScreen READ autoOffScreen WRITE setAutoOffScreen NOTIFY autoOffScreenChanged)
|
||||||
|
Q_PROPERTY(bool keepAlive READ keepAlive WRITE setKeepAlive NOTIFY keepAliveChanged)
|
||||||
|
Q_PROPERTY(bool simpleMode READ simpleMode WRITE setSimpleMode NOTIFY simpleModeChanged)
|
||||||
|
Q_PROPERTY(bool autoUpdateDevice READ autoUpdateDevice WRITE setAutoUpdateDevice NOTIFY autoUpdateDeviceChanged)
|
||||||
|
Q_PROPERTY(bool showToolbar READ showToolbar WRITE setShowToolbar NOTIFY showToolbarChanged)
|
||||||
|
Q_PROPERTY(QStringList availableBitRatesUnits READ availableBitRatesUnits CONSTANT FINAL)
|
||||||
|
Q_PROPERTY(QStringList availableOrientations READ availableOrientations CONSTANT FINAL)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ScrcpyManager(QObject* parent = nullptr);
|
||||||
|
virtual ~ScrcpyManager();
|
||||||
|
|
||||||
|
QStringList devicesList();
|
||||||
|
ScrcpyItem* currentDevice() const;
|
||||||
|
|
||||||
|
bool currentlyConnecting() const;
|
||||||
|
quint32 bitRateNumeric() const;
|
||||||
|
QString bitRateUnits() const;
|
||||||
|
|
||||||
|
int maxSizeIndex() const;
|
||||||
|
int recordFormatIndex() const;
|
||||||
|
QString recordPath() const;
|
||||||
|
int lockOrientationIndex() const;
|
||||||
|
|
||||||
|
bool framelessWindow() const;
|
||||||
|
bool recordScreen() const;
|
||||||
|
bool recordBackground() const;
|
||||||
|
bool reverseConnect() const;
|
||||||
|
bool showFPS() const;
|
||||||
|
bool windowOnTop() const;
|
||||||
|
bool autoOffScreen() const;
|
||||||
|
bool keepAlive() const;
|
||||||
|
bool simpleMode() const;
|
||||||
|
bool autoUpdateDevice() const;
|
||||||
|
bool showToolbar() const;
|
||||||
|
|
||||||
|
QStringList maxSizeArray() const;
|
||||||
|
QStringList availableBitRatesUnits() const;
|
||||||
|
QStringList availableOrientations() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void listDevices();
|
||||||
|
void connectToDevice(ScrcpyItem* item, const QString &serial);
|
||||||
|
|
||||||
|
void disconnectFromDevice(const QString &serial);
|
||||||
|
void disconnectFromDevice();
|
||||||
|
|
||||||
|
void setCurrentlyConnecting(bool val);
|
||||||
|
void setBitRateNumeric(quint32 val);
|
||||||
|
void setBitRateUnits(const QString &val);
|
||||||
|
|
||||||
|
void setMaxSizeIndex(int val);
|
||||||
|
void setRecordFormatIndex(int val);
|
||||||
|
void setRecordPath(const QString &val);
|
||||||
|
void setLockOrientationIndex(int val);
|
||||||
|
|
||||||
|
void setFramelessWindow(bool val);
|
||||||
|
void setRecordScreen(bool val);
|
||||||
|
void setRecordBackground(bool val);
|
||||||
|
void setReverseConnect(bool val);
|
||||||
|
void setShowFPS(bool val);
|
||||||
|
void setWindowOnTop(bool val);
|
||||||
|
void setAutoOffScreen(bool val);
|
||||||
|
void setKeepAlive(bool val);
|
||||||
|
void setSimpleMode(bool val);
|
||||||
|
void setAutoUpdateDevice(bool val);
|
||||||
|
void setShowToolbar(bool val);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void devicesListChanged();
|
||||||
|
void currentDeviceChanged();
|
||||||
|
void currentlyConnectingChanged();
|
||||||
|
|
||||||
|
void bitRateNumericChanged();
|
||||||
|
void bitRateUnitsChanged();
|
||||||
|
|
||||||
|
void maxSizeIndexChanged();
|
||||||
|
void recordFormatIndexChanged();
|
||||||
|
void recordPathChanged();
|
||||||
|
void lockOrientationIndexChanged();
|
||||||
|
|
||||||
|
void framelessWindowChanged();
|
||||||
|
void recordScreenChanged();
|
||||||
|
void recordBackgroundChanged();
|
||||||
|
void reverseConnectChanged();
|
||||||
|
void showFPSChanged();
|
||||||
|
void windowOnTopChanged();
|
||||||
|
void autoOffScreenChanged();
|
||||||
|
void keepAliveChanged();
|
||||||
|
void simpleModeChanged();
|
||||||
|
void autoUpdateDeviceChanged();
|
||||||
|
void showToolbarChanged();
|
||||||
|
|
||||||
|
void deviceConnected(const QString &serial);
|
||||||
|
void deviceDisconnected(const QString &serial);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleAdbResult(qsc::AdbProcess::ADB_EXEC_RESULT processResult);
|
||||||
|
|
||||||
|
void onDeviceConnected(bool success, const QString &serial, const QString &deviceName, const QSize &size);
|
||||||
|
void onDeviceDisconnected(const QString &serial);
|
||||||
|
bool isAdbBusy();
|
||||||
|
|
||||||
|
const QString &getServerPath();
|
||||||
|
void updateBootConfig(bool toView);
|
||||||
|
|
||||||
|
private:
|
||||||
|
qsc::AdbProcess m_adb;
|
||||||
|
|
||||||
|
QPointer<ScrcpyItem> m_currentItem;
|
||||||
|
QList<QString> m_deviceList;
|
||||||
|
|
||||||
|
bool m_currentlyConnecting = false;
|
||||||
|
|
||||||
|
quint32 m_bitRateNumeric = 0;
|
||||||
|
QString m_bitRateUnits;
|
||||||
|
|
||||||
|
int m_maxSizeIndex = 0;
|
||||||
|
int m_recordFormatIndex = 0;
|
||||||
|
QString m_recordPath;
|
||||||
|
int m_lockOrientationIndex = 0;
|
||||||
|
|
||||||
|
bool m_framelessWindow = false;
|
||||||
|
bool m_recordScreen = false;
|
||||||
|
bool m_recordBackground = false;
|
||||||
|
bool m_reverseConnect = false;
|
||||||
|
bool m_showFPS = false;
|
||||||
|
bool m_windowOnTop = false;
|
||||||
|
bool m_autoOffScreen = false;
|
||||||
|
bool m_keepAlive = false;
|
||||||
|
bool m_simpleMode = false;
|
||||||
|
bool m_autoUpdateDevice = false;
|
||||||
|
bool m_showToolbar = false;
|
||||||
|
|
||||||
|
const QStringList m_maxSizeArray {"640", "720", "1080", "1280", "1920", "original"};
|
||||||
|
const QStringList m_availableBitRatesUnits = {"Mbps", "Kbps"};
|
||||||
|
const QStringList m_availableOrientations = {"no lock", "0", "90", "180", "270"};
|
||||||
|
|
||||||
|
QTimer m_autoUpdateTimer;
|
||||||
|
};
|
|
@ -23,6 +23,7 @@ void XMouseTap::initMouseEventTap() {}
|
||||||
void XMouseTap::quitMouseEventTap() {}
|
void XMouseTap::quitMouseEventTap() {}
|
||||||
|
|
||||||
void XMouseTap::enableMouseEventTap(QRect rc, bool enabled) {
|
void XMouseTap::enableMouseEventTap(QRect rc, bool enabled) {
|
||||||
|
#if QT_FEATURE_xcb==1
|
||||||
if (enabled && rc.isEmpty()) {
|
if (enabled && rc.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -61,4 +62,8 @@ void XMouseTap::enableMouseEventTap(QRect rc, bool enabled) {
|
||||||
XUngrabPointer(display, CurrentTime);
|
XUngrabPointer(display, CurrentTime);
|
||||||
}
|
}
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
|
#else
|
||||||
|
Q_UNUSED(rc);
|
||||||
|
Q_UNUSED(enabled);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue