Compare commits

...

16 commits
v3.1.0 ... dev

Author SHA1 Message Date
Leonardo Chen
02ffb264c9 fix: windows build error
Some checks failed
Windows / Build (push) Has been cancelled
MacOS / Build (push) Has been cancelled
Ubuntu / Build (push) Has been cancelled
2025-04-10 08:42:47 +08:00
barry
314bd4f613 ui: update i18
Some checks failed
MacOS / Build (push) Has been cancelled
Ubuntu / Build (push) Has been cancelled
Windows / Build (push) Has been cancelled
2025-03-09 15:51:50 +08:00
barry
6f0f9447da feat: add for QuickAssistant 2025-03-09 15:46:12 +08:00
barry
cfe79c7d5a feat: config support set language 2025-03-09 15:40:06 +08:00
barry
dbf25166ea feat: add for QuickAssistant 2025-03-09 15:27:31 +08:00
Liu Jinchang
f5cccac0df feat: add IP history record feature
- Implement IP address history storage
- Add mechanism to record connection timestamps
- Ensure unique IP entries in history

Log: Add functionality to store and track IP address history

Issue: https://github.com/barry-ran/QtScrcpy/issues/1075
2025-03-06 14:18:13 +08:00
rankun
224f04ffa0 feat: add star badge 2025-03-04 17:21:22 +08:00
barry
a8d3609c19 feat: ad for QuickMirror
Some checks failed
MacOS / Build (push) Has been cancelled
Ubuntu / Build (push) Has been cancelled
Windows / Build (push) Has been cancelled
2025-02-21 22:25:18 +08:00
rankun
790f422f99 fix: not found adb on windows
Some checks are pending
MacOS / Build (push) Waiting to run
Ubuntu / Build (push) Waiting to run
Windows / Build (push) Waiting to run
2025-02-21 13:28:25 +08:00
re2zero
c1faff820d fix: #1117 Fix phone window show blank if run with Qt6
It needs to bind every time when GL paint on Qt6, and it works on Qt5 too.

Log: Fix phone window show blank if run with Qt6.
2025-02-21 11:11:11 +08:00
barry
5fa18219b6 fix: qt6 build error 2025-02-21 11:11:11 +08:00
barry
8196e46648 feat: update QtScrcpyCore 2025-02-21 11:11:11 +08:00
rankun
f863a91f94 feat: mac support arm64 2025-02-21 11:11:11 +08:00
rankun
ae1523b2a0 chore: add build annotation 2025-02-21 11:11:11 +08:00
re2zero
96fc6bfdf7 feat: Enable Qt6 build
Enable build with Qt6 and compat Qt5, the user can select which Qt version by configuration parameter.

Log: Enable Qt6 build.
2025-02-21 11:11:11 +08:00
barry
c1cb2dad3b fix: package x64 failed on mac arm 2025-02-21 11:11:11 +08:00
28 changed files with 553 additions and 110 deletions

View file

@ -13,12 +13,23 @@ on:
jobs:
build:
name: Build
runs-on: macos-14
# install-qt-action在arm上执行macdeployqt会报parse otool错误所以在intel mac上执行:
# 用qt6时在arm mac上编译arm和intel都没有问题
# qt5+intel mac编译intel没问题
# qt5+arm mac编译intel会报错
# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: macos-13
strategy:
matrix:
qt-ver: [5.15.2]
qt-arch-install: [clang_64]
clang-arch: [x64]
qt-ver: [5.15.2, 6.5.3]
# 配置qt-ver的额外设置qt-arch-installbuild-arch
include:
- qt-ver: 5.15.2
qt-arch-install: clang_64
build-arch: x64
- qt-ver: 6.5.3
qt-arch-install: arm64
build-arch: arm64
env:
target-name: QtScrcpy
qt-install-path: ${{ github.workspace }}/${{ matrix.qt-ver }}
@ -30,11 +41,19 @@ jobs:
with:
path: ${{ env.qt-install-path }}/${{ matrix.qt-arch-install }}
key: ${{ runner.os }}/${{ matrix.qt-ver }}/${{ matrix.qt-arch-install }}
- name: Install Qt
- name: Install Qt5
if: startsWith(matrix.qt-ver, '5.')
uses: jurplel/install-qt-action@v4.1.1
with:
version: ${{ matrix.qt-ver }}
cached: ${{ steps.cache-qt.outputs.cache-hit }}
- name: Install Qt6
if: startsWith(matrix.qt-ver, '6.')
uses: jurplel/install-qt-action@v4.1.1
with:
version: ${{ matrix.qt-ver }}
modules: qtmultimedia
cached: ${{ steps.cache-qt.outputs.cache-hit }}
- uses: actions/checkout@v2
with:
fetch-depth: 0
@ -46,7 +65,7 @@ jobs:
ENV_QT_PATH: ${{ env.qt-install-path }}
run: |
python ci/generate-version.py
ci/mac/build_for_mac.sh RelWithDebInfo
ci/mac/build_for_mac.sh RelWithDebInfo ${{ matrix.build-arch }}
# 获取ref最后一个/后的内容
- name: Get the version
shell: bash
@ -58,9 +77,9 @@ jobs:
id: package
env:
ENV_QT_PATH: ${{ env.qt-install-path }}
publish_name: ${{ env.target-name }}-${{ env.plantform-des }}-${{ matrix.clang-arch }}-${{ steps.get-version.outputs.version }}
publish_name: ${{ env.target-name }}-${{ env.plantform-des }}-${{ matrix.build-arch }}-Qt${{matrix.qt-ver}}-${{ steps.get-version.outputs.version }}
run: |
ci/mac/publish_for_mac.sh ../build
ci/mac/publish_for_mac.sh ../build ${{ matrix.build-arch }}
ci/mac/package_for_mac.sh
mv ci/build/QtScrcpy.app ci/build/${{ env.publish_name }}.app
mv ci/build/QtScrcpy.dmg ci/build/${{ env.publish_name }}.dmg

View file

