diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 6913772..a71e9d7 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -18,7 +18,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-18.04] + os: [ubuntu-22.04] qt-ver: [5.15.1] qt-arch-install: [gcc_64] gcc-arch: [x64] diff --git a/QtScrcpy/CMakeLists.txt b/QtScrcpy/CMakeLists.txt index 5ddf143..cc35681 100755 --- a/QtScrcpy/CMakeLists.txt +++ b/QtScrcpy/CMakeLists.txt @@ -79,8 +79,8 @@ set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) -find_package(QT NAMES Qt6 Qt5 COMPONENTS Widgets Network REQUIRED) -find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Widgets Network REQUIRED) +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) @@ -108,6 +108,13 @@ set(QC_UIBASE_SOURCES ) source_group(uibase FILES ${QC_UIBASE_SOURCES}) +# audio +set(QC_AUDIO_SOURCES + audio/audiooutput.h + audio/audiooutput.cpp +) +source_group(audio FILES ${QC_AUDIO_SOURCES}) + # ui set(QC_UI_SOURCES ui/toolform.h @@ -199,6 +206,7 @@ set(QC_PROJECT_SOURCES ${QC_MAIN_SOURCES} ${QC_GROUP_CONTROLLER} ${QC_PLANTFORM_SOURCES} + ${QC_AUDIO_SOURCES} ) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") @@ -242,6 +250,11 @@ set_target_properties(${PROJECT_NAME} PROPERTIES if(CMAKE_SYSTEM_NAME STREQUAL "Windows") get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY) set(QSC_DEPLOY_PATH ${QSC_BIN_OUTPUT_PATH}) + + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.bat" "${QSC_BIN_OUTPUT_PATH}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${QSC_BIN_OUTPUT_PATH}" + ) endif() # MacOS @@ -255,6 +268,8 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD # config file copy to Contents/MacOS/config COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/../config/config.ini" "${MACOS_BUNDLE_PATH}/MacOS/config/config.ini" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.sh" "${MACOS_BUNDLE_PATH}/MacOS" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${MACOS_BUNDLE_PATH}/MacOS" ) # Step 2. ues MACOSX_PACKAGE_LOCATION copy icns to Resources @@ -284,6 +299,11 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux") get_target_property(QSC_BIN_OUTPUT_PATH ${PROJECT_NAME} RUNTIME_OUTPUT_DIRECTORY) set(QSC_DEPLOY_PATH ${QSC_BIN_OUTPUT_PATH}) + add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.sh" "${QSC_BIN_OUTPUT_PATH}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/sndcpy/sndcpy.apk" "${QSC_BIN_OUTPUT_PATH}" + ) + set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -309,5 +329,6 @@ add_subdirectory(QtScrcpyCore) target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network + Qt${QT_VERSION_MAJOR}::Multimedia QtScrcpyCore ) diff --git a/QtScrcpy/QtScrcpyCore b/QtScrcpy/QtScrcpyCore index 7ac8571..3004e63 160000 --- a/QtScrcpy/QtScrcpyCore +++ b/QtScrcpy/QtScrcpyCore @@ -1 +1 @@ -Subproject commit 7ac8571daadf5af9b548a4c02eb7c6d504af6a97 +Subproject commit 3004e63935fe8a3e57b91e117a91c1a6aa68ae42 diff --git a/QtScrcpy/audio/audiooutput.cpp b/QtScrcpy/audio/audiooutput.cpp new file mode 100644 index 0000000..34e00cd --- /dev/null +++ b/QtScrcpy/audio/audiooutput.cpp @@ -0,0 +1,197 @@ +#include +#include +#include +#include +#include + +#include "audiooutput.h" + +AudioOutput::AudioOutput(QObject *parent) + : QObject(parent) +{ + connect(&m_sndcpy, &QProcess::readyReadStandardOutput, this, [this]() { + qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardOutput()); + }); + connect(&m_sndcpy, &QProcess::readyReadStandardError, this, [this]() { + qInfo() << QString("AudioOutput::") << QString(m_sndcpy.readAllStandardError()); + }); +} + +AudioOutput::~AudioOutput() +{ + if (QProcess::NotRunning != m_sndcpy.state()) { + m_sndcpy.kill(); + } + stop(); +} + +bool AudioOutput::start(const QString& serial, int port) +{ + if (m_running) { + stop(); + } + + QElapsedTimer timeConsumeCount; + timeConsumeCount.start(); + bool ret = runSndcpyProcess(serial, port); + qInfo() << "AudioOutput::run sndcpy cost:" << timeConsumeCount.elapsed() << "milliseconds"; + if (!ret) { + return ret; + } + + startAudioOutput(); + startRecvData(port); + + m_running = true; + return true; +} + +void AudioOutput::stop() +{ + if (!m_running) { + return; + } + m_running = false; + + stopRecvData(); + stopAudioOutput(); +} + +void AudioOutput::installonly(const QString &serial, int port) +{ + runSndcpyProcess(serial, port, false); +} + +bool AudioOutput::runSndcpyProcess(const QString &serial, int port, bool wait) +{ + if (QProcess::NotRunning != m_sndcpy.state()) { + m_sndcpy.kill(); + } + +#ifdef Q_OS_WIN32 + QStringList params; + params << serial; + params << QString("%1").arg(port); + m_sndcpy.start("sndcpy.bat", params); +#else + QStringList params; + params << "sndcpy.sh"; + params << serial; + params << QString("%1").arg(port); + m_sndcpy.start("bash", params); +#endif + + if (!wait) { + return true; + } + + if (!m_sndcpy.waitForStarted()) { + qWarning() << "AudioOutput::start sndcpy.bat failed"; + return false; + } + if (!m_sndcpy.waitForFinished()) { + qWarning() << "AudioOutput::sndcpy.bat crashed"; + return false; + } + + return true; +} + +void AudioOutput::startAudioOutput() +{ + if (m_audioOutput) { + return; + } + + QAudioFormat format; + format.setSampleRate(48000); + format.setChannelCount(2); + format.setSampleSize(16); + 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; + } + + m_audioOutput = new QAudioOutput(format, this); + connect(m_audioOutput, &QAudioOutput::stateChanged, this, [](QAudio::State state) { + qInfo() << "AudioOutput::audio state changed:" << state; + }); + m_audioOutput->setBufferSize(48000*2*15/1000 * 20); + m_outputDevice = m_audioOutput->start(); +} + +void AudioOutput::stopAudioOutput() +{ + if (!m_audioOutput) { + return; + } + + m_audioOutput->stop(); + delete m_audioOutput; + m_audioOutput = nullptr; +} + +void AudioOutput::startRecvData(int port) +{ + if (m_workerThread.isRunning()) { + stopRecvData(); + } + + auto audioSocket = new QTcpSocket(); + audioSocket->moveToThread(&m_workerThread); + connect(&m_workerThread, &QThread::finished, audioSocket, &QObject::deleteLater); + + connect(this, &AudioOutput::connectTo, audioSocket, [audioSocket](int port) { + audioSocket->connectToHost(QHostAddress::LocalHost, port); + if (!audioSocket->waitForConnected(500)) { + qWarning("AudioOutput::audio socket connect failed"); + return; + } + qInfo("AudioOutput::audio socket connect success"); + }); + connect(audioSocket, &QIODevice::readyRead, audioSocket, [this, audioSocket]() { + qint64 recv = audioSocket->bytesAvailable(); + //qDebug() << "AudioOutput::recv data:" << recv; + + if (!m_outputDevice) { + return; + } + if (m_buffer.capacity() < recv) { + m_buffer.reserve(recv); + } + + qint64 count = audioSocket->read(m_buffer.data(), audioSocket->bytesAvailable()); + m_outputDevice->write(m_buffer.data(), count); + }); + connect(audioSocket, &QTcpSocket::stateChanged, audioSocket, [](QAbstractSocket::SocketState state) { + qInfo() << "AudioOutput::audio socket state changed:" << state; + + }); +#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) + connect(audioSocket, &QTcpSocket::errorOccurred, audioSocket, [](QAbstractSocket::SocketError error) { + qInfo() << "AudioOutput::audio socket error occurred:" << error; + }); +#else + connect(audioSocket, QOverload::of(&QAbstractSocket::error), audioSocket, [](QAbstractSocket::SocketError error) { + qInfo() << "AudioOutput::audio socket error occurred:" << error; + }); +#endif + + m_workerThread.start(); + emit connectTo(port); +} + +void AudioOutput::stopRecvData() +{ + if (!m_workerThread.isRunning()) { + return; + } + + m_workerThread.quit(); + m_workerThread.wait(); +} diff --git a/QtScrcpy/audio/audiooutput.h b/QtScrcpy/audio/audiooutput.h new file mode 100644 index 0000000..1690168 --- /dev/null +++ b/QtScrcpy/audio/audiooutput.h @@ -0,0 +1,41 @@ +#ifndef AUDIOOUTPUT_H +#define AUDIOOUTPUT_H + +#include +#include +#include +#include + +class QAudioOutput; +class QIODevice; +class AudioOutput : public QObject +{ + Q_OBJECT +public: + explicit AudioOutput(QObject *parent = nullptr); + ~AudioOutput(); + + bool start(const QString& serial, int port); + void stop(); + void installonly(const QString& serial, int port); + +private: + bool runSndcpyProcess(const QString& serial, int port, bool wait = true); + void startAudioOutput(); + void stopAudioOutput(); + void startRecvData(int port); + void stopRecvData(); + +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; +}; + +#endif // AUDIOOUTPUT_H diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index b85d218..629e08b 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -22,21 +22,24 @@ int main(int argc, char *argv[]) { // set env #ifdef Q_OS_WIN32 - qputenv("QTSCRCPY_ADB_PATH", "D:/android/sdk/platform-tools/adb.exe"); + qputenv("QTSCRCPY_ADB_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/win/adb.exe"); qputenv("QTSCRCPY_SERVER_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server"); qputenv("QTSCRCPY_KEYMAP_PATH", "../../../keymap"); qputenv("QTSCRCPY_CONFIG_PATH", "../../../config"); #endif #ifdef Q_OS_OSX - qputenv("QTSCRCPY_KEYMAP_PATH", "../../../../../keymap"); + qputenv("QTSCRCPY_ADB_PATH", "../../../../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/mac/adb"); + qputenv("QTSCRCPY_SERVER_PATH", "../../../../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server"); + qputenv("QTSCRCPY_KEYMAP_PATH", "../../../../../../keymap"); + qputenv("QTSCRCPY_CONFIG_PATH", "../../../../../../config"); #endif #ifdef Q_OS_LINUX - qputenv("QTSCRCPY_ADB_PATH", "../../QtScrcpy/QtScrcpyCore/src/third_party/adb/linux/adb"); - qputenv("QTSCRCPY_SERVER_PATH", "../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server"); - qputenv("QTSCRCPY_CONFIG_PATH", "../../config"); - qputenv("QTSCRCPY_KEYMAP_PATH", "../../keymap"); + qputenv("QTSCRCPY_ADB_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/adb/linux/adb"); + qputenv("QTSCRCPY_SERVER_PATH", "../../../QtScrcpy/QtScrcpyCore/src/third_party/scrcpy-server"); + qputenv("QTSCRCPY_KEYMAP_PATH", "../../../keymap"); + qputenv("QTSCRCPY_CONFIG_PATH", "../../../config"); #endif g_msgType = covertLogLevel(Config::getInstance().getLogLevel()); diff --git a/QtScrcpy/res/i18n/en_US.qm b/QtScrcpy/res/i18n/en_US.qm index e64c864..ffb6372 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 986bd01..c8787cf 100644 --- a/QtScrcpy/res/i18n/en_US.ts +++ b/QtScrcpy/res/i18n/en_US.ts @@ -3,6 +3,114 @@ Dialog + + show + show + + + quit + quit + + + original + original + + + no lock + no lock + + + Notice + Notice + + + Hidden here! + Hidden here! + + + select path + select path + + + + QObject + + 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: + + + + ToolForm + + Tool + Tool + + + full screen + full screen + + + expand notify + expand notify + + + touch switch + touch switch + + + close screen + close screen + + + power + power + + + volume up + volume up + + + volume down + volume down + + + app switch + app switch + + + menu + menu + + + home + home + + + return + return + + + screen shot + screen shot + + + open screen + open screen + + + group control + group control + + + + VideoForm + + file does not exist + file does not exist + + + + Widget Wireless Wireless @@ -166,109 +274,20 @@ refresh devices - show - show - show + install sndcpy + install sndcpy - quit - quit - quit + start audio + start audio - original - original + stop audio + stop audio - no lock - no lock - - - Notice - Notice - Notice - - - Hidden here! - Hidden here! - Hidden here! - - - - QObject - - 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: - - - - ToolForm - - Tool - Tool - - - full screen - full screen - - - expand notify - expand notify - - - touch switch - touch switch - - - close screen - close screen - - - power - power - - - volume up - volume up - - - volume down - volume down - - - app switch - app switch - - - menu - menu - - - home - home - - - return - return - - - screen shot - screen shot - - - open screen - open screen - - - group control - group control - - - - VideoForm - - file does not exist - file does not exist + auto update + autp update diff --git a/QtScrcpy/res/i18n/zh_CN.qm b/QtScrcpy/res/i18n/zh_CN.qm index b98cc9c..a505d6d 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 a356f7e..093c35d 100644 --- a/QtScrcpy/res/i18n/zh_CN.ts +++ b/QtScrcpy/res/i18n/zh_CN.ts @@ -3,6 +3,114 @@ Dialog + + show + 显示 + + + quit + 退出 + + + original + 原始 + + + no lock + 不锁定 + + + Notice + 提示 + + + Hidden here! + 安卓录屏程序隐藏在这! + + + select path + 选择路径 + + + + QObject + + This software is completely open source and free. Use it at your own risk. You can download it at the following address: + 本软件完全开源免费,作者不对使用该软件产生的一切后果负责。你可以在以下地址下载: + + + + ToolForm + + Tool + 工具 + + + full screen + 全屏 + + + expand notify + 下拉通知 + + + touch switch + 触摸显示开关 + + + close screen + 关闭屏幕 + + + power + 电源 + + + volume up + 音量加 + + + volume down + 音量减 + + + app switch + 切换应用 + + + menu + 菜单 + + + home + 主界面 + + + return + 返回 + + + screen shot + 截图 + + + open screen + 打开屏幕 + + + group control + 群控 + + + + VideoForm + + file does not exist + 文件不存在 + + + + Widget Wireless 无线 @@ -166,109 +274,20 @@ 刷新设备列表 - show - 显示 - 显示 + install sndcpy + 安装sndcpy - quit - 退出 - 退出 + start audio + 开始音频 - original - 原始 + stop audio + 停止音频 - no lock - 不锁定 - - - Notice - 提示 - 提示 - - - Hidden here! - 安卓录屏程序隐藏在这! - 安卓录屏程序隐藏在这! - - - - QObject - - This software is completely open source and free. Use it at your own risk. You can download it at the following address: - 本软件完全开源免费,作者不对使用该软件产生的一切后果负责。你可以在以下地址下载: - - - - ToolForm - - Tool - 工具 - - - full screen - 全屏 - - - expand notify - 下拉通知 - - - touch switch - 触摸显示开关 - - - close screen - 关闭屏幕 - - - power - 电源 - - - volume up - 音量加 - - - volume down - 音量减 - - - app switch - 切换应用 - - - menu - 菜单 - - - home - 主界面 - - - return - 返回 - - - screen shot - 截图 - - - open screen - 打开屏幕 - - - group control - 群控 - - - - VideoForm - - file does not exist - 文件不存在 + auto update + 自动刷新 diff --git a/QtScrcpy/sndcpy/sndcpy.apk b/QtScrcpy/sndcpy/sndcpy.apk new file mode 100644 index 0000000..93e8cc2 Binary files /dev/null and b/QtScrcpy/sndcpy/sndcpy.apk differ diff --git a/QtScrcpy/sndcpy/sndcpy.bat b/QtScrcpy/sndcpy/sndcpy.bat new file mode 100644 index 0000000..f8a41a7 --- /dev/null +++ b/QtScrcpy/sndcpy/sndcpy.bat @@ -0,0 +1,53 @@ +@echo off + +echo Begin Runing... +set SNDCPY_PORT=28200 +set SNDCPY_APK=sndcpy.apk +set ADB=adb.exe + +if not "%1"=="" ( + set serial=-s %1 +) +if not "%2"=="" ( + set SNDCPY_PORT=%2 +) + +echo Waiting for device %1... +%ADB% %serial% wait-for-device || goto :error +echo Find device %1 + +for /f "delims=" %%i in ('%ADB% %serial% shell pm path com.rom1v.sndcpy') do set sndcpy_installed=%%i +if "%sndcpy_installed%"=="" ( + echo Install %SNDCPY_APK%... + %ADB% %serial% uninstall com.rom1v.sndcpy || goto :error + %ADB% %serial% install -t -r -g %SNDCPY_APK% || goto :error + echo Install %SNDCPY_APK% success +) + +echo Request PROJECT_MEDIA permission... +%ADB% %serial% shell appops set com.rom1v.sndcpy PROJECT_MEDIA allow + +echo Forward port %SNDCPY_PORT%... +%ADB% %serial% forward tcp:%SNDCPY_PORT% localabstract:sndcpy || goto :error + +echo Start %SNDCPY_APK%... +%ADB% %serial% shell am start com.rom1v.sndcpy/.MainActivity || goto :error + +:check_start +echo Waiting %SNDCPY_APK% start... +::timeout /T 1 /NOBREAK > nul +%ADB% %serial% shell sleep 0.1 +for /f "delims=" %%i in ("%ADB% shell 'ps | grep com.rom1v.sndcpy'") do set sndcpy_started=%%i +if "%sndcpy_started%"=="" ( + goto :check_start +) +echo %SNDCPY_APK% started... + +echo Ready playing... +::vlc.exe -Idummy --demux rawaud --network-caching=0 --play-and-exit tcp://localhost:%SNDCPY_PORT% +::ffplay.exe -nodisp -autoexit -probesize 32 -sync ext -f s16le -ar 48k -ac 2 tcp://localhost:%SNDCPY_PORT% +goto :EOF + +:error +echo Failed with error #%errorlevel%. +exit /b %errorlevel% diff --git a/QtScrcpy/sndcpy/sndcpy.sh b/QtScrcpy/sndcpy/sndcpy.sh new file mode 100755 index 0000000..c9dfb9e --- /dev/null +++ b/QtScrcpy/sndcpy/sndcpy.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +echo Begin Runing... +SNDCPY_PORT=28200 +SNDCPY_APK=sndcpy.apk +ADB=./adb + +serial= +if [[ $# -ge 2 ]] +then + serial="-s $1" + SNDCPY_PORT=$2 +fi + +echo "Waiting for device $1..." +$ADB $serial wait-for-device +echo "Find device $1" + +sndcpy_installed=$($ADB $serial shell pm path com.rom1v.sndcpy) +if [[ $sndcpy_installed == "" ]]; then + echo Install $SNDCPY_APK... + $ADB $serial uninstall com.rom1v.sndcpy + $ADB $serial install -t -r -g $SNDCPY_APK + echo Install $SNDCPY_APK success +fi + +echo Request PROJECT_MEDIA permission... +$ADB $serial shell appops set com.rom1v.sndcpy PROJECT_MEDIA allow + +echo Forward port $SNDCPY_PORT... +$ADB $serial forward tcp:$SNDCPY_PORT localabstract:sndcpy + +echo Start $SNDCPY_APK... +$ADB $serial shell am start com.rom1v.sndcpy/.MainActivity + +while ((1)) +do + echo Waiting $SNDCPY_APK start... + sleep 0.1 + sndcpy_started=$($ADB shell 'ps | grep com.rom1v.sndcpy') + if [[ $sndcpy_started != "" ]]; then + break + fi +done + +echo Ready playing... \ No newline at end of file diff --git a/QtScrcpy/ui/dialog.cpp b/QtScrcpy/ui/dialog.cpp index 1683cf6..6b0e84a 100644 --- a/QtScrcpy/ui/dialog.cpp +++ b/QtScrcpy/ui/dialog.cpp @@ -25,11 +25,21 @@ const QString &getKeyMapPath() return s_keyMapPath; } -Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) +Dialog::Dialog(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); initUI(); + updateBootConfig(true); + + on_useSingleModeCheck_clicked(); + on_updateDevice_clicked(); + + connect(&m_autoUpdatetimer, &QTimer::timeout, this, &Dialog::on_updateDevice_clicked); + if (ui->autoUpdatecheckBox->isChecked()) { + m_autoUpdatetimer.start(5000); + } + connect(&m_adb, &qsc::AdbProcess::adbProcessResult, this, [this](qsc::AdbProcess::ADB_EXEC_RESULT processResult) { QString log = ""; bool newLine = true; @@ -101,7 +111,7 @@ Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) m_menu->addAction(m_quit); m_hideIcon->setContextMenu(m_menu); m_hideIcon->show(); - connect(m_showWindow, &QAction::triggered, this, &Dialog::slotShow); + connect(m_showWindow, &QAction::triggered, this, &Dialog::show); connect(m_quit, &QAction::triggered, this, [this]() { m_hideIcon->hide(); qApp->quit(); @@ -123,7 +133,7 @@ Dialog::~Dialog() void Dialog::initUI() { setAttribute(Qt::WA_DeleteOnClose); - setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint); + //setWindowFlags(windowFlags() | Qt::WindowMinimizeButtonHint | Qt::WindowCloseButtonHint | Qt::CustomizeWindowHint); setWindowTitle(Config::getInstance().getTitle()); @@ -145,12 +155,6 @@ void Dialog::initUI() ui->lockOrientationBox->addItem("180"); ui->lockOrientationBox->addItem("270"); ui->lockOrientationBox->setCurrentIndex(0); - - updateBootConfig(true); - - on_useSingleModeCheck_clicked(); - - on_updateDevice_clicked(); } void Dialog::updateBootConfig(bool toView) @@ -181,6 +185,7 @@ void Dialog::updateBootConfig(bool toView) ui->closeScreenCheck->setChecked(config.autoOffScreen); ui->stayAwakeCheck->setChecked(config.keepAlive); ui->useSingleModeCheck->setChecked(config.simpleMode); + ui->autoUpdatecheckBox->setChecked(config.autoUpdateDevice); } else { UserBootConfig config; @@ -198,6 +203,7 @@ void Dialog::updateBootConfig(bool toView) config.framelessWindow = ui->framelessCheck->isChecked(); config.keepAlive = ui->stayAwakeCheck->isChecked(); config.simpleMode = ui->useSingleModeCheck->isChecked(); + config.autoUpdateDevice = ui->autoUpdatecheckBox->isChecked(); Config::getInstance().setUserBootConfig(config); } } @@ -238,17 +244,13 @@ QString Dialog::getGameScript(const QString &fileName) return ret; } -void Dialog::slotShow() -{ - this->show(); - m_hideIcon->hide(); -} - void Dialog::slotActivated(QSystemTrayIcon::ActivationReason reason) { switch (reason) { case QSystemTrayIcon::Trigger: +#ifdef Q_OS_WIN32 this->show(); +#endif break; default: break; @@ -464,8 +466,11 @@ void Dialog::onDeviceConnected(bool success, const QString &serial, const QStrin void Dialog::onDeviceDisconnected(QString serial) { GroupController::instance().removeDevice(serial); - - auto data = qsc::IDeviceManage::getInstance().getDevice(serial)->getUserData(); + auto device = qsc::IDeviceManage::getInstance().getDevice(serial); + if (!device) { + return; + } + auto data = device->getUserData(); if (data) { VideoForm* vf = static_cast(data); qsc::IDeviceManage::getInstance().getDevice(serial)->deRegisterDeviceObserver(vf); @@ -688,3 +693,36 @@ const QString &Dialog::getServerPath() } return serverPath; } + +void Dialog::on_startAudioBtn_clicked() +{ + if (ui->serialBox->count() == 0) { + qWarning() << "No device is connected!"; + return; + } + + m_audioOutput.start(ui->serialBox->currentText(), 28200); +} + +void Dialog::on_stopAudioBtn_clicked() +{ + m_audioOutput.stop(); +} + +void Dialog::on_installSndcpyBtn_clicked() +{ + if (ui->serialBox->count() == 0) { + qWarning() << "No device is connected!"; + return; + } + m_audioOutput.installonly(ui->serialBox->currentText(), 28200); +} + +void Dialog::on_autoUpdatecheckBox_toggled(bool checked) +{ + if (checked) { + m_autoUpdatetimer.start(5000); + } else { + m_autoUpdatetimer.stop(); + } +} diff --git a/QtScrcpy/ui/dialog.h b/QtScrcpy/ui/dialog.h index 4fa05bd..860c87e 100644 --- a/QtScrcpy/ui/dialog.h +++ b/QtScrcpy/ui/dialog.h @@ -1,24 +1,26 @@ #ifndef DIALOG_H #define DIALOG_H -#include +#include #include #include #include #include #include +#include #include "adbprocess.h" #include "../QtScrcpyCore/include/QtScrcpyCore.h" +#include "audio/audiooutput.h" namespace Ui { - class Dialog; + class Widget; } class QYUVOpenGLWidget; -class Dialog : public QDialog +class Dialog : public QWidget { Q_OBJECT @@ -57,6 +59,14 @@ private slots: void on_useSingleModeCheck_clicked(); void on_serialBox_currentIndexChanged(const QString &arg1); + void on_startAudioBtn_clicked(); + + void on_stopAudioBtn_clicked(); + + void on_installSndcpyBtn_clicked(); + + void on_autoUpdatecheckBox_toggled(bool checked); + private: bool checkAdbRun(); void initUI(); @@ -64,7 +74,6 @@ private: void execAdbCmd(); void delayMs(int ms); QString getGameScript(const QString &fileName); - void slotShow(); void slotActivated(QSystemTrayIcon::ActivationReason reason); int findDeviceFromeSerialBox(bool wifi); quint32 getBitRate(); @@ -74,12 +83,14 @@ protected: void closeEvent(QCloseEvent *event); private: - Ui::Dialog *ui; + Ui::Widget *ui; qsc::AdbProcess m_adb; QSystemTrayIcon *m_hideIcon; QMenu *m_menu; QAction *m_showWindow; QAction *m_quit; + AudioOutput m_audioOutput; + QTimer m_autoUpdatetimer; }; #endif // DIALOG_H diff --git a/QtScrcpy/ui/dialog.ui b/QtScrcpy/ui/dialog.ui index 97f4f0c..569e769 100644 --- a/QtScrcpy/ui/dialog.ui +++ b/QtScrcpy/ui/dialog.ui @@ -1,13 +1,13 @@ - Dialog - + Widget + 0 0 1293 - 419 + 454 @@ -100,17 +100,34 @@ - - - - 0 - 0 - + + + 0 - - Double click to connect: - - + + + + + 0 + 0 + + + + Double click to connect: + + + + + + + auto update + + + true + + + + @@ -941,6 +958,45 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + install sndcpy + + + + + + + start audio + + + + + + + stop audio + + + + + + diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index d1716cf..8212b76 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -87,6 +87,9 @@ #define COMMON_SIMPLE_MODE_KEY "SimpleMode" #define COMMON_SIMPLE_MODE_DEF false +#define COMMON_AUTO_UPDATE_DEVICE_KEY "AutoUpdateDevice" +#define COMMON_AUTO_UPDATE_DEVICE_DEF true + // device config #define SERIAL_WINDOW_RECT_KEY_X "WindowRectX" #define SERIAL_WINDOW_RECT_KEY_Y "WindowRectY" @@ -145,6 +148,7 @@ void Config::setUserBootConfig(const UserBootConfig &config) m_userData->setValue(COMMON_AUTO_OFF_SCREEN_KEY, config.autoOffScreen); m_userData->setValue(COMMON_KEEP_ALIVE_KEY, config.keepAlive); m_userData->setValue(COMMON_SIMPLE_MODE_KEY, config.simpleMode); + m_userData->setValue(COMMON_AUTO_UPDATE_DEVICE_KEY, config.autoUpdateDevice); m_userData->endGroup(); m_userData->sync(); } @@ -167,6 +171,7 @@ UserBootConfig Config::getUserBootConfig() config.autoOffScreen = m_userData->value(COMMON_AUTO_OFF_SCREEN_KEY, COMMON_AUTO_OFF_SCREEN_DEF).toBool(); config.keepAlive = m_userData->value(COMMON_KEEP_ALIVE_KEY, COMMON_KEEP_ALIVE_DEF).toBool(); config.simpleMode = m_userData->value(COMMON_SIMPLE_MODE_KEY, COMMON_SIMPLE_MODE_DEF).toBool(); + config.autoUpdateDevice = m_userData->value(COMMON_AUTO_UPDATE_DEVICE_KEY, COMMON_AUTO_UPDATE_DEVICE_DEF).toBool(); m_userData->endGroup(); return config; } diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index 0eeea83..137e375 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -21,6 +21,7 @@ struct UserBootConfig bool framelessWindow = false; bool keepAlive = false; bool simpleMode = false; + bool autoUpdateDevice = true; }; class QSettings; diff --git a/README.md b/README.md index 3d226c6..d819158 100644 --- a/README.md +++ b/README.md @@ -25,11 +25,11 @@ It focuses on: [lowlatency]: https://github.com/Genymobile/scrcpy/pull/646 -![win](screenshot/win.png) +![win](screenshot/win-en.png) -![mac](screenshot/mac.jpg) +![mac](screenshot/mac-en.png) -![linux](screenshot/ubuntu.png) +![linux](screenshot/linux-en.png) ## Customized key mapping You can write your own 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 rules. @@ -122,12 +122,7 @@ you can [build it by yourself](##Build)(just ubuntu test) ## Run -### Simple Mode Connect to your Android device on your computer, then run the program and click `USB connect` or `WiFi connect` -### Not Simple Mode -Connect to your Android device on your computer, then run the program and click the button below to connect to the Android device. - -![run](screenshot/run.png) ### Wireless connection steps (ensure that the mobile phone and PC are in the same LAN): 1. Enable USB debugging in developer options on the Android device @@ -139,7 +134,6 @@ Connect to your Android device on your computer, then run the program and click 7. Click update device again, and another device with IP address will be found. Select this device. 8. Click start service -​ Note: it is not necessary to keep you Android device connected via USB after you start adbd. @@ -187,6 +181,7 @@ Note: it is not necessary to keep you Android device connected via USB after you - `Ctrl`+`v` _pastes_ the computer clipboard as a sequence of text events (but breaks non-ASCII characters). - Group control +- Sync device speaker sound to the computer (based on [sndcpy](https://github.com/rom1v/sndcpy), Android 10+ only) ## Shortcuts diff --git a/README_zh.md b/README_zh.md index d820594..0698199 100644 --- a/README_zh.md +++ b/README_zh.md @@ -24,12 +24,11 @@ QtScrcpy可以通过USB(或通过TCP/IP)连接Android设备,并进行显示和 [低延迟]: https://github.com/Genymobile/scrcpy/pull/646 +![win](screenshot/win-zh.png) -![win](screenshot/win.png) +![mac](screenshot/mac-zh.png) -![mac](screenshot/mac.jpg) - -![linux](screenshot/ubuntu.png) +![linux](screenshot/linux-zh.png) ## 自定义按键映射 可以根据需要,自己编写脚本将PC键盘按键映射为手机的触摸点击,编写规则在[这里](docs/KeyMapDes_zh.md)。 @@ -126,12 +125,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: 目前只在ubuntu上测试过 ## 运行 -### 精简模式 在你的电脑上接入Android设备,然后运行程序,点击`一键USB连接`或者`一键WIFI连接` -### 非精简模式 -在你的电脑上接入Android设备,然后运行程序,按顺序点击如下按钮即可连接到Android设备 - -![运行](screenshot/run.png) ### 无线连接步骤(保证手机和电脑在同一个局域网): 1. 安卓手机端在开发者选项中打开usb调试 @@ -186,6 +180,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: - `Ctrl` + `Shift` + `v`将计算机剪贴板复制到设备剪贴板; - `Ctrl` +`v` 将计算机剪贴板作为一系列文本事件发送到设备(不支持非ASCII字符)。 - 群控 +- 同步设备扬声器声音到电脑(基于[sndcpy](https://github.com/rom1v/sndcpy),仅支持安卓10+) ## 快捷键 diff --git a/screenshot/linux-en.png b/screenshot/linux-en.png new file mode 100644 index 0000000..2fde865 Binary files /dev/null and b/screenshot/linux-en.png differ diff --git a/screenshot/linux-zh.png b/screenshot/linux-zh.png new file mode 100644 index 0000000..c2769ab Binary files /dev/null and b/screenshot/linux-zh.png differ diff --git a/screenshot/mac-en.png b/screenshot/mac-en.png new file mode 100644 index 0000000..3793967 Binary files /dev/null and b/screenshot/mac-en.png differ diff --git a/screenshot/mac-zh.png b/screenshot/mac-zh.png new file mode 100644 index 0000000..c7b0068 Binary files /dev/null and b/screenshot/mac-zh.png differ diff --git a/screenshot/mac.jpg b/screenshot/mac.jpg deleted file mode 100644 index f8f096f..0000000 Binary files a/screenshot/mac.jpg and /dev/null differ diff --git a/screenshot/run.png b/screenshot/run.png deleted file mode 100644 index 93460b5..0000000 Binary files a/screenshot/run.png and /dev/null differ diff --git a/screenshot/ubuntu.png b/screenshot/ubuntu.png deleted file mode 100644 index 5ed1ffc..0000000 Binary files a/screenshot/ubuntu.png and /dev/null differ diff --git a/screenshot/win-en.png b/screenshot/win-en.png new file mode 100644 index 0000000..7a5246e Binary files /dev/null and b/screenshot/win-en.png differ diff --git a/screenshot/win-zh.png b/screenshot/win-zh.png new file mode 100644 index 0000000..29f3e48 Binary files /dev/null and b/screenshot/win-zh.png differ diff --git a/screenshot/win.png b/screenshot/win.png deleted file mode 100644 index e2399fb..0000000 Binary files a/screenshot/win.png and /dev/null differ