feat: Add minimal QML support, add simple demo QML

app
This commit is contained in:
Mateusz Fibor 2025-01-06 20:32:03 +01:00
parent 4274042673
commit 9fa1933f46
10 changed files with 1668 additions and 5 deletions

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.19 FATAL_ERROR)
project(all)
add_subdirectory(QtScrcpy)
add_subdirectory(QtScrcpy)

View file

@ -18,6 +18,8 @@ set(QC_PROJECT_VERSION ${QC_FILE_VERSION})
project(${QC_PROJECT_NAME} VERSION ${QC_PROJECT_VERSION} LANGUAGES CXX)
message(STATUS "[${PROJECT_NAME}] Project ${PROJECT_NAME} ${PROJECT_VERSION}")
option(BUILD_QML_DEMO "Build demo QML app" ON)
# QC define
# check arch
@ -68,7 +70,12 @@ if (NOT MSVC)
add_compile_options(-Wall -Wextra -pedantic -Werror)
# 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()
#
@ -79,8 +86,8 @@ set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia OpenGL OpenGLWidgets REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 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 Quick REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(X11 REQUIRED)
endif()
@ -297,6 +304,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
target_link_libraries(${PROJECT_NAME} PRIVATE "-framework AppKit")
endif()
qt_add_library(QtScrcpyQml STATIC)
# Linux
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
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
# qx11
Qt${QT_VERSION_MAJOR}::CorePrivate
# xcb https://doc.qt.io/qt-5/linux-requirements.html
xcb
# pthread
@ -339,3 +347,26 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
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()

View 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
View 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
View 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
View 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
View 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

View 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();
}

View 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;
};

View file

@ -23,6 +23,7 @@ void XMouseTap::initMouseEventTap() {}
void XMouseTap::quitMouseEventTap() {}
void XMouseTap::enableMouseEventTap(QRect rc, bool enabled) {
#if QT_FEATURE_xcb==1
if (enabled && rc.isEmpty()) {
return;
}
@ -61,4 +62,8 @@ void XMouseTap::enableMouseEventTap(QRect rc, bool enabled) {
XUngrabPointer(display, CurrentTime);
}
XFlush(display);
#else
Q_UNUSED(rc);
Q_UNUSED(enabled);
#endif
}