@ -26,6 +26,19 @@ if(CMAKE_SIZEOF_VOID_P EQUAL 8)
else()
set(QC_CPU_ARCH x86)
endif()
# MacOS
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# mac default arch arm64
if(NOT CMAKE_OSX_ARCHITECTURES)
set(CMAKE_OSX_ARCHITECTURES arm64)
endif()
if (CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
set(QC_CPU_ARCH arm64)
endif()
endif()
message(STATUS "[${PROJECT_NAME}] CPU_ARCH:${QC_CPU_ARCH}")
# CMake set
@ -52,8 +65,9 @@ if (MSVC)
add_compile_options(/W3 /WX /wd4566)
# avoid warning C4819
add_compile_options(-source-charset:utf-8)
#add_compile_options(/utf-8)
#add_compile_options(-source-charset:utf-8)
# /utf-8 will set source charset and execution charset to utf-8, so we don't need to set source-charset:utf-8
add_compile_options(/utf-8)
# ensure we use minimal "windows.h" lib without the crazy min max macros
add_compile_definitions(NOMINMAX WIN32_LEAN_AND_MEAN)
@ -75,18 +89,49 @@ endif()
# Qt
#
# Find Qt version
if (NOT QT_DESIRED_VERSION)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Core)
message(" >>> Found Qt version: ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH}")
set(QT_DESIRED_VERSION ${QT_VERSION_MAJOR})
endif()
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network Multimedia REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network Multimedia REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(QT NAMES Qt6 Qt5 COMPONENTS X11Extras REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
set(qt_required_components Widgets Network Multimedia)
if (QT_DESIRED_VERSION MATCHES 6)
# list(APPEND qt_required_components Core5Compat)
list(APPEND qt_required_components OpenGL)
list(APPEND qt_required_components OpenGLWidgets)
else()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND qt_required_components X11Extras )
endif()
endif()
message(STATUS "[${PROJECT_NAME}] Qt version is: ${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}")
find_package(Qt${QT_DESIRED_VERSION} REQUIRED COMPONENTS ${qt_required_components})
set(LINK_LIBS
Qt${QT_DESIRED_VERSION}::Widgets
Qt${QT_DESIRED_VERSION}::Network
Qt${QT_DESIRED_VERSION}::Multimedia
)
if (QT_DESIRED_VERSION MATCHES 6)
# list(APPEND LINK_LIBS Qt${QT_DESIRED_VERSION}::Core5Compat)
list(APPEND LINK_LIBS Qt${QT_DESIRED_VERSION}::GuiPrivate)
list(APPEND LINK_LIBS Qt${QT_DESIRED_VERSION}::OpenGL)
list(APPEND LINK_LIBS Qt${QT_DESIRED_VERSION}::OpenGLWidgets)
else()
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND LINK_LIBS Qt${QT_DESIRED_VERSION}::X11Extras)
endif()
endif()
message(STATUS "[${PROJECT_NAME}] Qt version is: ${QT_DESIRED_VERSION}")
#
# Sources
@ -263,6 +308,9 @@ endif()
# MacOS
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# qt6 need 10.15 or later
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15")
# copy bundle file
get_target_property(MACOS_BUNDLE_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY)
set(MACOS_BUNDLE_PATH ${MACOS_BUNDLE_PATH}/${PROJECT_NAME}.app/Contents)
@ -312,8 +360,6 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE
# qx11
Qt${QT_VERSION_MAJOR}::X11Extras
# xcb https://doc.qt.io/qt-5/linux-requirements.html
xcb
# pthread
@ -331,8 +377,6 @@ add_subdirectory(QtScrcpyCore)
# Qt
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt${QT_VERSION_MAJOR}::Widgets
Qt${QT_VERSION_MAJOR}::Network
Qt${QT_VERSION_MAJOR}::Multimedia
${LINK_LIBS}
QtScrcpyCore
)

@ -1 +1 @@
Subproject commit 6beab5c967f7f77ddcfabe79757dc01202653ab7
Subproject commit 19e1ba8fb5c59c5a85c3c6a79967fab4c84739c7

View file

