From 1b242f7a3ddadfcca53d203fa3ff39ab9b0c69b0 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 15:08:05 +0800 Subject: [PATCH 01/24] feat: update speed ratio script --- keymap/gameforpeace.json | 2 ++ keymap/identityv.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/keymap/gameforpeace.json b/keymap/gameforpeace.json index d06f0913d..11dd20ee5 100644 --- a/keymap/gameforpeace.json +++ b/keymap/gameforpeace.json @@ -5,6 +5,8 @@ "x": 0.57, "y": 0.26 }, + "speedRatioX": 3.25, + "speedRatioY": 1.25, "smallEyes": { "comment": "小眼睛", "type": "KMT_CLICK", diff --git a/keymap/identityv.json b/keymap/identityv.json index c93706df6..b73f3b388 100644 --- a/keymap/identityv.json +++ b/keymap/identityv.json @@ -7,7 +7,8 @@ "x": 0.700, "y": 0.410 }, - "speedRatio": 2 + "speedRatioX": 3.25, + "speedRatioY": 1.25 }, "keyMapNodes": [{ "comment": "退出", From b517b8e69991a55e9e4ab1ccd5487a6c1201cb50 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 15:23:42 +0800 Subject: [PATCH 02/24] feat: update install qt script --- .github/workflows/macos.yml | 2 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index f3b16b83e..105ba56af 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -31,7 +31,7 @@ jobs: path: ${{ env.qt-install-path }}/${{ matrix.qt-arch-install }} key: ${{ runner.os }}/${{ matrix.qt-ver }}/${{ matrix.qt-arch-install }} - name: Install Qt - uses: jurplel/install-qt-action@v2.7.1 + uses: jurplel/install-qt-action@v2.13.0 with: version: ${{ matrix.qt-ver }} cached: ${{ steps.cache-qt.outputs.cache-hit }} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 50dc8fd13..89ec6f829 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -33,7 +33,7 @@ jobs: path: ${{ env.qt-install-path }}/${{ matrix.qt-arch-install }} key: ${{ runner.os }}/${{ matrix.qt-ver }}/${{ matrix.qt-arch-install }} - name: Install Qt - uses: jurplel/install-qt-action@v2.7.1 + uses: jurplel/install-qt-action@v2.13.0 with: version: ${{ matrix.qt-ver }} cached: ${{ steps.cache-qt.outputs.cache-hit }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0bcfc8196..bfddbb2e7 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -54,7 +54,7 @@ jobs: # 安装Qt - name: Install Qt # 使用外部action。这个action专门用来安装Qt - uses: jurplel/install-qt-action@v2.7.1 + uses: jurplel/install-qt-action@v2.13.0 with: # Version of Qt to install version: ${{ matrix.qt-ver }} From 6e94d041403999a4f4e6cb1bcfcc4204eaa03e97 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 15:32:53 +0800 Subject: [PATCH 03/24] feat: update dmgbuild to 1.4.2 --- ci/mac/package/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/mac/package/requirements.txt b/ci/mac/package/requirements.txt index aa42ddfda..a08fd98b9 100644 --- a/ci/mac/package/requirements.txt +++ b/ci/mac/package/requirements.txt @@ -1 +1 @@ -dmgbuild==1.3.3 \ No newline at end of file +dmgbuild==1.4.2 \ No newline at end of file From deea8483d5492641c87a83430ef5c6dbf1368cd2 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 15:38:18 +0800 Subject: [PATCH 04/24] fix: run error on win --- ci/win/publish_for_win.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/win/publish_for_win.bat b/ci/win/publish_for_win.bat index bbf912ddc..5b561122d 100644 --- a/ci/win/publish_for_win.bat +++ b/ci/win/publish_for_win.bat @@ -103,6 +103,7 @@ if /i %cpu_mode% == x86 ( :: 只有在64位下需要这个 if /i %cpu_mode% == x64 ( cp "C:\Windows\System32\vcruntime140_1.dll" %publish_path%\vcruntime140_1.dll + cp "C:\Windows\System32\msvcp140_1.dll" %publish_path%\msvcp140_1.dll ) ::cp "C:\Program Files (x86)\Microsoft Visual Studio\Installer\VCRUNTIME140.dll" %publish_path%\VCRUNTIME140.dll From ca01b730371cd83bce810d727b22f81c5bf6fcba Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 15:40:06 +0800 Subject: [PATCH 05/24] feat: update comment --- QtScrcpy/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index 890ee431e..9f55a0f5e 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -90,7 +90,7 @@ int main(int argc, char *argv[]) MouseTap::getInstance()->initMouseEventTap(); #endif - //加载样式表 + // load style sheet QFile file(":/qss/psblack.css"); if (file.open(QFile::ReadOnly)) { QString qss = QLatin1String(file.readAll()); From b5a7b8b399ccb8895fa7b20a6bb6148cab57b0e7 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 6 Mar 2021 20:48:05 +0800 Subject: [PATCH 06/24] feat: sync scrcpy --- QtScrcpy/device/controller/controller.cpp | 18 +++++++++++---- QtScrcpy/device/controller/controller.h | 4 +++- .../controller/inputconvert/controlmsg.cpp | 6 +++-- .../controller/inputconvert/controlmsg.h | 11 +++++++-- .../inputconvert/inputconvertbase.h | 1 - .../inputconvert/inputconvertgame.cpp | 6 ++++- .../inputconvert/inputconvertnormal.cpp | 21 ++++++++++++++++-- .../device/controller/receiver/devicemsg.cpp | 8 +++---- .../device/controller/receiver/devicemsg.h | 6 ++--- .../device/controller/receiver/receiver.cpp | 5 +++++ QtScrcpy/device/device.cpp | 3 ++- QtScrcpy/device/device.h | 4 +++- QtScrcpy/device/server/server.cpp | 1 + QtScrcpy/device/ui/videoform.cpp | 19 +++++++++++----- QtScrcpy/devicemanage/devicemanage.cpp | 2 -- QtScrcpy/util/config.cpp | 14 +++++++++++- QtScrcpy/util/config.h | 1 + README.md | 13 ++++++----- README_zh.md | 11 ++++----- config/config.ini | 5 ++++- docs/TODO.md | 4 +++- third_party/scrcpy-server | Bin 33142 -> 34930 bytes 22 files changed, 121 insertions(+), 42 deletions(-) diff --git a/QtScrcpy/device/controller/controller.cpp b/QtScrcpy/device/controller/controller.cpp index 953a11d03..7b171a056 100644 --- a/QtScrcpy/device/controller/controller.cpp +++ b/QtScrcpy/device/controller/controller.cpp @@ -109,6 +109,16 @@ void Controller::onPostVolumeDown() postKeyCodeClick(AKEYCODE_VOLUME_DOWN); } +void Controller::onCopy() +{ + postKeyCodeClick(AKEYCODE_COPY); +} + +void Controller::onCut() +{ + postKeyCodeClick(AKEYCODE_CUT); +} + void Controller::onExpandNotificationPanel() { ControlMsg *controlMsg = new ControlMsg(ControlMsg::CMT_EXPAND_NOTIFICATION_PANEL); @@ -136,7 +146,7 @@ void Controller::onRequestDeviceClipboard() postControlMsg(controlMsg); } -void Controller::onSetDeviceClipboard() +void Controller::onSetDeviceClipboard(bool pause) { QClipboard *board = QApplication::clipboard(); QString text = board->text(); @@ -144,7 +154,7 @@ void Controller::onSetDeviceClipboard() if (!controlMsg) { return; } - controlMsg->setSetClipboardMsgData(text, true); + controlMsg->setSetClipboardMsgData(text, pause); postControlMsg(controlMsg); } @@ -226,13 +236,13 @@ void Controller::postKeyCodeClick(AndroidKeycode keycode) if (!controlEventDown) { return; } - controlEventDown->setInjectKeycodeMsgData(AKEY_EVENT_ACTION_DOWN, keycode, AMETA_NONE); + controlEventDown->setInjectKeycodeMsgData(AKEY_EVENT_ACTION_DOWN, keycode, 0, AMETA_NONE); postControlMsg(controlEventDown); ControlMsg *controlEventUp = new ControlMsg(ControlMsg::CMT_INJECT_KEYCODE); if (!controlEventUp) { return; } - controlEventUp->setInjectKeycodeMsgData(AKEY_EVENT_ACTION_UP, keycode, AMETA_NONE); + controlEventUp->setInjectKeycodeMsgData(AKEY_EVENT_ACTION_UP, keycode, 0, AMETA_NONE); postControlMsg(controlEventUp); } diff --git a/QtScrcpy/device/controller/controller.h b/QtScrcpy/device/controller/controller.h index 3a7ef0614..29f77f1cd 100644 --- a/QtScrcpy/device/controller/controller.h +++ b/QtScrcpy/device/controller/controller.h @@ -31,6 +31,8 @@ public slots: void onPostPower(); void onPostVolumeUp(); void onPostVolumeDown(); + void onCopy(); + void onCut(); void onExpandNotificationPanel(); void onCollapseNotificationPanel(); void onSetScreenPowerMode(ControlMsg::ScreenPowerMode mode); @@ -43,7 +45,7 @@ public slots: // turn the screen on if it was off, press BACK otherwise void onPostBackOrScreenOn(); void onRequestDeviceClipboard(); - void onSetDeviceClipboard(); + void onSetDeviceClipboard(bool pause = true); void onClipboardPaste(); void onPostTextInput(QString &text); diff --git a/QtScrcpy/device/controller/inputconvert/controlmsg.cpp b/QtScrcpy/device/controller/inputconvert/controlmsg.cpp index 27e56eb73..a8ba69d1c 100644 --- a/QtScrcpy/device/controller/inputconvert/controlmsg.cpp +++ b/QtScrcpy/device/controller/inputconvert/controlmsg.cpp @@ -19,10 +19,11 @@ ControlMsg::~ControlMsg() } } -void ControlMsg::setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, AndroidMetastate metastate) +void ControlMsg::setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, quint32 repeat, AndroidMetastate metastate) { m_data.injectKeycode.action = action; m_data.injectKeycode.keycode = keycode; + m_data.injectKeycode.repeat = repeat; m_data.injectKeycode.metastate = metastate; } @@ -105,10 +106,11 @@ QByteArray ControlMsg::serializeData() case CMT_INJECT_KEYCODE: buffer.putChar(m_data.injectKeycode.action); BufferUtil::write32(buffer, m_data.injectKeycode.keycode); + BufferUtil::write32(buffer, m_data.injectKeycode.repeat); BufferUtil::write32(buffer, m_data.injectKeycode.metastate); break; case CMT_INJECT_TEXT: - BufferUtil::write16(buffer, static_cast(strlen(m_data.injectText.text))); + BufferUtil::write32(buffer, static_cast(strlen(m_data.injectText.text))); buffer.write(m_data.injectText.text, strlen(m_data.injectText.text)); break; case CMT_INJECT_TOUCH: { diff --git a/QtScrcpy/device/controller/inputconvert/controlmsg.h b/QtScrcpy/device/controller/inputconvert/controlmsg.h index 380247d76..e45506020 100644 --- a/QtScrcpy/device/controller/inputconvert/controlmsg.h +++ b/QtScrcpy/device/controller/inputconvert/controlmsg.h @@ -9,9 +9,15 @@ #include "keycodes.h" #include "qscrcpyevent.h" +#define CONTROL_MSG_MAX_SIZE (1 << 18) // 256k + #define CONTROL_MSG_INJECT_TEXT_MAX_LENGTH 300 -#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH 4092 +// type: 1 byte; paste flag: 1 byte; length: 4 bytes +#define CONTROL_MSG_CLIPBOARD_TEXT_MAX_LENGTH \ + (CONTROL_MSG_MAX_SIZE - 6) + #define POINTER_ID_MOUSE static_cast(-1) + // ControlMsg class ControlMsg : public QScrcpyEvent { @@ -41,7 +47,7 @@ class ControlMsg : public QScrcpyEvent ControlMsg(ControlMsgType controlMsgType); virtual ~ControlMsg(); - void setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, AndroidMetastate metastate); + void setInjectKeycodeMsgData(AndroidKeyeventAction action, AndroidKeycode keycode, quint32 repeat, AndroidMetastate metastate); void setInjectTextMsgData(QString &text); // id 代表一个触摸点,最多支持10个触摸点[0,9] // action 只能是AMOTION_EVENT_ACTION_DOWN,AMOTION_EVENT_ACTION_UP,AMOTION_EVENT_ACTION_MOVE @@ -67,6 +73,7 @@ class ControlMsg : public QScrcpyEvent { AndroidKeyeventAction action; AndroidKeycode keycode; + quint32 repeat; AndroidMetastate metastate; } injectKeycode; struct diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h index f16c631b2..ee1c6da7f 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h @@ -32,7 +32,6 @@ class InputConvertBase : public QObject protected: void sendControlMsg(ControlMsg *msg); -private: QPointer m_controller; }; diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp index 5278cfb74..d5687591b 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp @@ -176,11 +176,15 @@ void InputConvertGame::sendTouchEvent(int id, QPointF pos, AndroidMotioneventAct QPoint absolutePos = calcFrameAbsolutePos(pos).toPoint(); static QPoint lastAbsolutePos = absolutePos; if (AMOTION_EVENT_ACTION_MOVE == action && lastAbsolutePos == absolutePos) { + delete controlMsg; return; } lastAbsolutePos = absolutePos; - controlMsg->setInjectTouchMsgData(static_cast(id), action, static_cast(0), QRect(absolutePos, m_frameSize), 1.0f); + controlMsg->setInjectTouchMsgData(static_cast(id), action, + static_cast(0), + QRect(absolutePos, m_frameSize), + AMOTION_EVENT_ACTION_DOWN == action? 1.0f : 0.0f); sendControlMsg(controlMsg); } diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp index 63924e172..91d0681c1 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp @@ -1,6 +1,7 @@ #include #include "inputconvertnormal.h" +#include "controller.h" InputConvertNormal::InputConvertNormal(Controller *controller) : InputConvertBase(controller) {} @@ -44,7 +45,10 @@ void InputConvertNormal::mouseEvent(const QMouseEvent *from, const QSize &frameS return; } controlMsg->setInjectTouchMsgData( - static_cast(POINTER_ID_MOUSE), action, convertMouseButtons(from->buttons()), QRect(pos.toPoint(), frameSize), 1.0f); + static_cast(POINTER_ID_MOUSE), action, + convertMouseButtons(from->buttons()), + QRect(pos.toPoint(), frameSize), + AMOTION_EVENT_ACTION_DOWN == action? 1.0f : 0.0f); sendControlMsg(controlMsg); } @@ -81,6 +85,19 @@ void InputConvertNormal::keyEvent(const QKeyEvent *from, const QSize &frameSize, return; } + bool ctrl = from->modifiers() & Qt::ControlModifier; + bool shift = from->modifiers() & Qt::ShiftModifier; + bool down = from->type() == QEvent::KeyPress; + bool repeat = from->isAutoRepeat(); + + if (ctrl && !shift && from->key() == Qt::Key_V && down && !repeat) { + // Synchronize the computer clipboard to the device clipboard before + // sending Ctrl+v, to allow seamless copy-paste. + if (m_controller) { + m_controller->onSetDeviceClipboard(false); + } + } + // action AndroidKeyeventAction action; switch (from->type()) { @@ -105,7 +122,7 @@ void InputConvertNormal::keyEvent(const QKeyEvent *from, const QSize &frameSize, if (!controlMsg) { return; } - controlMsg->setInjectKeycodeMsgData(action, keyCode, convertMetastate(from->modifiers())); + controlMsg->setInjectKeycodeMsgData(action, keyCode, 0, convertMetastate(from->modifiers())); sendControlMsg(controlMsg); } diff --git a/QtScrcpy/device/controller/receiver/devicemsg.cpp b/QtScrcpy/device/controller/receiver/devicemsg.cpp index e2bb28d44..24673ab0a 100644 --- a/QtScrcpy/device/controller/receiver/devicemsg.cpp +++ b/QtScrcpy/device/controller/receiver/devicemsg.cpp @@ -32,7 +32,7 @@ qint32 DeviceMsg::deserialize(QByteArray &byteArray) char c = 0; qint32 ret = 0; - if (len < 3) { + if (len < 5) { // at least type + empty string length return 0; // not available } @@ -42,8 +42,8 @@ qint32 DeviceMsg::deserialize(QByteArray &byteArray) switch (m_data.type) { case DMT_GET_CLIPBOARD: { m_data.clipboardMsg.text = Q_NULLPTR; - quint16 clipboardLen = BufferUtil::read16(buf); - if (clipboardLen > len - 3) { + quint16 clipboardLen = BufferUtil::read32(buf); + if (clipboardLen > len - 5) { ret = 0; // not available break; } @@ -53,7 +53,7 @@ qint32 DeviceMsg::deserialize(QByteArray &byteArray) memcpy(m_data.clipboardMsg.text, text.data(), text.length()); m_data.clipboardMsg.text[text.length()] = '\0'; - ret = 3 + clipboardLen; + ret = 5 + clipboardLen; break; } default: diff --git a/QtScrcpy/device/controller/receiver/devicemsg.h b/QtScrcpy/device/controller/receiver/devicemsg.h index 1ed0f22dc..1bcb40b2b 100644 --- a/QtScrcpy/device/controller/receiver/devicemsg.h +++ b/QtScrcpy/device/controller/receiver/devicemsg.h @@ -3,9 +3,9 @@ #include -#define DEVICE_MSG_QUEUE_SIZE 64 -#define DEVICE_MSG_TEXT_MAX_LENGTH 4093 -#define DEVICE_MSG_SERIALIZED_MAX_SIZE (3 + DEVICE_MSG_TEXT_MAX_LENGTH) +#define DEVICE_MSG_MAX_SIZE (1 << 18) // 256k +// type: 1 byte; length: 4 bytes +#define DEVICE_MSG_TEXT_MAX_LENGTH (DEVICE_MSG_MAX_SIZE - 5) class DeviceMsg : public QObject { diff --git a/QtScrcpy/device/controller/receiver/receiver.cpp b/QtScrcpy/device/controller/receiver/receiver.cpp index 5e282ef69..32d819d3d 100644 --- a/QtScrcpy/device/controller/receiver/receiver.cpp +++ b/QtScrcpy/device/controller/receiver/receiver.cpp @@ -44,6 +44,11 @@ void Receiver::processMsg(DeviceMsg *deviceMsg) QClipboard *board = QApplication::clipboard(); QString text; deviceMsg->getClipboardMsgData(text); + + if (board->text() == text) { + qDebug("Computer clipboard unchanged"); + break; + } board->setText(text); break; } diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 55ca712ee..78c516491 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -151,6 +151,8 @@ void Device::initSignals() connect(this, &Device::postPower, m_controller, &Controller::onPostPower); connect(this, &Device::postVolumeUp, m_controller, &Controller::onPostVolumeUp); connect(this, &Device::postVolumeDown, m_controller, &Controller::onPostVolumeDown); + connect(this, &Device::postCopy, m_controller, &Controller::onCopy); + connect(this, &Device::postCut, m_controller, &Controller::onCut); connect(this, &Device::setScreenPowerMode, m_controller, &Controller::onSetScreenPowerMode); connect(this, &Device::expandNotificationPanel, m_controller, &Controller::onExpandNotificationPanel); connect(this, &Device::collapseNotificationPanel, m_controller, &Controller::onCollapseNotificationPanel); @@ -159,7 +161,6 @@ void Device::initSignals() connect(this, &Device::keyEvent, m_controller, &Controller::onKeyEvent); connect(this, &Device::postBackOrScreenOn, m_controller, &Controller::onPostBackOrScreenOn); - connect(this, &Device::requestDeviceClipboard, m_controller, &Controller::onRequestDeviceClipboard); connect(this, &Device::setDeviceClipboard, m_controller, &Controller::onSetDeviceClipboard); connect(this, &Device::clipboardPaste, m_controller, &Controller::onClipboardPaste); connect(this, &Device::postTextInput, m_controller, &Controller::onPostTextInput); diff --git a/QtScrcpy/device/device.h b/QtScrcpy/device/device.h index 38bc16463..bbbf693c2 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -70,13 +70,15 @@ class Device : public QObject void postPower(); void postVolumeUp(); void postVolumeDown(); + void postCopy(); + void postCut(); void setScreenPowerMode(ControlMsg::ScreenPowerMode mode); void expandNotificationPanel(); void collapseNotificationPanel(); void postBackOrScreenOn(); void postTextInput(QString &text); void requestDeviceClipboard(); - void setDeviceClipboard(); + void setDeviceClipboard(bool pause = true); void clipboardPaste(); void pushFileRequest(const QString &file, const QString &devicePath = ""); void installApkRequest(const QString &apkFile); diff --git a/QtScrcpy/device/server/server.cpp b/QtScrcpy/device/server/server.cpp index 1ff8c026d..5616622ed 100644 --- a/QtScrcpy/device/server/server.cpp +++ b/QtScrcpy/device/server/server.cpp @@ -162,6 +162,7 @@ bool Server::execute() // https://github.com/Genymobile/scrcpy/commit/080a4ee3654a9b7e96c8ffe37474b5c21c02852a // args << Config::getInstance().getCodecOptions(); + args << Config::getInstance().getCodecName(); #ifdef SERVER_DEBUGGER qInfo("Server debugger waiting for a client on device port " SERVER_DEBUGGER_PORT "..."); diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 36a6ef43b..65e705ef2 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -201,7 +201,7 @@ void VideoForm::installShortcut() connect(shortcut, &QShortcut::activated, this, [this]() { resizeSquare(); }); // removeBlackRect - shortcut = new QShortcut(QKeySequence("Ctrl+x"), this); + shortcut = new QShortcut(QKeySequence("Ctrl+w"), this); connect(shortcut, &QShortcut::activated, this, [this]() { removeBlackRect(); }); // postGoHome @@ -294,13 +294,22 @@ void VideoForm::installShortcut() emit m_device->collapseNotificationPanel(); }); - // requestDeviceClipboard + // copy shortcut = new QShortcut(QKeySequence("Ctrl+c"), this); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; } - emit m_device->requestDeviceClipboard(); + emit m_device->postCopy(); + }); + + // cut + shortcut = new QShortcut(QKeySequence("Ctrl+x"), this); + connect(shortcut, &QShortcut::activated, this, [this]() { + if (!m_device) { + return; + } + emit m_device->postCut(); }); // clipboardPaste @@ -309,7 +318,7 @@ void VideoForm::installShortcut() if (!m_device) { return; } - emit m_device->clipboardPaste(); + emit m_device->setDeviceClipboard(); }); // setDeviceClipboard @@ -318,7 +327,7 @@ void VideoForm::installShortcut() if (!m_device) { return; } - emit m_device->setDeviceClipboard(); + emit m_device->clipboardPaste(); }); } diff --git a/QtScrcpy/devicemanage/devicemanage.cpp b/QtScrcpy/devicemanage/devicemanage.cpp index 6df58781d..fd2421d2a 100644 --- a/QtScrcpy/devicemanage/devicemanage.cpp +++ b/QtScrcpy/devicemanage/devicemanage.cpp @@ -139,8 +139,6 @@ void DeviceManage::setGroupControlSignals(Device *host, Device *client, bool ins connect(host, &Device::installApkRequest, client, &Device::installApkRequest); connect(host, &Device::screenshot, client, &Device::screenshot); connect(host, &Device::showTouch, client, &Device::showTouch); - // dont connect requestDeviceClipboard - //connect(host, &Device::requestDeviceClipboard, client, &Device::requestDeviceClipboard); } else { disconnect(host, &Device::postGoBack, client, &Device::postGoBack); disconnect(host, &Device::postGoHome, client, &Device::postGoHome); diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index b06794139..8966a7db4 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -14,7 +14,7 @@ #define COMMON_PUSHFILE_DEF "/sdcard/" #define COMMON_SERVER_VERSION_KEY "ServerVersion" -#define COMMON_SERVER_VERSION_DEF "1.14" +#define COMMON_SERVER_VERSION_DEF "1.17" #define COMMON_SERVER_PATH_KEY "ServerPath" #define COMMON_SERVER_PATH_DEF "/data/local/tmp/scrcpy-server.jar" @@ -40,6 +40,9 @@ #define COMMON_CODEC_OPTIONS_KEY "CodecOptions" #define COMMON_CODEC_OPTIONS_DEF "-" +#define COMMON_CODEC_NAME_KEY "CodecName" +#define COMMON_CODEC_NAME_DEF "-" + // user data #define COMMON_RECORD_KEY "RecordPath" #define COMMON_RECORD_DEF "" @@ -289,6 +292,15 @@ QString Config::getCodecOptions() return codecOptions; } +QString Config::getCodecName() +{ + QString codecName; + m_settings->beginGroup(GROUP_COMMON); + codecName = m_settings->value(COMMON_CODEC_NAME_KEY, COMMON_CODEC_NAME_DEF).toString(); + m_settings->endGroup(); + return codecName; +} + QString Config::getTitle() { QString title; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index 681b422b6..e0e7f5178 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -23,6 +23,7 @@ class Config : public QObject QString getAdbPath(); QString getLogLevel(); QString getCodecOptions(); + QString getCodecName(); // user data QString getRecordPath(); diff --git a/README.md b/README.md index 6427b5d28..9a3327b1e 100644 --- a/README.md +++ b/README.md @@ -194,7 +194,7 @@ Note: it is not necessary to keep you Android device connected via USB after you | -------------------------------------- |:----------------------------- |:----------------------------- | Switch fullscreen mode | `Ctrl`+`f` | `Cmd`+`f` | Resize window to 1:1 (pixel-perfect) | `Ctrl`+`g` | `Cmd`+`g` - | Resize window to remove black borders | `Ctrl`+`x` \| _Double-click¹_ | `Cmd`+`x` \| _Double-click¹_ + | Resize window to remove black borders | `Ctrl`+`w` \| _Double-click¹_ | `Cmd`+`w` \| _Double-click¹_ | Click on `HOME` | `Ctrl`+`h` \| _Middle-click_ | `Ctrl`+`h` \| _Middle-click_ | Click on `BACK` | `Ctrl`+`b` \| _Right-click²_ | `Cmd`+`b` \| _Right-click²_ | Click on `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s` @@ -206,14 +206,17 @@ Note: it is not necessary to keep you Android device connected via USB after you | Turn device screen off (keep mirroring)| `Ctrl`+`o` | `Cmd`+`o` | Expand notification panel | `Ctrl`+`n` | `Cmd`+`n` | Collapse notification panel | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n` - | Copy device clipboard to computer | `Ctrl`+`c` | `Cmd`+`c` - | Paste computer clipboard to device | `Ctrl`+`v` | `Cmd`+`v` - | Copy computer clipboard to device | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v` + | Copy to clipboard³ | `Ctrl`+`c` | `Cmd`+`c` + | Cut to clipboard³ | `Ctrl`+`x` | `Cmd`+`x` + | Synchronize clipboards and paste³ | `Ctrl`+`v` | `Cmd`+`v` + | Inject computer clipboard text | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v` _¹Double-click on black borders to remove them._ _²Right-click turns the screen on if it was off, presses BACK otherwise._ +_³Only on Android >= 7._ + ## TODO [TODO](docs/TODO.md) @@ -241,7 +244,7 @@ There are several reasons listed as below according to importance (high to low). All the dependencies are provided and it is easy to compile. ### PC client -1. Set up the Qt development environment on the target platform (Qt == 5.15.0, vs == 2017 (mingw not supported)) +1. Set up the Qt development environment on the target platform (Qt == 5.15.2, vs == 2019 (mingw not supported)) 2. Clone the project 3. Open the project root directory all.pro with QtCreator 4. Compile and run diff --git a/README_zh.md b/README_zh.md index 1973cb95e..b16147a74 100644 --- a/README_zh.md +++ b/README_zh.md @@ -193,7 +193,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: | -------------------------------------- |:----------------------------- |:----------------------------- | 切换全屏 | `Ctrl`+`f` | `Cmd`+`f` | 调整窗口大小为 1:1 | `Ctrl`+`g` | `Cmd`+`g` - | 调整窗口大小去除黑边 | `Ctrl`+`x` \| _左键双击_ | `Cmd`+`x` \| _左键双击_ + | 调整窗口大小去除黑边 | `Ctrl`+`w` \| _左键双击_ | `Cmd`+`w` \| _左键双击_ | 点击 `主页` | `Ctrl`+`h` \| _点击鼠标中键_ | `Ctrl`+`h` \| _点击鼠标中键_ | 点击 `BACK` | `Ctrl`+`b` \| _右键双击_ | `Cmd`+`b` \| _右键双击_ | 点击 `APP_SWITCH` | `Ctrl`+`s` | `Cmd`+`s` @@ -205,9 +205,10 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: | 关闭屏幕 (保持投屏) | `Ctrl`+`o` | `Cmd`+`o` | 打开下拉菜单 | `Ctrl`+`n` | `Cmd`+`n` | 关闭下拉菜单 | `Ctrl`+`Shift`+`n` | `Cmd`+`Shift`+`n` - | 复制设备剪切板到电脑 | `Ctrl`+`c` | `Cmd`+`c` - | 粘贴电脑剪切板到设备 | `Ctrl`+`v` | `Cmd`+`v` - | 复制电脑剪切板到设备 | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v` + | 复制到剪切板 | `Ctrl`+`c` | `Cmd`+`c` + | 剪切到剪切板 | `Ctrl`+`x` | `Cmd`+`x` + | 同步剪切板并粘贴 | `Ctrl`+`v` | `Cmd`+`v` + | 注入电脑剪切板文本 | `Ctrl`+`Shift`+`v` | `Cmd`+`Shift`+`v` 鼠标左键双击黑色区域可以去除黑色区域 @@ -240,7 +241,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: 尽量提供了所有依赖资源,方便傻瓜式编译。 ### PC端 -1. 目标平台上搭建Qt开发环境(Qt == 5.15, vs == 2017 (**不支持mingw**)) +1. 目标平台上搭建Qt开发环境(Qt == 5.15.2, vs == 2019 (**不支持mingw**)) 2. 克隆该项目 3. 使用QtCreator打开项目根目录all.pro 4. 编译,运行即可 diff --git a/config/config.ini b/config/config.ini index ba116547b..6b85845f6 100644 --- a/config/config.ini +++ b/config/config.ini @@ -10,7 +10,7 @@ RenderExpiredFrames=0 # 视频解码方式:-1 自动,0 软解,1 dx硬解,2 opengl硬解 UseDesktopOpenGL=-1 # scrcpy-server的版本号(不要修改) -ServerVersion=1.14 +ServerVersion=1.17 # scrcpy-server推送到安卓设备的路径 ServerPath=/data/local/tmp/scrcpy-server.jar # 自定义adb路径,例如D:/android/tools/adb.exe @@ -19,6 +19,9 @@ AdbPath= # 例如 CodecOptions="profile=1,level=2" # 更多编码选项参考 https://d.android.com/reference/android/media/MediaFormat CodecOptions="-" +# 指定编码器名称,必须是H.264编码器 +# 例如 CodecName="OMX.qcom.video.encoder.avc" +CodecName="-" # Set the log level (debug, info, warn, error) LogLevel=info diff --git a/docs/TODO.md b/docs/TODO.md index 4d3ca811c..8447d77c9 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,4 +1,4 @@ -最后同步scrcpy 3c0fc8f54f42bf6e7eca35b352a7d343749b65c4 +最后同步scrcpy 08baaf4b575aef7ee56d14683be3f4e3a86d39aa # TODO ## 低优先级 @@ -12,6 +12,8 @@ - opengles 3.0 兼容性参考[这里](https://github.com/libretro/glsl-shaders/blob/master/nnedi3/shaders/yuv-to-rgb-2x.glsl) - 通过host:track-devices实现自动连接 https://www.jianshu.com/p/2cb86c6de76c - 旋转 https://github.com/Genymobile/scrcpy/commit/d48b375a1dbc8bed92e3424b5967e59c2d8f6ca1 +- 禁用屏幕保护 https://github.com/Genymobile/scrcpy/commit/dc7b60e6199b90a45ea26751988f6f30f8b2efdf +- 自定义快捷键 https://github.com/Genymobile/scrcpy/commit/1b76d9fd78c3a88a8503a72d4cd2f65bdb836aa4 ## 高优先级 - linux打包以及版本号 diff --git a/third_party/scrcpy-server b/third_party/scrcpy-server index 73d292aa873c3c39d107c223603b221f4c4ec46f..ab38830e678d059bed1aa22dfb5475f9d516d35a 100644 GIT binary patch literal 34930 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 C4`KxX delta 32688 zcmX7O1ytN@*DX%*7K&@3Sh3=6#l1*zeQ|diBn8^yUfkV^yF+nzm%-hcDL!-g{+pF# zC0S3Bz0Wy&Co4HOaY+Ayk;uO)A)_F?PH+CVLXaN)m5_$`y7799ATc(S2uMm$<3Lt_ zKQMLLAoR`h zSAoGT7;u;6cM(wHoy};Xs3d#arlJr8no_4VM#(UxHVl!wLfY&RzDF37Z9~S}!srO* z1QBU1{ro~%ny*B8qQ%x|Mh3WOWCto(g@Q51O|8_1JR-P*Xm^q)S7f9fRm+vNbO?%N zFT5AdsI`pbw(lNnD=Xgcg=zk=|D#v;p^fMSOWSr8%C4W2=M+XRt*oidT`}an^3@7Dd5G?wn=}_5Tj(FI+KKEc;6&7d@=rs_7rHu&?&xH1|?dV z+MUsA;gp}QE+%>ElX6AF=OJC0vH6Yxe}a7$XFIPO3-7B)xX}+J2^zQt8-kR3^b*j; zd|PVCq8ahvnOnPzY9a#}o_BnUX~srxJ?wkKpmfgtuE#%|dR<9{D`VJCZ_NJ)4((Iu z992Z`UZX5Q+tz@)VHQ6f0F_&W+J1|`PK+hlwsOeT3I`!90pZ{zdxO;M|izJk#GoR^`iE&n3+ zPda@T{5qHZUfzmuxBKx0IX}F>;Y%j|hiTkXZI`VLRdhf`;8ctwKzJ(Tgu@}Psih{3CF_Y5_u%Yap5Mn84iDIZV&^;4Skv@_ zX@(l;?Hu1xo=D774tejWt&mIacZS7Ke0(H(Ce*zn;6<05#_&Ls$z?o3{v=8GCWOp| zy#>MWja4vtkB|%AG8PpAUdT{S$rjcX;XM@vDjabUSz2rw+alNym_}Acf9Jy4f_#Sf z)I0V%f_ykIt`v&|VK6w{6ipX#F^IU=?27&#)j~x2H(px^=4V7hBqt<;9;q$IEAC;R zRGZJK2t>h@Tdr3W_bC48F+tj~ZwW(sO)*=L91!k;M7Q*=IPdv~LGPDP*-@85pj%Eg zs3N$yA$Pq)Tf%X`KN5A^VN@eYwP~#1A&91LNYH+T%$QQ7zF|k z$cQO@3*i}J_4lcuNA4T+h#rJ#gesKgV7XqiEr0-kVr8OzlB8@QazMfhTI#jlVvK_j ztuhN>KoGfm3|&xL5Z6)pLKu6@t}yP&?{O?TqEFFLbKgS{2~BxYv58)(LAR(}-ilxv zqh1DgBZ7r(2y%Ik5F&czT`+VpF3`|>Z@1X4aPBcmz2D(wqD1sad`416Ar2DU(!Toa zgAY8T`Xi@PG6<1E5Y65qD)oSBkVP=WP<#PNQ6#M1$}O2I-h0GnB&5r{?BGO6wa+A4 z-`yoh=D%Z@a;0Ke1^I5_*Weo>F9v74j-{%>IYYe*k?YaiLcOwfA@RqK`R?2c?iKHK z?(wC5K&6zlo+kVqJSQpcLR5;fV2VwG2QUONo05fn*V`g<5f?y85SI`~RqQeSio=d@ z+k;tyyN-B){M56%MgJOj3JTQj?|XRh;&I4rL9kr>`4Ib_z$+mEG$G)}4YH>xX$!gt zq8N%?PwbY?))R?saMRZNX=-d_y50v68fSBY4 z0^&5{@9%-8*r`a1L5^F@SNQjDUT=joRWgjngs>su{Fy=-(Y@Er1$h~b3TY|?ucvv7 z#O8tYg!2epHfY53Lkpq^3m zzF*TIX@B>7h3t1FEH4(!h0YjcyhZU9>mZ1wSIC7!03jPSwD;W=@-Ph35;vmv>t|F$ zoW+pNt+zf{uNgpq@=l`O^eE&~m!jVGENofTymLUu`+nG)vE{s_18fCtry}ZyAWc*L z4yo_a{Y+_ysr@~pr_+?$2dx893>A*F={zIbOY>SauQA|q;cme?Lx>IW?op#Tua;($ zKor1vt(?yo<0z56iZ1BO*i^`u-=})Kw}|6zC@&AD}V|? z?~~vyMI{b;FvVyg`-9s2{pmGH?*&jc(Q`t&g9~1z1_Ivieh?si9}<|06ot6k%QKBY z6jISMVanoz*@4l8DjsA`14S=H9etGz_Ixn&Yp#(*&^OVjL!4hRqYpo$42&#Cs6~3Bx zXNY*gCB61r$gg6ddPaUmOMfBX$JGyF&&7WAfgYRBq|!Z>Tc2u({+Ck9EgBy(5p-j; z%aEzpA_vo)i!))pMPUq@lMp1qU)pV z&H?EzUGT}O2Gs#|t;eed`3$AK$EgPW4D~f@E(oTps;ROB#eF}R+09NAQ4dOuUn#7>WKZ#ME11S2#?^GMn)~YFW>02(Eqa0 zy%&7J3`Fh@5%1wN{-}OWz1)M`(S!a>gYhxs{WNxAFi#vVmjuI`?|?%ba(W0+F1-zE za4xS6@@lYGuIQT}!#MOCN%ZM=9!RLUZ#~c$gAyf)rwKEWR8eJeNq&FVn8xuyFA3tB zCdfnt1=X6;zZw$)-s_XHZ|mI^lMl8CZZ?A2CnNp>xa&(p)DzOK*S}*SdvZAG;08X7 zJ6agUyT1i%JKJ(y25_0;yWnc!;JR>2S#$kKigZV@TNUgV?i#yRvedX7fVhq`DB4Hp z9mFz0|Kve_n<`TM*-324G0wOK|APF(3j_Qt2W7Ek{d5aPVDYIsrMDA}YoqXiad)$W zG7~82ZtQ%r$(z_`}xm@4D@JC@|i-+EP^%&CHJro=ma| z8sRoEw|0*vr)hT+=G#_t{mJ3A-`#%?wppjRv)UEIyc61m;anI*3ww5}mh>08m$*`M zIT^b<*#70l4*O>1A8&_y|DgU?}xC|`64tfp+n+De7loz|t4*S8riCI$oMGx;5!S43y`sVvu)Ds4Ei+=me)@Kc56BCIH&vqI$5$TN2B0sBX5 zkVku#`KWIYa|JYxI72LGPVel~)IimZp~K^kwJ5-E2G-)Y0`p0wXUL+jve!Epz2!mF&T<~(L~Z(Ol_J#Mu+Lpd0*3u}RAU9>!Twz6^W4<2^TdTz29EqNBTR+S3t z>s0Rq_7wUyR!mlVEg*V{n~uWuby|&1%lno{;f;eEvkiBrhWkJkGrT>@FLMOBJ!iQZ zSiAHr<}fm!eAl_e-+JQ}IR>zg}hHZeM6O zkn*B&@C7lY(iIrmH(eKZ4d8a_sv?hm;=c!5tW%N5@RRd{Plcn~ah{!H2Y>o?x)S#W z#Jb+O{UqIS(`lWh*mZNKZl7#n>;d4~yV)lZ!sr*TnBrM3PW^Vxss!Jv9m?vY8W>$f zvwP2WwPYANb#ct_G`HW>fC=y3rNFt_;VDKJ&4w3vh8G-$uH7Mi=8&$Q0689U^j))2 z!~Go4=WJD_6oXZH9(YyDI-Yc&e;xlt@-+|W5_fQ{^33!(JGn0)!xhgRC?fSrQd{1i za)e*lJhcCfa^JtTUwUcl*%#k66}g~D@xxfp$Uml;@GH0@gWbtY?T>Od_u|Ze_7!u6 zc$@4% z-8W)>`TpGphLdoY(q0CEiGLFFa8I37H=V~wL$mvAs;FX&yO%-HNl4;4Bb8X`Q~sLK z3!JhlF#J49O*z%i5O$Xi(KMBLA(wUJhHNJF&lX26PY$^zPw-``fLYk!cD8!9&&^U+ z68~y+K}YW?nx#9`MbN(kY*WZC{>AYDD;J>v#@|D!?8sYBo?=FT?-mq~72UBstQ2Kvu~#J9Wwi&6-gC2fsEIcr^;Iqm z6_W4zHLXDF*$Vuo0XJtnUvS+74r>fM74CnblSNP6+s!v zeYGsaAA2d_PJ7hJF*^44t+T@!QR}w1hW*L^#uL_VY38Y&rb1eb!YJf@cdAdHX!_Ui z1W6v<25uNmEN&@eLdk<~$Z-VUKbrNsr7cJM10oW(f~&*MGMgWl_}|~1Dkwd{*=MCT zK4w9A3=6wrFb#q7f!X3Obg>Z>s!zGpzx1yVEQ;c-3x0{%-1-j{m$0(sH2F@}r_1WI zIv1(au1!}b|EsR?rD{mu)Y9LyshtOj8C%@?+mJp=u(gw{EDUQ#i7X7$(nUs}RX1ch z0ejeV))9s(;$a<0un%0UhnAfQx9r6l@s+j}6YiW@)tu|p3CXw7hBR)xowScOEekvw z2d%4ikMsYPy$sR5NOl;9_>4*aI`)jGI|kFrX%b^Jr(e7k{I!X%CuQW2+~^T9d=2?C zaofF6r`uQ8E+3HXnp$~H;h%ZTIG7grA3%}PUS!#SAh>IHVsG2JIUFl<;&Fe^o^cof zozL|NEZKL1m`~}kmT2}I_zsh`X5$B#11`d<|HvLKDKkCXTpY>M^1owC#=K$_1n zr*C6?TVU~=u*Y)Lt=iaqRiB^;m41hpJ2!o}gVKadwzQ-4V_Ctlj7QS&KF_dKYdYH6 zF>{ zw`NN&n_rk6QX_x(G2xY1MNK*Ja%poSBbUjzU?r?*BF9*8iDG}m^2!?Ui1<0TWMmp6 zJohgBzgpJ9ZoLlKZtYpYl|pYW*({9zypMOQ-i$2yMYMuT$l&>@{*ot3?>fK0Ya!Rr zrp3#~l}(W0UqU)~O2msN`}w=p-*qwoS0h2YY>7@V_d%Gb2WI1YJ?TnmBF(RRWpyRh zDDYK1A%8z}IVitdYP=^PyGZVv7SAG$cBCI4U=uAbvlN-hdY;^O{@Ja0;9vZt33I37 z<(Mn+&8mJAPhC>HB%J~I%I0v~@W^i#H*x5@m6XQ_$h1|wEp7WufLoW7fOGn?f#?yR z61Dh$dswt41;jF4lGWkQ-;IgWzBq^ZC&E~7z6pJsefiB09?+-?%w9UJ>UhsjThtDf z+g8_(&C(kZpK#o~D}UGeb1=fltGZu&V!ndvpe6J;bo47cW2IxU3|caWJClPuwuM@| zgf<&V+dij6n=&)@ihzYSWsbAxyeY0IR!K&+cw08x`mAGs)^CoN_Gtz!9>`0P?y}y? zIsO)3tt6Ngb8`>$7y8BQIn!lv+z_-$>@l>>FK%-}kMsIz+88ur z)v9J8Xv6~BhtT8HHi!zUpN;I-@og^kpJJUtaEkWTXsdx-Rwe2~@`t#1az{4rH3zv9 zkH@?m2aZ_=xo5SKP?YxE8_{CB`9?-pN4P-p7q9AzPYesvHqRNli-3nw88-i;OCf2UA7&YVbt+cb(*#HlBsYa#^4}oR z<-8S4yC4wuiT*+TBhW6nHYFwOOr}ES1ZMZPY8NYB*fy`W^sSy#=5p- zsxXj9iQ33|@0Ql21dq+p`{`r06ng*W;=jJPtCm3FB>Zh+?1wiGjPXKnsjtXW$v%%l zlU#??iX3FV1yRIO@t0P#YhNWSs4OELN|f1Dg*G+a zax{Ckc+%cYWBoS;ra7#X2*2--q5cvWFL}aT7c=>OB|?_m4+xHL4x>;Op12ls?=Z0d zK!EB2vbWM>pE96h8^hwdL1z(IqY^X4H*}bKBM_RMCn`|IHS`Vg2NL=1NMTi<73!mp zs#MteV@;AyW>dvp+Nq$iDdvtb4LEw_U07?HOj2#!Y_WrdaZ;mJi4&R}m{W zBfa?2^6nsY-}a!TV%+}XExhJ9rLX0t1{n6`=^M_F5tEg&%r5AQEj*m?KXjx!oRLr( z3mYc#2odZuC~V4+n|1dHFTDN0mgqa;q~QF1e^x!M`{>&K{Y0)LS8SS`b!4V2cXWrs zo=2^)>`bD^uW@^iF4+QbrbR7l!L21M|7D0KYb80^JHkKKn9nVJ+}Wdr?1&@T7l@%! z8%%d2k4zVrhzZq58diK&!3vMuo?wHvgpvxn*inShbYzV#i$V0_Heienc3b? zuAG^d3jufIrR3wD7rVvOrUBKt*3ZRRLAV)?X@>;cd@-=!nb)G+7hw{R7ZT$JT_T) z&2Xf&vF(i?z2XVhKwa-JpeH-Yr;&50xIVOkuLe%p_lWD-6QzKBFRV|!UY!RYE;N~Cf~CopgTtxho@ZsRpRRH&WPo7UXUGQGv2Tuy(h(2} z6FzEhpB3*&88RAo&85CZp8@A_xEqP51>h>>b|2#ftOK_KkG#`hCrAnZsQBsS517~y zAp2;|Gb?u|uSF4=5^6B@#Nb5-Q!cpNIC}Kq82E5h9SkEb1gr|~cca$dyl@^p@ywcx zPG!A?QzizsD!?%dx@U(kqq>|6?r$i>W#Xx8S-WSS^hcpE%n^=Dx)2zvLee36__dx1 z+RK+Cp#Ci&QE*CLT9D^G6B85-Ns<+pv>nQoeXRcmY3=gDb}AG{N0)MerR3u6JWG)s zhi2T5&c@V}?ZZi6=6Y5T!e~gM@-w2W3A4S2{?ydZsn9!=OrUZmCx9YA6mm!UBFO%- zB-@ShC>PyTKJ%d0jrO<{73f3vVv#7GQ%MOdL_;*AVb}%KGxFUFN1>zvE$R=RTaS^q zYFv-;(U5gx+UC%VK2|(llLpDdDttZr%yIYTq_>W;~B|y3$z^DUn#(lJu1^3_eybmnt_aj_Aa&XR`i#r5{krL!4r3gdvPB*txHo78i^`&Pco z&NF&p+K>}bLX4zcp}$9yNZK^^i(%PX6cGZ}qnLi_v+SF@C-Eeh@H$sx`X@zdo?~c( zNLZGwtFhJ3(Ojj0`*`IKsAU}_v6}IBBI|RHaVcNiC`UahN9TWj+)KCvJjGh&F7vPj zGW+mjk;j0q_h+|?Lifk{Gez@&TLfbt0S2__pr)8EkDDo!R+a1~=|fS^ejT;#MSOM8VCs?CBs*TJ!W=9*3MU#X)Qww-WM zwP-$9v@v?R{6~MrNq@Et+)Tu2*t)LQ3A&~M>!ssZ%4x*%Y2MOlIhLa$`dvBaGf&E` z{^Bs|6ldptm7q(PC?iI8YeP@`NNvVtTYz?X+V?cH+_idk z{pvuS5kHv^_Q;@Y&{>gGi=*9l|Gm&Xt$Isjm9qCEm%Jc9kE$W~#Hzjb0pU;qsyMv; z^B8rLCn}X$Kn}$gAO_9sd;ynJthaNG+568GWwyrDPNc>@AJ_IV2DC)CpQvjlst?Gz zmW)E<`@l7Nmoqq8`*WS8zLKZzKOYh>Ezl~HMcg$Uwcbr$(F{Im5e|pEnDPoxf6CA} zmvE&oCqG>1lS6jd8>#1zQvs1ahTUR@{)HyzCpYKo$>=2m=%ND^S1Kt_L!P_;CJbkv z{cR$Cl;dqg{=;|mnbOJ=#|e~jVHB4e_Lu_~sFtowXef7r?CJxDK48(Ae1tS99{53} z!rd9|0t$~f%DLT$TsM0KHAO757k=*x8vTV1xlTVd5!x6hWGPZ`ffG-_0g@j*NZPd8 zr~lnu#V(5iL9vOR$d+mQ?{AvQ=3O%f%=al}_@B~rCFoO{^JcqBhvk+RJ$yp{)TP_BsMPDAr+*fz=dyruo~Br5#<3XMq(&P!-OaN zByk4j+C*bSuB1j?(jW6+<;&#$cZ~z~LL?KJ&64NpTkR($qVw5tZ1;q?d!6E(;}-vM zOwAnDx|8GZSnhZwu44ya?YQaD?gt05Fmp5GasLH{=1lJ~8!>=zZ&+jY=(y5xlMiq4`}TvaT0)@GVWM|dGFPsQ~UDZh#iyA}LW_z|kANJk(PF6B-o0KqqmW)tN&h|YjM zq0DrNr`T1B*k-hGtTq08a{et@Dn5HNl6dI?Tp)~2uzI=-;DR__rM7ammVb!0{RtP| zei5mqR+v)O0Oq{L(y4zUh9EO9ju1_Kx^XZv=0g=_dA~&d<4r*R%|6*Q*Gx?JUqQ^< z(=3*0>caBBUeHO6bGwBt<^9?(gjP$!k&Py@o$jq(MY%(B?9QF;X)T~n-IG#btdU?K zSAF8Ye>it?M&pKp)$bftC`S{83fCY}Y(fq#OvbW$rJ)(lVM-^HHk~(6X5ox_B)z&)+Ir^y2d^KW)G%Q7-l$eBy@@ z^M3sKzOS}z5_Q0If|%eYeE?>F5{BCq*0<3L^DD9H_h5f8_K0KgZ@i^e`{t{_!TuYw zj6y$oyQ~Vxm__r~o*kB^{Rly6DH{gX@cC5Hzn1Q|M4K6Lx|hglwG}w6)qMXc0!nKI z(iu%Q!av;4@_v_U`A<-F5w}=%F&-@U4YA&yebA!D{UppkrycFk)O?)Uz}?JtnDjzo z=!E{h8~uR$u-g%9L@3(Hx%Ot5dtZH_0pe*7(aWs3Nd7^#N!^9aY$`VjuXH;XX;hA3 z^zD+OG6_{Hj#zafc9`*EyZb<700^jxZOAs}AVs8A z^WZ=0R7ymqEcPlX==vBah<$EEZe0z-b{Y2tR*Aa!UreczwuF2_?QlSBKvzjy&a-R% z1^Vve_no)Ai)C8oDH_ow8S^Ua0bRQE8nH2tLacdc!e#DR>!#$935c+nJ^$f@EvUx+X!ElhhtH$HXHx<7U?4UXa192kQvrow z0GtZg2E%(&f#%oG7pcG$3jFpzSPA2OY>bh!-k%PO^Wpp0H=Zei9v)&>Dg_S`32As9 ztL0#KdzZ}*;)+6Xq&=Nik)7WY?uZ|6veqz+^7rpT8Lmj(|SANCUMSO%pqRo*X+WMcRH12gz%OvZ0t zX$0dE=CF4qP7=g|z{tmXq{)dt9VWd8-N4m-S@r;8X|3f>{lIZq`n(n5P@UDg&GGGZC!&k!lsJIn@+@nFFpGO zCEhoxjbS+g*}Fg81vdkKsK$fN<4Hd$db5l?YAl<*Ad?Vi{^1&nCFz^^Q#0-)NpNN* z-%0OT&xvbwO-gk@wZ>kbxGxZ?&!oDM{IaXEg2IDC^j=le-ij1cnCoaoxx6EQ|` zT%wlR?AG_b3iSE#pviT)PHVe@v}wuk$LK)cC#-|m2 zs+8yN$v;?YRx^4LY=XLQkR#Jb`57c!5wD-U5ot|Z8qoM)O>U4r<@%uQDBO0bq;~Wm zpoF>)WDC>?1qRE^;#GkT$(p=@!&bU(tmkv0mo6WliMOx4R}YZ_SObefdYz1F_M5+w zHN~0asx^EnyQJ31#^&8!ObX@v|KW*RJX5<(Xa{TSj`9)vN-1b^9VDk3telnP-(qM| z3z=ue3g9G!NNh)q*#JlWa+Q*MUSt8a zNE4Y&_C|tbv|e@a+GbDGbZO68J*KlzO(hWb=M!Fm;(Cb-7_V5PmzDW{I;BmNf6Dr*zaC0I_2~Q; zuqAJvVLP@>c2At%C6_m^w7ui{XAxt!ls_grxnM?%oJ6Kkk1lSiiVG<7{H648#XE2H zt#9L}b+5LgY2hEenH*- z2bUO&KRrE;%$&69b-{Kp@hB8<_N#LTiz8n~E&n-<2lnb_M6$4fenn`300$huDdWyx zMiqm(9iPI-*Ml3;?@n%T`c`cs)BciRrbJ2NHK~t&!as?I(Ja`#as2XPUHRz*zqLvw474HT$0GKvox_D^|B^2oO}5!_Km& z?$@03+B!GXbS$W1ka_#1aO^2;S8!np!2?KsUr{mTG&hywVGuR}&}Wfew-$0!+TOdO zI%sw`sEFTUr>G5)`W$|2_X#XxHBJf0)VWh{t7M)kkrSZqHlR6G>MGqFJ5?^QaJbFG zt zqBV}Id>0(o^4&WD?zjTEaYHvutN#)S4%9N5We%w|@eioUn`NHjM{2EA|LApB9=oGI z6oVToc6Q9Yb&!Q7YP$#Qh=wVS7ARPptYamV3X(ZirUodNn z8;GILkuRSrAf~4b(gnc@nry|n_$pt8T{}IGWhk{ERwwG<{M(GGSMGtzm?f0$!lM6V z#zJ#YXiDcqL^yIk#v;|1Gu9-*R&8}0$1#2H~GX1`Ee$PZ+InC`jRDD$v>Io zHirfl=x!qHJokj*!*e?d&8lj^={MO78hD3UruyGp!!9U#_tSv4Np|f6*EYECx01o) zQ~CZ1wvcIUq>qQvwW0GG6&0a6zsS?$C3{)Z3s*ZJMbbZoA9(D<=p2 zw4-ZSJL^o@WX?WSe#Sbs?w94W{etvEOguZCOlQ+Etg8}Jn4paEj2XhOc*p-pb#-Nbkb#ZrLx$@t4WOJIFwXDuY3#brVnlo;2% z3$6y-&F>=D_R$TX&7WEVKr@ffL;*v;g^v`KL+`prz8QG3Pr^j?9WWQPqDx!|4^J=! z{h(LZrSZ297#R9YPvXuF^$8+ND57*c5P3$&tiY3X0Gzwqykt+=#=@Mok#n+N!cK*>PD>YzK02`d_$kL4bGA*qAzt8-}5cbFLu&i zshNY+dpH)bBxR6wW-qQ4EO9e#3mG`XChwjtr?Z2j8_q$o!B2vR_=EwI4}ij7_sONt zZi{A!rWx!Rky{H%`h2bih0Yl-p7Z^JN2d#3PJ04KiO!Bv( z;HxPOwf-umu@kJjb_a~LXP{%VwthaZWs!6*@qTM41N2Ua3FnHl*;cg_ zRR|o(b_N@5-^~=O+qK!u)z2q*aXLQQ%oJDYT1GH5jGbqtWz7g z63gxmx|EEBj44n42iwyOe|~ezi&%T^Nk#&GuWry_ z<8j}xp{KH6o04!rcvH9AA=5$;=~e`B_+aG1L`{X0c49dBT#SdUHSbJ}r>)%A-P^^s zN#XVpU*fi%7)SiO|8&+PGx}df2)$hr4PVY?jhCe}-d97*fGPeNhV}*%-Wj6S211pF z0rqnU!z#DY>L|mi82kA+!)lF^!2*P;wE?Qqpk%ZYKemtTj(6g7p9~g(NnzS2^{58yzB@Z`BS`X2pqjryz~jqTVx#t z)rB{UvNoiJ<>(h{oAC#mTR+vt(AyohHt?!6+#0R^RB5nZrT_OKyjA~n)_7g|so^w+ zTD5e|`Rk5NfWU{%HH_=^#`{~8==LhumLliu$AKIIV5imIGSAI^-LcF)c+hC5n=*w2HREo&1UIy?kW~oq_L!BbW)MA;{Vv&{~vqR z|FQRv`yYFgkN;zDLcpWIJ^?1+8DXCgC*WabpLmUg75fBd%F8(YZ(Urh9RRvnZkvDp zp2Nugm3EMlaAUYvnv!r^_&>$}i!v|!c`C!|0{eL|!>WPds$g`(1vK;D8k9eUd~a;K zOe?fC(XZPZ_OHLy-lGb7=GCdwfdlQ!guE*{)P1ptDN=sqNdKI8gg@>UT$iwseZhGu z1**F3@p@qu!8^X1JOD7uIY2pI%43LZyCtTxSpsFWDUFI>ZbGsrB3?$%h4lZtw4rtq zd^AidiD575>3NR7oBjx2jTGT_|vEfGR|nY-bN_#?{9bZX-im^+W6jx`BWR;S^`XCMHrS3`v-%k2yLt; z8&zQD^dI(4E-|TxU5@(?rcyREgp#*QKS~zVSY8^^;Y4ydYUbLl$hX(`q9DKaNu{1f zVd1ut=g#v-(IEwDaQTkyqv^nSbUf z=wEk7)sgM;B%vKV}> z7QD!=h@U*!y_g|8Hq6+{Ay7JOLxE4!@>YMtcnGg{G>U(_*-m%TP1`Fjc+kz&E6#QR zEMP4DtrEM~tgoqO-Ch~LRC&bhgl&u#niN!hzPz%5175=Ur#oZ#&$rQVfpx)Vz>M|z z1_fSK3(@&_pVI%V!*G8WR#g9~w_Atx`5Oi-z#OK-c#jvcf18d1k4S}|puo#h;f)wD zI&;`kn)iPm3Tmr)r`|oxIJZygh~EwzL-n7Ks(bWVImy=OJBUbZE-B>~UKYPX!+5py z6R__9{c@_-WW@?gOR`#SEeMP)YN0+Oww-~@%SpM^n%f+@TTCz?OWqH5YLFGC_2`5Y z%_MCKd!>4(xX^DWc=C*m%)hq()honE9S_@rNq4b)9-Ji-LFx-SG5&n(FkUgW5$9&+Ryj!O`k!-7|NLf?5@cfUK@DJ6 zcQ$R!s&^2N^gnRA@JRj3JQfkDlj`=S%6{9uh;YDE^gjEy?rexdH&@Yl+3_O2uj0OU z(V1lS#{sobht6GBQ}W9k7S(AC4BzJQeon#)l02XPy4llL?GVy1}KDB?|AX&Q(48?2!|$vNf~K zMC|=JhH`dex~HeT0QN}m;p;Z}9mWxVha2mQ1;4)WI{PhoC9h6ei zmRyBFYZU7$@mHMF(7JKnQ?*Jym#g=UcBnj+-E&7k6U1|BEya2th({lkI?R5XhTna} ze4j+~Q(28_GDQw~W_EGN3pl!Iy?Z>G-K@+08n`&pS4lATDt(QoY@lkSlc5}`4L+&1zGbqB%5AaK{v4!Uz^0R3C4%JgkYZe66U`MX?vX-P3fjyLenv@Ty*{%9T zsNc4iyR9(Wq=6q5HY&y<&zB^uq--ihkYg{U47>T;#2pIG=4@;Q-KZ1y_1QkHSKMLj z_f~J(vimvth}Lu$w3lNq3dcqo-ZL#d4CNzj zA$E0_#jBaZ6Tq{?Wl3J=jHqPL`0!P-)(%KEqgiLE1nm%1rY6F@clQQ#N&nE~0=*C} z1x;EoeA?L+oF(Ryi1IUz=)2d08hII5&?4|ifz|r7? z4_%sjl$-yq$~c4+vN8uf$W0pk`nDTrlC6OPucQ7vFWZ;JIeJe()D@UMlnsORu(-A?4WD4+`51bMIQ&03j-= zm0lu!0yJ&+F4v6NzF%t2;<-iEeKR}!3XU{ijm!(sW~Fm7@p3ZzJnyZlER4P?r?M^* zv&K5_P1>N4ZzQ{3=eQ4A(#ZNCs+0UYz zieb*q@umE#3trSDYIi&Ls1Hqk`o8w>L{tRl#xd+MKk-|YQ7TzsOuwKxyp|L0;m*Di zTfz=y%n*H3@y{+pz|b>gMM>8vr@tHDI%ylmTVg(hiA-015r4@>T<`%3xwnxH;EMi9 z4UD~8xSOWSyRzwLztIh(uZnZ5q;VC5|L2Fzet~f?&}AYyZ)M$3tyXf<6*#Wep|t^) zT0r)H{VNlvi4QE5n_7D~v@dnY<=ySU+N9uK_!sGCK2ZW3qniUFN$lngI$pVW*0$&( zP&%afW~sDl>VWTd238MW6*r*lnP2%-fZwA3ZQyI8|4R2pBFmgpZkd{w#(YF7e0>K3 z>2a7KeZO=WM}UxT^%b0ASUJ-lmT!)H49uYkT}tR!@A?pcBeOAYA%1gSk^9I=U*5_; zcgP)}4bB2lJ9|C^Xni7@xG2ZlyO0J;v+t5BKXkhB)|n8_U6*EkJ+j1k+S4Os0>Bem z;`!2gecWt0W%fsIdz}i9hi?C)>*rbWWP@0z>jw4eCnEiF-ENuk$2*ejFH37p-X@D? zQTP;CU4r;kwk{<2N@+7!2IP0?NC1R7{*J7Q@!{Sp*ykFz6x(D@B51(oGCL- z;QCCRddvA&L^b#R`F!^pQFH8l%qSJe+^jh|5Cg$`IoT*Y=SmgN(KE$|dAnskrzLUl z08DkAUVC{d4Doxbo^fovtZeYvx;55LB!jt&__=GD>a0?wtG715*#lSxv^R;1?@U9p z^W#`xHCZ$FYxs+x2k=#v5T2Pe`K9P?60u3#W4bZJMn}rn%czzKPp5hDblJ^S?6#?3^~mL^Y zkg)$``z-76PTV~bDQzS3aNnMZr1B4+8{wl$lW^Wd@P;8-8{$5|jc~YQ0;MQ);2n9S z+TPS@y=mB!TCt_Zc=_eZ6gT#GL>N#y&k;XOeP(j@FZKs)Brt&BCD6KiJ3O$v4lI4% zymqhG!XJco&aZXs4$q+QmEz~z zhMEBF$Gon^@8;_G2-}JR^Md{dtzeS=9*>W$Z^W9tF4fPq!jDn5bTURRj|wBEhu`_; zFY%z~_cnyiC4Nuxe`ih$_Q`rF$2=ROzVi?EgPr~jON`S;<$U$k`Kte9-Y1Nsl(Ljo zl$X*CBit&KZi6$7XM^Zn^_5qm{pVT3P$FllyHQ^E^Lu0t|59gaN~EYrWu>6cOPQd! z&*R>P^L4ZSj#NrPN}4JYdxm`qy?i&mMd}4gbW~=in5&3>e`P2rwq%vOOQ>Wmgx9-n zl;0Fhk#Fz#VRgPJ{c4K*J}|#$@}7)=&EYDInMD;fE|F=LPp`&~lVvk|Iald#e6sr+N7 zem_p{rfxyqe=w&BRXHf7Hsr5r&#lgBLHcVP>A#i2rV7$!ir+rqdVWRBWs`P=s>%`f zRBG=nN6gp6MlACdP;OOk>VR-dO^nw;g1K^GJLw`6>3E z3){6jVb|`UfL&i5prWiy*IItsuK&$i{&1v&v_|YSe{16+4%*xKaI z*xF=vY;AJK|7dN}yDM>RawpYTJ4nAn$Nzq4)opeizprk+z45mVv@Kb%OTX(7L~Chq_Dfy^Dj&1rNa%p#ClZ0I zNa$~>gz~dD!S8!{2QTOe+mX;gjO_gG_CHiN3V0($Kd3M?hb0}JE9d9vRKhaP_?zlZ z`OS42lX=GDOG9oP)UKf4W7S@WTI>HzajJ7-f2m5EzM$t{R^r*%zk@gE*=or-itbnC zUYsuzsTcjIIw)RWPo_2=FZxLS_DZS>dVYzfL7YaBq!Chu!L)=irhjal_-kx7?S7m3 zQ`he|FOssoH1?gB0jBI5|Odh zf3g9DIuGMOsv0?KHwE$o-2yoQUr)4}K)?LFO;nj=`MB=#{I1exc`K7GyOJzVVOoB_ z1g)piqqX7Ld!%HFE&igC&1(Z_t%~oYT`G67MBY34hpNUE|DE$?!sTJqt*@D(W?i0X zr6m>mfCs=GOdc%H=vT$wwHeCg;kiatf6uL2boM2~WQ~B!VEiuqz1ARwt?=c$s3+=B zAU6O@Gmss=au1oF!y6csKV>7e@h_uFzt5WF|AwWfjlVTZW&G_((F4lsOZC=Xxr++h zy5r}UBDN2qe6}^oLTt`HvM zbB}p9lFMh-F8xfg-kBgad${i&3gq#f4%n4m%MRr5UFQ4eScT2fyto5i+yQUUr|-Zh zQzK;!(!NHun47aG%k}=*rd@j7e}=~<{{EhA1?((OTk2k(M)}Sr{u-ansdKAr?@gji zukJP3pKB729Q?(;#4u#u`Bn7HE^Y7CJCpgT8-;TZP(#uA%CQ4k8FrdoXx_^XlFn}t zt}Da*7<{tHbH?1XMa?Ll=G(jU`>yrQR7o?S8bTf_#`vOBpY}gUJ>8_8e?2#_$cgQ_ zQ1sF+y^n<3*%ZDy6)r)Vp$lbRQKr~h<2>-{3P!6Gexu!zFRMfxWxi>Zrf^GK9+?Vyc0Z{FO{qITxnT%4iRyYz32hwJyDUiFE+c@@YDWCc7y@0+T-(+b)HvD%iO ziy94Ux6)Fm$;((u9joFxe}+^F+qgpqin(sK@gckq;XMfNQT8PJ8>ye}c$|YwdC!XT zwSJfW-SqH;{m9p8@q7gWcF=Q!)!oT~u5qs7xhdyV5x20VJCtrIZM+EQP0U3!JfAPq zRX#gn<#X`3<#UOB{PIaULHQg!LHQ*8H|29sau9F3iyF1v1E^Wae`YOe=`Q_^U1?jn z(zcM-EH7GDRjO`XN#u#Puz6IN87PvrWqi#SQIc@1bclG`5B`6UcB=V`%s0FAv-NtX zp1M-&7?)o!?tC9nt5OZFcK%t-)y!dyseP)jQq9{&}9C>J^!(j`FicmPf=m6JDvOiQCq}g>(SohHPUKhAvS+?mSdw<)~ z1-9R-o}1@;7UhglIW>=F$QKTt5Q~Su$1LCVt@E0CRJ_}KqqE$7?USs4#n&FY;=cNd zqgUBpey!&YJ_A~XK7*1cr?PKGpQFu*zTjh|o2Jt=>u zEN|BbzJuL?bh${PNG+lW;U*^5caE1E1tr%nAKf}a2(eC&Gf;$6Ys?Xo**F=SySBrQY*L_V5jjrkSn9ZQ*+THrM zR>BkB;j#+I9Kw$|grBdeUg~md%<6Q{u2+ecQZUJHT->u;@*U#+@}0M!^_9xcW;+hE z9Ir>py3muFBz6HQu{;*2RDXMa8_M*j`SX%=>sUYqN&0ssq-~rLE%Wue&9`;Rf8RuT zbVeDx6I*u(s`0UQc)zs6fJ$tIZP5C{NPiqks-!jt=IwbifA)oo-bdcD zAU}-vGm86$pmj{e`V@rXknwAKEf)WS6UOfr?~@RULuQ_^?R^*Cy?oQ}I2t5c&V{pRv=TlApkvTVGQCh73Q1o#u&DH;p(@ZvF!M{u!oJ1e- z>ZKLP<2J|lM34KEJYQ3we>q&f=y<5xA^H(-gR_VG1wY(A(=O5WTOYUWchdK+z-!hT zllSQ7_B;mNC}YqB3%#L@8J?$m31%CCsFmT-R)&YqfL;*&FwUTObt}>5__J^owZCxf zo#z%T>gXB=OZVvd$$7X*kAs#OeLZ$`H1q^=wEe@Q0LEL_csRJxe|VT}&Ja#3@$ zzfbJAJ$irXB>EKftROy@#cVoTN?wm-@o{XZ6(7ewrNT50!}HsqwID&mDaOah*53e=OHimOufBRh}+F9=YJ^i@0Ri>^(0l#Sv`~k*LEWJ*~W^LnX-N}We{9cyRb{1@%)nMUml z=WNo#qVtT|9n#EK3VZL-zm2BPd66CHccOE)@nA++0GD4}e`RnbHOfb_&PNKRCd2Kk zY-af@Q>~;}evm0YA;2g1if&W+MAR4nr+v?%+&v7qXLLP~aQBCaM za+d}Dxx~qIf8}?=p6Gn%3#8R9F)ifjTq{3I*YS{@YSs4pf(o1IQE;6r-7h45run@n z{aY!Enn%afse7Vh>X&9rJ^txq`p0dLY zN8*-zAYcbNjf1ap)ahI_#j{M)*7d}~FN~cyh`@TvlMxZ_$hAlWa_L-aSTVO3*K7w+gc|)~u z#_p$+DE%|yj|8Rn>PjsYb-8L>t<^$hr&}|pUOT>GVMAR74Px(#)whgYU16oGvUKZW z)gt}+>#dCC*11!c%15WNK|VMSCmSnURw%u$e{0vS)GzKVQtY4}vYA@@!MB3Xy`XC? zjZrLN45ZUSu$HfF760K^if4jL!IkWBIsd5xlfh~}2vYj&uABPML`r{lQ(YZh3(sfk z;FwS8SJ7heou*o4*3mj>4sN=HHnOav>3p-!t5=y^sn$`9uYo5wU ze;;+eHM?fis8LpEDs@1p>0|5eubXULyqsEd)~$s@r|+*gwQJX2bQUhk>UNVgfBuS! zmDU2)Hhq(|@TQg4CG+Q4mrPw@T|5;00aUAW{9V3+I#5mBDRmX6RM1tNffbw9*U@q% zMMkfP>{4+E>IF})=&f=xhS3HN_H-R>e^R;>hYmaRW@X*j1--wpnyJim)dFF@;2`vk z;@sUk{!_l16}R_+rIb$X*spBC#a+9iYzpLF@lWjztsSy8XlfsAQZ`XAG*e{2+6 z9~E${%AuR6IS1qo!0G(K$8>s*|NMvlJjH)r#Gfx@bMGR!Du)dlR&jMj1(L%5Gz+cf zK3sBJ`jcGFjOVO{`)C{gTpx}qRJKEVMSYNy`Wk+jChif^k-@I$2GTM7q@&48 z^_9CpC-a{kXa`3V|NG(3uK(EM&qtg5*CdQfbM(*rzt;TFG!(Vx^XBuPe^dESsnVHK zC#T{5k|`(oR|v%}010G&D`NPWNL3hv~e;W8tUllD(bX%F{dfiPZ-Rn|fNI9B0_DX{u|+_^0JJ-$}Jh zSApiVLiXEDl~My|g4=Mgo;UcXUqYv$<(^6X;GVI*ZeiC`Xrz=Df2FVDUztqj*0_p) z^EsUkD^s!L2lu#$##cnf*FHUL#jra5R|#t4|GHWuhoS?rE>UgLkpO~UJ8U7b1>I$H z#kd6v7SwV7hL+n!otiQBs!uC64O?|!lhgGOdr*2%Yg=_AswJxl&S`D>^ZM3q7 zO3rplu69b>ORuZwf3<1s*gh3q>&Et~qh{uwbkt>c3d*fi<|gJYR$=t$vS94k=@k{@ z>MF(#TTofiYg|RIi|eMXfhFFU{_Xt*Q==VZg=pehbn9!$t;mike_B^ZZa`lT_1B`x0!mP4k-68v z;<}!30yNwXbdjw3BE7L@7B4HL9-dMkNuhf0c?~-R;3ftG4BpA$S_ZGf66%M(=7-5tC4qDeDMuA^jJ?b$5qa74;&@9$uP5!4^F zvma-h4y4{B2#oFhn|sI&yvovK`@`==vG^Vif6cWpa|8G;e(w4Ue1d;?czW;D>|eg? zby(vw<%Z3hp6hu8?W&2%NYCzfeVGKAitX#^>50tl?TO5Fs2!Uln>#w*CGFnc(E&3y z`8XT-J&~R{_P2}tBYupVRRQQW?W+5AKb;6X$Y#Nd?MJ_3h~n-Z1>O ze{?koB0%nS{C4jn_&MNe6qO%!?43PG?6VE-CWZS!;qDb~m2hts?%RcXn{f9y-XTN9 z{t30eF^JDo@Edqzi05%+{RtG8UleUf~F(L61}S+?^=NCy7U z{U|%7;m-$Bai7d3?gu_3{8-j;{Q3HA zB8OPHn?(7~2-o`N_Q!0x9>$y7#XDzsT~;ufLnQys9Nxw~wqIhr`+#tt=4bPw{5|Z? zYS+{J8*U<>BR2jBfB(7&yTmhjR!e#N-eKp-X$}eZN#?rQ*U9ocbN;y*&);FUf2oc% zuzVUse(c@>%og<`z5yn6beE7NtX@k+nJYwIE7%it)dIJktwqswiDxn;CcAt1du7EP z&|5{D-)8grZNeSn&lU-MJGq;MI|O~FaINpNhXh>++T&*Lvi<<;BNvn78oNDXLPFVd z`$9%O0O1cfc(_MAd$Y&xD`%Wrf4CnI?gPx71sC&tK*-rg*UwKdDTp2?eX5 zC>bc&_2%2#iFd7kTv5ly&CO)dS`imhcPpETd#!(ZGokAUZ6&K(ld>tdrnH2dYBkf@ zR^E`ue+NozM-0e{Ll1jq5kwO6Vq1(UC~08P&XD)D1|swEu~2X@ z8d?CqJr)o37UBm67tROq3+2P`JfFo*{9r5;9q9>eC!Sy=5Q`Bna|QzOJIS(OBs|dB zABX}6Lfhgay@72bf00n%7Ko||_V*6-$HI*3y@3I;At+~1Ok~5*5~^$}e%Wt{EHD^Y zWR5E!MA@9_S0F8>6~Gru$!`)G5ojsC*)KP6ojEEAgG z(J2@WoBYkt6PQMaYRQ?glmfcK(-rC*+(MeVLfwJENPHwb(j5)-hDO4D@lX^r74db2 zV!>#5Al@G(bzPyMa1bK9Mux)C_#i|#2~-~@>JEose{c$kT-v1=yMXt=_6!u5@B1#U zt`e5~pp=m71vbsQ8E~1Tqw`jXXk9a9CRw`jUT7yY5PEA#SAhaFb9x3EPAWN?ms-IC zKwZy$1wz(TfQzxR7mB${SIvBkYREfNI6|6@acvTYR4y^D7|mp;ON=wr)tQ@3|F}G% z_`4&4e=Q@i!GVGPXgoGD5bckLg7Hw-NatX8cPJY3kEZ0Ywf?lK>za<4CghBNC$tz^ zsjF;us1^B!K}f}j5txD@=m|mRMM*hyLnIu#6T;CbblzfHk^14kIj+M<@{`68n|rXp zjko$yXh7~@ULmxbNZk=qiPeVc20&4fH(jF*f20fuhM+7TD#@{wwo^`VKg?4(T@n6i z=(jYXVIqY);fL;4_(g~-9DykhKp4GS5kxS|U|%<^aKe3Cp+t~Q)SjWCP#-A4{;tqS zAhx|PNNz3_eODkHAL;Iof`s-VORh~A4aHzM*ad<<4r2U3V0)xLV9R*3h=vi;4;q|j ze^f-XRL~S_>{S-Ne=vCG!b!`+{k%(?!~K~7T}{jSG^*X;I1FFK*2SD=NjNG7$6c}s zf+)~ZI7YUF^E$}z%eunUiBLo^R7N!zX$okOBotsNIRm5*A{4)6#BAI^>R3hv()QMH z26PTYC!C7koX&!Ff%$u_A9N4M^=L|ye<1*}yavW#jg`oR5rl(sr9fB`p0tHkOxhqI zTO+U#3~e9j3IT@#!9ftn20I)KM}vdC-I35X2ho;jU?}7W$H8_8ks3QZ*c%B9#>rAd zV9#_9#GpGyLVeiRQOI4H_NQSSBe!5EX28&3$e@S+QW8KAknS5|Q1So|a8# z4`&p!HG(#MjM)k?W*j+tF;5_({H3A-$s|lcMmuS+DKw+CQ^hIL*$_mcf2rLv1QP)m zq3xu8UR-e|VDqPxcCy%s3|m<@7GT4;6d08gN>LsWP0J#60dhdr6H2O`T<1ham9%Cm zj!tND#>UvB6oiG)E+-@TT-6%V^^E37+E7f;Z#YY{kuXRMFttp>$&H+;rl8(q?c~ii zZbDTiTZuFWnmQRIWTHvFg^5MIqU2YD_$gAT^P}; zVTmlRMXce1nx-d#32U!O39W_xG*!iL3Rf|8J6T#77t0CZxFM3z(ye1kW;(6yR8xvr z(6&03fykwWiNpe^bS{pCOgp)$B$0E$f{lJ7bbMjcPMWVyokE&5e0TOOc{k z+p_VR6t}dK4Ogd_w>Cr#ebp3}ROS<`ylMhe!B>m7S^btbLdMPeH%QDa{?ZcRFv(G1 z7bXh#x08*f;z}7lr3I|e0x8{tkxWYjY`#?KibXP|2LlMP~13D}4&on8#+(tRfg zj2H-mQo0obb~~4qf1rq#E+fX4grN)?r&k$VoQ!v3se16K^lBqUP~37&inm^q;?~kb zf>YphQzBm7eHA?zGjVY_2vd+zn&__lmR#x4KM6(>sA{9NXn(J?sM{%uw~pRGAw?TT zrB#fbqHt;4PS#y3fpcy12FM4rNt_r3`8BJ9-G*xA4KoRce?`vdl>eME_$(-KVf*NgpbbMUbCLyVZX+1E~aBbGAY9Mc#q2Njr?FI`O z>hE^w+G-o$f2|^2Q$ZLe)`YHt1rAJFV#kF09T_;#C`T~P6gllIuRmux@Og4rxMXbf za*j^H0;V+pjL@0MgIhxFR(n`nr>3E;V%L))(Oiq+zE}pQ%q2xJ%#dp*bxt(zjd`dJ zkc1*spJsNWLU6RW)u%j1{Ko(_*>Ac(p;~1^P|94)A)`-T-L`FvtUI(pWM=hLfSJh*!g~e zU15frHWdrKJdef$1p}7iLGGgBofijUjazm}e?eo|`LZQuK-cAIG_h?#VCXw#W1Ow$ z7CVq(kT;pJq}GAuiHk8ERu_ncY(hp}*En!2`yxa~K_$3^8g|z+WJ1d%a=MQDGf<$FugV9iwRO8+q-$0FpheH^+2sGh{s5ku!BXx1A$;qU<*I*Aaw)% zcZH&S=eVyw+8cfZ3Duu=xM|#n)wur%d&!n0($2XHD#fBQEsdZS|9OGu^{|$vk zn&@pVdE)q3gv6n-72-$5u6SSzxhWnE^u<8@@vs-S>v4By{st(y-btrrtUj`$-Ru~B zDZ-IXkn55DsKxaDK2pu_c1ICEe-HOZB7uPz%6mNAZ5{Iu1o~iztz;CTZ4l~OD88P> zZ*g`%X<)d*Qx?+XL^{t_NUbvlPgF=_0VaQzB1(()#{+Q`U_7N1xB>wr%Wd2|fg{2A zxZ(%pnw-Q#6NO8VU*#7taE`%qbdL7RCOc6dQzt+QlgcPwj=(WV-iDd*e?wbK>-bmW zbqdF2jx_clOE1*ZYT)L$0)?`|?34R(c>+ZnuavBcYZ|1>O#8D!Ik0mab_yv@^w-Aq zX{=$+Q2bg3exs}ikT^BPYM;LlU~D4SA@!tcj{9YQ3N?nK6`M#oyO$8|BA&r`_sDIe zX)v=ZqfKV4+b81Sc9Wkhf3{-ynKvE<0hSJtDzP;@!aCUwvEeHXb;B=ATtne*1YzPC zVkaZukC3vVa6|-#fov;ywuVXN5T2s&e}IhuHiC)q5OFBNvfw8#;8?*3qrwdE#03CD zV{AD(5uO&0U=Z?12uVZ3kYpIc!;BRsG-wOUp?(a^?V+Ll(E+f^*h1(v)BPVjE zLR@kZ6fc0%2U27aM8|U3EFP?Y9`mnttL)OiUyV}_rS#6P9T5`S6e<|z3g&Q!WWgaEF8M8j@ z+;<^O7L{hK4?FD^vdj@vI6nisMHGti`r<6yqmC?dZXxVA#LW7@M0i|J|Re_M0rkohhBN}YoYj5}(q3EC=C?LEL!hjrb7qwFarp`y;UMO-z$=Tr7b74P>gG z$faRvG6hI}(m_1v(TUf-j6>=Y^R9Z;CW`j!$RZmPSEfwN1*R9~7z&bu6W8U8A;wBo zB7bgTkm`h%O37J68IUtd${>pi!=fcscxy>tH_Fzze`Et_pY>KxG3}I*Q|k?8dF_<- z^ zZKXgUf1k-x9_UgM`BMUvfJFi?F0qL;p>)=qhEjQen>d}*ahSEC4;Yf~HCQBJcC8I2 z9i$|UP9!i6x{^&Iz&S?*lai$c7&KEjWxa*R=JN4pRn-z1K@wBUe3P4O9Pcl#EU->X z?HIh*WLSH~FkE0pBNPU1Qm_WbR#HhRFyb1Lf4Fg$Q#jZ8dYn`z@$!{6ZE>uO+d7Cx z*Xm-j{37&9IP zPoznsGL;1x?}N#ERNQ4?gNQWEk1%-5%2F|6+*RUT$be9Lo>4aExksTvqyiz6Uk|&@ ze?xrC;X0}uYTYNNp&??#`nZc14#AJt%GNQ}Ry_)xwL8zd#f4!}3)dJ(xfeiykE(j% zbxyc=H8XJ4dA1rUGRc9QsHB&NK`_Wt^e&)_eSq&2>bMU5c=a=!u_T~z9!gZ0r4wqo z0{zpbVvt4P3+A;mMlj2WG>kb!*=kU&e-p`uObue3y0eiX7EqVVSwz7ZNob0zNyexW zaU%6i*P;-UNp+r%34%|A*<6W!>}_N%=$;fz@kz&S8*840XPaz{!4}5B$1Y`5&ZSJU zEbkZ0wxwnfi^4(3gs#pG+7wlYvPaw^XUJgfaGc!o^o3&*)3c3tQe_|Vd zR~w0cLVi$HVB=gvXTS|!64Lt7Zy;#MGM5vL69K%CIEankKr6c3-s+RvWLfe+5j&3iJkH9cHo`z=XxxXiZ!w5fOP>(ZO;^>|#-D zD^oN!f)j0$P5b@2YTcqEl`LwEA25}(KxT{;+$&FYXSx0WFEbUGpD&A9g`k=;+rZ-q zc%?BDCd0_bg4rjJ5RAkDG%!AGfXMMP0wz`5S7rGWJr9Unn5lz2J?F!tQRb!ur}`(882a&ylxQa>k81eY2j!_Y$2{-f5gT$-DF$@ z-(;L$3g8xq<88^z~OhQ+kqNmiA@KCH4Z zEN4u07w(WU%EC358!+1#+zw-tZ;E@ybQzYn<>R8%%5iq!!%f8Ue{tF9!&X*-2Lw%4 z@yQh8hR`^vK*+Ro)i}FECB{z~fBuP3mDH;1xCz3B!~_&4sY&={NI9 zmdr@)(6=t82>|d;e_CuUkGR(0!-RP4$Hhn)f4~G#Mu?s8B>`o0Dqp;^y$yxb ztA@~twy6Y7F4D-*?&%x)*^}#d*}FO=gHMmDlXUKAT=2V zM@ne?rKyG0eMQO=h(+K-y(^~0DiP!o?oP2;03q)KK!71sm+r)EK*vR6(vS}0hN_%$ zBFoeKZYpu3U_(%JY<*guS~r?C)|uKmvG+{M(tMr)B0kA@)Ap?5POG3=+!<6b)fr{d zSw_@Ve^;@A^-$5do>4wQbuV#$T7u?zrqQ8~D*gDTK|VpFo0TT-e(@{!sAtA2f&OT4}0 z_UiJam_*!cW3>VyD7Q2zM;{hYg&y0aD%07_e?yk&NvO;^Sqxr1THUB1c!hFG(Hjb1e3YxUxzU>6t{WT_G4XZuBa)@5QB&S&J!J74~B ze`Sd02jy;jB9PP}EV!Uc78wPWUm1ULkW?Fmd&Dk$$`EKxeyd1DsjtG|lxhg;#>g-I zF(yBV7du1wZBe6mxn6A)EZ0emQM7#b8IJX`A=b;yhP})!b?b%IC%>AlH|ATci}N_g zvZle9Z^V*iMoG)okqgMo-C|F(E|0Y~f9NU}_lmVK_GRG;0t;{&@K{e)A?yl= zac0Mn$N5Zc zFk`4=8PHxpI*4~;gQ$x=CfkPqN-f0}Q7&v{g=rZyi_Ai66_2_}SuUNDp`+?@#eHc-XOMM5m%2_ z1&TJ^+<3DX(&0lBq?$ST5#{4ySZ7R=3g*Xow88T6xtH(!R5GMBu2>+hK}FcZ7=%s{ zI!(6SdvDim_cY^1J{U1rfBd>M4CUrc&0+>^UfT>S!*wvI61$9C+GyT1DyIx(ZF5@9 ztdp~9^QK!iu5A`KG9cN;^?$Mcwp*ItPw43x_}vG;55n(5@S7!4f_}C9I@qP z%X5y&I5Co22(h-*>fm%kC>{e|&r1-?p9dY^0{! zn%>SclA6Ywrg>JL#;UeWw5^(AhJKE7&%@BhMZAz8&$#Ih$XKF0($&^L8(#%dQQJ-n z4YfN5=n)8Qicpr)U2$L8N{;EJ+znH!b%y16pHbNA8*=!JW!@A3=RNdgn*ZJ29e@Pem8-wzWK1`|i%>M>( z5y9aHF^J$Yf@6OT;M`wRjUGP$<-CmG0)jmc0XT$U4#5G)h3>XP<-gi`4Of|Gv<;39&39|iC#f?$$p*A^S5=K=zkD4!O)ch1o+rrDKl( zID_ESApi#+#WEfT@bcpn>M#e?^w=RPmAl>j%jxoW)KkabLll^H`WdLv*{7)FJ#~ai z(v?q9$oJ@{pzJw_p|gOb7oTPCi~0l=o&QPnNEPMvf5X`8RxKr7->0yybI(&c>pwP! z@Dzg=oILFCz!Of{gw$7L36?U!ssv#jB8*9{mD{uM@a{HqxMMdm+`{@Ld+5`ZL0$z=-E>^Qdd0A%4k{(1TmbnEA_M?a5m z)|JMff8Hms)<-`}zX=(AmSuG0D5R!TT}GGhf@b{<61;Mho`l>#N2!N;(#tY7E5D7g z`;OD^KxMxMIiEyu7QrP12foI>iSYMWi5HKffFd|^oE2|C`tM`FkXA@;1cFx(TtqN?4&?;FiwMpjIDZa#?%9u>_8|%CbooE& z9*NHUD~j0#Qe3IrE1=`8wQLEpLmK&64; z0A}KGJwlfqr1cMC(o_GA8sXnz7{7QPf5$B(l4gHR4@mUTudw>RqHcQp*HA&J>`^T5 zF--L8Z&4iGCw@y0N%Y`vkTL%U71nF0un-)?;Q7}mzFF&&5_H(U-%;ZBls{t$=X03N zC1|C0|LY(bFW)DDp!^T`X7@-hNc8gmAQL|Xvi>ZBlOF`|3i{5X@6wD!ApWy-f4~2P zGy@WIzw}K=b3el#1?;yFd-TK7-$?Y-hb5>4J&Bl^z0$X#zI!G7c*S?HS||5`I`N+c z_z}AQ0SV@XOW^mtvKPSIM*-}A5WsN+=O2`ym&!kfwf_Ou{=g&B8Ht_-PSH~j0eBU` zWduhUz=-1yOZd{Uf5NgZJ_4&l3RVJ}&*UME5){xyg|yfxO)HC5!wQOa`8TO1}CGq}lfr;Ex=E;GV;>r*t^Ymi;^(CQlIZ^DAm+d^$qiKBb9U^p_>cJ3A-+5z>4K_$`6<0jfVC)yp6k(##9e>mZuXOHzeY{zJ^@ zAH;C3w}$nM3y3csm&&Aze=kV)x#<3{K;g4rMKMA!^EK?3uR+$A(KmNoA}*KoQ=Z2I znD)#|(9APm2gLrDq>s4hg_p2}-;g|H5&4;5k&pWR$_4u9=t&9n(RpyZGay{v11A8S zLEk=*N#EsfVXuH3dXIhwz!e1h0q4E=x0vF)67hIEzsxt`ajd|(fA2vr&iy^MO zDwW-m_w2Nf9^UPybGtD`trUh7r|;QH58VTdfZzFhFhw1t=*3a~e#v|0eSP%!``q-v z``zVLuE#AgdD6jhe}GRht@U3pd{6v0Ox;I*2fmA!0GveM+5bkX;P(J7VeE^40Nmj# zI7eK8>Fnh{(Wjxee^1Nl!9Req+5ac}902_c4C9wj@($fEeFNl`5AJRI{ZCLp?*B9e zGver{8R_=@{wEQ6@H6xyrD9wm}zANiBb>0W@ICR<^zDYWtC^?H^oi z|LAJ_CzsUrEy{>|m5t+#YUAB)TWMPlZHv&hzol(ob+vs(YTM>&d%^{VD#|@=b4k)l z$g=Gxke$@_e{+hJ^0i%&+^=^X{xJXU$*=yv-$i&)xc^tU(jU27`A7Z^LqE7TJN$1I z@plNfN4P`6y<52N`y+ps;{D+6gWsb9|0&@f6M4P_?stU$jKlwm@FVT3!u_>ye=FQS z30m(JLTaz@@~#8-2Ka5b!sFiVh`U3?ZMnkJ4+!@?f5O#8nwcy4dd!0VWAICrQ~Uj^ zguQc>kR;&DB798Xt$&hm-;F#0{Ck8;9Pe|*e+s^vasmAPMf6sF9})O{Me*greMsOR z7Vaa$eN?!Q3HNc~9PVM^-*LeI z2;4bve@_?j`=^*U;La{kFDC|H#(NHEE)>ySE(*IV7H*$a-!Mei0!x%e`?kg_-eNyDzr%<2O zaJ}bcajJ#O2EI@C@Y~pXVaX6=c1H)K1wYpFz0U%^mJki| zIeZTC_w%y;?Rm)4&)+wT`W!znh50-R`pV|`7%mewftK$$fq*Ex8Qid_SEuv zKU+n~{{xe+g&Yt70CHt>Z*_8GWpgfJa&u#|Sj005JHhd3KFw){&| z000120000K0000000000000000LqilhbJ58=cn34d;kD=4FLcP000000000000000 X08f(=h&Tegev?dyAO>iF00000h(uu| From 6f762f8b3515fdb654317190cd2d3f95633020e7 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 11:06:29 +0800 Subject: [PATCH 07/24] feat: shoutcut no repeat --- QtScrcpy/device/ui/videoform.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 65e705ef2..8e69c7b83 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -189,6 +189,7 @@ void VideoForm::installShortcut() // switchFullScreen shortcut = new QShortcut(QKeySequence("Ctrl+f"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -198,14 +199,17 @@ void VideoForm::installShortcut() // resizeSquare shortcut = new QShortcut(QKeySequence("Ctrl+g"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { resizeSquare(); }); // removeBlackRect shortcut = new QShortcut(QKeySequence("Ctrl+w"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { removeBlackRect(); }); // postGoHome shortcut = new QShortcut(QKeySequence("Ctrl+h"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -215,6 +219,7 @@ void VideoForm::installShortcut() // postGoBack shortcut = new QShortcut(QKeySequence("Ctrl+b"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -224,6 +229,7 @@ void VideoForm::installShortcut() // postAppSwitch shortcut = new QShortcut(QKeySequence("Ctrl+s"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -233,6 +239,7 @@ void VideoForm::installShortcut() // postGoMenu shortcut = new QShortcut(QKeySequence("Ctrl+m"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -242,6 +249,7 @@ void VideoForm::installShortcut() // postVolumeUp shortcut = new QShortcut(QKeySequence("Ctrl+up"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -251,6 +259,7 @@ void VideoForm::installShortcut() // postVolumeDown shortcut = new QShortcut(QKeySequence("Ctrl+down"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -260,6 +269,7 @@ void VideoForm::installShortcut() // postPower shortcut = new QShortcut(QKeySequence("Ctrl+p"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -269,6 +279,7 @@ void VideoForm::installShortcut() // setScreenPowerMode(ControlMsg::SPM_OFF) shortcut = new QShortcut(QKeySequence("Ctrl+o"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -278,6 +289,7 @@ void VideoForm::installShortcut() // expandNotificationPanel shortcut = new QShortcut(QKeySequence("Ctrl+n"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -287,6 +299,7 @@ void VideoForm::installShortcut() // collapseNotificationPanel shortcut = new QShortcut(QKeySequence("Ctrl+Shift+n"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -296,6 +309,7 @@ void VideoForm::installShortcut() // copy shortcut = new QShortcut(QKeySequence("Ctrl+c"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -305,6 +319,7 @@ void VideoForm::installShortcut() // cut shortcut = new QShortcut(QKeySequence("Ctrl+x"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -314,6 +329,7 @@ void VideoForm::installShortcut() // clipboardPaste shortcut = new QShortcut(QKeySequence("Ctrl+v"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -323,6 +339,7 @@ void VideoForm::installShortcut() // setDeviceClipboard shortcut = new QShortcut(QKeySequence("Ctrl+Shift+v"), this); + shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; From 54038a7b47d85291d11c5744b147dd0008a5f310 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 11:35:51 +0800 Subject: [PATCH 08/24] feat: volume allow repeat --- QtScrcpy/device/ui/videoform.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 8e69c7b83..2d75c9adc 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -249,7 +249,6 @@ void VideoForm::installShortcut() // postVolumeUp shortcut = new QShortcut(QKeySequence("Ctrl+up"), this); - shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; @@ -259,7 +258,6 @@ void VideoForm::installShortcut() // postVolumeDown shortcut = new QShortcut(QKeySequence("Ctrl+down"), this); - shortcut->setAutoRepeat(false); connect(shortcut, &QShortcut::activated, this, [this]() { if (!m_device) { return; From b73498c6b52a16aaccf7108712d7da5850c89a6b Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 12:01:10 +0800 Subject: [PATCH 09/24] fix: set clipboard 16 len bug --- QtScrcpy/device/controller/inputconvert/controlmsg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtScrcpy/device/controller/inputconvert/controlmsg.cpp b/QtScrcpy/device/controller/inputconvert/controlmsg.cpp index a8ba69d1c..6a21634b2 100644 --- a/QtScrcpy/device/controller/inputconvert/controlmsg.cpp +++ b/QtScrcpy/device/controller/inputconvert/controlmsg.cpp @@ -128,7 +128,7 @@ QByteArray ControlMsg::serializeData() break; case CMT_SET_CLIPBOARD: buffer.putChar(!!m_data.setClipboard.paste); - BufferUtil::write16(buffer, static_cast(strlen(m_data.setClipboard.text))); + BufferUtil::write32(buffer, static_cast(strlen(m_data.setClipboard.text))); buffer.write(m_data.setClipboard.text, strlen(m_data.setClipboard.text)); break; case CMT_SET_SCREEN_POWER_MODE: From db97c4e0a5de8b8d9740c007ad1f31d80951d111 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 12:26:52 +0800 Subject: [PATCH 10/24] feat: add repeat --- .../inputconvert/inputconvertbase.h | 3 +++ .../inputconvert/inputconvertnormal.cpp | 21 ++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h index ee1c6da7f..d9f8dc5f3 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h @@ -33,6 +33,9 @@ class InputConvertBase : public QObject void sendControlMsg(ControlMsg *msg); QPointer m_controller; + // Qt reports repeated events as a boolean, but Android expects the actual + // number of repetitions. This variable keeps track of the count. + unsigned m_repeat = 0; }; #endif // INPUTCONVERTBASE_H diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp index 91d0681c1..260604ee5 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertnormal.cpp @@ -1,4 +1,5 @@ #include +#include #include "inputconvertnormal.h" #include "controller.h" @@ -85,19 +86,8 @@ void InputConvertNormal::keyEvent(const QKeyEvent *from, const QSize &frameSize, return; } - bool ctrl = from->modifiers() & Qt::ControlModifier; - bool shift = from->modifiers() & Qt::ShiftModifier; - bool down = from->type() == QEvent::KeyPress; bool repeat = from->isAutoRepeat(); - if (ctrl && !shift && from->key() == Qt::Key_V && down && !repeat) { - // Synchronize the computer clipboard to the device clipboard before - // sending Ctrl+v, to allow seamless copy-paste. - if (m_controller) { - m_controller->onSetDeviceClipboard(false); - } - } - // action AndroidKeyeventAction action; switch (from->type()) { @@ -122,7 +112,14 @@ void InputConvertNormal::keyEvent(const QKeyEvent *from, const QSize &frameSize, if (!controlMsg) { return; } - controlMsg->setInjectKeycodeMsgData(action, keyCode, 0, convertMetastate(from->modifiers())); + + if (repeat) { + m_repeat++; + } else { + m_repeat = 0; + } + + controlMsg->setInjectKeycodeMsgData(action, keyCode, m_repeat, convertMetastate(from->modifiers())); sendControlMsg(controlMsg); } From 1cb5792da65b251b21cdb55c53995e8e1564ac1c Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 12:41:23 +0800 Subject: [PATCH 11/24] feat: update platform-tools to 31.0.0 From d877436cd756c5dc40bc2ad594967c3ff1048176 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 13:18:15 +0800 Subject: [PATCH 12/24] feat: update speed ratio --- docs/KeyMapDes_zh.md | 4 +++- keymap/gameforpeace.json | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/KeyMapDes_zh.md b/docs/KeyMapDes_zh.md index 1ea0ce072..07669a0f1 100644 --- a/docs/KeyMapDes_zh.md +++ b/docs/KeyMapDes_zh.md @@ -25,7 +25,9 @@ - mouseMoveMap:鼠标移动映射,鼠标的移动将被映射为以startPos为起点,以鼠标移动方向为移动方向的手指拖动操作(开启鼠标移动映射以后会隐藏鼠标,限制鼠标移动范围)。 一般在FPS手游中用来调整人物视野。 - startPos 手指拖动起始点 - - speedRatio 鼠标移动映射为手指拖动的比例,可以控制鼠标灵敏度,数值要大于0.00,数值越大,灵敏度越低 + - speedRatio 鼠标移动映射为手指拖动的比例,可以控制鼠标灵敏度,数值要大于0.00225,数值越大,灵敏度越低,Y轴以2.25的比率平移。如果这不适合您的手机屏幕,请使用以下两种设置来设置单个灵敏度值。 + - speedRatioX 鼠标X轴的速度比灵敏度。此值必须至少为0.001。 + - speedRatioY 鼠标Y轴的速度比灵敏度。此值必须至少为0.001。 - smallEyes 触发小眼睛的按键,按下此按键以后,鼠标的移动将被映射为以smallEyes.pos为起点,以鼠标移动方向为移动方向的手指拖动操作 - keyMapNodes 一般按键的映射,json数组,所有一般按键映射都放在这个数组中,将键盘的按键映射为普通的手指点击。 diff --git a/keymap/gameforpeace.json b/keymap/gameforpeace.json index 11dd20ee5..d06f0913d 100644 --- a/keymap/gameforpeace.json +++ b/keymap/gameforpeace.json @@ -5,8 +5,6 @@ "x": 0.57, "y": 0.26 }, - "speedRatioX": 3.25, - "speedRatioY": 1.25, "smallEyes": { "comment": "小眼睛", "type": "KMT_CLICK", From 84278a61d0c7681913450c5575191b6b5fe3b18a Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sun, 7 Mar 2021 16:02:29 +0800 Subject: [PATCH 13/24] feat: update qm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close #315 --- QtScrcpy/main.cpp | 2 +- QtScrcpy/res/i18n/QtScrcpy_en.qm | Bin 4238 -> 4793 bytes QtScrcpy/res/i18n/QtScrcpy_en.ts | 46 +++++++++++++++++++++++++------ QtScrcpy/res/i18n/QtScrcpy_zh.qm | Bin 3221 -> 3634 bytes QtScrcpy/res/i18n/QtScrcpy_zh.ts | 46 +++++++++++++++++++++++++------ 5 files changed, 77 insertions(+), 17 deletions(-) diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index 9f55a0f5e..bac0c2c0b 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -106,7 +106,7 @@ int main(int argc, char *argv[]) qInfo( "%s", - QObject::tr("This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the " + QObject::tr("This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the " "following address:") .toUtf8() .data()); diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.qm b/QtScrcpy/res/i18n/QtScrcpy_en.qm index 9022ff731f0c660c0f09547455660407884bb3df..87eb16a9b7bc1a594bc456c4b40ff13e081f8778 100644 GIT binary patch delta 909 zcma)3TSyd982-l19d&2Ths+?6Be95S7mF^6P`FT72!d`wcw1){)3vdu)s^#oH|O5o1yIQK-bhCaAUsFmgDxO=9sY0ofYK$1%@Nu^qH^*eu<<_j zm)`=!_lPAMfr2|YU3U@ibt0)PP`?7hwPBj`MQB`kMz{juP}N~zL#@!hbd~BCn2_rh zfZ}OkX6On~)aMJlsv_WL-(txPplnxmq(RJ0bOR;ZvUAlnC*1RNJS7g#j?%5Lm>ZcQ zd5^_++gAvEPyF86Lo}DNKUz=9%2&FY4H)S$YSjcD)xD-xIPhft2q_k&mYz<)e@5!+ zd;xg~P27|afglhVP`t&Kw@ke5!p*hGQ`+%aKqLD|E}Wns2BgPo9( zfJGmN4hIZ_bV@-(6ej&?FrZMSiOwN36j8eH)2fgYmc@044f2#0HJN58yorSxMCz5b z>Sn?T8~RypIY*-!7kpHe@=P{y7|?#r$_2cf&0Qf_ cUcpqG*^Z^kPCUg-%ZRD{Dyg2_96aay2?*Ze9smFU delta 341 zcmdm~+NWr0vm-^~)cZM8&nn97PguD3t`h?T;|2!SZ7~cC+;bV&zL_#G@El=aw+RK( zZy97ZTQD&2uAgWxXT>YV(4*-LRQ-w3^3gefile transfer failed - + install apk install apk - + file transfer file transfer - + wait current %1 to complete wait current %1 to complete - + %1 complete, save in %2 %1 complete, save in %2 @@ -41,7 +41,7 @@ %1 complete\n save in %2 - + %1 failed %1 failed @@ -228,6 +228,32 @@ no lock + + InputConvertGame + + + current keymap mode: %1 + current keymap mode: %1 + + + + custom + custom + + + + normal + normal + + + + KeyMap + + + Script updated, current keymap mode:normal, Press ~ key to switch keymap mode + Script updated, current keymap mode:normal, Press ~ key to switch keymap mode + + QObject @@ -241,9 +267,13 @@ You can download it at the following address: This software is completely open source and free.\nStrictly used for illegal purposes, or at your own risk.\nYou can download it at the following address: - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: + This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: + + + + This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: + This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: @@ -337,7 +367,7 @@ You can download it at the following address: file transfer failed - + file does not exist file does not exist diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.qm b/QtScrcpy/res/i18n/QtScrcpy_zh.qm index 3c6f972cf02d4e1179df67440f7c501b07e3c8fa..dfe62bfaa45c453b23a1b144f58f267b765957f8 100644 GIT binary patch delta 826 zcmbO#xk;wpW=D#|srPfHo>i3DpRjQ6-Q^4n46Icd@y>ou3=E7H7+AN(FfeeQVPN}a z%D}+2o`Kya6i8oVklAd(z`$L?U?#eVfq{*SA*0+9sQw>AmUa^Z15+DAdF~;gy59^v zn$AG+5=P5M=YV_#MteC21_ow*#(<;Kf#OmMjB8>U85r238F#fz0veRY%zaf3Xz)tr z!(0o1^!16ohU{xr#{!kCpSV|1J#2CfkokbUXHOqcR*`*8?+&2zKC(a4dIZ!xhyA-} z9MGEPiGMxAlT*GjFmUj5=hsaIN?CFr(7p+j3gkYq-wo)+Kpv;K5C#U8*E}&Hw}9$4 z^3<7M0UGJS^LouupbzfzPE4N#bZIi5v}Fs>N>#qqmqi#Dc$g{lKfn38tNfBShADj zED-AR@{4j4bFit0D7R{qO4o_~l=dZc!+#!h5B!$`8TOwinUkSzs$LL#auTxl;FkCX zCl_TF)R!of7NjJWq^9U7VDl%$B{~WLMXANb3U#0`P$mW=D#|srPfHo>i3DpRjQ6T_*+x#tjUt+hQ0PI3gL?zL_#GaN01i+k^t? zWCoed77Pqrj1%qUtT;OudNiGZs!JFxADsiLUdw1N=fJ?gtj`#5bUIL6ig8!VBnAey zG-mFrazI^X%!j!a0O^#8^9?87P>}E0(+4zj9s8Qz9YEc6?9a3w0aedo|Lz$F6c?B* z&*jo|oSLGLmS3chnUj;6o|vOh RP+C-wUz}RJIiLG569D++biM!p diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index 27ea074f1..eee9e68b2 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_zh.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_zh.ts @@ -16,22 +16,22 @@ 文件传输失败 - + install apk 安装apk - + file transfer 文件传输 - + wait current %1 to complete 等待当前%1完成 - + %1 complete, save in %2 %1完成,保存在%2 @@ -41,7 +41,7 @@ %1完成\n 保存在 %2 - + %1 failed %1 失败 @@ -228,6 +228,32 @@ 不锁定 + + InputConvertGame + + + current keymap mode: %1 + 当前按键映射模式: %1 + + + + custom + 自定义 + + + + normal + 正常 + + + + KeyMap + + + Script updated, current keymap mode:normal, Press ~ key to switch keymap mode + 脚本已更新,当前按键映射模式:正常,按~键切换按键映射模式 + + QObject @@ -241,9 +267,13 @@ You can download it at the following address: 本软件完全开源免费.\n严禁用于非法用途,否则后果自负.\n你可以在下面地址下载: - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: - 本软件完全开源免费,严禁用于非法用途,否则后果自负,你可以在下面地址下载: + 本软件完全开源免费,严禁用于非法用途,否则后果自负,你可以在下面地址下载: + + + + This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: + 该软件是完全开源和免费的。 严禁将其用于非法目的,否则后果自负。 您可以从以下地址下载它: @@ -337,7 +367,7 @@ You can download it at the following address: 文件传输失败 - + file does not exist 文件不存在 From c65d7ed14c8ce99348fa62fc512515943d8bb542 Mon Sep 17 00:00:00 2001 From: msnh2012 Date: Sun, 11 Apr 2021 18:22:46 +0800 Subject: [PATCH 14/24] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=80?= =?UTF-8?q?=E9=94=AE=E8=BF=9E=E6=8E=A5=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=8F=82=E6=95=B0=E4=BF=9D=E5=AD=98=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=8E=86=E5=8F=B2=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E8=AE=BF=E9=97=AE=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E5=91=BD=E5=90=8D=20(#386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加一键连接功能,添加参数保存功能,添加历史设备访问,添加设备命名 添加一键连接功能,添加参数保存功能,添加历史设备访问,添加设备命名 * 修复mac os注释,命名统一 --- QtScrcpy/QtScrcpy.pro | 1 - QtScrcpy/dialog.cpp | 236 ++++++++++++++++++++++++++++++- QtScrcpy/dialog.h | 30 +++- QtScrcpy/dialog.ui | 116 ++++++++++++--- QtScrcpy/main.cpp | 4 +- QtScrcpy/res/i18n/QtScrcpy_en.qm | Bin 4793 -> 5193 bytes QtScrcpy/res/i18n/QtScrcpy_en.ts | 176 +++++++++++++++++------ QtScrcpy/res/i18n/QtScrcpy_zh.qm | Bin 3634 -> 4052 bytes QtScrcpy/res/i18n/QtScrcpy_zh.ts | 176 +++++++++++++++++------ QtScrcpy/res/image/tray/logo.png | Bin 0 -> 11965 bytes QtScrcpy/res/res.qrc | 3 +- QtScrcpy/util/config.cpp | 73 +++++++++- QtScrcpy/util/config.h | 20 ++- 13 files changed, 716 insertions(+), 119 deletions(-) create mode 100644 QtScrcpy/res/image/tray/logo.png diff --git a/QtScrcpy/QtScrcpy.pro b/QtScrcpy/QtScrcpy.pro index 0afdc4e7c..e61704ce9 100644 --- a/QtScrcpy/QtScrcpy.pro +++ b/QtScrcpy/QtScrcpy.pro @@ -179,7 +179,6 @@ macos { APP_CONFIG.files = $$files($$PWD/../config/config.ini) APP_CONFIG.path = Contents/MacOS/config QMAKE_BUNDLE_DATA += APP_CONFIG - # mac application icon ICON = $$PWD/res/QtScrcpy.icns QMAKE_INFO_PLIST = $$PWD/res/Info_Mac.plist diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index e5f68279a..b7ea3c112 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -74,6 +74,20 @@ Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) outLog(log, newLine); } }); + + m_hideIcon = new QSystemTrayIcon(); + m_hideIcon->setIcon(QIcon(":/image/tray/logo.png")); + m_menu = new QMenu(); + m_quit = new QAction(); + m_showWindow = new QAction();; + m_showWindow->setText(tr("show")); + m_quit->setText(tr("quit")); + m_menu->addAction(m_showWindow); + m_menu->addAction(m_quit); + m_hideIcon->setContextMenu(m_menu); + connect(m_showWindow, &QAction::triggered, this, &Dialog::slotShow); + connect(m_quit, SIGNAL(triggered()), this, SLOT(close())); + connect(m_hideIcon, &QSystemTrayIcon::activated,this,&Dialog::slotActivated); } Dialog::~Dialog() @@ -119,14 +133,18 @@ void Dialog::initUI() ui->recordPathEdt->setText(Config::getInstance().getRecordPath()); ui->framelessCheck->setChecked(Config::getInstance().getFramelessWindow()); + on_useSingleModeCheck_clicked(); + + updateConnectedList(); + #ifdef Q_OS_OSX // mac need more width - setFixedWidth(520); + setFixedWidth(550); #endif #ifdef Q_OS_LINUX // linux need more width - setFixedWidth(480); + setFixedWidth(520); #endif } @@ -140,6 +158,15 @@ void Dialog::execAdbCmd() m_adb.execute(ui->serialBox->currentText().trimmed(), cmd.split(" ", Qt::SkipEmptyParts)); } +void Dialog::delayMs(int ms) +{ + QTime dieTime = QTime::currentTime().addMSecs(ms); + + while (QTime::currentTime() < dieTime) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + } +} + QString Dialog::getGameScript(const QString &fileName) { QFile loadFile(KeyMap::getKeyMapPath() + "/" + fileName); @@ -153,6 +180,77 @@ 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: + this->show(); + m_hideIcon->hide(); + break; + default: + break; + } +} + +void Dialog::updateConnectedList() +{ + ui->connectedPhoneList->clear(); + QStringList list = Config::getInstance().getConnectedGroups(); + + 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"); + + for (int i = 0; i < list.length(); ++i) + { + QString phone = QString(list[i]); + if(phone != "common" /*&& regIP.exactMatch(phone)*/) + { + ui->connectedPhoneList->addItem(phone+"-"+Config::getInstance().getUserName(phone)); + } + } +} + +void Dialog::updateUser() +{ + +} + +void Dialog::loadUser() +{ + +} + +void Dialog::closeEvent(QCloseEvent *event) +{ + int res = QMessageBox::question(this,tr("warning"),tr("Quit or set tray?"),tr("Quit"),tr("Set tray"),tr("Cancel")); + + if(res == 0) + { + event->accept(); + } + else if(res == 1) + { + this->hide(); + m_hideIcon->show(); + m_hideIcon->showMessage(tr("Notice"), + tr("Hidden here!"), + QSystemTrayIcon::Information, + 3000); + event->ignore(); + } + else + { + event->ignore(); + } + + +} + void Dialog::on_updateDevice_clicked() { if (checkAdbRun()) { @@ -166,6 +264,21 @@ void Dialog::on_startServerBtn_clicked() { outLog("start server...", false); + UserBootConfig config; + + config.recordScreen = ui->recordScreenCheck->isChecked(); + config.recordBackground = ui->notDisplayCheck->isChecked(); + config.reverseConnect = ui->useReverseCheck->isChecked(); + config.showFPS = ui->fpsCheck->isChecked(); + config.windowOnTop = ui->alwaysTopCheck->isChecked(); + config.autoOffScreen = ui->closeScreenCheck->isChecked(); + config.windowFrameless = ui->framelessCheck->isChecked(); + config.keepAlive = ui->stayAwakeCheck->isChecked(); + + Config::getInstance().setUserBootConfig(ui->serialBox->currentText(),config); + + updateConnectedList(); + QString absFilePath; if (ui->recordScreenCheck->isChecked()) { QString fileDir(ui->recordPathEdt->text().trimmed()); @@ -422,3 +535,120 @@ void Dialog::on_framelessCheck_stateChanged(int arg1) Q_UNUSED(arg1) Config::getInstance().setFramelessWindow(ui->framelessCheck->isChecked()); } + +void Dialog::on_usbConnectBtn_clicked() +{ + on_stopAllServerBtn_clicked(); + delayMs(200); + on_updateDevice_clicked(); + delayMs(200); + if(ui->serialBox->count()==0) + { + qWarning() << "No device is found!"; + return; + } + + 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"); + + for (int i = 0; i < ui->serialBox->count(); ++i) + { + if(!regIP.exactMatch(ui->serialBox->itemText(i))) + { + ui->serialBox->setCurrentIndex(i); + on_startServerBtn_clicked(); + break; + } + } + + updateConnectedList(); +} + +void Dialog::on_wifiConnectBtn_clicked() +{ + on_stopAllServerBtn_clicked(); + delayMs(200); + + on_updateDevice_clicked(); + delayMs(200); + if(ui->serialBox->count()==0) + { + qWarning() << "No device is found!"; + return; + } + + on_getIPBtn_clicked(); + delayMs(200); + + on_startAdbdBtn_clicked(); + delayMs(1000); + + on_wirelessConnectBtn_clicked(); + delayMs(2000); + + ui->serialBox->clear(); + + ui->serialBox->addItem(ui->deviceIpEdt->text()+":5555"); + + on_startServerBtn_clicked(); + delayMs(200); + + updateConnectedList(); +} + +void Dialog::on_connectedPhoneList_itemDoubleClicked(QListWidgetItem *item) +{ + ui->serialBox->clear(); + ui->serialBox->addItem(item->text().split("-")[0]); + ui->serialBox->setCurrentIndex(0); + + UserBootConfig config = Config::getInstance().getUserBootConfig(ui->serialBox->currentText()); + + ui->recordScreenCheck->setChecked(config.recordScreen); + ui->notDisplayCheck->setChecked(config.recordBackground); + ui->useReverseCheck->setChecked(config.reverseConnect); + ui->fpsCheck->setChecked(config.showFPS); + ui->alwaysTopCheck->setChecked(config.windowOnTop); + ui->closeScreenCheck->setChecked(config.autoOffScreen); + ui->framelessCheck->setChecked(config.windowFrameless); + ui->stayAwakeCheck->setChecked(config.keepAlive); + ui->userNameEdt->setText(Config::getInstance().getUserName(ui->serialBox->currentText())); + + on_startServerBtn_clicked(); +} + +void Dialog::on_updateNameBtn_clicked() +{ + if(ui->serialBox->count()!=0) + { + if(ui->userNameEdt->text().isEmpty()) + Config::getInstance().setUserName(ui->serialBox->currentText(),"PHONE"); + else + Config::getInstance().setUserName(ui->serialBox->currentText(),ui->userNameEdt->text()); + + updateConnectedList(); + + qDebug()<<"Update OK!"; + } + else + { + qWarning()<<"No device is connected!"; + } +} + +void Dialog::on_useSingleModeCheck_clicked() +{ + if(ui->useSingleModeCheck->isChecked()) + { + ui->configGroupBox->hide(); + ui->adbGroupBox->hide(); + ui->wirelessGroupBox->hide(); + ui->usbGroupBox->hide(); + } + else + { + ui->configGroupBox->show(); + ui->adbGroupBox->show(); + ui->wirelessGroupBox->show(); + ui->usbGroupBox->show(); + } +} diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 20eae6f7f..bae804497 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -1,8 +1,13 @@ -#ifndef DIALOG_H +#ifndef DIALOG_H #define DIALOG_H #include #include +#include +#include +#include +#include + #include "adbprocess.h" #include "devicemanage.h" @@ -66,16 +71,39 @@ private slots: void on_framelessCheck_stateChanged(int arg1); + void on_usbConnectBtn_clicked(); + + void on_wifiConnectBtn_clicked(); + + void on_connectedPhoneList_itemDoubleClicked(QListWidgetItem *item); + + void on_updateNameBtn_clicked(); + + void on_useSingleModeCheck_clicked(); + private: bool checkAdbRun(); void initUI(); void execAdbCmd(); + void delayMs(int ms); QString getGameScript(const QString &fileName); + void slotShow(); + void slotActivated(QSystemTrayIcon::ActivationReason reason); + void updateConnectedList(); + void updateUser(); + void loadUser(); + +protected: + void closeEvent(QCloseEvent *event); private: Ui::Dialog *ui; AdbProcess m_adb; DeviceManage m_deviceManage; + QSystemTrayIcon *m_hideIcon; + QMenu *m_menu; + QAction *m_showWindow; + QAction *m_quit; }; #endif // DIALOG_H diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index ae80cab4b..ed900ab15 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -6,19 +6,19 @@ 0 0 - 420 - 517 + 500 + 745 - 420 + 500 0 - 420 + 500 16777215 @@ -26,6 +26,56 @@ QtScrcpy + + + + Use Simple Mode + + + true + + + + + + + Simple Mode + + + false + + + + + + + + WIFI Connect + + + + + + + USB Connect + + + + + + + + + + 16777215 + 16777215 + + + + + + + @@ -350,21 +400,43 @@ USB line - - 3 - - - 5 - - - 5 - - - 5 - - - 5 - + + + + + + + 110 + 0 + + + + device name: + + + + + + + + 16777215 + 16777215 + + + + + + + + update name + + + false + + + + + @@ -382,6 +454,12 @@ + + + 110 + 0 + + device serial: diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index bac0c2c0b..50178f071 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -1,4 +1,4 @@ -#include +#include #include #include #include @@ -106,7 +106,7 @@ int main(int argc, char *argv[]) qInfo( "%s", - QObject::tr("This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the " + QObject::tr("This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the " "following address:") .toUtf8() .data()); diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.qm b/QtScrcpy/res/i18n/QtScrcpy_en.qm index 87eb16a9b7bc1a594bc456c4b40ff13e081f8778..99b69d0e43fa7537f04a2ca49924a62c329ec9d6 100644 GIT binary patch delta 1440 zcmah{du$VR9RFTl*WO)wLvatmd5ma)Dd-HsnkDU8GUy+`NS$c1ES~Le-I4aLv{$-` zQq&~|Gsv`n#26q%BjGVa8jKPIADNG6d;~FQVnQ~Ygqe`wGL6a9&uz5E8a0>8_wM)m zejeY)+jo7kWcZGPZ3#Jt97MJTa=zJ3MI^ju7cVvv&0A{!Ksbz;a6B6N4us6{ z^y8<9xRZ_pk+X<9oqX>y;AWh4D<@%8cP@YBH6r_Q=fiL8!~IXr=O%9dp`%@xcYR61l(N*EatEf_{GJ>>gCIojR!`)a5bg{Vu0W_w2!41D2i?LG&k7(|EgC!ET$mEyZT=QQ+r^L1ci{e{6zX{h zu}(_7(|```k#?;58U~L^lP5-DBqUuQMTOE0@2<{Oh>N{1Bwoh(HSceiEuEiz3t9(p ze$aRP!hCe}kgPl~K7xvO%i5N42u5V>C`RB8$WKjnqSdd;XD)z%|5g5e7o2nRHK3+T zQ`{g|kSI(l=@cVINw?KLm-U8KJ;su}^|?k`U>oMu+1HI>ug$ABA! zvT$S~n)p*`fC3eQc@}|UWRxs36*Hp_1Pax3H5C0jyaIMdZ-S~JwSZ`pqBJD|RcJkY zphFd)7}ZkgBvaNK@qZLq?6(K*siiJZga8c#s^ekk&|h_nUD1#dHguiE%;M|zaG?dF% z;%plRtLSQq`AfadD_otMwpI^yD`ryi^L`sHvsfvE@)ZV0w$I3y;+_$%yB4@KBvs7S zs{E#0B~{F&XfKp_`I6YH9b+Zi6b`dzT%LVHAH9k##&Jm z^BjOuZnoF);+J H_uKyjBq=;p delta 1121 zcma)3dx#8i9RJSk&CI^+Y#yCF&aZTF59H)=QqHZsuP*L{IM(jC9k;VHW@eoeyW&c8 z>|iDDN1pE!|4ws--6N8ukmQwKzn? zec&pd-32a>n_gH+lwZu%bRWQc3)i`VBl2`{7h3ibxmUS-4-0{N@4D_iL}?0+%U@dc z1jI$WxTXZeDZXH076kA3?b-fBv|rfJyA$(5VNYNth=vHACsz=8M+!IJv|xXg@On!X zRuU_R4j>YW#o47VAbeR|Y;VH3Wn$~a-4G0kox9E>=uPou$uk%^E`FU`pN8Y@?rAed zLg1sjzWfe&9=X4{h7s{2JeB1#4nFsUd-6el+LK)J2}XOog(n6(a^A`tmmqk~yK$Qr zhP!-`=w}GF`LxtN;70kbls*Q3$am}VESwkpRrR$vzs|qB7J+@XKQ(#T12{AM+0Jel zxZba=p&dkxfuR*h=8*#@?++vjHb|`Z6b!wRl(oGe+9WB*hv5I9v^iS?Ls99%ecBQf8i297&%ArK7Apf8EQEGcUU_u zf}lYPVmPsqSfdDKAxxCYFhTJ2e!ZhG`Q-EzrmM_I_)# zFU890MG-^MEtb@yVM~sdu|L)NBWf9|F=W$ZDW`H~Y0PX=tVrzF3vk*H=F831Vcn^m zn`eh4nXiK-*}f{3j$tt7n079~X(oa-j>~4~#z4t2%vR^jqa!;jwQ)!7&B5| Dialog - + Wireless Wireless - + wireless connect wireless connect - + wireless disconnect wireless disconnect - + Start Config Start Config - + record save path: record save path: - - + + select path select path - + record format: record format: - + record screen record screen - + frameless frameless - + + Use Simple Mode + Use Simple Mode + Use Simple Mode + + + + Simple Mode + Simple Mode + Simple Mode + + + + WIFI Connect + WIFI Connect + WIFI Connect + + + + USB Connect + USB Connect + USB Connect + + + lock orientation: lock orientation: - + show fps show fps - + stay awake stay awake - + + device name: + device name: + device name: + + + + update name + update name + update name + + + stop all server stop all server - + adb command: adb command: - + terminate terminate - + execute execute - + clear clear - + reverse connection reverse connection @@ -144,57 +180,57 @@ auto enable - + background record background record - + screen-off screen-off - + apply apply - + max size: max size: - + always on top always on top - + refresh script refresh script - + get device IP get device IP - + USB line USB line - + stop server stop server - + start server start server - + device serial: device serial: @@ -203,47 +239,101 @@ Config - + bit rate: bit rate: - + start adbd start adbd - + refresh devices refresh devices - + + show + show + show + + + + quit + quit + quit + + + original original - + no lock no lock + + + warning + Warning + Warning + + + + Quit or set tray? + Quit or set tray? + Quit or set tray? + + + + Quit + Quit + Quit + + + + Set tray + Set tray + Set tray + + + + Cancel + Cancel + Cancel + + + + Notice + Notice + Notice + + + + Hidden here! + Hidden here! + Hidden here! + InputConvertGame current keymap mode: %1 - current keymap mode: %1 + custom - custom + normal - normal + @@ -251,7 +341,7 @@ Script updated, current keymap mode:normal, Press ~ key to switch keymap mode - Script updated, current keymap mode:normal, Press ~ key to switch keymap mode + @@ -266,14 +356,10 @@ Strictly used for illegal purposes, or at your own risk. You can download it at the following address: This software is completely open source and free.\nStrictly used for illegal purposes, or at your own risk.\nYou can download it at the following address: - - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: - - This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: - This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: + This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: + This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.qm b/QtScrcpy/res/i18n/QtScrcpy_zh.qm index dfe62bfaa45c453b23a1b144f58f267b765957f8..16fef0dc63d2df057cf92af3be7674df139c1972 100644 GIT binary patch delta 1346 zcmZuvdrTX39RFT>y*ql>c5{e(ARKcV910tXv)eK{m>ngdgI&9_42z|`mc!D+K89Tw zm5Ff`SSz?e=He^P7+sv&XqE+WapKhZSTZndiI0F=%rMSG+~Q2YZ_$=n{p0ex`+h%< z@Avb&cXP+`4^$Ffi|pIf}38aw-#MOfzRpQ;R86pqx7-96S% zlO4amWHxTT0}WR(_R_ORf0db4<|1F4WhfO9SCZv8hsiOUvO@JUXq>HPi{Hb#O`Z3~ zQgl0|&M%Mrh(?FhhEGPYU{~*XQjLNqSh*7?XI@}W6H+)7ZGhqcEJ#h?1xRv5c5N2F<7 zmE>}XKE7G>iv^iv(s?etJ`JVJa!_)LDNrP3LB^r(MjA!dg`{8#lY-K|Bx~{j2R9V zw-hw$(mt#pwYGUhzD9PX_rMtkG|Kx|X_IKMf&dk<@0>>$I98#8uatd0(HR_AtK9G~ zXDJC4{zqD84T${z%g1abRlSA{_okaJjtn%*?TP2;9Yhu9o)O>f8HxCk;tMN= z+FXv{-_n^Ol09xqbs9;xJN!P$=NY)jgss({dt$Qr+OFQZ=0@uUP+bDa6ka2LjN zV9@Rz4;99<=Qzt<+m~6io+DjX`ts+vZI1|7&6jM2W_Ga2w#H^Kv$4H5Z8KD{zZUac!0d?Nn zsQU@c(-vc2r7Z8duqM>PbSr=+n<~27j(sx~t)KvyGU{s60pz$f?6Y*_p48ki9)&Ql zFSKj(UZL=?j&V;%@G0Gx*>MEi*X@iC2LP4c*s~k^rF3iR5(Ipqqo*qnc$U83*M#r+ z^oOl^cmrb{KLS8|ky)DAi@XA6Rb(@E-!jeD_Ts=vOtj?+2K~&upZ*RxN0=W4&MyXiU?*`s! z#!Zbz)E{E`s&4>#6Dx(<&}U(9WxhndoqcdKA9Z#v&*{Jg<6OA|1MAOop}D2qxY;r; z9=(g;H$(PUcOZyQ9dE&8Oh)SIry~J4jfwP}LF0ClSlff3s7dTJW4+w8C0>L{yG&P~ zqE9>8^m^At4A3p2X+dm7N;(y}tj&&?b(ygXx;a#A9OI#4o%(}R%w^1{A_-OukF6{z zF60euGu)(HTRW-J1i3&U17_(F1T>y8d_a~3pGr0e^>Kmt@Rh@NH;;&mT}1 zOTM5Wt4m#8fzl*h4Si)Dwt@iy%?A`!@(yMQ_dB#3c;~#odY>eFU7o@8{qo!fb9Gk5 z7x!1;T!Q;QK*F4aB{<#yp|%;t48Q*0cmqQUOL$rIhgA~rSGiQ7DvJ!3vwxE;QX~tC zLPE(uAgV-^I#K0o{{4~Uvh;GHzR>0W^9QASdiP1T@9ylfW_wK~_*xy!_QDoRuxVxr z5L-rlLpf~^2Bt2s?QM5@o&`S!n-ZL3O7S>HYW0zZzLtqe*}Be>PqtoL7an%?%+*#K zyKhTbW5PU;;Y8#*my^9x5r|uoiRkeN)h-Y52V}pb2nrDuqNuXStDbt|m!%qUy@)|$ J!-m{R`3)iR9CZKy diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index eee9e68b2..9315d4440 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_zh.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_zh.ts @@ -49,93 +49,129 @@ Dialog - + Wireless 无线 - + wireless connect 无线连接 - + wireless disconnect 无线断开 - + Start Config 启动配置 - + record save path: 录像保存路径: - - + + select path 选择路径 - + record format: 录制格式: - + record screen 录制屏幕 - + frameless 无边框 - + + Use Simple Mode + 启用一键模式 + 启用一键模式 + + + + Simple Mode + 一键模式 + 一键模式 + + + + WIFI Connect + 一键WIFI连接 + 一键WIFI连接 + + + + USB Connect + 一键USB连接 + 一键USB连接 + + + lock orientation: 锁定方向: - + show fps 显示fps - + stay awake 保持唤醒 - + + device name: + 设备名称: + 设备名称: + + + + update name + 更新设置名称 + 更新设置名称 + + + stop all server 停止所有服务 - + adb command: adb命令: - + terminate 终止 - + execute 执行 - + clear 清理 - + reverse connection 反向连接 @@ -144,57 +180,57 @@ 自动启用脚本 - + background record 后台录制 - + screen-off 自动息屏 - + apply 应用脚本 - + max size: 最大尺寸: - + always on top 窗口置顶 - + refresh script 刷新脚本 - + get device IP 获取设备IP - + USB line USB线 - + stop server 停止服务 - + start server 启动服务 - + device serial: 设备序列号: @@ -203,47 +239,101 @@ 配置 - + bit rate: 比特率: - + start adbd 启动adbd - + refresh devices 刷新设备列表 - + + show + 显示 + 显示 + + + + quit + 退出 + 退出 + + + original 原始 - + no lock 不锁定 + + + warning + 警告 + 警告 + + + + Quit or set tray? + 退出还是最小化到托盘? + 退出还是最小化到托盘? + + + + Quit + 退出 + 退出 + + + + Set tray + 最小化到系统托盘 + 最小化到系统托盘 + + + + Cancel + 取消 + 取消 + + + + Notice + 提示 + 提示 + + + + Hidden here! + 安卓录屏程序隐藏在这! + 安卓录屏程序隐藏在这! + InputConvertGame current keymap mode: %1 - 当前按键映射模式: %1 + custom - 自定义 + normal - 正常 + @@ -251,7 +341,7 @@ Script updated, current keymap mode:normal, Press ~ key to switch keymap mode - 脚本已更新,当前按键映射模式:正常,按~键切换按键映射模式 + @@ -266,14 +356,10 @@ Strictly used for illegal purposes, or at your own risk. You can download it at the following address: 本软件完全开源免费.\n严禁用于非法用途,否则后果自负.\n你可以在下面地址下载: - - This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: - 本软件完全开源免费,严禁用于非法用途,否则后果自负,你可以在下面地址下载: - - This software is completely open source and free. Use for illegal purposes is strictly prohibited, or at your own risk. You can download it at the following address: - 该软件是完全开源和免费的。 严禁将其用于非法目的,否则后果自负。 您可以从以下地址下载它: + This software is completely open source and free. Strictly used for illegal purposes, or at your own risk. You can download it at the following address: + 本软件完全开源免费,严禁用于非法用途,否则后果自负,你可以在下面地址下载: diff --git a/QtScrcpy/res/image/tray/logo.png b/QtScrcpy/res/image/tray/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8fbaafe2e4c986114e34dd9434bc9f4e25f778be GIT binary patch literal 11965 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 font/fontawesome-webfont.ttf image/videoform/phone-h.png - image/videoform/phone-v.png + image/videoform/phone-v.png qss/psblack.css qss/psblack/add_bottom.png qss/psblack/add_left.png @@ -24,5 +24,6 @@ qss/psblack/radiobutton_unchecked_disable.png i18n/QtScrcpy_en.qm i18n/QtScrcpy_zh.qm + image/tray/logo.png diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index 8966a7db4..b9393ad52 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -1,6 +1,7 @@ -#include +#include #include #include +#include #include "config.h" @@ -62,6 +63,16 @@ #define SERIAL_WINDOW_RECT_KEY_H "WindowRectH" #define SERIAL_WINDOW_RECT_KEY_DEF -1 +#define USER_NAME "PHONE" +#define USER_RECORD_SCREEN "RecordScreen" +#define USER_RECORD_BACKGROUD "RecordBackGround" +#define USER_REVERSE_CONNECT "ReverseConnect" +#define USER_SHOW_FPS "ShowFPS" +#define USER_WINDOW_ON_TOP "WindowOnTop" +#define USER_AUTO_OFF_SCREEN "AutoOffScreen" +#define USER_WINDOW_FRAMELESS "WindowFrameless" +#define USER_KEEP_ALIVE "KeepAlive" + #define COMMON_FRAMELESS_WINDOW_KEY "FramelessWindow" #define COMMON_FRAMELESS_WINDOW_DEF false @@ -76,6 +87,8 @@ Config::Config(QObject *parent) : QObject(parent) m_userData = new QSettings(getConfigPath() + "/userdata.ini", QSettings::IniFormat); m_userData->setIniCodec("UTF-8"); + + qDebug()<childGroups(); } Config &Config::getInstance() @@ -113,6 +126,37 @@ void Config::setRecordPath(const QString &path) m_userData->endGroup(); } +void Config::setUserBootConfig(const QString &serial, const UserBootConfig &config) +{ + m_userData->beginGroup(serial); + m_userData->setValue(USER_RECORD_SCREEN, config.recordScreen); + m_userData->setValue(USER_RECORD_BACKGROUD, config.recordBackground); + m_userData->setValue(USER_REVERSE_CONNECT, config.reverseConnect); + m_userData->setValue(USER_SHOW_FPS, config.showFPS); + m_userData->setValue(USER_WINDOW_ON_TOP, config.windowOnTop); + m_userData->setValue(USER_AUTO_OFF_SCREEN, config.autoOffScreen); + m_userData->setValue(USER_WINDOW_FRAMELESS, config.windowFrameless); + m_userData->setValue(USER_KEEP_ALIVE, config.keepAlive); + m_userData->endGroup(); + m_userData->sync(); +} + +UserBootConfig Config::getUserBootConfig(const QString &serial) +{ + UserBootConfig config; + m_userData->beginGroup(serial); + config.recordScreen = m_userData->value(USER_RECORD_SCREEN, false).toBool(); + config.recordBackground = m_userData->value(USER_RECORD_BACKGROUD, false).toBool(); + config.reverseConnect = m_userData->value(USER_REVERSE_CONNECT, true).toBool(); + config.showFPS = m_userData->value(USER_SHOW_FPS, false).toBool(); + config.windowOnTop = m_userData->value(USER_WINDOW_ON_TOP, false).toBool(); + config.autoOffScreen = m_userData->value(USER_AUTO_OFF_SCREEN, false).toBool(); + config.windowFrameless = m_userData->value(USER_WINDOW_FRAMELESS, false).toBool(); + config.keepAlive = m_userData->value(USER_KEEP_ALIVE, false).toBool(); + m_userData->endGroup(); + return config; +} + int Config::getBitRateIndex() { int bitRateIndex; @@ -191,6 +235,23 @@ void Config::setFramelessWindow(bool frameless) m_userData->endGroup(); } +void Config::setUserName(const QString &serial, const QString &name) +{ + m_userData->beginGroup(serial); + m_userData->setValue(USER_NAME, name); + m_userData->endGroup(); + m_userData->sync(); +} + +QString Config::getUserName(const QString &serial) +{ + QString name; + m_userData->beginGroup(serial); + name = m_userData->value(USER_NAME,"PHONE").toString(); + m_userData->endGroup(); + return name; +} + bool Config::getFramelessWindow() { bool framelessWindow = false; @@ -301,6 +362,16 @@ QString Config::getCodecName() return codecName; } +QStringList Config::getConnectedGroups() +{ + return m_userData->childGroups(); +} + +void Config::deleteGroup(const QString &serial) +{ + m_userData->remove(serial); +} + QString Config::getTitle() { QString title; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index e0e7f5178..af3104d8e 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -1,15 +1,27 @@ -#ifndef CONFIG_H +#ifndef CONFIG_H #define CONFIG_H #include #include #include +struct UserBootConfig +{ + bool recordScreen = false; + bool recordBackground = false; + bool reverseConnect = true; + bool showFPS = false; + bool windowOnTop = false; + bool autoOffScreen = false; + bool windowFrameless = false; + bool keepAlive = false; +}; class QSettings; class Config : public QObject { Q_OBJECT public: + static Config &getInstance(); // config QString getTitle(); @@ -24,6 +36,8 @@ class Config : public QObject QString getLogLevel(); QString getCodecOptions(); QString getCodecName(); + QStringList getConnectedGroups(); + void deleteGroup(const QString &serial); // user data QString getRecordPath(); @@ -38,6 +52,10 @@ class Config : public QObject QRect getRect(const QString &serial); bool getFramelessWindow(); void setFramelessWindow(bool frameless); + void setUserName(const QString &serial, const QString &name); + QString getUserName(const QString &serial); + void setUserBootConfig(const QString &serial, const UserBootConfig &config); + UserBootConfig getUserBootConfig(const QString &serial); private: explicit Config(QObject *parent = nullptr); From fe4cb202f6a3d8e27abba60454a9d8e9557f5645 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 12:10:02 +0800 Subject: [PATCH 15/24] feat: disable single mod default --- QtScrcpy/dialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index ed900ab15..fa800823a 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -32,7 +32,7 @@ Use Simple Mode - true + false From a4daf33bfe89f9631ce062983368f8b3d3042026 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 13:56:44 +0800 Subject: [PATCH 16/24] fix: magnetic widget adsorb bug --- QtScrcpy/uibase/magneticwidget.cpp | 11 ++++++++--- QtScrcpy/uibase/magneticwidget.h | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/QtScrcpy/uibase/magneticwidget.cpp b/QtScrcpy/uibase/magneticwidget.cpp index 3c7a9c27b..70e06201e 100644 --- a/QtScrcpy/uibase/magneticwidget.cpp +++ b/QtScrcpy/uibase/magneticwidget.cpp @@ -9,6 +9,7 @@ MagneticWidget::MagneticWidget(QWidget *adsorbWidget, AdsorbPositions adsorbPos) Q_ASSERT(m_adsorbWidget); setParent(m_adsorbWidget); setWindowFlags(windowFlags() | Qt::Tool); + m_adsorbWidgetSize = m_adsorbWidget->size(); m_adsorbWidget->installEventFilter(this); } @@ -30,6 +31,10 @@ bool MagneticWidget::eventFilter(QObject *watched, QEvent *event) if (watched != m_adsorbWidget || !event) { return false; } + // 始终记录adsorbWidget最新size + if (QEvent::Resize == event->type()) { + m_adsorbWidgetSize = m_adsorbWidget->size(); + } if (m_adsorbed && QEvent::Move == event->type()) { move(m_adsorbWidget->pos() - m_relativePos); } @@ -48,7 +53,7 @@ bool MagneticWidget::eventFilter(QObject *watched, QEvent *event) pos.setY(pos.y() - m_relativePos.y()); break; case AP_OUTSIDE_RIGHT: - pos.setX(pos.x() + m_adsorbWidget->width()); + pos.setX(pos.x() + m_adsorbWidgetSize.width()); pos.setY(pos.y() - m_relativePos.y()); break; case AP_OUTSIDE_TOP: @@ -165,8 +170,8 @@ void MagneticWidget::moveEvent(QMoveEvent *event) void MagneticWidget::getGeometry(QRect &relativeWidgetRect, QRect &targetWidgetRect) { relativeWidgetRect.setTopLeft(m_adsorbWidget->pos()); - relativeWidgetRect.setWidth(m_adsorbWidget->width()); - relativeWidgetRect.setHeight(m_adsorbWidget->height()); + relativeWidgetRect.setWidth(m_adsorbWidgetSize.width()); + relativeWidgetRect.setHeight(m_adsorbWidgetSize.height()); targetWidgetRect.setTopLeft(pos()); targetWidgetRect.setWidth(width()); diff --git a/QtScrcpy/uibase/magneticwidget.h b/QtScrcpy/uibase/magneticwidget.h index 23b6cffe0..843c835b1 100644 --- a/QtScrcpy/uibase/magneticwidget.h +++ b/QtScrcpy/uibase/magneticwidget.h @@ -46,6 +46,9 @@ class MagneticWidget : public QWidget QPoint m_relativePos; bool m_adsorbed = false; QPointer m_adsorbWidget; + // 单独记录adsorbWidgetSize,因为Widget setGeometry的时候,会先收到Move事件,后收到Resize事件, + // 但是收到Move事件时Widget的size()已经是setGeometry指定的size了 + QSize m_adsorbWidgetSize; AdsorbPosition m_curAdsorbPosition; }; From 26bf95a43f0822a2e128e31ed27fb56c79d744f5 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 16:50:07 +0800 Subject: [PATCH 17/24] feat: perfect config --- QtScrcpy/device/device.cpp | 4 +- QtScrcpy/device/device.h | 4 +- QtScrcpy/dialog.cpp | 204 ++++++++++++++++++------------------- QtScrcpy/dialog.h | 12 +-- QtScrcpy/util/config.cpp | 183 ++++++++++++--------------------- QtScrcpy/util/config.h | 34 +++---- 6 files changed, 184 insertions(+), 257 deletions(-) diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 78c516491..fc0b9597c 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -33,7 +33,7 @@ Device::Device(DeviceParams params, QObject *parent) : QObject(parent), m_params m_decoder = new Decoder(m_vb, this); m_fileHandler = new FileHandler(this); m_controller = new Controller(params.gameScript, this); - m_videoForm = new VideoForm(Config::getInstance().getFramelessWindow(), Config::getInstance().getSkin()); + m_videoForm = new VideoForm(params.framelessWindow, Config::getInstance().getSkin()); m_videoForm->setDevice(this); } @@ -382,7 +382,7 @@ bool Device::saveFrame(const AVFrame *frame) // save QString absFilePath; - QString fileDir(Config::getInstance().getRecordPath()); + QString fileDir(m_params.recordPath); if (fileDir.isEmpty()) { qWarning() << "please select record save path!!!"; return false; diff --git a/QtScrcpy/device/device.h b/QtScrcpy/device/device.h index bbbf693c2..e07ee7664 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -26,6 +26,7 @@ class Device : public QObject struct DeviceParams { QString recordFileName = ""; // 视频录制文件名 + QString recordPath = ""; // 视频保存路径 QString serial = ""; // 设备序列号 quint16 localPort = 27183; // reverse时本地监听端口 quint16 maxSize = 720; // 视频分辨率 @@ -37,7 +38,8 @@ class Device : public QObject QString gameScript = ""; // 游戏映射脚本 bool renderExpiredFrames = false; // 是否渲染延迟视频帧 int lockVideoOrientation = -1; // 是否锁定视频方向 - int stayAwake = false; // 是否保持唤醒 + bool stayAwake = false; // 是否保持唤醒 + bool framelessWindow = false; // 是否无边框窗口 }; enum GroupControlState { diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index b7ea3c112..e18bdac1f 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -43,8 +43,10 @@ Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) if (args.contains("devices")) { QStringList devices = m_adb.getDevicesSerialFromStdOut(); ui->serialBox->clear(); + ui->connectedPhoneList->clear(); for (auto &item : devices) { ui->serialBox->addItem(item); + ui->connectedPhoneList->addItem(item+"-"+Config::getInstance().getNickName(item)); } } else if (args.contains("show") && args.contains("wlan0")) { QString ip = m_adb.getDeviceIPFromStdOut(); @@ -92,6 +94,7 @@ Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog) Dialog::~Dialog() { + updateBootConfig(false); m_deviceManage.disconnectAllDevice(); delete ui; } @@ -109,7 +112,6 @@ void Dialog::initUI() ui->bitRateBox->addItem("50000000"); ui->bitRateBox->addItem("100000000"); ui->bitRateBox->addItem("200000000"); - ui->bitRateBox->setCurrentIndex(Config::getInstance().getBitRateIndex()); ui->maxSizeBox->addItem("640"); ui->maxSizeBox->addItem("720"); @@ -117,11 +119,9 @@ void Dialog::initUI() ui->maxSizeBox->addItem("1280"); ui->maxSizeBox->addItem("1920"); ui->maxSizeBox->addItem(tr("original")); - ui->maxSizeBox->setCurrentIndex(Config::getInstance().getMaxSizeIndex()); ui->formatBox->addItem("mp4"); ui->formatBox->addItem("mkv"); - ui->formatBox->setCurrentIndex(Config::getInstance().getRecordFormatIndex()); ui->lockOrientationBox->addItem(tr("no lock")); ui->lockOrientationBox->addItem("0"); @@ -130,12 +130,13 @@ void Dialog::initUI() ui->lockOrientationBox->addItem("270"); ui->lockOrientationBox->setCurrentIndex(0); - ui->recordPathEdt->setText(Config::getInstance().getRecordPath()); - ui->framelessCheck->setChecked(Config::getInstance().getFramelessWindow()); + updateBootConfig(true); + + ui->userNameEdt->setText(Config::getInstance().getNickName(ui->serialBox->currentText())); on_useSingleModeCheck_clicked(); - updateConnectedList(); + on_updateDevice_clicked(); #ifdef Q_OS_OSX // mac need more width @@ -148,6 +149,45 @@ void Dialog::initUI() #endif } +void Dialog::updateBootConfig(bool toView) +{ + if (toView) { + UserBootConfig config = Config::getInstance().getUserBootConfig(); + + ui->bitRateBox->setCurrentIndex(config.bitRateIndex); + ui->maxSizeBox->setCurrentIndex(config.maxSizeIndex); + ui->formatBox->setCurrentIndex(config.recordFormatIndex); + ui->recordPathEdt->setText(config.recordPath); + ui->lockOrientationBox->setCurrentIndex(config.lockOrientationIndex); + ui->framelessCheck->setChecked(config.framelessWindow); + ui->recordScreenCheck->setChecked(config.recordScreen); + ui->notDisplayCheck->setChecked(config.recordBackground); + ui->useReverseCheck->setChecked(config.reverseConnect); + ui->fpsCheck->setChecked(config.showFPS); + ui->alwaysTopCheck->setChecked(config.windowOnTop); + ui->closeScreenCheck->setChecked(config.autoOffScreen); + ui->stayAwakeCheck->setChecked(config.keepAlive); + } else { + UserBootConfig config; + + config.bitRateIndex = ui->bitRateBox->currentIndex(); + config.maxSizeIndex = ui->maxSizeBox->currentIndex(); + config.recordFormatIndex = ui->formatBox->currentIndex(); + config.recordPath = ui->recordPathEdt->text(); + config.lockOrientationIndex = ui->lockOrientationBox->currentIndex(); + config.recordScreen = ui->recordScreenCheck->isChecked(); + config.recordBackground = ui->notDisplayCheck->isChecked(); + config.reverseConnect = ui->useReverseCheck->isChecked(); + config.showFPS = ui->fpsCheck->isChecked(); + config.windowOnTop = ui->alwaysTopCheck->isChecked(); + config.autoOffScreen = ui->closeScreenCheck->isChecked(); + config.framelessWindow = ui->framelessCheck->isChecked(); + config.keepAlive = ui->stayAwakeCheck->isChecked(); + + Config::getInstance().setUserBootConfig(config); + } +} + void Dialog::execAdbCmd() { if (checkAdbRun()) { @@ -198,33 +238,6 @@ void Dialog::slotActivated(QSystemTrayIcon::ActivationReason reason) } } -void Dialog::updateConnectedList() -{ - ui->connectedPhoneList->clear(); - QStringList list = Config::getInstance().getConnectedGroups(); - - 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"); - - for (int i = 0; i < list.length(); ++i) - { - QString phone = QString(list[i]); - if(phone != "common" /*&& regIP.exactMatch(phone)*/) - { - ui->connectedPhoneList->addItem(phone+"-"+Config::getInstance().getUserName(phone)); - } - } -} - -void Dialog::updateUser() -{ - -} - -void Dialog::loadUser() -{ - -} - void Dialog::closeEvent(QCloseEvent *event) { int res = QMessageBox::question(this,tr("warning"),tr("Quit or set tray?"),tr("Quit"),tr("Set tray"),tr("Cancel")); @@ -247,8 +260,6 @@ void Dialog::closeEvent(QCloseEvent *event) { event->ignore(); } - - } void Dialog::on_updateDevice_clicked() @@ -264,21 +275,6 @@ void Dialog::on_startServerBtn_clicked() { outLog("start server...", false); - UserBootConfig config; - - config.recordScreen = ui->recordScreenCheck->isChecked(); - config.recordBackground = ui->notDisplayCheck->isChecked(); - config.reverseConnect = ui->useReverseCheck->isChecked(); - config.showFPS = ui->fpsCheck->isChecked(); - config.windowOnTop = ui->alwaysTopCheck->isChecked(); - config.autoOffScreen = ui->closeScreenCheck->isChecked(); - config.windowFrameless = ui->framelessCheck->isChecked(); - config.keepAlive = ui->stayAwakeCheck->isChecked(); - - Config::getInstance().setUserBootConfig(ui->serialBox->currentText(),config); - - updateConnectedList(); - QString absFilePath; if (ui->recordScreenCheck->isChecked()) { QString fileDir(ui->recordPathEdt->text().trimmed()); @@ -308,6 +304,8 @@ void Dialog::on_startServerBtn_clicked() params.renderExpiredFrames = Config::getInstance().getRenderExpiredFrames(); params.lockVideoOrientation = ui->lockOrientationBox->currentIndex() - 1; params.stayAwake = ui->stayAwakeCheck->isChecked(); + params.framelessWindow = ui->framelessCheck->isChecked(); + params.recordPath = ui->recordPathEdt->text().trimmed(); m_deviceManage.connectDevice(params); @@ -454,7 +452,6 @@ void Dialog::on_selectRecordPathBtn_clicked() void Dialog::on_recordPathEdt_textChanged(const QString &arg1) { - Config::getInstance().setRecordPath(arg1); ui->recordPathEdt->setToolTip(arg1.trimmed()); ui->notDisplayCheck->setCheckable(!arg1.trimmed().isEmpty()); } @@ -515,34 +512,13 @@ void Dialog::on_recordScreenCheck_clicked(bool checked) } } -void Dialog::on_bitRateBox_activated(int index) -{ - Config::getInstance().setBitRateIndex(index); -} - -void Dialog::on_maxSizeBox_activated(int index) -{ - Config::getInstance().setMaxSizeIndex(index); -} - -void Dialog::on_formatBox_activated(int index) -{ - Config::getInstance().setRecordFormatIndex(index); -} - -void Dialog::on_framelessCheck_stateChanged(int arg1) -{ - Q_UNUSED(arg1) - Config::getInstance().setFramelessWindow(ui->framelessCheck->isChecked()); -} - void Dialog::on_usbConnectBtn_clicked() { on_stopAllServerBtn_clicked(); delayMs(200); on_updateDevice_clicked(); delayMs(200); - if(ui->serialBox->count()==0) + if(ui->serialBox->count() == 0) { qWarning() << "No device is found!"; return; @@ -552,6 +528,7 @@ void Dialog::on_usbConnectBtn_clicked() for (int i = 0; i < ui->serialBox->count(); ++i) { + // 连接第一个usb设备 if(!regIP.exactMatch(ui->serialBox->itemText(i))) { ui->serialBox->setCurrentIndex(i); @@ -559,8 +536,6 @@ void Dialog::on_usbConnectBtn_clicked() break; } } - - updateConnectedList(); } void Dialog::on_wifiConnectBtn_clicked() @@ -570,12 +545,30 @@ void Dialog::on_wifiConnectBtn_clicked() on_updateDevice_clicked(); delayMs(200); - if(ui->serialBox->count()==0) + if(ui->serialBox->count() == 0) { qWarning() << "No device is found!"; return; } + bool found = false; + 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"); + for (int i = 0; i < ui->serialBox->count(); ++i) + { + // 选中第一个usb设备 + if(!regIP.exactMatch(ui->serialBox->itemText(i))) + { + ui->serialBox->setCurrentIndex(i); + found = true; + break; + } + } + + if (!found) { + qWarning() << "No use device is found!"; + return; + } + on_getIPBtn_clicked(); delayMs(200); @@ -585,52 +578,49 @@ void Dialog::on_wifiConnectBtn_clicked() on_wirelessConnectBtn_clicked(); delayMs(2000); - ui->serialBox->clear(); + on_updateDevice_clicked(); + delayMs(200); + + // 找到第一个无线设备 + found = false; + for (int i = 0; i < ui->serialBox->count(); ++i) + { + if(regIP.exactMatch(ui->serialBox->itemText(i))) + { + ui->serialBox->setCurrentIndex(i); + found = true; + break; + } + } - ui->serialBox->addItem(ui->deviceIpEdt->text()+":5555"); + if (!found) { + qWarning() << "No wifi device is found!"; + return; + } on_startServerBtn_clicked(); - delayMs(200); - - updateConnectedList(); } void Dialog::on_connectedPhoneList_itemDoubleClicked(QListWidgetItem *item) { - ui->serialBox->clear(); - ui->serialBox->addItem(item->text().split("-")[0]); - ui->serialBox->setCurrentIndex(0); - - UserBootConfig config = Config::getInstance().getUserBootConfig(ui->serialBox->currentText()); - - ui->recordScreenCheck->setChecked(config.recordScreen); - ui->notDisplayCheck->setChecked(config.recordBackground); - ui->useReverseCheck->setChecked(config.reverseConnect); - ui->fpsCheck->setChecked(config.showFPS); - ui->alwaysTopCheck->setChecked(config.windowOnTop); - ui->closeScreenCheck->setChecked(config.autoOffScreen); - ui->framelessCheck->setChecked(config.windowFrameless); - ui->stayAwakeCheck->setChecked(config.keepAlive); - ui->userNameEdt->setText(Config::getInstance().getUserName(ui->serialBox->currentText())); - + Q_UNUSED(item); + ui->serialBox->setCurrentIndex(ui->connectedPhoneList->currentRow()); on_startServerBtn_clicked(); } void Dialog::on_updateNameBtn_clicked() { - if(ui->serialBox->count()!=0) - { - if(ui->userNameEdt->text().isEmpty()) - Config::getInstance().setUserName(ui->serialBox->currentText(),"PHONE"); - else - Config::getInstance().setUserName(ui->serialBox->currentText(),ui->userNameEdt->text()); + if(ui->serialBox->count()!=0) { + if(ui->userNameEdt->text().isEmpty()) { + Config::getInstance().setNickName(ui->serialBox->currentText(), "Phone"); + } else { + Config::getInstance().setNickName(ui->serialBox->currentText(), ui->userNameEdt->text()); + } - updateConnectedList(); + on_updateDevice_clicked(); qDebug()<<"Update OK!"; - } - else - { + } else { qWarning()<<"No device is connected!"; } } diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index bae804497..6db054a14 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -63,14 +63,6 @@ private slots: void on_recordScreenCheck_clicked(bool checked); - void on_bitRateBox_activated(int index); - - void on_maxSizeBox_activated(int index); - - void on_formatBox_activated(int index); - - void on_framelessCheck_stateChanged(int arg1); - void on_usbConnectBtn_clicked(); void on_wifiConnectBtn_clicked(); @@ -84,14 +76,12 @@ private slots: private: bool checkAdbRun(); void initUI(); + void updateBootConfig(bool toView = true); void execAdbCmd(); void delayMs(int ms); QString getGameScript(const QString &fileName); void slotShow(); void slotActivated(QSystemTrayIcon::ActivationReason reason); - void updateConnectedList(); - void updateUser(); - void loadUser(); protected: void closeEvent(QCloseEvent *event); diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index b9393ad52..ee7397af6 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -44,7 +44,7 @@ #define COMMON_CODEC_NAME_KEY "CodecName" #define COMMON_CODEC_NAME_DEF "-" -// user data +// user config #define COMMON_RECORD_KEY "RecordPath" #define COMMON_RECORD_DEF "" @@ -57,26 +57,41 @@ #define COMMON_RECORD_FORMAT_INDEX_KEY "RecordFormatIndex" #define COMMON_RECORD_FORMAT_INDEX_DEF 0 -#define SERIAL_WINDOW_RECT_KEY_X "WindowRectX" -#define SERIAL_WINDOW_RECT_KEY_Y "WindowRectY" -#define SERIAL_WINDOW_RECT_KEY_W "WindowRectW" -#define SERIAL_WINDOW_RECT_KEY_H "WindowRectH" -#define SERIAL_WINDOW_RECT_KEY_DEF -1 +#define COMMON_LOCK_ORIENTATION_INDEX_KEY "LockDirectionIndex" +#define COMMON_LOCK_ORIENTATION_INDEX_DEF 0 + +#define COMMON_RECORD_SCREEN_KEY "RecordScreen" +#define COMMON_RECORD_SCREEN_DEF false + +#define COMMON_RECORD_BACKGROUD_KEY "RecordBackGround" +#define COMMON_RECORD_BACKGROUD_DEF false + +#define COMMON_REVERSE_CONNECT_KEY "ReverseConnect" +#define COMMON_REVERSE_CONNECT_DEF true + +#define COMMON_SHOW_FPS_KEY "ShowFPS" +#define COMMON_SHOW_FPS_DEF false -#define USER_NAME "PHONE" -#define USER_RECORD_SCREEN "RecordScreen" -#define USER_RECORD_BACKGROUD "RecordBackGround" -#define USER_REVERSE_CONNECT "ReverseConnect" -#define USER_SHOW_FPS "ShowFPS" -#define USER_WINDOW_ON_TOP "WindowOnTop" -#define USER_AUTO_OFF_SCREEN "AutoOffScreen" -#define USER_WINDOW_FRAMELESS "WindowFrameless" -#define USER_KEEP_ALIVE "KeepAlive" +#define COMMON_WINDOW_ON_TOP_KEY "WindowOnTop" +#define COMMON_WINDOW_ON_TOP_DEF false + +#define COMMON_AUTO_OFF_SCREEN_KEY "AutoOffScreen" +#define COMMON_AUTO_OFF_SCREEN_DEF false #define COMMON_FRAMELESS_WINDOW_KEY "FramelessWindow" #define COMMON_FRAMELESS_WINDOW_DEF false -// 最大尺寸 录制格式 +#define COMMON_KEEP_ALIVE_KEY "KeepAlive" +#define COMMON_KEEP_ALIVE_DEF false + +// device config +#define SERIAL_WINDOW_RECT_KEY_X "WindowRectX" +#define SERIAL_WINDOW_RECT_KEY_Y "WindowRectY" +#define SERIAL_WINDOW_RECT_KEY_W "WindowRectW" +#define SERIAL_WINDOW_RECT_KEY_H "WindowRectH" +#define SERIAL_WINDOW_RECT_KEY_DEF -1 +#define SERIAL_NICK_NAME_KEY "NickName" +#define SERIAL_NICK_NAME_DEF "Phone" QString Config::s_configPath = ""; @@ -110,99 +125,45 @@ const QString &Config::getConfigPath() return s_configPath; } -QString Config::getRecordPath() -{ - QString record; - m_userData->beginGroup(GROUP_COMMON); - record = m_userData->value(COMMON_RECORD_KEY, COMMON_RECORD_DEF).toString(); - m_userData->endGroup(); - return record; -} - -void Config::setRecordPath(const QString &path) +void Config::setUserBootConfig(const UserBootConfig &config) { m_userData->beginGroup(GROUP_COMMON); - m_userData->setValue(COMMON_RECORD_KEY, path); - m_userData->endGroup(); -} - -void Config::setUserBootConfig(const QString &serial, const UserBootConfig &config) -{ - m_userData->beginGroup(serial); - m_userData->setValue(USER_RECORD_SCREEN, config.recordScreen); - m_userData->setValue(USER_RECORD_BACKGROUD, config.recordBackground); - m_userData->setValue(USER_REVERSE_CONNECT, config.reverseConnect); - m_userData->setValue(USER_SHOW_FPS, config.showFPS); - m_userData->setValue(USER_WINDOW_ON_TOP, config.windowOnTop); - m_userData->setValue(USER_AUTO_OFF_SCREEN, config.autoOffScreen); - m_userData->setValue(USER_WINDOW_FRAMELESS, config.windowFrameless); - m_userData->setValue(USER_KEEP_ALIVE, config.keepAlive); + m_userData->setValue(COMMON_RECORD_KEY, config.recordPath); + m_userData->setValue(COMMON_BITRATE_INDEX_KEY, config.bitRateIndex); + m_userData->setValue(COMMON_MAX_SIZE_INDEX_KEY, config.maxSizeIndex); + m_userData->setValue(COMMON_RECORD_FORMAT_INDEX_KEY, config.recordFormatIndex); + m_userData->setValue(COMMON_FRAMELESS_WINDOW_KEY, config.framelessWindow); + m_userData->setValue(COMMON_LOCK_ORIENTATION_INDEX_KEY, config.lockOrientationIndex); + m_userData->setValue(COMMON_RECORD_SCREEN_KEY, config.recordScreen); + m_userData->setValue(COMMON_RECORD_BACKGROUD_KEY, config.recordBackground); + m_userData->setValue(COMMON_REVERSE_CONNECT_KEY, config.reverseConnect); + m_userData->setValue(COMMON_SHOW_FPS_KEY, config.showFPS); + m_userData->setValue(COMMON_WINDOW_ON_TOP_KEY, config.windowOnTop); + m_userData->setValue(COMMON_AUTO_OFF_SCREEN_KEY, config.autoOffScreen); + m_userData->setValue(COMMON_KEEP_ALIVE_KEY, config.keepAlive); m_userData->endGroup(); m_userData->sync(); } -UserBootConfig Config::getUserBootConfig(const QString &serial) +UserBootConfig Config::getUserBootConfig() { UserBootConfig config; - m_userData->beginGroup(serial); - config.recordScreen = m_userData->value(USER_RECORD_SCREEN, false).toBool(); - config.recordBackground = m_userData->value(USER_RECORD_BACKGROUD, false).toBool(); - config.reverseConnect = m_userData->value(USER_REVERSE_CONNECT, true).toBool(); - config.showFPS = m_userData->value(USER_SHOW_FPS, false).toBool(); - config.windowOnTop = m_userData->value(USER_WINDOW_ON_TOP, false).toBool(); - config.autoOffScreen = m_userData->value(USER_AUTO_OFF_SCREEN, false).toBool(); - config.windowFrameless = m_userData->value(USER_WINDOW_FRAMELESS, false).toBool(); - config.keepAlive = m_userData->value(USER_KEEP_ALIVE, false).toBool(); - m_userData->endGroup(); - return config; -} - -int Config::getBitRateIndex() -{ - int bitRateIndex; - m_userData->beginGroup(GROUP_COMMON); - bitRateIndex = m_userData->value(COMMON_BITRATE_INDEX_KEY, COMMON_BITRATE_INDEX_DEF).toInt(); - m_userData->endGroup(); - return bitRateIndex; -} - -void Config::setBitRateIndex(int bitRateIndex) -{ - m_userData->beginGroup(GROUP_COMMON); - m_userData->setValue(COMMON_BITRATE_INDEX_KEY, bitRateIndex); - m_userData->endGroup(); -} - -int Config::getMaxSizeIndex() -{ - int maxSizeIndex; - m_userData->beginGroup(GROUP_COMMON); - maxSizeIndex = m_userData->value(COMMON_MAX_SIZE_INDEX_KEY, COMMON_MAX_SIZE_INDEX_DEF).toInt(); - m_userData->endGroup(); - return maxSizeIndex; -} - -void Config::setMaxSizeIndex(int maxSizeIndex) -{ - m_userData->beginGroup(GROUP_COMMON); - m_userData->setValue(COMMON_MAX_SIZE_INDEX_KEY, maxSizeIndex); - m_userData->endGroup(); -} - -int Config::getRecordFormatIndex() -{ - int recordFormatIndex; m_userData->beginGroup(GROUP_COMMON); - recordFormatIndex = m_userData->value(COMMON_RECORD_FORMAT_INDEX_KEY, COMMON_RECORD_FORMAT_INDEX_DEF).toInt(); - m_userData->endGroup(); - return recordFormatIndex; -} - -void Config::setRecordFormatIndex(int recordFormatIndex) -{ - m_userData->beginGroup(GROUP_COMMON); - m_userData->setValue(COMMON_RECORD_FORMAT_INDEX_KEY, recordFormatIndex); + config.recordPath = m_userData->value(COMMON_RECORD_KEY, COMMON_RECORD_DEF).toString(); + config.bitRateIndex = m_userData->value(COMMON_BITRATE_INDEX_KEY, COMMON_BITRATE_INDEX_DEF).toInt(); + config.maxSizeIndex = m_userData->value(COMMON_MAX_SIZE_INDEX_KEY, COMMON_MAX_SIZE_INDEX_DEF).toInt(); + config.recordFormatIndex = m_userData->value(COMMON_RECORD_FORMAT_INDEX_KEY, COMMON_RECORD_FORMAT_INDEX_DEF).toInt(); + config.lockOrientationIndex = m_userData->value(COMMON_LOCK_ORIENTATION_INDEX_KEY, COMMON_LOCK_ORIENTATION_INDEX_DEF).toInt(); + config.framelessWindow = m_userData->value(COMMON_FRAMELESS_WINDOW_KEY, COMMON_FRAMELESS_WINDOW_DEF).toBool(); + config.recordScreen = m_userData->value(COMMON_RECORD_SCREEN_KEY, COMMON_RECORD_SCREEN_DEF).toBool(); + config.recordBackground = m_userData->value(COMMON_RECORD_BACKGROUD_KEY, COMMON_RECORD_BACKGROUD_DEF).toBool(); + config.reverseConnect = m_userData->value(COMMON_REVERSE_CONNECT_KEY, COMMON_REVERSE_CONNECT_DEF).toBool(); + config.showFPS = m_userData->value(COMMON_SHOW_FPS_KEY, COMMON_SHOW_FPS_DEF).toBool(); + config.windowOnTop = m_userData->value(COMMON_WINDOW_ON_TOP_KEY, COMMON_WINDOW_ON_TOP_DEF).toBool(); + 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(); m_userData->endGroup(); + return config; } void Config::setRect(const QString &serial, const QRect &rc) @@ -228,39 +189,23 @@ QRect Config::getRect(const QString &serial) return rc; } -void Config::setFramelessWindow(bool frameless) -{ - m_userData->beginGroup(GROUP_COMMON); - m_userData->setValue(COMMON_FRAMELESS_WINDOW_KEY, frameless); - m_userData->endGroup(); -} - -void Config::setUserName(const QString &serial, const QString &name) +void Config::setNickName(const QString &serial, const QString &name) { m_userData->beginGroup(serial); - m_userData->setValue(USER_NAME, name); + m_userData->setValue(SERIAL_NICK_NAME_KEY, name); m_userData->endGroup(); m_userData->sync(); } -QString Config::getUserName(const QString &serial) +QString Config::getNickName(const QString &serial) { QString name; m_userData->beginGroup(serial); - name = m_userData->value(USER_NAME,"PHONE").toString(); + name = m_userData->value(SERIAL_NICK_NAME_KEY, SERIAL_NICK_NAME_DEF).toString(); m_userData->endGroup(); return name; } -bool Config::getFramelessWindow() -{ - bool framelessWindow = false; - m_userData->beginGroup(GROUP_COMMON); - framelessWindow = m_userData->value(COMMON_FRAMELESS_WINDOW_KEY, COMMON_FRAMELESS_WINDOW_DEF).toBool(); - m_userData->endGroup(); - return framelessWindow; -} - QString Config::getServerVersion() { QString server; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index af3104d8e..2049c4fc0 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -4,15 +4,21 @@ #include #include #include + struct UserBootConfig { + QString recordPath = ""; + int bitRateIndex = 0; + int maxSizeIndex = 0; + int recordFormatIndex = 0; + int lockOrientationIndex = 0; bool recordScreen = false; bool recordBackground = false; bool reverseConnect = true; bool showFPS = false; bool windowOnTop = false; bool autoOffScreen = false; - bool windowFrameless = false; + bool framelessWindow = false; bool keepAlive = false; }; @@ -23,6 +29,7 @@ class Config : public QObject public: static Config &getInstance(); + // config QString getTitle(); QString getServerVersion(); @@ -37,25 +44,18 @@ class Config : public QObject QString getCodecOptions(); QString getCodecName(); QStringList getConnectedGroups(); - void deleteGroup(const QString &serial); - // user data - QString getRecordPath(); - void setRecordPath(const QString &path); - int getBitRateIndex(); - void setBitRateIndex(int bitRateIndex); - int getMaxSizeIndex(); - void setMaxSizeIndex(int maxSizeIndex); - int getRecordFormatIndex(); - void setRecordFormatIndex(int recordFormatIndex); + // user data:common + void setUserBootConfig(const UserBootConfig &config); + UserBootConfig getUserBootConfig(); + + // user data:device + void setNickName(const QString &serial, const QString &name); + QString getNickName(const QString &serial); void setRect(const QString &serial, const QRect &rc); QRect getRect(const QString &serial); - bool getFramelessWindow(); - void setFramelessWindow(bool frameless); - void setUserName(const QString &serial, const QString &name); - QString getUserName(const QString &serial); - void setUserBootConfig(const QString &serial, const UserBootConfig &config); - UserBootConfig getUserBootConfig(const QString &serial); + + void deleteGroup(const QString &serial); private: explicit Config(QObject *parent = nullptr); From 16a9d253eceea450bfb0e693a117b8a8e20e0422 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 16:56:35 +0800 Subject: [PATCH 18/24] feat: trans simple mode --- QtScrcpy/res/i18n/QtScrcpy_zh.qm | Bin 4052 -> 4052 bytes QtScrcpy/res/i18n/QtScrcpy_zh.ts | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.qm b/QtScrcpy/res/i18n/QtScrcpy_zh.qm index 16fef0dc63d2df057cf92af3be7674df139c1972..be37ff083553f0a6f96ec544ecd69986f75bd225 100644 GIT binary patch delta 26 ecmca2e?@*n7Yj?xzUqd_eJp#xjLm+mZCn7I7Yhym delta 26 ecmca2e?@*n7YmCY!&JS=eJp#xjLm+mZCn6>O9;jQ diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index 9315d4440..7b9c6cb1c 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_zh.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_zh.ts @@ -97,14 +97,14 @@ Use Simple Mode - 启用一键模式 - 启用一键模式 + 启用精简模式 + 启用精简模式 Simple Mode - 一键模式 - 一键模式 + 精简模式 + 精简模式 From 3e4e693c0110515b486172e949bc6d33b74d315c Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 17:13:11 +0800 Subject: [PATCH 19/24] chore: find device frome serial box --- QtScrcpy/dialog.cpp | 62 +++++++++++++++------------------------------ QtScrcpy/dialog.h | 1 + 2 files changed, 22 insertions(+), 41 deletions(-) diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index e18bdac1f..467892224 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -518,24 +518,30 @@ void Dialog::on_usbConnectBtn_clicked() delayMs(200); on_updateDevice_clicked(); delayMs(200); - if(ui->serialBox->count() == 0) - { - qWarning() << "No device is found!"; + + int firstUsbDevice = findDeviceFromeSerialBox(false); + if (-1 == firstUsbDevice) { + qWarning() << "No use device is found!"; return; } + ui->serialBox->setCurrentIndex(firstUsbDevice); - 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"); + on_startServerBtn_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"); for (int i = 0; i < ui->serialBox->count(); ++i) { - // 连接第一个usb设备 - if(!regIP.exactMatch(ui->serialBox->itemText(i))) + bool isWifi = regIP.exactMatch(ui->serialBox->itemText(i)); + bool found = wifi ? isWifi : !isWifi; + if(found) { - ui->serialBox->setCurrentIndex(i); - on_startServerBtn_clicked(); - break; + return i; } } + + return -1; } void Dialog::on_wifiConnectBtn_clicked() @@ -545,29 +551,13 @@ void Dialog::on_wifiConnectBtn_clicked() on_updateDevice_clicked(); delayMs(200); - if(ui->serialBox->count() == 0) - { - qWarning() << "No device is found!"; - return; - } - bool found = false; - 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"); - for (int i = 0; i < ui->serialBox->count(); ++i) - { - // 选中第一个usb设备 - if(!regIP.exactMatch(ui->serialBox->itemText(i))) - { - ui->serialBox->setCurrentIndex(i); - found = true; - break; - } - } - - if (!found) { + int firstUsbDevice = findDeviceFromeSerialBox(false); + if (-1 == firstUsbDevice) { qWarning() << "No use device is found!"; return; } + ui->serialBox->setCurrentIndex(firstUsbDevice); on_getIPBtn_clicked(); delayMs(200); @@ -581,22 +571,12 @@ void Dialog::on_wifiConnectBtn_clicked() on_updateDevice_clicked(); delayMs(200); - // 找到第一个无线设备 - found = false; - for (int i = 0; i < ui->serialBox->count(); ++i) - { - if(regIP.exactMatch(ui->serialBox->itemText(i))) - { - ui->serialBox->setCurrentIndex(i); - found = true; - break; - } - } - - if (!found) { + int firstWifiDevice = findDeviceFromeSerialBox(true); + if (-1 == firstWifiDevice) { qWarning() << "No wifi device is found!"; return; } + ui->serialBox->setCurrentIndex(firstWifiDevice); on_startServerBtn_clicked(); } diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 6db054a14..9a5ec571e 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -82,6 +82,7 @@ private slots: QString getGameScript(const QString &fileName); void slotShow(); void slotActivated(QSystemTrayIcon::ActivationReason reason); + int findDeviceFromeSerialBox(bool wifi); protected: void closeEvent(QCloseEvent *event); From 8ecf9a0f97e86fa309775b9fd52eec4d2579dce4 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 17:19:11 +0800 Subject: [PATCH 20/24] feat: add doublic click tip --- QtScrcpy/dialog.ui | 7 +++ QtScrcpy/res/i18n/QtScrcpy_en.qm | Bin 5193 -> 5300 bytes QtScrcpy/res/i18n/QtScrcpy_en.ts | 93 ++++++++++++++++--------------- QtScrcpy/res/i18n/QtScrcpy_zh.qm | Bin 4052 -> 4121 bytes QtScrcpy/res/i18n/QtScrcpy_zh.ts | 93 ++++++++++++++++--------------- 5 files changed, 105 insertions(+), 88 deletions(-) diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index fa800823a..b098da0c7 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -63,6 +63,13 @@ + + + + Double click to connect: + + + diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.qm b/QtScrcpy/res/i18n/QtScrcpy_en.qm index 99b69d0e43fa7537f04a2ca49924a62c329ec9d6..32fa7302b8384a4d26db165ddc6ad34db69ba0c9 100644 GIT binary patch delta 671 zcmWkrYe-XJ7=F&q&N}Wk2rS=(bTnou|sfAydv&JAl*-s7D?He#yP(H&Vuh zZuZ%vvYQ?3+aw*0CjdAt@0~nGfe+<&TOnZSlb?QXMKj=+zqJ&w0g5+yE?~}5iqgMO zzy~EdaDtnRE3xBuxj{hrnl?e9W6F%LlKCa3-29~^9yL{Fz2Nwm>7STFrv$S%D@1}s z^N!baPW@!A-}altdsHps4)evT^J)_FK6O#v6#2zY)%)Z=i3iojLn>vQ*YqYT*S2a8 z(%+GITpJ!-#{q|>pmGcQ8ZA}#7I3p)mZqGybjE6l$A&1R+cNDbrX$xa(?i_UT54@8 z$syqp>&dEf#NBB9`+17al-ZKqt*i^SYa^aSAi-dFP2QmLpZ0L|B#C17@D)CVsms1U zUP9%Hy?=xNrtS8RZQMXe40EWwcY~ov*qn(%=%_~kwFtq5AY+*S8r~zU%w}}d@eA=5 mfyXp!wak-LsMiN-L#|+LI9TI~=&qn%R~HIKJiR}}3det0%%DyH delta 579 zcmWkrdq`7p6#nkEd+&Dd-R>&GOj(f-&XLi;bTQamkqm+cSro{qz~+P!0#UM@)gX*1 z7QKkjhyw9Z$TC7qd$1l9Jut(9Ae=36K~Q25tor)v@cYg=-{bslJD-(wJ7Oge6DGh= z0P#*OV0#B?u^iBIki9zr`&!8V`|=yey%9iJgQ8?5@i}Zb7kGv>nou`8Z<0UTLm9zyZkq!exN@m0a!mOoA3U zG1kQ-t@2>!U0&v|oGG25(I)w4Fw8^~W^Yw76CE~(-Osr`ZT@2{B1@0O@2=zAuw~zC zUQRh`Y1+L&VY{N1jS&B$IQmxrmW;B>H%on^SMfg{Cs3;#IjNAKQf+Lbb7euj?|M(6 zR`uz`R`!RqK)8lAUyIx;;Dx%hwu-kj*so;=Q#2CLzNMH@`)=(BRj?LY&qOX0pSS-0 zG)tWywn9&wc#myhGLIMS)mNw8H<`Fok2a(!7}TSE`~tI2Kb8&g)YtXVNfOAv^p72M TF62eIwV13i3=7G6(>D7*^ska^ diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.ts b/QtScrcpy/res/i18n/QtScrcpy_en.ts index e2ae0fe34..2161044b6 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_en.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_en.ts @@ -49,48 +49,48 @@ Dialog - + Wireless Wireless - + wireless connect wireless connect - + wireless disconnect wireless disconnect - + Start Config Start Config - + record save path: record save path: - + select path select path - + record format: record format: - + record screen record screen - + frameless frameless @@ -119,59 +119,64 @@ USB Connect - + + Double click to connect: + Double click to connect: + + + lock orientation: lock orientation: - + show fps show fps - + stay awake stay awake - + device name: device name: device name: - + update name update name update name - + stop all server stop all server - + adb command: adb command: - + terminate terminate - + execute execute - + clear clear - + reverse connection reverse connection @@ -180,57 +185,57 @@ auto enable - + background record background record - + screen-off screen-off - + apply apply - + max size: max size: - + always on top always on top - + refresh script refresh script - + get device IP get device IP - + USB line USB line - + stop server stop server - + start server start server - + device serial: device serial: @@ -239,34 +244,34 @@ Config - + bit rate: bit rate: - + start adbd start adbd - + refresh devices refresh devices - + show show show - + quit quit quit - + original original @@ -276,43 +281,43 @@ no lock - + warning Warning Warning - + Quit or set tray? Quit or set tray? Quit or set tray? - + Quit Quit Quit - + Set tray Set tray Set tray - + Cancel Cancel Cancel - + Notice Notice Notice - + Hidden here! Hidden here! Hidden here! diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.qm b/QtScrcpy/res/i18n/QtScrcpy_zh.qm index be37ff083553f0a6f96ec544ecd69986f75bd225..e8b1e2163287a218a0f30fb2929e4debeaafba48 100644 GIT binary patch delta 633 zcmWkrYe-W882#?H``F#x20h58Yy*Gk$ZVvxC2ek^SP>Ij6x2Y+np?|3Xi5)Pwg_#N zMbZRmbwXOCij$D=P9>w3scs zI*d6pyY0>Z@CWRP{1PH+=elxz&_B-gYii;5m3#IjN)e(u?(-1`28g#8F2#E-UvK&d z0iKVIpGJ}qK7Q&x5_IvON@qdb!2fLUpf3v5HN`M)7Ch#cpbrUu zp2FpXgUXKW-(cJ*s><%7e_qtzEP&4+(O{c{zbq}OnJcdEyx zH!z-7J)fupJ|#IkTTz3O|KUpToKkmH28+Cw^6?~i>!o??MkKaL^GPHX-RfRf6%14A zv;ND#C)B?`%pq8-rqB`vZqVFHTbJRqeOhDgHiQ-0Krjd6E!w~ceg*eJdpPd`;g@zS z4FPUI`@ROy+DdikFBeSY5eJLk+*_B!IrAf_yU za24YHZGg4_(r*vzAINo^0Nn}se_!%Iz8nVRZ>UJGCO(2y^REF)47)nLfcYz;u5Q56 zfzCaT*l$E?c?0ow!JB_gy{O=G_yKXF(7ZB6zb8U4dlrbB!oqN(k3!?Z+m~e&=r`5W zPXZ>dS(|iF=!f~SdIhj8wrmZ&r(n`@xcx2#e3t%}3~QMv@2n#4xwz0%q`|aUeenkQ zzr@Xhr`bOs9xcuV&>`(B9OvA6sn@;^untONUlND|P15H>0S-X+m(Sy-Gjg-*69rz# zsmuum*ewtA3~_@s@^n?6L_wZu3Gw|(sjFW?!=*~dJx=;{W!79lzTN6~@8DdGb^9A$ zPFZW++xC;jC8}0^kN6hVajTSWqiUsZivFfEs{iQ-fsnfYh)TgBE!M^4$|Y^o^^Qgt zv}X^S*gvHQLanST^zeg42`{?Aa2DKmn0U;HL<=;WV?=K93#1<7U@=HS#u%QUfYfGuNHRHc R5web@TTKl@I%jUw{{ftCjMV@D diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index 7b9c6cb1c..8b6fcb4e6 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_zh.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_zh.ts @@ -49,48 +49,48 @@ Dialog - + Wireless 无线 - + wireless connect 无线连接 - + wireless disconnect 无线断开 - + Start Config 启动配置 - + record save path: 录像保存路径: - + select path 选择路径 - + record format: 录制格式: - + record screen 录制屏幕 - + frameless 无边框 @@ -119,59 +119,64 @@ 一键USB连接 - + + Double click to connect: + 双击连接: + + + lock orientation: 锁定方向: - + show fps 显示fps - + stay awake 保持唤醒 - + device name: 设备名称: 设备名称: - + update name 更新设置名称 更新设置名称 - + stop all server 停止所有服务 - + adb command: adb命令: - + terminate 终止 - + execute 执行 - + clear 清理 - + reverse connection 反向连接 @@ -180,57 +185,57 @@ 自动启用脚本 - + background record 后台录制 - + screen-off 自动息屏 - + apply 应用脚本 - + max size: 最大尺寸: - + always on top 窗口置顶 - + refresh script 刷新脚本 - + get device IP 获取设备IP - + USB line USB线 - + stop server 停止服务 - + start server 启动服务 - + device serial: 设备序列号: @@ -239,34 +244,34 @@ 配置 - + bit rate: 比特率: - + start adbd 启动adbd - + refresh devices 刷新设备列表 - + show 显示 显示 - + quit 退出 退出 - + original 原始 @@ -276,43 +281,43 @@ 不锁定 - + warning 警告 警告 - + Quit or set tray? 退出还是最小化到托盘? 退出还是最小化到托盘? - + Quit 退出 退出 - + Set tray 最小化到系统托盘 最小化到系统托盘 - + Cancel 取消 取消 - + Notice 提示 提示 - + Hidden here! 安卓录屏程序隐藏在这! 安卓录屏程序隐藏在这! From 09c53d68eec5323c6c0cdda568f6a69c9647e52b Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 17:23:09 +0800 Subject: [PATCH 21/24] fix: nick name show error --- QtScrcpy/dialog.cpp | 7 +++++-- QtScrcpy/dialog.h | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index 467892224..e9b56e308 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -132,8 +132,6 @@ void Dialog::initUI() updateBootConfig(true); - ui->userNameEdt->setText(Config::getInstance().getNickName(ui->serialBox->currentText())); - on_useSingleModeCheck_clicked(); on_updateDevice_clicked(); @@ -622,3 +620,8 @@ void Dialog::on_useSingleModeCheck_clicked() ui->usbGroupBox->show(); } } + +void Dialog::on_serialBox_currentIndexChanged(const QString &arg1) +{ + ui->userNameEdt->setText(Config::getInstance().getNickName(arg1)); +} diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 9a5ec571e..b29d123c3 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -73,6 +73,8 @@ private slots: void on_useSingleModeCheck_clicked(); + void on_serialBox_currentIndexChanged(const QString &arg1); + private: bool checkAdbRun(); void initUI(); From 041e327d9f123bca154e6d276e875ff730fdfce2 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 17:34:43 +0800 Subject: [PATCH 22/24] feat: config simple mode --- QtScrcpy/dialog.cpp | 2 ++ QtScrcpy/util/config.cpp | 5 +++++ QtScrcpy/util/config.h | 1 + 3 files changed, 8 insertions(+) diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index e9b56e308..71a4a07de 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -165,6 +165,7 @@ void Dialog::updateBootConfig(bool toView) ui->alwaysTopCheck->setChecked(config.windowOnTop); ui->closeScreenCheck->setChecked(config.autoOffScreen); ui->stayAwakeCheck->setChecked(config.keepAlive); + ui->useSingleModeCheck->setChecked(config.simpleMode); } else { UserBootConfig config; @@ -181,6 +182,7 @@ void Dialog::updateBootConfig(bool toView) config.autoOffScreen = ui->closeScreenCheck->isChecked(); config.framelessWindow = ui->framelessCheck->isChecked(); config.keepAlive = ui->stayAwakeCheck->isChecked(); + config.simpleMode = ui->useSingleModeCheck->isChecked(); Config::getInstance().setUserBootConfig(config); } diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index ee7397af6..e5897ef7d 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -84,6 +84,9 @@ #define COMMON_KEEP_ALIVE_KEY "KeepAlive" #define COMMON_KEEP_ALIVE_DEF false +#define COMMON_SIMPLE_MODE_KEY "SimpleMode" +#define COMMON_SIMPLE_MODE_DEF false + // device config #define SERIAL_WINDOW_RECT_KEY_X "WindowRectX" #define SERIAL_WINDOW_RECT_KEY_Y "WindowRectY" @@ -141,6 +144,7 @@ void Config::setUserBootConfig(const UserBootConfig &config) m_userData->setValue(COMMON_WINDOW_ON_TOP_KEY, config.windowOnTop); 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->endGroup(); m_userData->sync(); } @@ -162,6 +166,7 @@ UserBootConfig Config::getUserBootConfig() config.windowOnTop = m_userData->value(COMMON_WINDOW_ON_TOP_KEY, COMMON_WINDOW_ON_TOP_DEF).toBool(); 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(); m_userData->endGroup(); return config; } diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index 2049c4fc0..e978be435 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -20,6 +20,7 @@ struct UserBootConfig bool autoOffScreen = false; bool framelessWindow = false; bool keepAlive = false; + bool simpleMode = false; }; class QSettings; From f929e20d8bcdf744446d8776a5bffb99041ee7c0 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 18:04:19 +0800 Subject: [PATCH 23/24] docs: update readme --- README.md | 4 +++- README_zh.md | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a3327b1e..fb92d8284 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,9 @@ 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) diff --git a/README_zh.md b/README_zh.md index b16147a74..32a3c6572 100644 --- a/README_zh.md +++ b/README_zh.md @@ -128,7 +128,9 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: 目前只在ubuntu上测试过 ## 运行 - +### 精简模式 +在你的电脑上接入Android设备,然后运行程序,点击`一键USB连接`或者`一键WIFI连接` +### 非精简模式 在你的电脑上接入Android设备,然后运行程序,按顺序点击如下按钮即可连接到Android设备 ![运行](screenshot/run.png) From dbb06a0f389cb95761c33ca7fa114817f9262c2f Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Sat, 17 Apr 2021 18:09:17 +0800 Subject: [PATCH 24/24] docs: update license --- LICENSE | 3 ++- README.md | 2 +- README_zh.md | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index 261eeb9e9..c37d81f54 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,8 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright (C) 2019 Rankun + Copyright (C) 2019-2025 Rankun Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index fb92d8284..8d6ea4665 100644 --- a/README.md +++ b/README.md @@ -262,7 +262,7 @@ All the dependencies are provided and it is easy to compile. ## Licence Since it is based on scrcpy, respect its Licence - Copyright (C) 2020 Barry + Copyright (C) 2025 Rankun Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README_zh.md b/README_zh.md index 32a3c6572..4aba9f11c 100644 --- a/README_zh.md +++ b/README_zh.md @@ -258,7 +258,7 @@ Mac OS平台,你可以直接使用我编译好的可执行程序: ## Licence 由于是复刻的scrcpy,尊重它的Licence - Copyright (C) 2020 Barry + Copyright (C) 2025 Rankun Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.