diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index aaf52ed..ab9965c 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -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-install,build-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 diff --git a/QtScrcpy/CMakeLists.txt b/QtScrcpy/CMakeLists.txt index a9d1c64..736dd23 100755 --- a/QtScrcpy/CMakeLists.txt +++ b/QtScrcpy/CMakeLists.txt @@ -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 ) diff --git a/QtScrcpy/QtScrcpyCore b/QtScrcpy/QtScrcpyCore index 6beab5c..b118082 160000 --- a/QtScrcpy/QtScrcpyCore +++ b/QtScrcpy/QtScrcpyCore @@ -1 +1 @@ -Subproject commit 6beab5c967f7f77ddcfabe79757dc01202653ab7 +Subproject commit b118082a176c051b327622a5fd11dabd731a2a2b diff --git a/QtScrcpy/audio/audiooutput.cpp b/QtScrcpy/audio/audiooutput.cpp index 34e00cd..033abc8 100644 --- a/QtScrcpy/audio/audiooutput.cpp +++ b/QtScrcpy/audio/audiooutput.cpp @@ -4,11 +4,23 @@ #include #include +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) +#include +#include +#include +#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) { diff --git a/QtScrcpy/audio/audiooutput.h b/QtScrcpy/audio/audiooutput.h index 1690168..d9060c1 100644 --- a/QtScrcpy/audio/audiooutput.h +++ b/QtScrcpy/audio/audiooutput.h @@ -6,6 +6,7 @@ #include #include +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 m_outputDevice; QThread m_workerThread; QProcess m_sndcpy; QVector 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 diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index a24ba60..c6b01bb 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -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 ").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") + " "); + qInfo() << QObject::tr("If you need more professional batch control mirror software, you can try the following software:"); + qInfo() << QString(QObject::tr("QuickMirror") + " "); + + qInfo() << QObject::tr("If you need more professional game keymap mirror software, you can try the following software:"); + qInfo() << QString(QObject::tr("QuickAssistant") + " "); + + qInfo() << QObject::tr("You can contact me with telegram "); 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); } diff --git a/QtScrcpy/render/qyuvopenglwidget.cpp b/QtScrcpy/render/qyuvopenglwidget.cpp index 783bdc9..e4f58b3 100644 --- a/QtScrcpy/render/qyuvopenglwidget.cpp +++ b/QtScrcpy/render/qyuvopenglwidget.cpp @@ -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) diff --git a/QtScrcpy/res/i18n/en_US.qm b/QtScrcpy/res/i18n/en_US.qm index a8f465e..cfbc0aa 100644 Binary files a/QtScrcpy/res/i18n/en_US.qm and b/QtScrcpy/res/i18n/en_US.qm differ diff --git a/QtScrcpy/res/i18n/en_US.ts b/QtScrcpy/res/i18n/en_US.ts index 793171b..894ff5b 100644 --- a/QtScrcpy/res/i18n/en_US.ts +++ b/QtScrcpy/res/i18n/en_US.ts @@ -31,6 +31,10 @@ select path select path + + Clear History + Clear History + QObject @@ -38,14 +42,26 @@ This software is completely open source and free. Use it at your own risk. You can download it at the following address: This software is completely open source and free. Use it at your own risk. You can download it at the following address: - - If you need more professional screen projection control software, you can try the following software: - If you need more professional screen projection control software, you can try the following software: - QuickMirror QuickMirror + + If you need more professional batch control mirror software, you can try the following software: + If you need more professional batch control mirror software, you can try the following software: + + + If you need more professional game keymap mirror software, you can try the following software: + If you need more professional game keymap mirror software, you can try the following software: + + + QuickAssistant + QuickAssistant + + + You can contact me with telegram <https://t.me/+Ylf_5V_rDCMyODQ1> + You can contact me with telegram <https://t.me/+Ylf_5V_rDCMyODQ1> + ToolForm diff --git a/QtScrcpy/res/i18n/zh_CN.qm b/QtScrcpy/res/i18n/zh_CN.qm index 4794f80..791f1d1 100644 Binary files a/QtScrcpy/res/i18n/zh_CN.qm and b/QtScrcpy/res/i18n/zh_CN.qm differ diff --git a/QtScrcpy/res/i18n/zh_CN.ts b/QtScrcpy/res/i18n/zh_CN.ts index 4cd66c4..953cc8e 100644 --- a/QtScrcpy/res/i18n/zh_CN.ts +++ b/QtScrcpy/res/i18n/zh_CN.ts @@ -31,6 +31,10 @@ select path 选择路径 + + Clear History + 清理历史 + QObject @@ -38,14 +42,26 @@ This software is completely open source and free. Use it at your own risk. You can download it at the following address: 本软件完全开源免费,作者不对使用该软件产生的一切后果负责。你可以在以下地址下载: - - If you need more professional screen projection control software, you can try the following software: - 如果你需要更加专业的投屏控制软件,可以尝试作者开发的商业版: - QuickMirror 极限投屏 + + If you need more professional batch control mirror software, you can try the following software: + 如果你需要更专业的批量控制投屏软件,你可以尝试下面软件: + + + If you need more professional game keymap mirror software, you can try the following software: + 如果你需要更专业的游戏映射投屏软件,你可以尝试下面软件: + + + QuickAssistant + 极限手游助手 + + + You can contact me with telegram <https://t.me/+Ylf_5V_rDCMyODQ1> + 你可以通过QQ群联系我 <901736468> + ToolForm diff --git a/QtScrcpy/ui/dialog.cpp b/QtScrcpy/ui/dialog.cpp index 493060c..ea59d23 100644 --- a/QtScrcpy/ui/dialog.cpp +++ b/QtScrcpy/ui/dialog.cpp @@ -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; +} diff --git a/QtScrcpy/ui/dialog.h b/QtScrcpy/ui/dialog.h index 860c87e..6766017 100644 --- a/QtScrcpy/ui/dialog.h +++ b/QtScrcpy/ui/dialog.h @@ -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); diff --git a/QtScrcpy/ui/dialog.ui b/QtScrcpy/ui/dialog.ui index e46e06b..7a7702d 100644 --- a/QtScrcpy/ui/dialog.ui +++ b/QtScrcpy/ui/dialog.ui @@ -1059,22 +1059,25 @@ 5 - + 0 0 - + + + 200 + 0 + + + + true + + - - 128 - - - 192.168.0.1 - diff --git a/QtScrcpy/ui/toolform.cpp b/QtScrcpy/ui/toolform.cpp index 35614f7..4d5c0e5 100644 --- a/QtScrcpy/ui/toolform.cpp +++ b/QtScrcpy/ui/toolform.cpp @@ -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(); } } diff --git a/QtScrcpy/ui/videoform.cpp b/QtScrcpy/ui/videoform.cpp index 5806f1d..e692da5 100644 --- a/QtScrcpy/ui/videoform.cpp +++ b/QtScrcpy/ui/videoform.cpp @@ -1,4 +1,4 @@ -#include +// #include #include #include #include @@ -13,6 +13,10 @@ #include #include +#if defined(Q_OS_WIN32) +#include +#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); } diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index 43bcee0..c8b8eee 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -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() @@ -18,7 +21,7 @@ #define COMMON_PUSHFILE_DEF "/sdcard/" #define COMMON_SERVER_VERSION_KEY "ServerVersion" -#define COMMON_SERVER_VERSION_DEF "3.1" +#define COMMON_SERVER_VERSION_DEF "3.2" #define COMMON_SERVER_PATH_KEY "ServerPath" #define COMMON_SERVER_PATH_DEF "/data/local/tmp/scrcpy-server.jar" @@ -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()<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(); +} diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index 6f3c1d9..e45e4cd 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -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(); diff --git a/QtScrcpy/util/mousetap/xmousetap.cpp b/QtScrcpy/util/mousetap/xmousetap.cpp index 18ba226..7d52a74 100644 --- a/QtScrcpy/util/mousetap/xmousetap.cpp +++ b/QtScrcpy/util/mousetap/xmousetap.cpp @@ -1,4 +1,10 @@ -#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#else +#include +#endif #include #include diff --git a/README.md b/README.md index cbd2bb4..08f4721 100644 --- a/README.md +++ b/README.md @@ -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 Group:https://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 number:901736468 +Telegram Group:https://t.me/+EnQNmb47C_liYmRl ## Requirements diff --git a/README_zh.md b/README_zh.md index 2c9f7e2..62bbc25 100644 --- a/README_zh.md +++ b/README_zh.md @@ -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) + ## 批量操作 你可以同时控制所有的手机 diff --git a/ci/mac/build_for_mac.sh b/ci/mac/build_for_mac.sh index b38a4bf..df3a4ba 100755 --- a/ci/mac/build_for_mac.sh +++ b/ci/mac/build_for_mac.sh @@ -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" diff --git a/ci/mac/publish_for_mac.sh b/ci/mac/publish_for_mac.sh index c16b426..01e6714 100755 --- a/ci/mac/publish_for_mac.sh +++ b/ci/mac/publish_for_mac.sh @@ -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 diff --git a/config/config.ini b/config/config.ini index a35cc0d..4642b8f 100644 --- a/config/config.ini +++ b/config/config.ini @@ -1,4 +1,6 @@ [common] +# 语言 Auto=自动,zh_CN=简体中文,en_US=English +Language=Auto # 窗口标题 WindowTitle=QtScrcpy # 推送到安卓设备的文件保存路径(必须以/结尾) @@ -10,7 +12,7 @@ RenderExpiredFrames=0 # 视频解码方式:-1 自动,0 软解,1 dx硬解,2 opengl硬解 UseDesktopOpenGL=-1 # scrcpy-server的版本号(不要修改) -ServerVersion=3.1 +ServerVersion=3.2 # scrcpy-server推送到安卓设备的路径 ServerPath=/data/local/tmp/scrcpy-server.jar # 自定义adb路径,例如D:/android/tools/adb.exe diff --git a/docs/KeyMapDes.md b/docs/KeyMapDes.md index 3d83df9..d51eb39 100644 --- a/docs/KeyMapDes.md +++ b/docs/KeyMapDes.md @@ -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) diff --git a/docs/KeyMapDes_zh.md b/docs/KeyMapDes_zh.md index 3cace6f..4fdb45f 100644 --- a/docs/KeyMapDes_zh.md +++ b/docs/KeyMapDes_zh.md @@ -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) diff --git a/screenshot/game.jpg b/screenshot/game.jpg deleted file mode 100644 index 2d523ab..0000000 Binary files a/screenshot/game.jpg and /dev/null differ diff --git a/screenshot/game.png b/screenshot/game.png new file mode 100644 index 0000000..a222c69 Binary files /dev/null and b/screenshot/game.png differ