@ -4,11 +4,23 @@
#include <QTime>
#include <QElapsedTimer>
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
#include <QAudioSink>
#include <QAudioDevice>
#include <QMediaDevices>
#endif
#include "audiooutput.h"
AudioOutput::AudioOutput(QObject *parent)
: QObject(parent)
{
m_running = false;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
m_audioOutput = nullptr;
#else
m_audioSink = nullptr;
#endif
connect(&m_sndcpy, &QProcess::readyReadStandardOutput, this, [this]() {
qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardOutput());
});
@ -69,15 +81,10 @@ bool AudioOutput::runSndcpyProcess(const QString &serial, int port, bool wait)
}
#ifdef Q_OS_WIN32
QStringList params;
params << serial;
params << QString("%1").arg(port);
QStringList params{serial, QString::number(port)};
m_sndcpy.start("sndcpy.bat", params);
#else
QStringList params;
params << "sndcpy.sh";
params << serial;
params << QString("%1").arg(port);
QStringList params{"sndcpy.sh", serial, QString::number(port)};
m_sndcpy.start("bash", params);
#endif
@ -86,11 +93,11 @@ bool AudioOutput::runSndcpyProcess(const QString &serial, int port, bool wait)
}
if (!m_sndcpy.waitForStarted()) {
qWarning() << "AudioOutput::start sndcpy.bat failed";
qWarning() << "AudioOutput::start sndcpy process failed";
return false;
}
if (!m_sndcpy.waitForFinished()) {
qWarning() << "AudioOutput::sndcpy.bat crashed";
qWarning() << "AudioOutput::sndcpy process crashed";
return false;
}
@ -99,6 +106,7 @@ bool AudioOutput::runSndcpyProcess(const QString &serial, int port, bool wait)
void AudioOutput::startAudioOutput()
{
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
if (m_audioOutput) {
return;
}
@ -110,8 +118,8 @@ void AudioOutput::startAudioOutput()
format.setCodec("audio/pcm");
format.setByteOrder(QAudioFormat::LittleEndian);
format.setSampleType(QAudioFormat::SignedInt);
QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());
if (!info.isFormatSupported(format)) {
qWarning() << "AudioOutput::audio format not supported, cannot play audio.";
return;
@ -123,17 +131,47 @@ void AudioOutput::startAudioOutput()
});
m_audioOutput->setBufferSize(48000*2*15/1000 * 20);
m_outputDevice = m_audioOutput->start();
#else
if (m_audioSink) {
return;
}
QAudioFormat format;
format.setSampleRate(48000);
format.setChannelCount(2);
format.setSampleFormat(QAudioFormat::Int16);
QAudioDevice defaultDevice = QMediaDevices::defaultAudioOutput();
if (!defaultDevice.isFormatSupported(format)) {
qWarning() << "AudioOutput::audio format not supported, cannot play audio.";
return;
}
m_audioSink = new QAudioSink(defaultDevice, format, this);
m_outputDevice = m_audioSink->start();
if (!m_outputDevice) {
qWarning() << "AudioOutput::audio output device not available, cannot play audio.";
delete m_audioSink;
m_audioSink = nullptr;
return;
}
#endif
}
void AudioOutput::stopAudioOutput()
{
if (!m_audioOutput) {
return;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
if (m_audioOutput) {
m_audioOutput->stop();
delete m_audioOutput;
m_audioOutput = nullptr;
}
m_audioOutput->stop();
delete m_audioOutput;
m_audioOutput = nullptr;
#else
if (m_audioSink) {
m_audioSink->stop();
delete m_audioSink;
m_audioSink = nullptr;
}
#endif
m_outputDevice = nullptr;
}
void AudioOutput::startRecvData(int port)
@ -165,7 +203,7 @@ void AudioOutput::startRecvData(int port)
m_buffer.reserve(recv);
}
qint64 count = audioSocket->read(m_buffer.data(), audioSocket->bytesAvailable());
qint64 count = audioSocket->read(m_buffer.data(), recv);
m_outputDevice->write(m_buffer.data(), count);
});
connect(audioSocket, &QTcpSocket::stateChanged, audioSocket, [](QAbstractSocket::SocketState state) {

View file

@ -6,6 +6,7 @@
#include <QPointer>
#include <QVector>
class QAudioSink;
class QAudioOutput;
class QIODevice;
class AudioOutput : public QObject
@ -30,12 +31,16 @@ signals:
void connectTo(int port);
private:
QAudioOutput* m_audioOutput = nullptr;
QPointer<QIODevice> m_outputDevice;
QThread m_workerThread;
QProcess m_sndcpy;
QVector<char> m_buffer;
bool m_running = false;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QAudioOutput* m_audioOutput = nullptr;
#else
QAudioSink *m_audioSink = nullptr;
#endif
};
#endif // AUDIOOUTPUT_H

View file

@ -55,10 +55,12 @@ int main(int argc, char *argv[])
QApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
#endif
QSurfaceFormat varFormat = QSurfaceFormat::defaultFormat();
@ -113,8 +115,13 @@ int main(int argc, char *argv[])
"following address:");
qInfo() << QString("QtScrcpy %1 <https://github.com/barry-ran/QtScrcpy>").arg(QCoreApplication::applicationVersion());
qInfo() << QObject::tr("If you need more professional screen projection control software, you can try the following software:");
qInfo() << QString(QObject::tr("QuickMirror") + " <https://lrbnfell4p.feishu.cn/docx/QRMhd9nImorAGgxVLlmczxSdnYf>");
qInfo() << QObject::tr("If you need more professional batch control mirror software, you can try the following software:");
qInfo() << QString(QObject::tr("QuickMirror") + " <https://lrbnfell4p.feishu.cn/drive/folder/KviYfz5uFlpUT8dXgdjccmfUnse>");
qInfo() << QObject::tr("If you need more professional game keymap mirror software, you can try the following software:");
qInfo() << QString(QObject::tr("QuickAssistant") + " <https://lrbnfell4p.feishu.cn/drive/folder/Hqckfxj5el1Wjpd9uezcX71lnBh>");
qInfo() << QObject::tr("You can contact me with telegram <https://t.me/+Ylf_5V_rDCMyODQ1>");
int ret = a.exec();
delete g_mainDlg;
@ -130,7 +137,13 @@ void installTranslator()
static QTranslator translator;
QLocale locale;
QLocale::Language language = locale.language();
//language = QLocale::English;
if (Config::getInstance().getLanguage() == "zh_CN") {
language = QLocale::Chinese;
} else if (Config::getInstance().getLanguage() == "en_US") {
language = QLocale::English;
}
QString languagePath = ":/i18n/";
switch (language) {
case QLocale::Chinese:
@ -142,7 +155,10 @@ void installTranslator()
break;
}
translator.load(languagePath);
auto loaded = translator.load(languagePath);
if (!loaded) {
qWarning() << "Failed to load translation file:" << languagePath;
}
qApp->installTranslator(&translator);
}

View file

@ -157,6 +157,8 @@ void QYUVOpenGLWidget::initializeGL()
void QYUVOpenGLWidget::paintGL()
{
m_shaderProgram.bind();
if (m_needUpdate) {
deInitTextures();
initTextures();
@ -175,6 +177,8 @@ void QYUVOpenGLWidget::paintGL()
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
m_shaderProgram.release();
}
void QYUVOpenGLWidget::resizeGL(int width, int height)

Binary file not shown.

View file

@ -31,6 +31,10 @@
<source>select path</source>
<translation>select path</translation>
</message>
<message>
<source>Clear History</source>
<translation>Clear History</translation>
</message>
</context>
<context>
<name>QObject</name>
@ -38,14 +42,26 @@
<source>This software is completely open source and free. Use it at your own risk. You can download it at the following address:</source>
<translation>This software is completely open source and free. Use it at your own risk. You can download it at the following address:</translation>
</message>
<message>
<source>If you need more professional screen projection control software, you can try the following software:</source>
<translation>If you need more professional screen projection control software, you can try the following software:</translation>
</message>
<message>
<source>QuickMirror</source>
<translation>QuickMirror</translation>
</message>
<message>
<source>If you need more professional batch control mirror software, you can try the following software:</source>
<translation>If you need more professional batch control mirror software, you can try the following software:</translation>
</message>
<message>
<source>If you need more professional game keymap mirror software, you can try the following software:</source>
<translation>If you need more professional game keymap mirror software, you can try the following software:</translation>
</message>
<message>
<source>QuickAssistant</source>
<translation>QuickAssistant</translation>
</message>
<message>
<source>You can contact me with telegram &lt;https://t.me/+Ylf_5V_rDCMyODQ1&gt;</source>
<translation>You can contact me with telegram &lt;https://t.me/+Ylf_5V_rDCMyODQ1&gt;</translation>
</message>
</context>
<context>
<name>ToolForm</name>

Binary file not shown.

View file

@ -31,6 +31,10 @@
<source>select path</source>
<translation></translation>
</message>
<message>
<source>Clear History</source>
<translation></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -38,14 +42,26 @@
<source>This software is completely open source and free. Use it at your own risk. You can download it at the following address:</source>
<translation>使</translation>
</message>
<message>
<source>If you need more professional screen projection control software, you can try the following software:</source>
<translation></translation>
</message>
<message>
<source>QuickMirror</source>
<translation></translation>
</message>
<message>
<source>If you need more professional batch control mirror software, you can try the following software:</source>
<translation></translation>
</message>
<message>
<source>If you need more professional game keymap mirror software, you can try the following software:</source>
<translation></translation>
</message>
<message>
<source>QuickAssistant</source>
<translation></translation>
</message>
<message>
<source>You can contact me with telegram &lt;https://t.me/+Ylf_5V_rDCMyODQ1&gt;</source>
<translation>QQ群联系我 &lt;901736468&gt;</translation>
</message>
</context>
<context>
<name>ToolForm</name>

View file

@ -82,21 +82,21 @@ Dialog::Dialog(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
log = "ip not find, connect to wifi?";
break;
}
ui->deviceIpEdt->setText(ip);
ui->deviceIpEdt->setEditText(ip);
} else if (args.contains("ifconfig") && args.contains("wlan0")) {
QString ip = m_adb.getDeviceIPFromStdOut();
if (ip.isEmpty()) {
log = "ip not find, connect to wifi?";
break;
}
ui->deviceIpEdt->setText(ip);
ui->deviceIpEdt->setEditText(ip);
} else if (args.contains("ip -o a")) {
QString ip = m_adb.getDeviceIPByIpFromStdOut();
if (ip.isEmpty()) {
log = "ip not find, connect to wifi?";
break;
}
ui->deviceIpEdt->setText(ip);
ui->deviceIpEdt->setEditText(ip);
}
break;
}
@ -164,6 +164,16 @@ void Dialog::initUI()
ui->lockOrientationBox->addItem("180");
ui->lockOrientationBox->addItem("270");
ui->lockOrientationBox->setCurrentIndex(0);
// 加载IP历史记录
loadIpHistory();
// 为deviceIpEdt添加右键菜单
if (ui->deviceIpEdt->lineEdit()) {
ui->deviceIpEdt->lineEdit()->setContextMenuPolicy(Qt::CustomContextMenu);
connect(ui->deviceIpEdt->lineEdit(), &QWidget::customContextMenuRequested,
this, &Dialog::showIpEditMenu);
}
}
void Dialog::updateBootConfig(bool toView)
@ -216,6 +226,12 @@ void Dialog::updateBootConfig(bool toView)
config.autoUpdateDevice = ui->autoUpdatecheckBox->isChecked();
config.showToolbar = ui->showToolbar->isChecked();
// 保存当前IP到历史记录
QString currentIp = ui->deviceIpEdt->currentText().trimmed();
if (!currentIp.isEmpty()) {
saveIpHistory(currentIp);
}
Config::getInstance().setUserBootConfig(config);
}
}
@ -344,7 +360,7 @@ void Dialog::on_wirelessConnectBtn_clicked()
if (checkAdbRun()) {
return;
}
QString addr = ui->deviceIpEdt->text().trimmed();
QString addr = ui->deviceIpEdt->currentText().trimmed();
if (!ui->devicePortEdt->text().isEmpty()) {
addr += ":";
addr += ui->devicePortEdt->text().trimmed();
@ -356,6 +372,12 @@ void Dialog::on_wirelessConnectBtn_clicked()
return;
}
// 保存IP历史记录 - 只保存IP部分,不包含端口
QString ip = addr.split(":").first();
if (!ip.isEmpty()) {
saveIpHistory(ip);
}
outLog("wireless connect...", false);
QStringList adbArgs;
adbArgs << "connect";
@ -516,7 +538,7 @@ void Dialog::on_wirelessDisConnectBtn_clicked()
if (checkAdbRun()) {
return;
}
QString addr = ui->deviceIpEdt->text().trimmed();
QString addr = ui->deviceIpEdt->currentText().trimmed();
outLog("wireless disconnect...", false);
QStringList adbArgs;
adbArgs << "disconnect";
@ -618,9 +640,18 @@ void Dialog::on_usbConnectBtn_clicked()
int Dialog::findDeviceFromeSerialBox(bool wifi)
{
QRegExp regIP("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\:([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])\\b");
QString regStr = "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\:([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])\\b";
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QRegExp regIP(regStr);
#else
QRegularExpression regIP(regStr);
#endif
for (int i = 0; i < ui->serialBox->count(); ++i) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
bool isWifi = regIP.exactMatch(ui->serialBox->itemText(i));
#else
bool isWifi = regIP.match(ui->serialBox->itemText(i)).hasMatch();
#endif
bool found = wifi ? isWifi : !isWifi;
if (found) {
return i;
@ -758,3 +789,45 @@ void Dialog::on_autoUpdatecheckBox_toggled(bool checked)
m_autoUpdatetimer.stop();
}
}
void Dialog::loadIpHistory()
{
QStringList ipList = Config::getInstance().getIpHistory();
ui->deviceIpEdt->clear();
ui->deviceIpEdt->addItems(ipList);
ui->deviceIpEdt->setContentsMargins(0, 0, 0, 0);
if (ui->deviceIpEdt->lineEdit()) {
ui->deviceIpEdt->lineEdit()->setMaxLength(128);
ui->deviceIpEdt->lineEdit()->setPlaceholderText("192.168.0.1");
}
}
void Dialog::saveIpHistory(const QString &ip)
{
if (ip.isEmpty()) {
return;
}
Config::getInstance().saveIpHistory(ip);
// 更新ComboBox
loadIpHistory();
ui->deviceIpEdt->setCurrentText(ip);
}
void Dialog::showIpEditMenu(const QPoint &pos)
{
QMenu *menu = ui->deviceIpEdt->lineEdit()->createStandardContextMenu();
menu->addSeparator();
QAction *clearHistoryAction = new QAction(tr("Clear History"), menu);
connect(clearHistoryAction, &QAction::triggered, this, [this]() {
Config::getInstance().clearIpHistory();
loadIpHistory();
});
menu->addAction(clearHistoryAction);
menu->exec(ui->deviceIpEdt->lineEdit()->mapToGlobal(pos));
delete menu;
}

View file

@ -67,6 +67,8 @@ private slots:
void on_autoUpdatecheckBox_toggled(bool checked);
void showIpEditMenu(const QPoint &pos);
private:
bool checkAdbRun();
void initUI();
@ -78,6 +80,8 @@ private:
int findDeviceFromeSerialBox(bool wifi);
quint32 getBitRate();
const QString &getServerPath();
void loadIpHistory();
void saveIpHistory(const QString &ip);
protected:
void closeEvent(QCloseEvent *event);

View file

@ -1059,22 +1059,25 @@
<number>5</number>
</property>
<item>
<widget class="QLineEdit" name="deviceIpEdt">
<widget class="QComboBox" name="deviceIpEdt">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
<property name="currentText">
<string/>
</property>
<property name="maxLength">
<number>128</number>
</property>
<property name="placeholderText">
<string notr="true">192.168.0.1</string>
</property>
</widget>
</item>
<item>

View file

@ -68,7 +68,11 @@ void ToolForm::updateGroupControl()
void ToolForm::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
#else
m_dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
#endif
event->accept();
}
}
@ -81,7 +85,11 @@ void ToolForm::mouseReleaseEvent(QMouseEvent *event)
void ToolForm::mouseMoveEvent(QMouseEvent *event)
{
if (event->buttons() & Qt::LeftButton) {
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
move(event->globalPos() - m_dragPosition);
#else
move(event->globalPosition().toPoint() - m_dragPosition);
#endif
event->accept();
}
}

View file

@ -1,4 +1,4 @@
#include <QDesktopWidget>
// #include <QDesktopWidget>
#include <QFileInfo>
#include <QLabel>
#include <QMessageBox>
@ -13,6 +13,10 @@
#include <QWindow>
#include <QtWidgets/QHBoxLayout>
#if defined(Q_OS_WIN32)
#include <Windows.h>
#endif
#include "config.h"
#include "iconhelper.h"
#include "qyuvopenglwidget.h"
@ -363,21 +367,18 @@ void VideoForm::installShortcut()
QRect VideoForm::getScreenRect()
{
QRect screenRect;
QWidget *win = window();
if (!win) {
return screenRect;
}
QWindow *winHandle = win->windowHandle();
QScreen *screen = QGuiApplication::primaryScreen();
if (winHandle) {
screen = winHandle->screen();
}
if (!screen) {
return screenRect;
QWidget *win = window();
if (win) {
QWindow *winHandle = win->windowHandle();
if (winHandle) {
screen = winHandle->screen();
}
}
screenRect = screen->availableGeometry();
if (screen) {
screenRect = screen->availableGeometry();
}
return screenRect;
}
@ -572,23 +573,32 @@ void VideoForm::mousePressEvent(QMouseEvent *event)
}
}
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPointF localPos = event->localPos();
QPointF globalPos = event->globalPos();
#else
QPointF localPos = event->position();
QPointF globalPos = event->globalPosition();
#endif
if (m_videoWidget->geometry().contains(event->pos())) {
if (!device) {
return;
}
event->setLocalPos(m_videoWidget->mapFrom(this, event->localPos().toPoint()));
emit device->mouseEvent(event, m_videoWidget->frameSize(), m_videoWidget->size());
QPointF mappedPos = m_videoWidget->mapFrom(this, localPos.toPoint());
QMouseEvent newEvent(event->type(), mappedPos, globalPos, event->button(), event->buttons(), event->modifiers());
emit device->mouseEvent(&newEvent, m_videoWidget->frameSize(), m_videoWidget->size());
// debug keymap pos
if (event->button() == Qt::LeftButton) {
qreal x = event->localPos().x() / m_videoWidget->size().width();
qreal y = event->localPos().y() / m_videoWidget->size().height();
qreal x = localPos.x() / m_videoWidget->size().width();
qreal y = localPos.y() / m_videoWidget->size().height();
QString posTip = QString(R"("pos": {"x": %1, "y": %2})").arg(x).arg(y);
qInfo() << posTip.toStdString().c_str();
}
} else {
if (event->button() == Qt::LeftButton) {
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
m_dragPosition = globalPos.toPoint() - frameGeometry().topLeft();
event->accept();
}
}
@ -601,9 +611,15 @@ void VideoForm::mouseReleaseEvent(QMouseEvent *event)
if (!device) {
return;
}
event->setLocalPos(m_videoWidget->mapFrom(this, event->localPos().toPoint()));
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPointF localPos = event->localPos();
QPointF globalPos = event->globalPos();
#else
QPointF localPos = event->position();
QPointF globalPos = event->globalPosition();
#endif
// local check
QPointF local = event->localPos();
QPointF local = m_videoWidget->mapFrom(this, localPos.toPoint());
if (local.x() < 0) {
local.setX(0);
}
@ -616,8 +632,8 @@ void VideoForm::mouseReleaseEvent(QMouseEvent *event)
if (local.y() > m_videoWidget->height()) {
local.setY(m_videoWidget->height());
}
event->setLocalPos(local);
emit device->mouseEvent(event, m_videoWidget->frameSize(), m_videoWidget->size());
QMouseEvent newEvent(event->type(), local, globalPos, event->button(), event->buttons(), event->modifiers());
emit device->mouseEvent(&newEvent, m_videoWidget->frameSize(), m_videoWidget->size());
} else {
m_dragPosition = QPoint(0, 0);
}
@ -625,16 +641,24 @@ void VideoForm::mouseReleaseEvent(QMouseEvent *event)
void VideoForm::mouseMoveEvent(QMouseEvent *event)
{
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPointF localPos = event->localPos();
QPointF globalPos = event->globalPos();
#else
QPointF localPos = event->position();
QPointF globalPos = event->globalPosition();
#endif
auto device = qsc::IDeviceManage::getInstance().getDevice(m_serial);
if (m_videoWidget->geometry().contains(event->pos())) {
if (!device) {
return;
}
event->setLocalPos(m_videoWidget->mapFrom(this, event->localPos().toPoint()));
emit device->mouseEvent(event, m_videoWidget->frameSize(), m_videoWidget->size());
QPointF mappedPos = m_videoWidget->mapFrom(this, localPos.toPoint());
QMouseEvent newEvent(event->type(), mappedPos, globalPos, event->button(), event->buttons(), event->modifiers());
emit device->mouseEvent(&newEvent, m_videoWidget->frameSize(), m_videoWidget->size());
} else if (!m_dragPosition.isNull()) {
if (event->buttons() & Qt::LeftButton) {
move(event->globalPos() - m_dragPosition);
move(globalPos.toPoint() - m_dragPosition);
event->accept();
}
}
@ -657,8 +681,16 @@ void VideoForm::mouseDoubleClickEvent(QMouseEvent *event)
if (!device) {
return;
}
event->setLocalPos(m_videoWidget->mapFrom(this, event->localPos().toPoint()));
emit device->mouseEvent(event, m_videoWidget->frameSize(), m_videoWidget->size());
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
QPointF localPos = event->localPos();
QPointF globalPos = event->globalPos();
#else
QPointF localPos = event->position();
QPointF globalPos = event->globalPosition();
#endif
QPointF mappedPos = m_videoWidget->mapFrom(this, localPos.toPoint());
QMouseEvent newEvent(event->type(), mappedPos, globalPos, event->button(), event->buttons(), event->modifiers());
emit device->mouseEvent(&newEvent, m_videoWidget->frameSize(), m_videoWidget->size());
}
}
@ -714,7 +746,11 @@ void VideoForm::paintEvent(QPaintEvent *paint)
{
Q_UNUSED(paint)
QStyleOption opt;
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
opt.init(this);
#else
opt.initFrom(this);
#endif
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}

View file

@ -11,6 +11,9 @@
#define GROUP_COMMON "common"
// config
#define COMMON_LANGUAGE_KEY "Language"
#define COMMON_LANGUAGE_DEF "Auto"
#define COMMON_TITLE_KEY "WindowTitle"
#define COMMON_TITLE_DEF QCoreApplication::applicationName()
@ -108,15 +111,21 @@
#define SERIAL_NICK_NAME_KEY "NickName"
#define SERIAL_NICK_NAME_DEF "Phone"
// IP history
#define IP_HISTORY_KEY "IpHistory"
#define IP_HISTORY_DEF ""
#define IP_HISTORY_MAX 10
QString Config::s_configPath = "";
Config::Config(QObject *parent) : QObject(parent)
{
m_settings = new QSettings(getConfigPath() + "/config.ini", QSettings::IniFormat);
m_settings->setIniCodec("UTF-8");
m_userData = new QSettings(getConfigPath() + "/userdata.ini", QSettings::IniFormat);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
m_settings->setIniCodec("UTF-8");
m_userData->setIniCodec("UTF-8");
#endif
qDebug()<<m_userData->childGroups();
}
@ -363,6 +372,15 @@ void Config::deleteGroup(const QString &serial)
m_userData->remove(serial);
}
QString Config::getLanguage()
{
QString language;
m_settings->beginGroup(GROUP_COMMON);
language = m_settings->value(COMMON_LANGUAGE_KEY, COMMON_LANGUAGE_DEF).toString();
m_settings->endGroup();
return language;
}
QString Config::getTitle()
{
QString title;
@ -371,3 +389,34 @@ QString Config::getTitle()
m_settings->endGroup();
return title;
}
void Config::saveIpHistory(const QString &ip)
{
QStringList ipList = getIpHistory();
// 移除已存在的相同IP避免重复
ipList.removeAll(ip);
// 将新IP添加到开头
ipList.prepend(ip);
// 限制历史记录数量
while (ipList.size() > IP_HISTORY_MAX) {
ipList.removeLast();
}
m_userData->setValue(IP_HISTORY_KEY, ipList);
m_userData->sync();
}
QStringList Config::getIpHistory()
{
QStringList ipList = m_userData->value(IP_HISTORY_KEY, IP_HISTORY_DEF).toStringList();
return ipList;
}
void Config::clearIpHistory()
{
m_userData->remove(IP_HISTORY_KEY);
m_userData->sync();
}

View file

@ -34,6 +34,7 @@ public:
static Config &getInstance();
// config
QString getLanguage();
QString getTitle();
QString getServerVersion();
int getMaxFps();
@ -62,6 +63,11 @@ public:
void deleteGroup(const QString &serial);
// IP history methods
void saveIpHistory(const QString &ip);
QStringList getIpHistory();
void clearIpHistory();
private:
explicit Config(QObject *parent = nullptr);
const QString &getConfigPath();

View file

@ -1,4 +1,10 @@
#include <QX11Info>
#include <QtGlobal>
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
#include <QtX11Extras/QX11Info>
#else
#include <QtGui/private/qtx11extras_p.h>
#endif
#include <xcb/xproto.h>
#include <stdlib.h>

View file

@ -7,6 +7,7 @@
![license](https://img.shields.io/badge/license-Apache2.0-blue.svg)
![release](https://img.shields.io/github/v/release/barry-ran/QtScrcpy.svg)
![star](https://img.shields.io/github/stars/barry-ran/QtScrcpy.svg)
[中文用户?点我查看中文介绍](README_zh.md)
@ -31,14 +32,30 @@ It focuses on:
![linux](screenshot/linux-en.png)
## The author has developed a more professional screen casting software called `QuickMirror`
QuickMirror function&features:
- Equipment screen casting&control: batch screen casting, individual control, batch control
- Group management
- WiFi screen mirroring/OTG screen mirroring
- Adb shell shortcut command
- File transfer, apk installation
- Multiple screen mirroring: In OTG mirroring mode, with low resolution and smoothness settings, a single computer can manage 500+phones simultaneously
- Low latency: USB screen mirroring 1080p latency is within 30ms, which is lower than all screen mirroring software on the market in terms of latency at the same resolution and smoothness
- Low CPU usage: pure C++development, high-performance GPU video rendering
- High resolution: adjustable, maximum support for native resolution of Android terminals
- Perfect Chinese input: Supports Xianyu app, supports Samsung phones
- The free version can cast up to 10 screens, with unlimited functionality (except for automatic screen mirroring)
- QuickMirror tutorial: https://lrbnfell4p.feishu.cn/docx/EMkvdfIvDowy3UxsXUCcpPV8nDh
- QuickMirror Telegram communication group: https://t.me/+Ylf_5V_rDCMyODQ1
- Preview of QuickMirror Interface:
![quickmirror](docs/image/quickmirror.png)
## Mapping Keys
You can write your script to map keyboard and mouse actions to touches and clicks of the mobile phone according to your needs. [Here](docs/KeyMapDes.md) are the script writing rules.
Script for TikTok and some other games are provided by default. Once enabled, you can play the game with your keyboard and mouse. The default key mapping for PUBG Mobile is as follows:
![game](screenshot/game.jpg)
[Here is a video demonstration playing PUBG Mobile.](http://mp.weixin.qq.com/mp/video?__biz=MzU1NTg5MjYyNw==&mid=100000015&sn=3e301fdc5a364bd16d6207fa674bc8b3&vid=wxv_968792362971430913)
![game](screenshot/game.png)
Instruction for adding new customized mapping files.
@ -50,6 +67,22 @@ Instruction for adding new customized mapping files.
- Press the ~ key again to switch back to normal mode
- (For games such as PUBG Mobile) If you want to move vehicles with the STEER_WHEEL keys, you need to set the move mode to `single rocker mode`.
If you don't know how to manually write mapping rules, you can also use the `QuickAssistant` developed by the author
QuickAssistant Features&Functions:
- Play Android mobile games smoothly through keyboard and mouse
- Interface based editing of key mapping script
- Support pausing the computer screen and using only keyboard and mouse operations
- Screenshot&Recording of Mobile Screen
- Simple batch control
- Android 11+supports playing mobile audio on computers (under development...)
- Mobile app installation free
- Fast and instant connection
- Low latency: USB screen mirroring 1080p latency is within 30ms, which is lower than all screen mirroring software on the market in terms of latency at the same resolution and smoothness
- Low CPU usage: pure C++development, high-performance GPU video rendering
- High resolution: adjustable, maximum support for native resolution of Android terminals
- Telegram Grouphttps://t.me/+EnQNmb47C_liYmRl
- [QuickAssistant](https://lrbnfell4p.feishu.cn/drive/folder/Hqckfxj5el1Wjpd9uezcX71lnBh)
## Group control
You can control all your phones at the same time.
@ -89,9 +122,9 @@ It details the development architecture and the development process of the entir
Course introduction[https://blog.csdn.net/rankun1/article/details/87970523](https://blog.csdn.net/rankun1/article/details/87970523)
You can join my QQ group for QtScrcpy and exchange ideas with like-minded friends.
You can join Telegram Group for QtScrcpy and exchange ideas with like-minded friends.
QQ Group number901736468
Telegram Grouphttps://t.me/+EnQNmb47C_liYmRl
## Requirements

View file

@ -6,6 +6,8 @@
![license](https://img.shields.io/badge/license-Apache2.0-blue.svg)
![release](https://img.shields.io/github/v/release/barry-ran/QtScrcpy.svg)
![star](https://img.shields.io/github/stars/barry-ran/QtScrcpy.svg)
![star](https://gitcode.com/barry-ran/QtScrcpy/star/badge.svg)
[Speaks English? Click me for English introduction.](README.md)
@ -53,9 +55,7 @@ QtScrcpy 可以通过 USB / 网络连接Android设备并进行显示和控制
默认自带了针对和平精英手游和抖音进行键鼠映射的映射脚本,开启平精英手游后可以用键鼠像玩端游一样玩和平精英手游,开启抖音映射以后可以使用上下左右方向键模拟上下左右滑动,你也可以按照[编写规则](docs/KeyMapDes_zh.md)编写其他游戏的映射文件,默认按键映射如下:
![game](screenshot/game.jpg)
[这里有玩和平精英的视频演示](http://mp.weixin.qq.com/mp/video?__biz=MzU1NTg5MjYyNw==&mid=100000015&sn=3e301fdc5a364bd16d6207fa674bc8b3&vid=wxv_968792362971430913&idx=1&vidsn=eec329cc13c3e24c187dc9b4d5eb8760&fromid=1&scene=20&xtrack=1&clicktime=1567346543&sessionid=1567346375&subscene=92&ascene=0&fasttmpl_type=0&fasttmpl_fullversion=4730859-zh_CN-zip&fasttmpl_flag=0&realreporttime=1567346543910#wechat_redirect)
![game](screenshot/game.png)
自定义按键映射操作方法如下:
- 编写自定义脚本放入 keymap 目录
@ -66,6 +66,22 @@ QtScrcpy 可以通过 USB / 网络连接Android设备并进行显示和控制
- 再次按~键切换为正常控制模式
- (对于和平精英等游戏)若想使用方向盘控制载具,记得在载具设置中设置为单摇杆模式
如果不会自己手写映射规则,也可以去使用作者开发的`极限手游助手`
极限手游助手功能&特点:
- 通过键盘鼠标畅玩安卓手机游戏
- 按键映射脚本界面化编辑
- 支持暂停电脑端画面,只使用键鼠操作
- 截图&录制手机画面
- 简单批量控制
- 安卓11+支持电脑播放手机音频(开发中...
- 手机端免安装App
- 极速秒连接
- 低延迟usb投屏1080p延迟在30ms以内在相同分辨率流畅度情况下比市面上所有投屏软件延迟都低
- cpu占用率低纯C++开发高性能GPU视频渲染
- 高分辨率:可调节,最大支持安卓终端的原生分辨率
- [QQ交流群901736468](https://qm.qq.com/q/wRJJaWLWc8)
- [极限手游助手说明文档](https://lrbnfell4p.feishu.cn/drive/folder/Hqckfxj5el1Wjpd9uezcX71lnBh)
## 批量操作
你可以同时控制所有的手机

View file

@ -8,7 +8,6 @@ echo ---------------------------------------------------------------
# 从环境变量获取必要参数
# 例如 /Users/barry/Qt5.12.5/5.12.5
echo ENV_QT_PATH $ENV_QT_PATH
qt_cmake_path=$ENV_QT_PATH/clang_64/lib/cmake/Qt5
# 获取绝对路径,保证其他目录执行此脚本依然正确
{
@ -22,6 +21,7 @@ cd $(dirname "$0")
# 启动参数声明
build_mode=RelWithDebInfo
cpu_arch=arm64
echo
echo
@ -36,8 +36,30 @@ if [[ $build_mode != "Release" && $build_mode != "Debug" && $build_mode != "MinS
exit 1
fi
echo
echo
echo ---------------------------------------------------------------
echo check cpu arch[x64/arm64]
echo ---------------------------------------------------------------
cpu_arch=$(echo $2)
if [[ $cpu_arch != "x64" && $cpu_arch != "arm64" ]]; then
echo "error: unkonow cpu mode -- $2"
exit 1
fi
# 提示
echo current build mode: $build_mode
echo current cpu mode: $cpu_arch
cmake_arch=x86_64
if [ $cpu_arch == "x64" ]; then
qt_cmake_path=$ENV_QT_PATH/clang_64/lib/cmake/Qt5
cmake_arch=x86_64
else
qt_cmake_path=$ENV_QT_PATH/macos/lib/cmake/Qt6
cmake_arch=arm64
fi
echo
echo
@ -58,7 +80,7 @@ fi
mkdir $build_path
cd $build_path
cmake_params="-DCMAKE_PREFIX_PATH=$qt_cmake_path -DCMAKE_BUILD_TYPE=$build_mode -DCMAKE_OSX_ARCHITECTURES=x86_64 -D CMAKE_OSX_DEPLOYMENT_TARGET=10.10"
cmake_params="-DCMAKE_PREFIX_PATH=$qt_cmake_path -DCMAKE_BUILD_TYPE=$build_mode -DCMAKE_OSX_ARCHITECTURES=$cmake_arch"
cmake $cmake_params ../..
if [ $? -ne 0 ] ;then
echo "cmake failed"

View file

@ -7,7 +7,6 @@ echo ---------------------------------------------------------------
# 从环境变量获取必要参数
# 例如 /Users/barry/Qt5.12.5/5.12.5
echo ENV_QT_PATH $ENV_QT_PATH
qt_clang_path=$ENV_QT_PATH/clang_64
# 获取绝对路径,保证其他目录执行此脚本依然正确
{
@ -21,6 +20,27 @@ cd $(dirname "$0")
# 启动参数声明
publish_dir=$1
cpu_arch=$2
echo
echo
echo ---------------------------------------------------------------
echo check cpu arch[x64/arm64]
echo ---------------------------------------------------------------
if [[ $cpu_arch != "x64" && $cpu_arch != "arm64" ]]; then
echo "error: unkonow cpu mode -- $2"
exit 1
fi
# 提示
echo current cpu mode: $cpu_arch
if [ $cpu_arch == "x64" ]; then
qt_clang_path=$ENV_QT_PATH/clang_64
else
qt_clang_path=$ENV_QT_PATH/macos
fi
# 提示
echo current publish dir: $publish_dir
@ -30,7 +50,7 @@ keymap_path=$script_path/../../keymap
# config_path=$script_path/../../config
publish_path=$script_path/$publish_dir
release_path=$script_path/../../output/x64/RelWithDebInfo
release_path=$script_path/../../output/$cpu_arch/RelWithDebInfo
export PATH=$qt_clang_path/bin:$PATH

View file

@ -1,4 +1,6 @@
[common]
# 语言 Auto=自动zh_CN=简体中文en_US=English
Language=Auto
# 窗口标题
WindowTitle=QtScrcpy
# 推送到安卓设备的文件保存路径(必须以/结尾)

View file

@ -76,7 +76,11 @@ Description of the unique attributes of different key mapping types:
## Visual Key Mapping Tool
A web-based GUI tool is available to help you create and manage key mappings visually: [ScrcpyKeyMapper](https://github.com/w4po/ScrcpyKeyMapper)
1. Just use [QuickAssistant](https://lrbnfell4p.feishu.cn/drive/folder/Hqckfxj5el1Wjpd9uezcX71lnBh)
![game](../screenshot/game.png)
2. A web-based GUI tool is available to help you create and manage key mappings visually: [ScrcpyKeyMapper](https://github.com/w4po/ScrcpyKeyMapper)
![ScrcpyKeyMapper Screenshot](https://raw.githubusercontent.com/w4po/ScrcpyKeyMapper/main/assets/screenshot.png)

View file

@ -75,8 +75,11 @@
- downOffset 按下下方向键后模拟拖动到相对centerPos位置水平偏下downOffset处
## 可视化按键映射工具
1. 直接使用[QuickAssistant](https://lrbnfell4p.feishu.cn/drive/folder/Hqckfxj5el1Wjpd9uezcX71lnBh)
现在有一个基于Web的GUI工具可以帮助你直观地创建和管理按键映射[ScrcpyKeyMapper](https://github.com/w4po/ScrcpyKeyMapper)
![game](../screenshot/game.png)
2. 还有一个基于Web的GUI工具可以帮助你直观地创建和管理按键映射[ScrcpyKeyMapper](https://github.com/w4po/ScrcpyKeyMapper)
![ScrcpyKeyMapper截图](https://raw.githubusercontent.com/w4po/ScrcpyKeyMapper/main/assets/screenshot.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

BIN
screenshot/game.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 823 KiB