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 d06f091..11dd20e 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 c93706d..b73f3b3 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 f3b16b8..105ba56 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 50dc8fd..89ec6f8 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 0bcfc81..bfddbb2 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 aa42ddf..a08fd98 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 bbf912d..5b56112 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 890ee43..9f55a0f 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 953a11d..7b171a0 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 3a7ef06..29f77f1 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 27e56eb..a8ba69d 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 380247d..e455060 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 @@ public: 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 @@ private: { 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 f16c631..ee1c6da 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h @@ -32,7 +32,6 @@ signals: 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 5278cfb..d568759 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 63924e1..91d0681 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 e2bb28d..24673ab 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 1ed0f22..1bcb40b 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 5e282ef..32d819d 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 55ca712..78c5164 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 38bc164..bbbf693 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -70,13 +70,15 @@ signals: 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 1ff8c02..5616622 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 36a6ef4..65e705e 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 6df5878..fd2421d 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 b067941..8966a7d 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 681b422..e0e7f51 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -23,6 +23,7 @@ public: QString getAdbPath(); QString getLogLevel(); QString getCodecOptions(); + QString getCodecName(); // user data QString getRecordPath(); diff --git a/README.md b/README.md index 6427b5d..9a3327b 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 1973cb9..b16147a 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 ba11654..6b85845 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 4d3ca81..8447d77 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 delta 34525 zcmey?#Pn$bQ+P4hif>|GW?E`- ziC#r+&eTa8^MoBmT;Es7Rtw7%C`EHAZPE~670TYpn2t=M;JdcuKgsZWc7W;W##g=U#)FV z{48ZxolVYfe{t-JPvH7*y)|#Qy{}Ox>El=l5E%KP#Km9-*>+XFs1N?7ht0vEDs5M|bS>I-;Ibr2x zU#4r!av|NFODZ9cQwd{az7VV_8dP58N&QS)vWe4h1s)xWyg zwWo`t=T%3X*!pp?%=DkN#^>Zz8vyy3O}*0$G1OeaE)d;fb&?|c9M|L;E+^3UC|y!`yk8B62h_kOc%j90Iz_OI&m+yChKt8l&}uHCSX94&y}+Qr!hmH3+pEUpM$QAB2Q&|u zJn%^{+aR(-c!zidvkjvS`yPfpJbzgJF#X|s!?xyl#~}yB3t}%A-thA#kd2F4)zH=1zZOF5nO8+ z=P}taU71|Z!QI2+$Ee4Y$9|7tAIm;=qxni%tW%i&HZDBik|0)KyMgTn+aBH;raxRy z?0vWB8wl)Rt>CC&s*t`Vcf%A!y!^{>s}&Mz_)>`Lgoic!1=|z zjXVo=~KY-cQIINkWWp}pbifvN=V4=NvQK1h8~`=I)P_k;2W=?~)1 zH}`6ApJ3){U~b4#U_8VAj>V2)k^X|%hR=;p4}?A7R`{g!h~aP}%LaxW(iLJqL^$@f zl(F(LwJ|+ze0gAM{Q;u{<_~Hgm<1|luFy6RSi$^^iJ$3qV{F53u45tvDg{;>L@FdI zWPg}%xW3vY^nl2Ns08B;%qQx2b2(&K=kTv#ies%~-<8w;hS9h&yJ30b{e~N_Czw8n zcwm#j`GMnu<%ExHft&^`9t_-#p$Ezyh%fj$TjH{VY=PberXMUBU+cv#Dm?H>ko;gZ z;iK6zt^}gvZhY#=_I5cOg`2}eSzBw#s7^g9;ZV+$iZ@9zX z(bahGKm^FT4WbpiKcqP7I_e8KzOcJ7t1~}m?ryMdiKNQUhT3m>Z<>pJFlEZ3ONGw3tFXN+gEXZ~V%ibsU`HzPaq;fBWz z!VQ-jx*Jsws6AkPkoq9^LGgp|g!TRg%NY+hY96?G;OT+h1I`EB9%MdXPEfD^z&+uw z<}>C6+82ahF#9l^V}HiDor#}uJ%c?%<9UO74RQw*9#}jmNsudG|Dbx|W$~hY2Us2` zC76C-{GcxIgQ?i|0;>g!1YZwBAG05;RHJd@=7!G=-HogVlpa()NK4TCps;}}f<1># zj(Hwy9OFHvd+c^>pmbix{Ev+(Ua7wR0P8I#*~W7Ys-QgR#;~}d@PNvLlm{jccpm6H zuzBG0K$ALbX{YJZV8kh2h;!LP$Mjaj;Y;H_E;PD_Mfvv!BgFpoD9+p2`B7eAxc;+z0@ZRCK;jQ7jaX;Z4!!E|X4fTf` zlp7}=s7T;4;NHO$!F7jw4VxS**r|+V4EGs!H}>QkvNOmwoIB9yP_Mw}!{Wyz)yUnj zx?$-7s|TJB!WCXyuQ5DecVOXxzylEpN(E9NJ9n_|;J(4Jhoy$$4@1QLr*?_J*kkq!PF{@K&(@kom#M@u#tn;|;$I6Az;svvA|&hM5N} z57;~qtACJ}V7h^`f~$hHLi&f$58)Gkg+KELFv{@B@cOX&F{d$dH?$t$Jz$hz`hnwv z$_J(mvOgq#2>oC@@rV1fZ~)H~#=Q-72Lum9JTOU6E6_9G-@&|c1A;ci3G0`mfi3-S`&bGXhhonsbbGG{1f{9e!ay3xHsykUETf8%|Ia`R^@ z3xr4hjdXV@aGr{_U;RoggO&Q_};u-aQsnf(JMrNIXzkAUK2f4C6CqbB66q zryF|@1RuD4;P-*_1O5l*J6JG(WwT;j>L3ZO*qJ(*lN(n$FdGQZV86qn!}^Zx8nZux zKGS>#>&E+xQ|nirV01e0@jyy~R)N+9j*_ESw(1usO<)jX_|EX1={w_hW_|{KCVock zhN%a%HvhlCvWD>*)9Z$>2iOvnH*ntIvEj*Klw;h+)X!w_k~7Wf;2*Ua*I9~ZCa?*g z32TbYqt`KDj zZP?IrdLH+*h(ke|zx5*aad-oEB z-84hcrSU>)OWwgd4PWKzIhZXSvEK`evarEzZqpM$#sn+W4k z2hIzUH>6)Mm9Tm><{hv*uUSR2JnS2+-;O|@LwQaAP~U$h3yvuJ99fj zJ5xJjZKKu#`3v$A3_L7sjLnV8jfw{X5+nk6=WyNOvSHi9v4>?3&mN}wJzRU(a@gb8 z?=jqCx%X&Sz$^LFn^W%u_x!$mHtJaJk(eTQ`KZ~qplGH1*l9 zclocXr86uyoH?@gL+ks@OX>gPj{RqQ{A`E0^mpNo-A8uBxBV`Ad@u3T&C^RZ-`TPA z&zpCL>pvwQk~x(r^*zz9eCm3hzcr~638xPHYpzX|dz1ZHQKx-boO5mBjkh@$U-Eoj zYJ6wPgPeD8VN zb33L>mh%h$lZc;@T>I{E&AZ37?>gY z2PHmQRxsG8Ys3nbLf>e0JT= zW7p?h|B+I?@#Fq$J1Xz(JGf(SU1ZL$)Rl$nZ!9|gDarV_p4q0?Uw@z7_v!wd*WIf7 z_C;jfDWBk%y?5nlBjdP=y}K^AJS=^!_jupk9g%hK4Dx2q@BPgw7k}*R--_wa`|C~K zo4w2Zu>bgu`_0um3+uVQ?>%<*>)!J__Ba0j8y8n#pKwP<`gM;rk84hE;r7<**23ct z?q%Hh{qs=BuREdrOD@U0DgHd?&xFG32kN&L%D?Tl=6v@xuC}&tdahrq3q5R)M)e<2X-rAXe@1Fk5bf3|kH|MWEudm;K{(n_`oUh&gvv%Kw_wBg-;NFv| zjPJG{+k04C?M?Ro{F;}4o)v$d5%u)s`D-C}3io}w^M1AR>9@_t-sbskjlR8hpIz(j zldps0XW8(p-?gisr)^eI`-;1`L;uXf=-S*zuXiWf{r_<-#OiU)JI#XpZ+!L(pRWoZJDdF0^YJsouTu&?|N6J3@P3cU z{Evrn{B~CEvB|$vu6$1<}QZN^!X_H(Sf$x|)5Ql+F`v1$bC&Eww|8NNj`BXd^%yyeor%vNw)T}pa&L1Omz zpl6pwwuL^J&yaimrIgjAw<16I-GAsUSi@an{PYEz?u64D`!$c-J%~TCjcd!Fd)tz=x7xb5=8}NBd@}EN7JhzN`T5Vhh4Y)_SWZo2!0vA?c0C1jQU@~UlN}DS9+n? z_RV!y^3-KtosI`LUvSpEf9YRyi{9c1obS18d(yaN9qWr*^_HjSUi+Rq>v-Av%=cQC z@BRG{lVEuD?S=bWs_QSW`|E4{&HSwe!@pq9;B)m2lJ?9#|9sLpWtEHU1eD_=eM)~g zO8lJ`aKiM5yTj_l+fO-c;t^paWc@^Oqb&R5{aMcHQdXBQxZh}V|54a>PSk2f+@_ml z8&7VlpK)%vnbY*(@P(B{?x`J`>Yv+ZJU0`{H`r?W(OP=f_O*}C8<+Gh?3ett?yT8F z-N$n@`#&d6(3X3C_`{v3dF_`17FNaZIPd9qSRENx`JPegL7>@fFRMERTVAs+Fty?T zc&EQ8&i7a4mP;k87RH>HI+CCKvcS1TZ<)XD+4njxElTQpE~J)B3y7UBn9;8%7Cf6x z;+ET=;}`fYMD;YBcW{@IYGg{aKs^p)@wQ}~^-ww$yT)V&5MqTuE%H0*2 zk$p@2k4{AHp8b3nPB;pV;iX^#8*OY`0OZ)jFE^G)x6-mP=5 zeOG5!OWA^2US08B`m!n}E}>5T3#wb@xqs)1b>N)+@@mPfwe!uTf3ajVa>hGYXU@Lx zc8l~Dfz7JV<2~+Q$P{~D`|{J4W$qcBXG^s2YJD-><@{pu7N#w{xf2U=FYK59B&Yjm z>!ynOexKv{vuomDjyFw#@Z3&$`q(tL$CEJnIW5zw9|$ zqTMV0Up=$w?C-p}nUgMTe<^!G;!yv#%JsS2yjy3KEi|g!mf3ZIwWRC9>6hlh@5-J3 z^F}efu33D6qn2rxxk}FTv%fdoELm~X#HU&J@1LCu?>E)QXPt`7da^>~<#&O-ey8Kj zAKyQFBG&xy{o_Y&AOG;(@y!0#te>~pE$1j-;a-0`RsNU5ll=I{`|WGJ#iM<82Ws2M zH+($uQZD0?y};^6Oc#Z}7Fz~AC|hap>GItl-#so(ca^KYkR6ip`nh|~+5Z7wj;O1| z_+NQ0*8Q?@OYDo*l8*Wh-xoVB>VNsAYN1Q&qUW+6cUz|g^Dh*=5}YCVRp{2-s%Wbk z`NiUL=`TW@#ie!~x$x9!`{ln*%3mg2(2CLwxtJxKxA%f@irmBJ(|K!szu3f{YyR=Y z^~>?Unf1=AdETaH)H}0FRJAN@Zk^}g&UM#8o$IZCaI4jqT^D2)PF1sCR-fK&w}WBR z=`VF3pFdx>$Up3o^0c~t7cS(t?DMW zUe#mWN&fQ2alaM5CoNQdS-Q}6#q^fj^ZmqrX~e30aoiR0*9m=@7r*otA%fg?4o|Y`bGUM-xl?^#(B->c6aeh4U}H z7Jk39cN<^*{I!R(Ya_nLi&mA`cdwkbT1z?n?}GPYC-41WdZkeHNzCe6nr+1K*Zj6$ z_%9aMub-5Dt@^oy?T;YmeAAM}7Y=^mdGS!~-@`3^bz5hy6Soq0t@E3IN&Q8Y#ro$3 zZGTFBly9l5+hp_l>jRF<^6oRf^eqs6>Af_w?}Pp_W}Uw^GZwy={jCT@7}U-gUfuiHQB zwW~Jvo&WWjb5ZN_t8XY79Qw_Ej(!8Mki+Je~^oL@RFtdf&y`<3GL<7&zD z3#T)7UJ%dRdV_aY?aXIUKEE$*bZXvm=)(HeeU8&M8uJOfx^z4#K4WjXg{kDK)Ao0! zBwMIFN?f?>&tXHOG!L8D{>483OutN8Xnw*fq`9bE>{jD9@hBg5uD2edPWemg>zwW{ zR&KR(_%HU0SF|g3(R0OZ>0(@aoAbn~lx;I!fAHsu_1@nxw`lJZd!|YEJl{-L)>JFe zms0(rBBgEOl`i%BnT@kKhpf-~8PCnne4g#ZzkL3Yf024Gug|J1T2d*Od9B%E?S$t$ zD&UTPgTT=l3PiyE6#W9 zUHUYq=cW3#-Rq3L=sMdAT1EWI0kO_t`}&}I(PX2;>(`0O2g^%`Kerm<` z`OQzU%lAye-$G@ZNtt=5Nj9j1N?7yxw+c zrBnN5R;T_&*)7*DXf3S19JWyTMdZS*U$))IxBC0|?5>kr>fK)GWSNxAyOCdIH!tB+ z>aD0h-YGG@29GnDj@|5ZO25eDEN}jiUS*{--3ze}1gEG?OQ;dW|`ugTMy9Usm8 z-W-lJxqm^Mcdz4$xaU89sxG{Ja@vpbiz}VPFPL4J+&j0xYf|3wePZ_;E8^z)KQ29( zHcNdAsZ}MtLkZ)-TZU!-V01p8ebwl?mzM6v8zPk@`ztQ9UlLeePJ_CyyIp4QIj)C zR*`Rxc>C6Uei`cg)y63reoIe)pgs?I(7V!d_Cx#jAudCU7-=J~R2 z<1@c>?fk5H-p4uTX1}ORdavi^FQ0$NoWpj0o!P?~arw7CvwiuIcQpOwub*m9wjWNa znsdY0d~tpK{-9a!at&qmpYENhw?HS_-kfV|>i@54zvjhl{P`wg`+1?B9KGvnzsx*w za_tm<`RJPXRJph5pCwPLmdrc*{Djs2)D!a`>n_&MTy#NvX{@|cC6{h(ygTdHxr}zk zw)5XNYzYlHDSq_KT?zYoz4^}@<5Pd!P32cDjZc(!W^ewVU9Y|G@~m^~Q*MiW>}R~6 zw>D*c)XyI^_f`4+RjU1sJNxMS*K-FC@+J}7tpW4G_mY_rXE3|w`;WnA|ju)lKMPUW@1#rY59XD_~N zpTqyBz}i(^s_uT;*W|kD5B1Uq>{(uhn?9~CPx^N1>c`)vk3ZYa{44N3;PCtVYwBv^ zpA~#K-gIC5C4Yw4{oUHdY(JV0+V7E^|9(Y}^Pk;5znFc0O_$o|ZZ7?AL0ZjwzEiW? z>RmQ}`I&d@rpJHx5A90z`?fy`d->b=PrgOJ?1$|?-M>q+o=I+=!@vGUd%>Ul`N!s`Jo=yZNdD@Y zKf6Enzij+D%YKn${jSdX4G;Ts)b1NU_$&8m|B(-ms~^-e{}Vba|MYRb*&q9Z`9^;( zp8b?wXR+#q=3)8EJ@%)kyw^W$zf1Ce&f)bP8TIb5#{U+#*&q0D+$?+l-pG9u>*qea zZvI&QOfB0#)i(Qc$De;Z{rIENhxzRPS-zY%Jo`yLKjS%*?Ef2SHT(Rx@_giH{;zbz zex2lhmUs0Z{7pX|H-CKIb6v4{-j;?{&$W%XNfS@W#9QEI_6pC#={4>ljojYB2ly= zgz**2S|#~1Z!^;)MhQ=!%>MSEUVV1i3q`Q9ti_WYtR3HN7rxII>!IedwLAC*}9NZJytCpI?{O3{Ntg zw@Rem_0#7Nm#3P1$Ddk?Jv3Un_b0E+y2wR3{7X;%l|R)mb&uk`AT~YEQx5;^PaM$C zG&0yelcylF>A^;xm~EX;?wF`D@0XDDIMSAM<;d@+?;VAX{Z?_@z5J+JVsEA*C!;dw zpGb}`>eox}+kU%hfBKmI*_7mXW$E8tIVH_GGIxEP_GUD%a+T(a`RuYbp;>K?cMt)Kbq{f9`q*K0oH zAI)8|Nn_LI-J*-UX53g>*5b9cBf4{6u1@w6rQKIDr|kZ<^Aelaqx-ujJ^9!3%xA&e z6^i$5AMe%*ys~6sd{({lT(&@tcj8Z2%qH1;w1zC+?R<~BTZEshRp(i&hPKGh&ioT0 zQ}~x8rQ3K_ajQLS+09aa*zb_7cZO=t9gQ%Lxn)!4ZC>2CwanzkJfYBV&Ge#`bA`2Z z*J!-wxaWC_`|CW>t@7KY@7|x79pQPR=~BVc$*o_+74BYg;QOV)y!E|Q{i+hCW800M z1eugzTp)(XuGP*G>`wYJ|C^!n<= zkE&a`l(e$`Mjs3dn)pZK$)wn~q5MyNw@on!is2Q^EBU`RhwJvlPgxx%)9*c8e)!`P z1N{U3g>_ono;#N9cY4wC+w^$hqDHy;72o4b)~-3!b@+SyhqI=|tfwz`Yk#fgJ6ghZ z>}6cX-yMf6N`Lw#W@@n4xd$b`(Qi3pnr-&PskWd&TQI%P;Mm!!^fFD34*@;>P=HJYIJlGAKMDq%IR>s3~}xHSUI9 z{Z?o8R=0H$=g-d+Ip?#(zCcRkrJ&|r|2+38Wu2EYv;}|r{n0k8Qg&VXHZa`!>2r(JO12flC+M%qWGWVxyoy+&X``huIc9-#ax7j)kU;ZlQOPqCH zSzdhlLB8azj1xgw4FKd;Z%*3VtEdH$*eg-7+-lb-#Z9P_!a z$Gm?_)YG$1RWpk#>n`n&_+xzb&yROrYrWkJy^D6AnOJr(X6D5Cfj-w4^H!KIheS28eT%)~J$L;16%lr9p`>Ms zQ+h#$9&`IVWpmY>l~1#^zy7wrw=2ZwYZzzL3A4}l#aDl^wktgIzn*itzv*U;IFkuo zpLXwFv9bH6-w(&jX8)s>-uLagUhrc@OTWgxkJHz0iLajPrn@`ZS!dIlTHS3q-MM$; z%v|S2i<~~oaOIVLs8Z>H8%K78&A9CCEp_MU4C_Oywj?)K_m*wVE=m9RYl#=P^qtjP z;^Ufkd+gpMb!YWf?F$d|M8fmW)XxcOi(sF^$kunt?_J=ByQ=%1UkN={X=NoLsb%(A z{Pe|3p;7JK;Y)9=V0c~RpQpHG>$iN}f4%Q`jkJ&a-WV_aPF?i<`nz|;J~H0w-I!(` zHrF-JH6rD_;pgLfS6A$FiA(*rhby@9;Qoty98c~0YvR`&UK79cJJTcHe@1VnYB@W1|I&JUeD{l+V!M4M zW#8UE*RnV8)-rYa(?2aXWqXz z{IT8XwO{&K{``(Em#SkfOs9iXAJ62ulwH`HyV9_}J)>tXx3AiqEA2aS<06*cPygKC z=ux+6n_+$KdE>Q9zg92$9-dj-?)@fUn!D)rn-BLn%-!jKX7l0mKHYC0UJ-b|^o>&8 zYpdApHFL{O&-na$4&%=HBX764J=L_8yP5x6OZR-$8`ay3_h9Wu} z4%;}_x&yHtlRnf}tXz?kgE{tC2Tf9(oZqLqL@?q`A7jTx8cGb@<{%|JhHT$PG zTOXGg-|sJ~R=GXhQa0?RO)c$dY*X#3|buNV+R<9jaZPs4F zcPl)0!R{8$8I{}5oLRlk;;GE?d#_q8o^})$%;J&T<8}M{z7IdGYVwWEnuXT=*Se;A z>5yn;s$}-}CA%IthR8d}Ei^J-U;pA(f5g)3M?>~7t%;1|d;CUjQ`(D7J+Vu_=GEWu z`BZe$|J2jUlV3N7-`Z8R@Z>OQfBO6Wn!fy}`lNrNng1?N z`giy8AAQeX;mdx;PyOzYqBrj+SZCq|J-Ffx)D(!RjauR;cOpL47nR$J5Fy-CE zG{e06h7#8q+0gCf(ew5nv70qJKKu$_&`j5`1r5*oyr%fJzB>|>#mY7Lyz*58Esm6c zC8ahda~?@v6*<{cpR)DTv01|NE}MS}SJ|#`zKA>EjO4qMd(N%cvGle|lg+1)=q=jU zA6~oqYp?6NX4c=vk^C*^IJQixy5dyd$x;<9d7)Qd&Umz*pr*04yW7iP@4G8HKO@elIZDrk0amO&z)WP zSaOATn(3uCE1#vNweBkCJ?Pb&@P&Wzr91Cj6>lwUl9bmF)#3f>7C&owL)PDYS)a>Q zqpU7jn(M3_FZZ3&?upGzKUK`QHE_;0-RBeTRn*mPHJ_K= z^Kbp}8>Oqi#w7=>pWiHc_0WlpyDm@kYr872n^`Rsyt%t z{g?SuqUR^h>WV2oD|Ps(PSo1(USF+bZr*$`&!Ot8Y|qZ!Qvd$2w*R^jxT0Qc>I(Li z`!X!9y<&e5?)z2MdBwiZ6FEw)gS59*{;iqJc3}Up8};mSj(4x#n)l>ua{h-$8x=3j zzq3ehhrRP^DZcRL-6Dlv|C6F;oQ~ZWRnM-x?}Nvc^42N35A_6U3**1Gy|LKeG$mf( zx>;BJ(&|32KRqZFs$Ov3KCS<4c!JEn)NuX%4yX*17BA%?CMC zZx}zQ@`=*cws2m{r8~TqT65>bO;3I}Fa6Ww*y3yK`j>x(-SvFC=F+P#ADZ{< zy3IM=-*A(xu>Fhz{fT#{wVdAlJ5qAt>gDazidVm%KJU=IINpn^%L_Yi)i3`XU1_<% zMSO-(Y*x{pfaz<}B)6`f5X+Zroon!3GA`-f*7+&t6pqi@yj82XP0(PQ@ac~(#&ya@ z(ceqDFQlhtES-Fy*X5N`wc~Xgf%Cxu?H#eimT=OXZ)aUB3Q0W8Rw$vW+3y6Hocpdz3bPHQu^y{=&?ydv(ek*5%*-{Q2rUzAIvh zKTUQ=Y<2!E(-o>AXw4xsXFiW~?xWwoa)f)_ykj+WZ_X}%`%y+;s`JD3nd<$~znZW8 zkGNj?V)oTvHhNyqZ%_3Lf3YHWLNM#cbFt-ro=&{ruA-Eb_^;)TRQ(rs{tK6`S(dGw z+I+7*p?mA9^aMy zAnfzDZPnB#Q=VI|w6S>V!?`-;{^aNPXKeKTE2FG^Ovx^UC0XGe(a)gXPT z`=)$* zXmv&WgI%m@^#8Ehlpg>1Q~8(f^K|ZZS3Yvk^SYvExPD=;SCPBw&c!_8?{8a0Et~aa zyEE5|f-}>~ohQ|~*uOs#!NtzCX5Wm1=Uc3~wH=e!=6==ruOV<|M@>j$sH;2cl+vG1 zniA~GSnau5ZrI<5ub-!9zgfY;A?9Oa@`{bGf;}qMy?A;jZKGOl^^xS#3EEe-youEc z-g&aP)!XcKach;S*jv-S6E}j-?5)|HcfaCh#H)s{#X3i|k0tfpR>_^((Hr`=GF)|= zDo3glf;q zU8<))hX?6;2x7TvAYbtWI+;Rn${+v`kt?v2rj#c`zykrw+=3n2f;i|rH zdickeH>X>Ec%OByY08}R%(;8#o)oW_x-FHvywqV{@I<}+{?oKp+R0xmj&}?4i(apI zQTeNd$F0+ON~!w0m!GPfxc2;~YcF=QeB2Z~(YbMHY>t2VmJ0n}ypp|BGlb`Vb2L9a zFY3T!>pk1AIh}tot04Y)2l`*R*dcPLzFm9~K zjnm5CsCsH$xZ&(zt!>K!uh=mefBU@3>7K5p!}?GE>TkBG?R9@~li{c(d%%_=_%fDxQo+4R% zVWDwq{_e0Zna_^Y|CaZk*yOUNKa}yD>MAy=ygjcflH|pwb-mN}``@Hi>vl;aKJ!7T zp|Bb6`+t8( zGJXHf{}}q=b_)-8&RF>)%-GS)bi`yn80uTH>8^ zl;`l@zm#BN;imIjA?Lt%&xN)(gKWdTF<)Ret>TmF-@_Hc{_5c=zS4p_FHT;u?wD|} z+vE5-)5^PQm8y4Kow_p@T-m+wjd4+P?BuuB%RFD#-`%Bmx<^OHyxPy|%*Se;;_WS* zQafi{T=G5rZ|sE-|`j#k}{QmHaUji?Br>bUM-?TLK`K_aUaosXqY;N7U zV$=8Artm%zj@q^_=SOaU;m-I81@d3-9uC-Yc5;1B&*PfatK$Fd-5~$tkl&x<8_r&z zX8bQvH_Icsn=iKh@lW$*Kfinb^7sBMKjVx2-7|-))_*%-8nypQcKF<^16dVpS8py4 zo@?Cuim7*>?Lp=1Ci~kTc+6US{Y918w9;)|Zw%6|`JXT?_qO=|%5;tN#5Zr)FC1=9 z3Nb&HWx%%iir4lp-(^CjPcHtr&HJvlLax1tHRp*plUE5;#p%5?y)v=h_|3V*^>)FN zzRsK%cAoF#-$}-ktMA^CKECea@0Nd`A7)P}xBC-*>*oja2*Fog%Ytj48()5R`_H^L zizPE_O=^3e+Z*ei55D%ISiF8-_tgJByBtJMuUvIo=licoGf&jE<@YDQlX`!~e(JQM zJzM^@Y?&pprsj0FaXx?Hn);5%YiIASzy8A5qE3Iw#oEZsySrspXGfF<8JWLC)2;MQ1_0?<<^Ws$n#+q|f@2p|^ zn%p(JbXLJ;jXAn^-Y|Ww?wVb@tYEXIj9A_dhri1v-T6@PuuXH0@*SBYzJjo1LsQ>rMV`p}(Y1M*jI$6Tbt;E7g02zZBl{fBfW^h5XV&C%bdunR9sLpWim| zTh{+MSW5l$@t@^V>bH;o)b4$pzkR`ab9RB^;+kAJufor2lR9U=S$|J6@123sR==X( zKXxqN@_;YkpHORU{hY(KABE!fEXg@myD8FJEpL+bspE66HCbOfF6(*r^V;uEtE;+e z(tkec%N6{t6MtuO)RDh8#lLrmExq;h@w^*NvnNja_2IJaj+u7v#C3P1+P%9VcBe@G z`xCJ{himS<61%g{?%hh=9fk7Sm!-=8+`+56qqz2tq3*4pO!DP!-0za@>NWrOJ#R=^ zb$)4Kj{NhZCVh_|=lDNnshLxL*}_r&`C}8m6UTpA^-g~HxMupXlV48zf<3C-`*{9t z<0ZV^)mYsDn-D>;he(N2q<@xgEUfg;7^5q`fdH(X>mx$dd zJbSFj>br#e_b+01j6-(y)K1^FuYT^zOQ||@t?wRRn|*NZ!^h{c4#wUvtmX66zZz=y zE>d?#|L>|l$4ze5wO$H;Er0pZ-g#o@^^eY9Qut@nuV($DF5M#KOOJl`PF?w__)qf6 zXsP4+kAp8+{M%?^+WUC=$Fs73W&UT>S@b@ZPu*YY^M1Sh<V@@%%4* z2KB{jn7-CX9sgDs=k?ga?woYyoQD3-aZ>6p9@oq|#`)9Yzt`iG9~SqQ7aG;hiN9nK zX?JdZ=A0b)^zHuj&&0g)+h=O--2N)6VE2-!>r!``ZeKk<_fk{!!hY*ztlyQuVVr9B z?xOCF>A!avM;EOBTzEd?*w_8(1=pnB^Zcp4lwSW}^QCuR=kH$jG*)U`c2f3NYsR;n zx!{q7-4Hvg~;e#!LMe4>)=QGJ7{=azfypY*3a!>NETlzpn{!C9x% z=X_&%&9uzVw0`A*kkWZKR#f?0@49wn^Bdi#Q7er%BxZk=D?0So)vrwb!qG?1?#`1t zWvzWaIIjBBZ$8GApJTQ>J(`(&Gb{A7Eyvwycdt#VkuPO>b?3#TOV6!jpX6*dG;2C_ z;HTnSoqw`R&p-N^zq9;To_f;xgL?j718+tZ_r2a7C~xq4kL~qW-|H``Em#{=DfZgt zbk@ot^*`0~ zziZ|{?#chIFZp@iXWH|wEuVU#U%#!_KI_e0?{%r(an^rH&;O?{|2gmXD}LrPd-a$5 z4bIlrY_gfYWa@Iom-UgiW?sIS{r%6p8>VVCb7!T0TesyuZ}#55f4ndKnJ)3a@YN-g z|JIWpK41Rx{#+lBY0M`-OketQy`0t%=<$t6kOZTn~D?B70@^+BXZ8%U-Q{dEayAp3MH=+Y7(9nOOeN(=<2# zcX;2suaEN0Z~Mz>mOE(d)Rv7-zgO$*`$)8Y`vsHl4z;&;t(fq{a+|!D)HbWg!vUw) z)$nDOzCUkT@2wg7%XNvt`Hj9&zt>&P4!(2sW0IQO{lnkRb)LR9v1aqag=?I6IbMA4 zvDjKM^IY`1yO9y>Nj3YfKlnDmyhiIz*o@@1KdpaYrcH{xD#jfi-?-CKhqw+ zIak~@H&djZ_sWeLE~(|~{7$^tyDfV8`&bu^d;Z5CPF=qIc9i|`AKy<@Y<(J4cF<

h|%Dwc1v-ro1+gpjKSE-aH)QS#u5qgJ=HKLeYRk6Z>&jh5Q?>5RmU)t*VfV9s^VheR8go5=i=5`2 zx%2L8wk^&v-(ITOhK65^`hK~a_g$k_#=YQ_&J&T#&p+Bzt#?n_>FW#Y|9=;su=@PY zNhdVzO0IeRe5<%Cd!Bv^{Ix9X#0S0$Us#x~VzLr*+Wq396y4v}Wfa#^b?7HjYUTu0_sd-D&V9ukz(*tUr z)C*-8-d)RO>3_cQ{#p_F($zn%Ivvtt)_uP6O64gb&G-v1?2aq{kDOHRs?aC4hU1O% z#7$gFCcGC>-k&7ZHF29trSz=Nk3MJpt-B~>6qLJu%>&`g>s{ZI>lSY*b-wASwcr!; z%94_pxqW@7U#`))v{J2RrPy|l((@}-YgUS?2YWBA|9gI^*U8YkR_k-~eD}#8os)d+ ztP_yjmOW z+Q$_1P4V@f-)+D09M0&q^v|>sKuTM?^q>2cpzqIO=G|GcKU*j@_M)EeWxn=+ zS!FlgEPE~gX_`a5`%dTS`R1qAuWc2q7x4O^IyE?4bnE_Y(s%uY4$XfjN;5^7G-%jT0>0?^SR9XExz~>`A9>bCQ04^7Ssa-^myja$&Pln`L;S%iF`| z(+u2xgk_mq{>?1e{bBD?2a_LLZFRS;H;w$k7`b|tN1j)G%CmsbN%i&ee@_39;eCCn zHcjWH>8G>4zxH_;Zgq?Np_Uv{x^?lj>t&19$dtu|ScWZn;}DS_xLdC5rQ#9))Qs6H zLRaYVUaY+LSzK(r-@VH-%cM(hAI<1rWu&ETb>-xOoy?2;ID>2(R=qjpkbUOE?_%CJ znRzRh-v7pXc)j%gmoG|+s*=(q7S*qMa&1NGt+!WwqvqX^>v&>*Om50;-j&uX)$_l- z-`g28DO;yS?OnujuL6H+!G(g|V!??Vso5!c~3y?@IHi^;@@AmoHk? z;UpRvwsx-P``g=sGY^O99NWVYSTe1HAq zeaTPPEzI$$Q#)^* z-ZJy+?$C0@xcX}U7&mqveKStuaD*J>?#ooVuM(r?Ir3l71We;BtVO z$J^P53wJ$Qc~SPn_xj^^&K)uIdsvwasjv%v zCwJ`ET<<@z-2wakUd8O|ResIduM<7Bto!Ti$xqHPeowxrP&wzO>(Yx6`|CGPy0iH} z?eQbF4Uf}~|2oTV?!4pk&$ZG&e%-TvcCj~djl;i3GxL+XPp^5qApcdgew+Nq9}&E4 z74J1~+kC%ibpEwD%kTVa_q#Ht#P7}OS$lX#?wY98Nz78ozN^^&@psfbl?V^tynTvh z(F?WxmVvt?Ur!9Ym06svp`BH$wmEp>-1_Wyp`T+4j-~zOOITd9aK*wm9HA-G+ZJCr zI{WL>(^j|c{}yY$b2&tO#e^rT-%q=ef9;#|p0u4i)1z)rIJtjXR_G%B=*#c!E}b~h z^!thJdZ!H+R9QPp-JIZ>{P1{5%3IqlmSXbFH&R{0JUX?-Hb0xV<4#zZM~scOkAIzT z`-^E$cGb^t-}=`m;WX>YGKYpNe*Qq`J-jgqf2s-@-cw1+Nr*30zgGU@u`(d_DQ~m%s0BtzuuXJELAZ>)zyhAMc%c z{$28M=y&hjCwj&E+b3CtW;R&93C`(t_-*IGtK1)St)u4j?{CLKUwR+K&kM4ayjowi zV$!3AYb8}yKN;8ShpYbnv~WxLWpnAdeb1fu`JXI(vc00j;@|1gOSi&5?_D1Cz0u_M zpMRyl=1#bEqj^L7Q)#waO(j{~ato&FT`K$Y@cKJLoA%bV&)0;!f4Kht-fhJwXfeO7nWxTeg`ySMQFz|GRs)_7A&-`)12m&raI+Lx0WZ zN9%LG`K?+M?koA|o!7qKC+jr# zfA8jfU)!(sK3d*ypRnq?)ALhPHG(GoyW73%K>dWn@iT88EPpEdFN^=@{#(f#|88_% zGsok*qkwT;{iOJ)jh{1IpNp3f473|(rzw^Gm;OX@Jbua7d-rmS}*%dGS%{uP) z-h@9^o8lLLvkt7U)CkkK7RI);{f>8y@$8>s9r7SAXBbw0*Ki%-w5* zH+I-Oy`*ruF>U?R1ujt&o-Vi|_oQIOwT%BYT4g`HP1H|J&-k*P?b7eUiJzjq15f3r zng4w3s=01Yy8m(iHQ%ot)2^TJ&ZEViZKdfEnaa=2^|Cst{67BN((WrBN45N2{ywJi zpY`=hs}CnL-!CeUTz|h_``C?~f}h^AdiDNqi(hOV`F+OhrCVfoL?`(N{`#Y)b6?=( zqJ6KwzMtg2@Z|5?%g>$o$)bIH#}8?vdGUFd^e22d?f2_xb+XI$r8V_j-x^QchyWXIAsqcc!yHvCzIy zYZT_|epK-C$}L~EZ@#dr@z|!z846!Yr(bT$tN${g=HZeXs~kIIzBSKHFmCOep!-wx z4$r!9w-qT>8`fv6X8d-(@5a=dZ&zCEHV?a{x<$MyQLBA(<-GIL%Io}ub}zOF=oVWQ zo_(QC|B#c;RwrAbcg-=s-?&6>?Mey16|;|F%WbdaQ+LQ{l@@RE=3acWX7*$LCC~Yr zXVo6JN;(RpT_RZxuuxxqFAf=y29+O&|ZVtQ|`>qb+&X`++lp*>b;X`_}T3*@=N2A z+J89+{@Z_I()n-d`!h@4RZY6T|MK@ww*&ZZL~Wc^vxfPtX2|9G&f^vdO%eX#BN6ha|i;}eH>&oI=TF6m3w=+28vhd?? z1=@<*S;)&_FiMj()`Mo zyO%!mJ$Uzo`l(u_b+%7_)UB(JoX-3#M#XyNesjnAcwfQY3%Bu36@BviTzOvne|yXO zlFcO(uANXT-nV&Hmit;i>-!(~P26&N&*=c4j^MrmQQt+`pUyT;EB_t7{B5|u|GQ-K zrEkOUZ?>!5ziZ{uy05SP?}-0<;McU@J?X07SI?gG`|8@szpt+TAN~8P`8xmm>+=@X zTdjHfOE&1=_O!j*?7Nfpp4u^&eRAr4zYU9ie&7D!+3lOzceuZn``51N#w+PP@87#Tojpy*-M7s7W{#-f$p_{eD|PC2#MMu> zYzjy}yorBx;N@#UOH?i?U(ynK(;F!KGCpX}q*cai6i(~i-gwnfJ78|+&8sPvOD$FZ zUf$uib@9%Rm#=%Co)Y~>)igTo!1>5;YggF%-CFAOjQ9E~*WThEYtFW4*Ka!(=zMo+ z)~%mE!~EPb1qv8;ES}l?bl&l$C(94F6;xYv%;cnI6y&p7u zH$Qs5e&M}U_9d4&w!HF4{IjSuC9U5fCEz5d_KMmJyU_NgYb_VP`^^2eu+L}e$L+$Y z>sLD%$gJ3(!8^6=!*TtWeK#CixNcozspsCPdpna~S1QC)$J*y>@wE#(xUUDvnB1>< z?67Zh*V0sZWBsE2e>cX}o9o4Gomchw_qk9jc}Zng)t}lLNn5v1oW6FA$CABw17iZS zXHC&()7>g(XjxWw$*j7%_E+_}*H89Z+8Vc*z4-i$?Yqpzz(;?t1umIUZIcw9Y}mKu z(YntQ=2t=W3| z_dw0KEuB)QpJ`n06M1sevTWr~JI5&{m8S#r=j}dm+q&L)=I6BMXP%x9uE?CX#c}`d zyAR@2GTlOw&nzrjV{G-c@_+A@V&7Y>o0W@yJ}rK4`~9Qzm$@o(`Qim{L-sIb9@7)C zUKO;l?L_K>yDDpV#KCYjuRxJJ8t8(1AdUF29H#cIg2G;lY zbBF9{d~*BW$2Q(ix9d;d``tV-DPx``Z??UCLI0m-iw~i`)BvFv>efs zH=H85nIvLO6K!`Ho%rZ>qeSa$|CEzAjP^Zy?Ic+dX62&w_Thi=OW*dLD0#;=>HVJ1 z5Z_}p@>v``R)D&;{^dxHL(!7+WHIZkOKjeHk^x$d4%m>yV&)!Z+t-8Y1Zry4f zP-mguBPe>TgUh}6OGnkgJ*#C*?i*QN=vl3tE^iplt8#Pe^V02^&2h7ZQ@KN1g~fK; zJN)@u|F)sVbf?n1`FHYn<~INP%k=M^UcKtVfV+wHTiaCE9MU~>x#yStytM_&1+o@v z|9-CFU%o}SzIEp1EAiPGr+=?nySQZGd42J>C#=7}kJPkhx6&}VH+Sj&Z?CvlH}BP4@k!-k9bq{J)%E96&j;@L zQ`r^1r@^pn-JNK@nH#^KVcVO3`$gQ(_%HDx3-_O5n;?C~_GEg-w4T@H<}dOCj)w1< zF=zSWEWIzQCQTAGIrvjf%x!Bj`;KjTS<>-8l|Jq{R4hIJVp5#ptcgFx@+|CgE*(52 zBPzSyqvTG^&cotcZ+FJ;ytDu8)c4=r=GI3(y6*p`C&rHbe1}zD@%}R_j4v70b^TqS zm?CaFuG@7iPS^V4-Z@ro^Za5@e$V<5Z#}o}%lv(7KQHose&*JUOO5NE&I|td zZ~eh%8uc&tO5$teak!lYmY7S&#j$5H~p!F$9>0N67ShPe=$#fxm)e;wawhmof5rYy|Xs2*!6q0 zTX}sh|E}M4Zsp&v{af*M@6=0odXnyd8Q>KVN6e3$9pTObtu-LhVH!Q@MC*X>`qL;ddM zZ1MAK8P>bM#)QuJC0AOq`1{_c*8|(Xxoy{4BBxhh>~S*u8&lbyKfO<6STuF61+7ph z{n~N7@NcQsof&b<%_r^Zu3z-2_gDFbN00A6-qzKjE8L_j{K(3*L#DiC>dJ8by^~F+ zFOpPz{OIc8N$(f#SGu3q6IH)@lG8Qy#9UUH%-2Eca}I8+H`Tn@khZE|MeqvYr|m^f zM_Z;_5BC@ zBx;i<{?pkKQoDcqN)zQ;o1#@wbz5pJS8v{~ZjiC~-u$lP_Z;`FeempN;qokv-OiC( zUv1wf{j&>yW8=5~g)HaX`EO3Y>V0N?J@tK1{?F9@^~;{Kbdpp|Ni6;!uRJMzNz+> z?YrEzqK$3!PByD-J@w9>Q#_;6GLvPkp~c(p28Yt*PR^fe9{x1I=q&f7%zMB8Tb}jV ztQM1g>=DZ^kwU&LN=I$J?ePC{%l_ccKBct}xvH`k$@S0hXX>qg*KK3p@_=vF$4<)y zkL8{>=0dpYB}!%jt+$koOt9DY<%73R{hRQc!M@M|VA=NLCDK1+VG z;Kn!4?>9fMYpAKdb7k-CH|vV8-dp+ggQ@cTn6}U=+bw6b-rTsi>V->Grff$z^P1iJ zN(&1%mu|f9M0oDqvUOR$Cr#^fp1ReFS25KxTE3pOX>Hi*UGI$m)+K$_g|nt(cgM*QPJJEHN(RDsy{gU%UCXVeP(x#oMQCUb8iCS@zH8KkwMSiuf7TrleSJ zESKk8wK1jYZ_mA3_g>!c$u||6edoCID@T8Onfxz@w^_X{i;I-^*M9Bz?f?aR$<<%@5= z|9@3w&^xb?8Hv35fJ1r`go$iAg|eKH@IqK`KQ+(|Dd;@mS5_vh^T&^v#a}M$hpL~S6=(i z^5p;2x?rL6^Rvw-o+VkGjjfw>HF2Y9?ABAVwim7T=GA|BaWO21YpTMPlgZ21&9=Jv z%YJSxcTLFg((h;PUj3~rCzS9(>9m=O5YzFMF)D9#E^$qZwOuuJgJ9WL-PPJJ4`r2a z{=QDmqI5&E?gmdM;hw@K;ZLGTcP6mhwm8P=>-kvaNU8L!9lDtwuT}EA_wUg4%kT>N z|LEWOGk5Op*5;hUUtjpV@Y$KyXDrjt*?hY`M^{2OND;A!2|Ix$lQxENodL3^jtubO+ z&Gd$0Yx=Jnli7Z2On2G4r|)lvu%|TB^9IA8XJ3}qIJ!=dRXvk0_2zU;eNcJu2liO| z7ba|c>#ChsKRA8*r<1~7<$`M)Gp*L?|Nm2)a&)@%{GHvb7xR9dsQf8A*;-oKaZ~hB zhjnZjm#;mTC%1X)%CE;B{Fj_@ZP|}KpTa&imTt?eYS527u&dGYe@9of+Dz9g3%Y-w z<;XZGwl?X|kLe2Q*WFk*^?%uKxwXH3)~COFbam>jW4^~DcpqMBuRC>fwa6cq*rWFD z3qKe1-9JBX$p^`zGR4f_-{L?06#c$Y`^t6O@0<7;{|U5OHy=!Ru+f2G>jV4QEf;D9 zKP-JyJcp~lE_!yAoZI>(2@U7hr!05d(ZzD`$tqPc3*jJjCGNU=4o zPqQtx>m9dDy}IF3YGXq7<4q1-p+a@qI-5T6n|9sbIgMY!ljqZ-PX`V%bOf!Mu{ZR? zud^D-mh+Z=(bAYCeQLwwM$hOJiIsgtC7T34i>wmf=-P3yTrvBj&N1iY!}q-Xj9H!) zecQi%-}@i#4e#UUeavU;Q-1pC`^)`4mDy@yyC=R<{g>`r|71^pck;BNfA@@f%z{OP z>$L?euAMRySa?0~(#Ej3Xw6NGZxpvw71jAOeG=Hi%0Kt^1<8DNxvKlGm|L3C73YN> zSyr*@1JC8_E+6l7KDP}y=U2pb;-KK5D_v(>LpFSlg@;%!T8WE#E(X)Z}S7_s&%L*=7-}R$NCna&$4o+1@@R z#nC$DUh?wA%+Xta%cnbPz3SXs&BV^X?|{~o8RjQmWvvJZ**V8~#%u4NYnSHec22TB zYrn?7u%ID4jIH^0W%AM5`zhZi>wLOZ6t`EvYVs->(Je=vbN;AgyL6O1tpDqB{H3*P z{9=Ep;NI0QlAk=y>6rRHm6Iz{WS7sv_@bj7S9Cq(r&h-+l-<9U&$hgo?QiL$o3nmq z-}xoHwp{qi^fLSX3uRgr4j*p{c3L3S$Np~aoH-^QkxoyfD}s+6SUjEic$0PfjnC8P zzHVxqZ(~)NpKyMsqFk3%mcHNxqCO?|v-~7F@@!IeEO%Kf9G%~z<(ycu!!OOW)XJyJ?jg7&=t5OtW zn-|sBL?1CZzFI3TAyH*cRXSHzCHu*o2NLtcJHs}GPI;=Xe^|-IYW>P5-m7lBPu+Ah z^VLtu-QBvpKJSk*Z1%f!uwhL>fdl(Pecmk9Jv-+Vgl%qH{__*pW4*ZWjeYv7BhIo- z?7qKqg6q`%qP=(3(|SHJEOWZ}=H<1=Ioq3j`L_tI52)Y2O5?Qd)!Y3K?|<=ssitGK z&82i#Gv4VeZ-qk$aR*pPl-K@zn{# zxY(obCM$o^CypBD5>GoCs%C+mIPi0n2 z)L;GR^Qr|RDVMzsv={^RBDZHe`gSe(So=QDD{=~~_E{{~HnL3Oc~pL_UhP(ChdrIyM0fq*o}3Ozv8UuUDsJ}tj+p#@mMsU+EEslcoBxu{R~REPRZNZzOmhq zmYMXaE2X`eIr#GAYPsF-Wxs6;P&HIKbYMxrwuuWO;dw%rt z?eap|lccX&<@bNuuKj=UDgEGo)l=)2f4Z*uKk)ePIXiB>fA}(gi_u4Ji;ua+-zt9n z{y3}t#P<~kRR2HLI&V0&EaqVtL$uzjLabbMn3H z_3>-}k9>GL)kf>z zn@#gXE9V70{j=}W-c$B7k6CLzIv%?J+k)2mMVDe;eb3S>D-W}deUth0|H_)>8-Kh? zZku<`e!E2fZqeFI|HOs2%%?xy>H5P=!(iq<0iCs-d)G(p%wPU0X4$DIwS7AA3lp|? znwtI;(mK6EaIet&sKD>_IoFrH+#mKcndu)R>wktT97oNUz1R1$UvP;zV}EoF|I0)} zH@$hrtmB8;&#D$i|icJr?-=z`V?#bPkdS*^D|!H-*JKEo9x#e>-e$v zf}mb-soh$Oe}PlKZIRHt>2~u_bJzOmkI&2Tdv>#3oyK;R-}~Z&1)t_8-Z@+^R`KC{ zkBV}qrhoF|35jZp9%}MOPh!26Jn_ebc{x3;$4kzyU2|)u#k0uGzqZ|;`7H8am#y}m zjSExmNPIGgJO1gFb6A$N_KN#&c)$I2I#c`k)opdj{Zpsj+x0H2-)`mwY z8*ws5J1np5@pt3zU5q1KQ)zy>hrGFx)mEqL z$t$84-+8}tbE~Lng zar)l8c?akOYZBDFP_;)y|mPX`e&{*oEy5%U-II@Z525;Bqo}EHrrOe;jA&*#_zHoo@zT3CCxqiHRDgEZlJ!6S4rMV74uF>V`{^ck1SKNE~ zHgWasLxxF{He?=CcDr%OHnG0{>;(k_kCfh52@73LeR}b@>P*to+(28I*Zv+xQ#&Mh z1GEmVy0yqHDcWX^442fhrN_UhL{DH#?aeNgPEmf;*)8~a#;&c`4r{TliEdE2R>Q;5 zwXtf_$HL?j=dAk|oUGUqZ0=Z3cflJi2d-Ou?mb@qVxHk$sa}3d;UL2kcli3J zcx_vvyRuzZN@3{)o7>B5mQ23pqW%D@H z4+=U@Q5NcV3b|JG%D`uedZ$@?vT~qomdg6O+xOKoug?8mTJgKNw)x+|zPSA5`4{eA zeg3*tF8S!&eF4$y_b*?+dwS^B-)6sWPq<;Xl06239pakrg} zpR4Y+4fV0+`A{gr#9{6RBK@tF0D zYl11}am$(4>{6cB6Hs4f%J=bMVwjIrsfyHnvA$CVYFk5s!XIk#C_F2$zx?O*#|is6 z{U_}F<9MdO;E%{17rzOQ4!wPK;L+*(TRFEj$9vxVW%hg50^{AcYn$&~n6}QW>~+k^ z^6I~}&9Zm*KDep3=RLq(k6lhH-kW*7|Cq;FR(&}pe)%LtA`_JmzzpSmB_EpPxNLTHhUE5b7?;|O{+T-|d>*jm^?6yr{F!>~F ztH@LTeZl(fik0F^UkA@K)Zkg9nqtSc+nwR|qvXyVnHICeQ=$Ym3FRDS-q|u^hlkgl zfl*@=f;-iJkAf z@MwkET(^Hqt{$3|t`Zxp_3CQRl|$G5^+}#wx4vD~*z)S3y#N1C+&(m`ZhMUGnmV_( zBQqwySn@kM!dEI=MbfWFN^tW^@m;%H@6Pd)xZK`T5UjrQkjLujW?RqfFUm7{xUBV% zrQ6OGx;Z!JWNwhvS9F;h#N(l!hE*RYTB}glk>5r${JVo z-zfqilhW6zyuToj7QMOktatK}v&+7+MKb$22~4;q_jR`6+k;y9iC2v;6>%R_(pa^9 z!%x2j_w{zEalcx=^JUsjghLrSXX(Qo2o3k zYqo~7+=5dx{jplSkz}{^rgNuVUzp9^y0meEpYXG#sa7i|ZtBsvWqavRp6}sfytRuo z%ezjUcu~Es`Nfozzseh5zj?GK!Y^(jN9O0dCl4xhh8Rs_PMnc^@?60o%c8Pfr*(C-3tTv!8j*K7DTX?5^S&b;X}^xAB^NpTM5{ z)$zuWi#w}@dd})52UYnPtw?hh=t_9^$IJhW?G3JjJdM>~c2-}U{Oy%Z( zYP+@+doOmGyx1<>`bkKgu3)gl_E_=9wbzgCsLhj{BvEg2_w2+z(TjI@rotTo9b1!bttav76F*iwgVnw;yB>Bw|L86;#rhW0N&6(|V zq4nC#P9Y<=Bn7V^^s%OwLN;E#@&Dy-w4%(g;w z&IIOHUD2{j1Y~U2l|1;7v~uIj3D?%RS!}Y(ezRciC;^$eoX3zF8xIgjQ z?5?9r&fJ;6yS2x#wy*2y8zBG-fL|C0GXZS0YyH{vty_Rf1WP;uj)74xXYZ-rqTO=&{ zA>)u8rmS*(R%WHZ0S_B7Pu0xh;=i2xmt2sWoOUSE&?&--f9WEX%|fBRyHdR3t)1Py z{It!_^<8J5_P$>-Y*uKopu=)QXj#`ToKRoh?EmIq+aG4TC70J_oVb4N?p3*@$}L~zQ@E{pFN?Sv1U7%$pU6C` z#A|QBoU+c~e+TW}|60Jc`Oms{2j}e*xP3*md`?#H$(8#K3U{h6lM+rmF+Y8Fp6`W= z9k&A6y%kG&il@%|;&Z`MHJ9P?MoaC`Y^JL#I#IB)!& z{5m4i|Jt>ZR@uGwjuNjg__W_#*0=1)F<$K#Us-GKP3YHs@%8JEJ+-es2yXeFC;5hd zW9!~snJ0rcSY77wuRJ9DP_{}ox_(mQaSu&*Th3L7jN4S%SD%TMHkM0bUHFYbHfz(Q z3G2&^HB|g=Yil?ipZ-8JVC(d&tu?jHGnFd>&Rw5Z%@*u$v2a`5{Po!v9N)^UnG{eS z&CrpwAnR#Td!JT9QO9D#(s_|{w?+lAoYCpM;#hlP-%>~S+M8i(S;Q`K9dUE}@Q<@T z`Gi#Laj_>+TZEQh+Nd&7VEc|;M*6pBg|1PEjESuYFf6@$u$hvO z>|C79Rr1bxceZi)uA4#XaW-lEv*x}L6L%1sWW7Vq$Nt9MY~j7TyQgwjR4tg{er$KM zW4?b!%fj>b6XOG3ek^mGHl35_zHP_W^V8yVFV&k{w9h{)yx|SAeb{RjtNx!6OO0Fk zzDM|qsT8kSp7r|eyU*VQcMJ1tMTjwPI+}Tdr!6_RcJVIu%@;1P%-wg>oQ0>dutP0* z7o*a_c^g#UHT6h5c6!Nkxm8Xm;#-}TOPu+JtFzNtV;PHLoTL^Diu{}^7sHs<_i9B* z$h3XMMpAP1>erUt4mdscW!dGBUAscI?$MW1ziaW-Q{$=9IUZT{NoyABTe{Bmn7K<` z?y#{=6yq|JMT?qe-qw2-we3^Yja9M^dm97Pd9tqve{*SlcH%;cuC~DU;y-V7*6&}R z{DCj*w)}0uZvQ1)x9*y?;o9M6w>3Y#W1kyvDRoBO<-+vlcUd3m&#noXE6Bs8=5@TK z>VjGMgOA47)9TqR>!xjwoO4c5CgtbszmB4k47b!+%zj3mOtU&U`9A|Mlhl$kC2{*K zHM%A&umA8SBOoD=vE&uIdxPfIhCK_;c(XS=IpuO>%UNcPv;+F*?mgr;_Vmfl z|GL>5y}tNN$nMg0u|K9p-~Vb>^Z45RO!>N>*IvJ43Co|m^WW_4rQ5b1z83lB*P^vS zHt(5}mo`7Tb}mZY<<8p$$sbHw-npkRW?W!6A>GoV5?N>AG|fbtuUj?#@2jh;W$Pc_ zzVy(P??-Omhu;Ev6Cd_vR2*5tbX0a@e^}d-0Ii7& z9-I3xwLa9i8{zWoX_ik|t8izier9gidta3oe|r698>g;t zz8?DT%Rb5MITOp)3dd|aX80|r*dK5 z2aG38ydohseKNx-WrJz%jVjt+w|{6P9eaE7l*hR~ZEbJoUtc`Cy$+hF+61eYRMeg< zI1^G7B6U(`#mqG}Lj7)sIfLJG9{;l9 z^{q2%_1vL5C+4YtbbD9s1%7wF;t=+wVgeeA?}d4OWlKuf+cd39XG_6JiM21fS#r(V zzF5p$tT5Fgc8MsvQP*bE31+n?+@^M%-g7fdL3>NS*(M>?l9JtyB8odUM6t@;uFsj* zWcmG{Llx(g=f5+IW7k}%zwY08axpg>=jsUydUj0LU`X81YIJFxyHUHt5xG>Zb$Y$4 zZ1!zzF>P%-(edQQ*WlU>diuSqZUl>z-+v z@KnWcb*@FA1J}B{A+zU;czJ9$ie2Y>_(_77u5)hG48Midjmv6P7!nVKoT#@@sq>h~ zHpzU6d+CYCdlYYp`@Sqvcv8l#Ex#zc=cDHAq@S0TDOvpd_G9zW>k*bBH_A)8+j2x3 z|J<~ix>)uSL$c~A0pY_jE1MlB3)(I&m|&Bq^4Rd!MVEQ}%MMLgXEQg?euGr45o6|y zCQgGj@9uIGCT}$s_L;dUXPt=ZLe&z%`kSk#q~F^x!}9Lo7qZ$a;UCK9?YMcYt5yAK zBx97W)r9payCZ&;EEG@NwfLsSmV}=MZh7f#6FzT#yh?PPpogQN%=#(&vqVe$ZkOoa zsy=&j*BpZ+XIssyCzK@Fr*CK#nd3V7eEFfvr}ju(`06!3Ja9%{^6zA`$yd%Wtjz1E z@meolA5?Mbl5bdBK(yQ5;HC|`ZtM|c-+ueQz z4{6?=h5dA(zN{NT)FUr5e$YeX8Zn>5nO# zvo?8u;##)ng3c*1V}Y-0^G$3fI(tv_3sd1Os@Iv7SbtGz(%ri3AAS2$w9?{*)bi?; ztn$L5nty4kXEQF{BkR0K^XB2GEN7RKQ(ouY3KW}h^R$b(!gr2x?v#jm22Xlb4o6HB z?^zY)?RM02k%=F-+u=0vubV^|Hz>ZTUNk{^V_=xax(m;iE`D@r{-n({Kc(G|@rGrZ zcvZV|oUvGSxku3^V(Uex;!V{vre2DRi(>7VQD571ubfw7ys7?nyDJ z&DocW4x8Ojjhi_!^}uu~zRP#l`S+||<-bAfwD9VhLuD^gr(X~*c%pE2dw6cpZjY0R zu5Gm;>zclB2Ts$k4`rM1Qh50ZXCsYe-b-f5e#ve!a$a}ns9Nv2RlLEOSGI(*IX!t% zERa;j8=teJ{;KuUQ-NPKqID&VynDC~zwu7IDSnhIqlqE!@R6=v7XsMluPJI@r+z8v zwyn*X7;6J~T5I6!-ctffvFp_C28m8BdRX7rW!V;ES;TOFXSGzc zfAqq>>fbr{X3RI6bvRIcLxNA-)wDbzb{A3Wkp5P6>2~3?#iHBmr=0n8{D}^mie;;4 z@6zWw-)At&K3%3;7AALDeNO2Vg?HlRn-}_f3%=}Jq<3(-+z}_XzbXOmI?q~Mah=!_ z?wVJxlbR&B;Kej~WoGVr?wvo(qE;G<|GnkC*g0s;qq%8Y_g1Z*Re3E~rvIk2{25vQ zhKE`wO7q%8j!brDyLnNy@c4rrj|3J>VsVqXBJ|2q-|_gTH6azR0%nMI&WO?KT(IN+ zp;fLH)gSj>U@Ts@S6ilF<)XO_XHM-7Xw(f|X<+N+5}+p^F|{LQ?e6THfOqwqqSHcx zJ?2?hwW;fLY-%aKEwy#&+Rjxj@AOQgQnT&X%SO5Ge-yRx(&bl*Wlld!7@RN5e6YQi zaYM*mSt8l$yy2NMhF-hUDkXeU)z_xnSg`N5n)f`5zM~ozXQ!=lnN&A*Re6)|6MqQ< zCWGWR7e(0r8SFj!?0r>FX89)lsXa?Y>wOXrZL8E0`t(n%;DGXjXH1M8TryjY1E*~8 zIq|W>QN%yd?_|`YogSZ`9bPz5t3lt_CG(izMsDG6i^}e?2Uit&ZK|%iyg);8UV!oB z=o|y_N!)iWj&W(Y9Gw=VkbCs0`q7eOVk%!d@_Jt16u+Yxo}$I{>YB<|!Kirk;_q3F zjA`|3`R9Whb*#5&%$(;DC_J+)C-YO3n%r*9pL#kDQ&i9FSG|;fM4(6dp}^;(`9+G=m!^B$3mh}k)S6_O6hHHf?TYe)MTy*J%_Qle+Z?YngG zjT-AIrrA$c*Pco;POcG4zt`7LQptG8*WMs4^hw&?`l$5Ov}v)k^y@p$#C%asT@v|e z*&IiqK(C9QheO(?&b)bM!IYjk=gW^7Ngqvn$L6wg=GMmtw9d?t?MS-3@rn7>N%M;= zHAAL%o-f_(y+wbs!g{mxTbDl@8yxq_nP++VNLu$9%eQ&%nh&cW{d%qx|4nk~+m`E=^c z&3lZ}qiqE&)9zR)WN7W4q2=F%#7>(`?Tym6$9sduD#JgIwj)le*VpmrhGwdvw*>ncC}XvacAt z4&SWwYog5w3D&i`yJAmrt=X}o{_b(FH5O|%Q&jGEb?@Ejq`l5XpflM0MDwk9EFZN#aK%U1QLv~8d9$m{RZ-qUY09wsiW zpQ;~kaj+}nTZeh;ab=I-)u)661Kjm$ns%z6@Y0yQfK%7^txaKY=TrX4M9XB#(QgcZqfx_#XZIX2P6(PtS#RGTd@fRjMsCarqO| z`O)*(@*B4NHW%uS|1B-q!uYyW-F^`Z944JJF_&q{cvD# z{t9X1q~3=uf!n`rGvzIPmBzd2?zXJ2tE}Ix_^o^_{%-4YM;rc!KRM>vMjZZBue+o1 z`n61DkqcSg+1+m+RA+_XIBA$Wh3Tj1(zDqWzM;j|>whNt&Rxd5g(|^}B4hoc?R3NA_I)kk@{h&# z|1v!H^R@hA)zsG1uP-Oft@o`zVe$LE%BnpdB13k*zFg2Z;j@(GZvMlTj_0JGys7Vc z|LD)9;1wCy(;vtvJy)@Oeq!erO^ftE9(!Mn=O-)Y3iS!U(Q5rxKTSMKb=@`BJ!17w z^p*uwo38(-;XdEb?quw(iT*|F-dZy}=PmkX&-eU@qs8ud4aLs{u6LbR2-)*oU{1Yq zyM@)iZ*?MdkNPVW_kU4g@BF#^=epQ;r{_P-_%=^DMqBHumDuN_(t-T{R-88q`73gN z^Xb|b7Ryih%ey}npXVqmzTYEMuCe$mq{?^}59a=g=v56>-kmHm)> zb?^Dkum@%T8M8P;i;v$Iw-EduY4YpR^a~TN@2fv!ntT59AC?tgWNsCTY5rko`1AJp z(e96NPr7{lf)9iS1QzM{-h1S@`|P5x`hmOS|F>m)U$@^u-B$eHoI9rv*?uT3I$zs- zXZwF1~4wCVLsdFj>cUsw7KH zteP04U(^0y;_Sk*H~;xQN?4|!to(cX(Qi}xrEYKR+TG?TKL21@A6U!v^Z(3G^Z)Ao z7k~0g@9F<_9(G zCoF%4uXrKU%D0E>xr}B0W$`0--W;C)>4UnWP2ck_jTiB*j_&$`*$cKTTK)e}?25we zE7ttD-tzgGYUAeT_dU!`?({m>SpVN$vFP0YIaZaOul`x?{rjaYbKTj$-zT)`z5DO~ zPp~)sSGm&l`U5*Zs0H@Vi51!FbxyJ9obV}6uDQz3jTXL9ydhZRH@Wzay{5jU_=%l9 z=jNSmaXOb-bZ&BS=bYI5z>`nrs63akT)*G-%lYa#$G&a8vg`EzhwdhIQ!n-XlR76^ zR3E4O{6hW$<;sV`nT{9w0^5wke?B*@`!v19@!Zn*fKU-D|ApP*4`US5l$k zdvx`Fm&o(~%q6~Gs(1e6|4-cMd7Wp~v+zGEEuXkd|8tdp4&Lv?y!FrdfZF(lKH~Q# zay^!$8v4ckDKKc23(O@$-l8D`q&en|M4u zZW32hYCq?+d~5Ne$+xnl?0FRIzj9ivpH%#^|KT3rg|Sa$ZmoAb`Z4`y-T8jg7dvD7 zv;TM4^mD%`Zulqcc;0^9-gO_%8Cid*vshLI{9bwNyv&ss)h_mn)AkldD2p}a^2tE$Mj{Ko8y?j{+#{f zO`X&^Pp-L3T8jO%zq~uD9diBl@r$=NA2$^`7ccnWfHA`yh3But9b~tEYr1si*7}uy z4y!K=@SD%@;=uF=bAE67CN1#0*C#Pq{Nf31eWmmD6N4M(*D+pn zKCe{rO?y^Y&A%<*7IeqghMj(Ve17|?%CDAdRqW6IO|L0DH-CoadCTP;d-W!~`(e&t z`B7-u{n+}xrv5tKbF>y=BMN}F)qivRq|e}~6~eM@UE9rfSm zbNHwDEa`~*lS6lO?e`D0@$+auzwkr--TRIc#rG}FbamgqR<`J)@O`GerYrmtYP&Y* z)r&{8?_Vuj_VMy_leGcbKdQA}w0mt>oy%C?HG#jz|Mc~5dl%KRvHn%nvj3&EQP9%; zKi9d}^{tEe|C>6A|BFi2oBmBW^4N3D_ftQ=csBJtW5KIE-?ntW|1bSu??>|}Iql?p z4WW9`&yVzXESP=n|6I3SnfLD}i}X(_{`I?HUBHVc{1+A;uIElSpU`zKzM=R@`;m_c z9~LqHcd)EmvT@IzJ@uzc9-5dY+AX%S4mjoU5!QE0S}(uE%3ATd1`;?{bTX+#V^9;pS?R|bX{=v zv-2%5Z&tAkoaal!63S(gIf0ISe zOFd+z&l#o{|=*57{mEa7!h z`NG{RHvBbzzvE8oyPscq@9bSN*KyfG+nv8<&;D&p>yYQT|IhyY4h_Z+e^Srhxw~fP z?3uf7KFw?UWBz{v_m)SKX33gfoVD@U?~2dzpPo71`_sSYXMF5g@vYD9u6q_96LEZk z%ExEYCb^Paxpmw}GtcgCdR-=Ne*a0){>$}sEoU;11-X8bj^x*G88)^Zx>^$Nvx2yS4fs{6A~e><9M`exBv^dn5CEUiN$aqW8{+?hKz2 zAENYERkhye{HaMkzpt(Mu(H+WZ(NSrzQf0Ea2`1K%L^nAMc`?F%hB9H&dPjCOf^83L} zy#}2F@yDDG}%v_oY!t=>%K2tD zalZI;eyZ@=`Nuc!4%+wqw|GU>GtXN>_44s*_xbN}9hn!cqJQ!CgYf$YU#z|NCDxne zTCw;iC7-wdMOtPp*12Tsettns|C-nD&n@!l|Kn$JR`Sb2XRYUaFZS5**Y5A$_&<)} zZlvb^ojxU}zMpvidS~{|9~H`5cfV&l_fLHCo;n#xPKKh?;{4L0M4)0?ae zEDQ_`44YrnOsu!WG%B#Aw1b2ILr0+bt(UY%@L82zj(_2Jo_5zzM9k6 zviAGj>UXuv%jc=bdwIKk-sS7rSam*7DUM}!f_zM~`v<0b2dsZEoOavA%>RIMPm^Jl zSjhEOC)pK?yk%d7=x$AvsxJtdSLd15exK`1LYn{ak}l&$rAO1w-E?*S<+sWwBwToz z+q?3sZ{$N_-5-AcnYnf~hgnDPn(Q@^n-q76z2Xkby!BNtYW~8!4r!}Gbh-N_Q%j>Hje7rI`Lb_L|DxR5HBA-QF75reb;*x)62|P$9EGxX}q3Dr$qF}rNe_3QVGjbFZh`t|H8 zYhmM*ws=t7iNNE|WZNI_U?!7 z3=9>#llz#&>W}{V|No3-^rQ(zn>;nVZ>u;oH7#pkVda$NyeKeDh>1(XLnGVCJy2lc zqD2X;>tX|{*sJqze7&{fTi4T9dn>M1@4bHL{bw8F-PLA|`9J^u|9?Hc=J`zH^JnH* z7B90gPCs{(TTS0XQ|qmtyv_CfSGixZi~rm8e&?2T%JsAB&iCcki@vtXF4)J&4 zb}Qwc@H4-7)uO{@o&ReeeC1h`uaK^A8t+U4oqjy@Tc zUs8WWBfblpvO8YjEMaq9p#Fkc%Tbi0S*k#92SW@;T9fnvs{;NT+;R;3&BqTcf581g z^oN`b>wm^;%r15pcv6~T>Mt<+unQI#?qGhy`1|1L2izZ|Pt;9V!7agXtf@1BErRh{ zqxiwZ52`;{Q*2zO@b7BoGGL5h%43Q?;QS!>gYk)q85ssM7{r=n9~6F&`N2}d-q#q` zFCf}{FoA0aQx3!TM%xFG9}It(pQsYK&Z@_Hz4_~dybY{2e0udX3*1wh851}p*xH&eCdka-&|@(^X!?NR2gjdtFKzlv4pU~h{;J+Z} zfPw+*mByJ5f(mSRNWWqAZ~lHT@Imb)4bCM@>?zID7U-T}pX+G&f{(3n;R3E6md6KF z3KVDX=`n>j>OOESU|F%eqpJC4f^tQ@U=3f;Jhxd1799<%50-tf`N8&wvFW_PT&7>m zQU=^>7`8XHABcXCKH-y2pyCVGWlh-!^*$*55W8`H;U7oc8T{**e;>U4z+T~fWsL9) z=64MKP3IlYDaIJjU2Y9Ca7@T?G@C}+X#!+!c8-v_oIf=~9e ze5+^7I^a~mUBObrwJGk{&jU;wSY_D$g?5&UJ%%MV&Vuvhq6x~Ob{wFFaI!|Q{LAC!LZ{b6)^QT?m= zWP<7r(Kn3i8_qZWGXBKgb%5al!#bAjP3{L1AJhxif90OTrsXKRLoA1hzw!FP^att_ zzKGq@wBY>0XnY{1fOQAs9aghu^#h3?IDbh0;7!@n`w4d?5b%yW)(-#a~2R#ZnBRJo&u^;^WK=%Xh56%_kg8Xw>x(+xPF!`{E zH(EXj`(RKZRR4#yhIy8(nw8`i#zX-|?*mmEm~A-iSSsrsZJD?au6^M8!Sn~?A10>v z3+FXWcJw!p2x%yGkhs#onP4=7Gp(`mfzSq_80KqDvk$I)Ah&_{4eR%2_XEKXSU;o} zNYwE3HRK<>f6)G5{((yQpRBTu=@&$&G+lPIFA!V7ay5ZRg1=s^Q8PiRqapHvRDrz( z(TGafyWP2Kj{6Ckzw86@PvP&{RHM+jsF%1U1`of zQ1`*gg3YSFmBlY*zU2{Gyl`Sc)n@d0&WZ5kcN*6OkoYx2R1*b{$TB4*JQ-5-Wc~l zCV=}{qxM0o0(}dHd93XR1wSyK_{WvX*L5(!fHj8qTJzoq0u|gfJbesbP0z7@I^elL zeg*6216LoUZ4k_1=Wlp^aQ1`z2jTS(8XwMJjA~}OAUmab^MMEh*%+>{hS>-9CdgN? z?P0WI{m*nS|DxML1p_8f7#I~8@8EvJ(BE8sz+d6*dKX;>Mho6AjEM%EXIQ!qdKIwW z;E`i~-*Emw`2+C@pJd(Vlh6zR;4Xz2AJJ@m<%A0*3)O|3hkg1np zdf%XbAn=3ri63&6EVc&}1lWWR_!zM3uxcNODv-ayEyr}eDf|HY1C0#{bJ!i#1K8Rc zKR&225I(~+z2WHtwF2=S{BM}p4=O*1{Gj-Q>yL27zdj=d%LS$utY6pt2R zz_~+e4eR!X-v{bGh*+@PW1ru=ziIIU^$At=B99dVSg*&y^<41jQXpHthG<G^>~P_~s}*8K1E zpUtXC?}5n$c^-CQ2iqC!daS1pTzwE$AYj9Mk8yr|)BA?^;xXJDO*R)qzp$h?2PVkO z;Cj}$`oPx*W*Y?GF!DDoKbZZ1`Ge&Txj)Q**k=8ee##fssJDP8q@nmghJmEZfz$_D z8^l0SD*Hg8Ldu3cj_rN(`2*V@+*f#OufZ+AqTYDf!BT=#t?^}o*$j5ECi8>4AJl#@ z{2^1~^wpxCa}NKq=BE$jHVEc0eV=9dJ8c7N4(sg$WgD2^u+b_~1q=zd z{ARcbFFV|#sh9Z5YsNI8WwTo}RT6Kxr)9a%@Dp6-*RoPD@s{(9TLR1UTUK% z6IdqEvXUk7mBS1*fn^L|q}FRP%y`Cfd3HjOuupPRrnf;bXT6&G!8s~3dRT&c6QTsq zC^hYLHb~`I#(uCxVFqK%!#M^#!fio^E_g^h;y5gp_+p|dHxF}5-kb|(g?4#F?swSk z{6df8?7RyhCrvD!wyK9jI)y5SupiP@7P0)xRmZ%)+5UiGoJ!yi`5OnA_Z(pVqrj=s zEOLQoNt4M0J|hP;j)wZ23Cy#a#TKY$ur@6)&0xOORJOpDqcLUz`zJ^C3xXv~tP2E6 z*wh;*JBnWrox|kE>bgK;N<+{E-V*lZjYb!Q=P*_^ZcI?U!6wJR-_(BK@B_gQ8WlV? zeDfImwPMW||1bQzrRO)(BYU$3cHJ} z%^vy}w(6HX-GAiA{fs*@-_su3aecRX{Ezotbgy;ke!t~MmJ}~9lsC$YE6&M0G^KFX zvAdU_+8v(S9=FbBPUfcr|Nh2QzDqx}xtihn-oI979Kt2a#NS?j$y2n{_{h|M0+0Wz zKh~=^ei^dk5D3k%xu16DX~F%xJI+3L9;&@Nd#v`#9P^w%H*GIHl}o*|U9$Yf660H< zX+5W79~WKUb?0&HJMP$bR@19z72fyV#oKTEyi4N$YtQXVxqIsOuC{p{TiSp7Cr``H z#**E0lTTGPYOek|L43AdkVJ(6mPwmx-L61?bb!vW3`X}?f5_U-HzX-J9Ukp9(%s`Qt3VI z*z2WBcdlDu9TmR*QQd(hm0MSzTVHmSvCKZ<)c>gPow2W19lkF4f9tWkm$rO=9rLt) zuIB&UJ=JqgGVh*$V}1SY_od79W0TJb?!9il^HW*(>H57Z|J&TlG5BZm?$5~`>&^1k z@n4rPf1bXzP`>H^)?;VCKec*XC;M*evA>Md*A~_vf0}T|zOdFfZ{IrU-rwdw?x)<5 zlW;YcHoyPcx_3A4pZxw~t<`%A#Shl@7k)2lm3&kbzVnjI`*Um2FFRouISvJ-G#?$k;|KDZ&+wkA0 zcxwMR!RDRJ^lHvKvlqQoI9t8=yVSq$uIHbgGTwfW_hQvkuldJKx4q0WeVe_zuz3FF z!#j4?zBS2{%Px~N-@fwhv9piA%00f<_vO3W;rG3}ckY!jc*ib~Hovg=ZeLsFp{MmH zrt)2%`S)1;*>4V4{+EW&C<_189h3NDo?Oo|&hLMZ*`E6{`Hs4r-HP9fZpZ%pFT1X; z^KFm6&y4Drf8S?)2~KeREA5tR{N>)%BbSzS-#B-?aN1e3IllL`b^m8&MrQum?wscE zZ9_!W3lW*^E_(uP*c<9~&odX)ESp$+^IdyA=b`7$^SVEmnmn(6pK|U#=fAHx-w$6p zw`Hr2(I+Y8c=J!ZC-gY}uUY!kOlE#p$tTa#OcS?v@g6!Z-nAuj>ZRl1A7hw`WX`KA z^hfB3Uv@UTooVK)rkk#*`&`2E`iV-nbLK^j6S;f$+&JOg6O-~}soTD^C--}w6s7#! zFfm@T(q~D#mB+re3!X})JL{(?+}}R?&pV?}bti&Onl=+|cM1ERdcy{*AODAUc+{yeL zb7b1LGm~zAR=6UccfNT?Y=?|T<)ihle=8ih&2K63tLW$_wO1*hWn9~(n?6bNT8jKi zJW@3CMsH2MjYFp8trPjK_f)Q%i+q3W>HYh^-X-U|Ds#V@#4Op@#}!k*>gc6qPRjjV zGJd}o$hSp(U`SayF@15HO~&V%fTA8^_B%qT${Q2Y_Z|o^@exkCr$g@8>3KUx>xB}#*V8dcc1ONmG$dN(#pTb zmHs;JiEFMn?b7zLbfWUh`%3P?;+)%Oy%Vr(yC)Rs9Gf$3Yf1Bok6+fD{k;F`rijCv zc6-#$E1a?RqfE`5m+Hr6)+N=;Oy9jU^~f{J$62qkGk4xQ(eL`I>5XyXNXno1D3?=lW^a`)6LvPCWA0>&e_NTQB`OC-hF&YjfpZH~HoD zGb>&iR&JfRt8Uq4>yjHipZ!n&Sa;acMlNbkS+TKdXO8z-*Lg0vF27bTKH*w2^~6(? z6KOkVvEBal!%8`SW941<<+EM+1Dj>+eKxz;d3|=Va}0M#yWA|KCtc6~F-rN--(#

#WXPbwbc2JY~kZZ6!Na=jg6)%c;-1l$&@dGx3yWMqjf;`?d%j`DJpC z{>^0H_DzR!5&>OM#PYMQlIyu141 z_bk~jv?uPWt1szly?i}3q0joV;H;#LI!pFP?s3~6cFFSQl5!)rxtvMmFCVYtIC;Ig zYv=vtQvVmtsBwJdEx6a||HdyLzizwse(TM9RWXM|mH$ug`dg~^R&eK(O|JDuTZMPK zEe~%pe%4`I!nIW-(q*lSSa86Tm7A8YXZe3C(yFF9+5M||(Ts_u^={XsD`!sBc42!F zHL*6M*TeOC_Z*$K(+3j9hIY{Z{9mG;l#-) z+C}zX1#eFO)%E2<+Qdwy-4|<%_T^dR&$pTB6#EH*z z%I~O|{7>gos8#5tzf;sYYq$Ec>0oRjQA`4PlBriceWnu ziY;4z#QyA+)pE~vw7+aW74U!d%{|vveqDZ<@8W#E(+^lTUT3@1t+e~Hx6<^BrLIGZ4;LEd%OBAd+0VV{mq`EO4XN5m407jn^?bay34-wC-Yy1PF(M8Ik)fA zkNP7^s{5YgE6>&cB)rSviQ29!*BN> zEMv9*J^iA}yK#%HW#F5WPdAy}yME*~_v98<1PPO`saj+(z&|jw{zTI zmHb*bKS%$Pn)@IB60P4pF*XSH;G=A_+7kc;`Ht}p5A9a8Y!m?U^e3N{;+OO5=9d3fndi)Y)-O5tUtn^- zBF9hliB|ue1DMZGtN3Mg&gi+km%dcx;vRA5iSN^tZU;D|$fb7Yxa+#)E$~yizckn- zZ&|tPtA8mP&%7tQ-t+sqZQE!4?WH?PmA+r#PC9>I;rUvJ&L2nm%TZ?r@$3wrY`O+Qw!t+*?&ew`4R_`S{TW&ilyWaE7cF9}3bz9$e^_lfAewp66 zBR{X*M56M*33V5~X}^tSu2--3sGYyc*KSp!oPK!6 zrw#QdDoqZbnC)uk=A1fj@!|d@&(GF8&sBbY(ZTz4#|g2LX3KriOSa8-zf@e(x#Y8= z?&Qjod7k+qPcH?|-2dWZw#eB9jz;}6-P610xEc5E@qALU(c10e`>xm&(YxwJ7P3=*_W13;*3+!ByYs(|RN|9Xqxl#AzOyKfo3{US zje7j8%NCdWjxDU^6}9YNCS5tbbAICHKf-4PDrcm1?U|}4R5^Rg`j2Y%k1zf^m0D-g zzf7`n;zVxOSB2gE_1n8^rrHTrit|n>nRa>Zgvq_NcB*ReOZhm=MZfOcYqNXvOP|@P zZ6`7<-YZVj@0`UB9bI!?p*`FSvqno98eCwln`|~m9Ym~(w zSl!zgu`ED(_EQVaa>HL==j%?3yb{)Ty!(GrvAp==Wpk5XChz}PFZ8MC3-_A+GLOqA z9{t2LvphEO9J}@@kGuQ{*ZlXo*7;3UDz)Fxv**(L&mYT`_AkrMofLY7S82J_Pu5%^ z$%VRZ|2LfIc8SaURITduiu>rMgD2`E-OiZ>&42E7=lD$_|NGOQmQ2|9#8&1@!;;OD z<}I_Dy>y=cI;YwS?~QYOPU}uPz4FGh=bg_d>wXfLoBrAOD5uf7Sl^Q-{!jjvtoxDu zH*1zl{l*_V?<-YBZ;WyHFH&h(saqS9_VRhtUl%>YOVhrrJN$Q-)03$s{U^d-8cyVX zSuI@OcRlxK#A)vrCeh0mKUJEU9_KziXy%F2E0(W)%qzHa#ys@*8#bU-}(&>gzwQllt$KoB!!}PJPba`7tt{?XS~|`pp9M z4vSw)uUNdW{9<2Y@0Y1=_Kumy7r6b}9QtMFzA~P?CHCy>&ISLbFW`Ul$64xS`|Ka5 zF7<7edir1DUvgyH7t!7?lYRa$y2Wc|mqeXE-+I3NJIkER2ROU{>eDw zh&)fN{*m>WOXLrJm;Y6%_V0$Kvp@c{-NPq!UB2~S-3#prb?)JR z;(h+lowDi$ukilK+I1oFP5-1{v@8D$`2O{+@~jhYg{!1%k7vAS-}y@Z{o@C}whH~~ z@Lyg(%d%cQ{Qq{oU#V{Q5C1UFUJ{@GWm@KQ?d`ArPDpg09)4wh{TaKH3VWAt*?;ZM z?)K|%E{4g()vlYW{xB|M#@p&2JI&tjeWPlv)cnd;lymB1t3xl&F@-5)U+b6`BGYA& zeu}Gl)+N1rk6%rc;QGI*Fi!Qim%Pc#uUl+7=4KYgrL~?~;C=MRL?1=gm3t)mgC~Bs zxpVo~gy~L2vZqRAPhDwMc{1sHeW~3}_xB5VEan^*>SsP(AMoSNXD_QCFBe^0dZoUo zOZ~7g=hvmrHY`(KXspq$P zWACSNJ6w9|TIJMi&zo}h7Jn~1cKTa#@V1Eo@;zz~^Y~ap6bx4HQZFq?Qhj5n`sQK% zr?V4+G7Il)vMiige`m!RZl9Ur9y7~bX3kGNWfQe;{@aVceRkH^|C67dpMS)+@qIy0 zth!!$Olja$&GgiDOA7TcQF1c`l1e`7`S{>`lMke#Pd;_7vmu+kePyNUImB?T#tlAEa^K_0)>Sc!}n_)*RuN z9=w^hJf!iAqYxl zSDH#@%Wet3>F7T}>1pwltNSnbeHKfb{-kAr^x{Pe<+e1vO51(#C&P2kuU;n}w!UP& zXVNo6)54u4vtGRF;v=W~t7Pu%HaeYMb9w2UoqZe6&glR1a^qq3+8w>SUj+tF4m|2s zxct=DiHGlRhE~AuyE7L&Ik{Qi-M32eSI1|r_CsH%Et?01s;td*#}&Di z<5j+F^LkeO)w#1+J*#%f3;k8bMPh~b4q67=q|UoK<*8-tp|8gaFZ|vnnh`f?u9u9_ z{mHNH*E*_{NbCyPec{{p5B)c6>l^zgYnoa<+H*8BU_tbP4c+%%-qQ3~9zF4b^rQH; zX(A_dbA*@7kEyd@!Ku$?a7^@g|+GCSUwlxJ@Be+S^}#`H{Y#X_Xqqhi>ct`zz+!Jx_aK zVdeU2XPa< z-j2_fPD@n2`gth}TXi}Y%UOI8is{w0)qmDI`|EF(i~H=SXRWo#oV`o`_SV+l`BNNM z*kAf|a`O9v`gaVc&gzsF9ObLhzxdnd=9}A#p0hpF;`_h*U3ZB8lIbhA+&BEl9lrUu z_!nl)YbV9dYkkUzjWc7l)fTxf-NI43=+jfF3q}|F_8+q3`uuvX_67MRs}7ZYJs0#g z>*@oaryHF_)2>^0Z;f}`TfDPu>4M{z_44-?&)iX^wf~%RhfeqX`UQb!lfLnOcaJ?2 zW4T^1*H`w`WUD2&zx3!@!q1Z?Ji9f3Y*Xe#gDJ?bklqTruD2w)AgB=%SfN^Cr9ZfBS#7C06t4 z#J5i?!!oDJ*It-pXL(PxmaEpAv*-hN=&9AoAMDnP+^@OMb24|?6#nZwv$ub!J9gw# z;_M~W9$9l&e>t79J>bA2?QKuHEM1&4)O0$p%&=9j~IbGB`NIb)0Q zcHQSw|4PK_bZ*8{v^r=_wbC!#5_g~g^D^EP`#7 z@A>2NgQj4Ty-SU#pc`>7w#+Nec}JNoc~e}(1$r5W{sP4)dV>I<9d*UzZ;Y^uLMqdv3g|A86x+gbkKno)nA<^SiT zf6LW>^!LT)N3PkE^7h1!evZ=VmL(;2AAA@8beJ@gzvR}c`^DJ>_Z02DtR_ev*!!V- zZ^NYT;=ex4z0DcB=;`-%b)I7pqRZ;5etiFu?3VHL8vC1f+nk$i>a1;k>STPs;P6Gs z)^?3;&h1qn+IgNHp1IRso+W0JFn|9Uv;}+ns}IRsW(@(cE*IKuJO;F8Xn(WlHz;(*`4Vp zIJVTAY*yROn&rzCc~2+G$|%T$&5Y-|(&^c<)^ZEmf~JWVFM5{v-+)U!G(f>}!lcVQ zj~!l3vTMB0{4!E<+T_-pbLGd1?yo;IZQnhHa}yUvEjsf<%I%Sqp5gU`l@5Qb1guWZ zR=ys{YpwL)ZKs{yVd-z7ldjKa&9-ts>+SWC^5*H622d5j%}6ye(I{tf3E*lTaS8W7B&TSoL?%v?Uh~A z`_dzp4kBCqPjlzi&wJcjY+e+Au0{L3sM9K6Qpbic&mvuie- z$#WEbljgBkwRE!~U)o1Wxo2{>^)^p_YSA5~;dgi1-^ae!tIwHj+mgpTHTvtUiZgC; zO%qQ!o!#d3QpbN%sQj*JY>VT*ekogIvwoYvUXd@nQ)V7bxia&Q=IoC8=NvOGS8CbX ziGEQ@7W#7W%KigJ#V^he()sow;$Na92{!;y+iicY!x4>=65URYCVer#jnhEwmF;#sdgoA0_+)}YFI^~9)oce~S-R@83& znJyhY>wMwQuw3h1@4fwB{Iu|9eOf=yVuMv(lj|k^byd%$ww2dE->JNx`_EgkzxygG z&I%m4ukf?mg8S$-tH{0T{@M51rUlz;xUUbYSi8^pj@0QZ#Rc(8+-tt^+ot@HFFG66 z6c%yHOuW+BGuY_av>DMi-z;DBnKM}4DtS)71@EM0hX)RMGcSD$Jrb#~bG81iiIdiz zt1p>)@@m;f-Nj2S{@=7X9c)pbz9n+)T_w$PhVFl+HXfRJ>+gn@hl3{^mkucBQdf(+ z9E$an%Cp~Em`9$Hdk4f1~coreR9w~wb}33$BZYo z3D=*b2lnk<;_q9>*ZP}(vDcmD1^2mDwQMbs_^I(n%O(WW;#r8yOlbR=|=WVi(vH(B|pZ0MZET|?{u<4GiFcO{bO+q_fAuD|IOTMP42ko z?PWFFePjQDyn?)mH`^wjil6uVWuH`~@W)l>^0xgK+%|m;kLii+IzM9SukJa;__ttM zS?$mHCxYj^?3~{m>}QsJ%yQX#o|!wN!Y68ecv*1s%D3Oxy$bZ;Yqa* zJd$T;o_;9xLwwdg_k~xyA3iEvbB*)y3iG~s(-vP0k(O}X8mTAA>SZ>0SHIXlOed4(^eXqm0dW{wf(`Q;;JN<;Ncg!er@v%OXaXB^S>b4`y z(O--2=twop`(QRJrMCU|pJlH+R^HQD&0lrjuweND@%$Bbi*||Ezxt}WR&HkM)XSD} zscYxWdHVXa-u>lq_P>AsI(hNc&d7cL9&z!@c=A47zu?%i?!Tu}zvZ2&I~Xy$TU)oP ztt+IeK8LsZeB9o)*wx1t?9u8xbEj!5`}bF$cn{yo=$?5%R<>u~UA|)W=y~;plI1;5AAgLn%c_v|__Xl( z%C31gSHIm`5XgIJ&-p7m#KKC_X6Z?tQ+#bc+a~3t;rF@q8*Qd$*gmfdd!hG*agutg zji&oG4GVwuRic>JC_*CX8tH-%?aMW#n=8b&vgIek`>QYc30G`)QOv3 zJ16{m;I6fr=a;)%$nTFF&^mfFlPUsh{@0(+snZwqw66edDz! zrGNaP4KYn^%cVGPTq<^BETQ^l~d1T>TI_E-3Xv;*0FJ7-J<>S80JZF0Q z%jX8R`pVDqkMB!~2-x%Kqh$XcjrnaCtVAan_21hsefF>P%Q^a$oom0kyw~pau9+tB zdA`EGy5e~2{L8;ND@;D0`Z{;r7TzQA_Zp81a;@}By1eCaRqtc1qvi4QHvTkvc>nMe z@91m4S3SMSW0kP6yQ0MVV@&>SIXTflbDQ74-s`R0_Sa~4y|cuT^FrOvs^qsXc_`EN z?PuBi`RuRv>^l)Q_l-~dazUuO`Z=#;E>)vfI|Lt4LWi7l?_KoHj zbFc5_zvQZuzgww4jEm1(m$|2FU839Ry~%5A=Za_hZ!+6-_RMsd>$T;t&iuWiUAKA8 z-i==tX&0RJshT`t`{VoT>x+}Wci(=>9{qK@){fe$i@&~3wuq^|sv@)YxZwViq0yy! z=l0MqR`ur7_W%bt&A6HEL{F9S2O5?YEe$JWq9`81MW`4Q*yZN+vZJ&Zf zU-4Hjs8Tv4H)(fhaOphH;-yPwrq6BLRxO=&?5gsFs*AQ6*9D)im$@I_c1=>QeyMkB zwujtTha1)jQ{9}z52#=G;~ca~{iDprX*=$_ybODJFPZ6`=d>*EyD~F{e;n(znqT!x z;8OX{mM#+|l6`C${c0c8gme+2*v?sX!*7^1s%5^+di(*;cp0nZKw13+tn<|-) z0T-;D(!8qnUGY7}U$^N*vGVNWo?EKx(qs$GX4ymqc|UV~ZI<=d;(Eu7i0!2}v||(1 z=NRNXE=;_}d2FJ`A&XD3XZ|l@`M=ok|1XyR^OOGZKAYJeH~CM->}~vCcLXn1k~orpsaQ-ud8R+y18AFA5I^6eb_G9_s=%p`t+{w zm6dPa_Er`-XE5A~=(_b-D}+$^0unK>9^%`A<^1%KkGdY6`#9qP;~9d`GvRm zHlMWJu60AMwob%)a&425&XlF+5?2CO_m1dnEfMQ8_C~%s zU%2+W%*CpooZ8v7k+1X%*XA$DxETM__4WCn`A4??>s<8p^XIT@YN;#NKfbl(L%O%gXx5 zSK^|Vm(A0?72@S9oGbO{*w#$Ji^~GDc4b`Nd}UIZVeYSti_3zu+A@qcU3prVd#vVS z+Kxy64vTJI=6Odxwou&d@v)+>DQ~)@=9vGycg#dzB_n70v#Bh-$Cp+bJ3cPWyyt!4 zN6ajKVUw47XP0wI?f3rd+oX1K=}$Yy$!{+GnNh#MvoQ1Di%Y=70UtuGIsh$;1{-sNq*xO3b7qURb{mr6e967M|Lef8{y z)p=o)JEbr8m1L%yUhFH)bPss^`P!~?*1Jzuitl>Vmn-x=Df~|N>?40qik25lj`{oM zQrr!f*&^$|uB<PVmr5p`ffR@_x6k0E#uf*r_^rQ>AgLpc58!o^wlqN zJGXrG-Qpd4>!RV)40uZ#L8_MLHKNW9O58+vC~b4vAle-3Lp_WIJE`3pYo zG?}lO@kj6McFtKU)1T#Y&PplY>v>_x4wLKs^IJy4er7f8u_V>MZ zDCbIFJ@1msKE2FCIM;o8S(b3F`Sh|3;oS1+Wl_R!cev$R23p@+I`^)NwY%!IjoXEe zZQrT2u}?MY`OYn-zFR)W-fH#Tl6_0{n_}nAsMnX`?zsHk^14snZ^iBWfJYg%7K-<7 zbAG(D^~YzH-rei|J@7KTq;hn=aQ&m5*{KVQvhU=sEN(g$dwjWC#+}@idXA6RJ?`I9 zoPB5Q&x3tlyT*TdMcAySz1Cr~R)x&M~!j>e4tbkngj>zV`$9zGec* z_wgzq-^UA^cEdS?x%|2?BRC4!IpQT^9@t1=h87158) z_7Ki(^tRsGSvJ$#dRON+uj!yrHr0DOL+#d8y|+zjx1wWiS@v#u_jS&<+h5J6OFudN zaAipQ&dJsL%l~~lvNOA6-Pbo|d&Bqeemy+ri{R4d5r0$qmiqkCpJg!lh0vLzrcIOX z&U%W-$ zeE;OIoy?y<{;;j8-hIydz*cMPUCaOMef!JyxTnVIAC{lLSa!1oPF()ca{f>KbCJ&; ziq3zZ@?G-iy4R;~l*X3Lc`a2l=XKS${d?BEhD{aJ7MZrN&Mn<2Jb&){I4&wUiA9WX`w%#d;eS9vD{vtsPpF&%l~y7zl5v&n119>xZ02J z9ZNTB9o!$T^Cy(^Uv2WgaP=SjT~9yHV)@@S6booKW~ehwNlC6FH0{@y=PrE zqkMXS>cdH;CZ|tc*4~SXUh^F`hLrl z?e<=CWWGo?uh?f3`Sokt?!sT0VnsK6k3Wh%({4L2S#qAuc^gRFhak~${Dda!;xhLDYtlnztl7hU)lf1*L#3$W< zA+z`V^N5G$RXrbGe7|0{ z%EzwXye?Bdckivp?0F|&8LhmXcyBV#)5~hB^x~9%|9(=d*M0x_D!n-A@0>5p_Y~>; zO)-y(yQBA6daY^r%&os(-KjU-6!-qXdP~>8*QECAl&)OkX1(!h&EDhT+aGM?ObuIn zEueR+*|#}OFM4Hv-4!g~f92(mzd;-JmUFcJUc3B5$JY&Bo7M{5%%91&pI1a~r{kYh zfrn4+@%_E>hjG`}N%r$MrrI6+DSqmw_nyQl37y|nxJ0HZwEFnVoty5?eDTt?<7M?P zKJEPRQ}6nvg8yGrvbOGB-ZKAzRoD*w*N20yeRW+sH9WQI@yi!GtYl@UU%okcb+GI^ zudCNpu0CFFc4z7={b^A)Z-t+0#jWRi-!biM+l^|IH9r&D>;5fx9lE2!_T?`Ty?YMo ztK92t{cKm6ebt!LUDIkewQ7oU;C|j?@9Ts6v#!=xt=eklVf)GJUi=NgyUokzFKvHg zlWh_6d_`T{zNFNn^H&O**71j)xBOH6^^TH9|1Dm>)i?%1Py=2a$V@)o{} zS*HPTV{$l&Kx0;&`x1V(4 z-r`%aROVz1}%>qh=cp8az2JI~EidMtJS@xdVdAM@nyE}q})cD(z>I>vP6cS`S1|Mh$s z8&rOA#{sofCwDbxZF;}#)*S1z^?w}RFJHL#qOD`&nG+wTg*>Wqo4q~!`f|4CRV)2% zjGuhxsCuvZb6dgJch)Pa&fgWeSFLU;BC1ulUPq{Ef}G<_vlHvxmqzh!I^2EV`R>`* z`eyG~^(dv+&2DNfq*4?qwz^C#{2c z>s^>^{TADe`)^4HloRULZcJyn5okH&;t<$K82$ zi1CVZ`*-fhjXTw*zpSkO`tM#x@xGgPs~ElgSG;G^st?Y5^8R+5Zo~WFOZ-LMn?e(x z2i)&YzSX_3HcUr3FTN}<(cntCpN}>BEqC8LHhs5su9Qn|@mv(^c4t%W)2i!1a}{mF zr~Nr<__Qn2LU5kG0{k46u)RXB-Qcltr z;-pVqUmftj?SgLoDz1OoOY)XVhBrnsdrq@@5l~WpYtr6Zc3hV(n@w-q)O0OaYwQ(9ZOgIxTh?rg zn9}Jty(41hAK5RIKK0>0)8)y>xtNt#y_@)6SA4PJ*DpUZTxyMaTjlFd3ts(xL3!ba zj3auVBI;DvO~_rVP-yu-shUUc9p|&7`{Et1q@{0J@4x)jzPdC1dqVEscfQJ86C%$y zAy}yX`l^KVlAPVw_qZ?o{^Z{-=l^9w?`FNJ=&{g0b?>OnRK4Ua;bjZ>(=*v^zA7_a z+3q}Zy?kv$R&?6W0I$1W_2vYa`PNUdO}Z--#@;rooJnfVWuG1AuefmiVp~B@1 zemqmd`+_qJU!)%#Ui_LfEnoHds(U9@>gFr7U!Sz+&OXIjUe~)A@+-0A`ctu&R?1RqUgz9&o4js*Sz@n=aTfa=>EKqTLRbr7K>UHvEOyoukRA` zoUY&ARvv#Xu}4UTd!MDyI_a21zU6K!>Q>D@b^i0Ki@SHLU%p+DtN13zvqJxS&)i#n z5BE*@e&wU-r`-}(GXI}uUs_ygvUYO0Tkgx>LeFL|e^-Cyy<%9qS#qEH`<&O26PH_k zx4U(Cb-7&mk9Ws;&nFtze|!@t{~*7uYrXN)w^#Px7TezcqwkblRdV9?-a1Z(@*C4) zJLiA!3EKZQ%)R*Iw$QbzpZ7n^wc<}qmws>ic&d2( zgkQ7b)b8$?e){jJka&~5fg8VUGxuC?Htk;7lJ84A*YC=#x)yTZDZa)w^Uyh$+4{TO zGe4bs@%v?Ab!pO@XrB4Iyrx}utek!zA#!EUH`nO$SNS%s`*Q>2YRk_|ep$~cbpLdD zYRHMu#=50D=RUCKxa|2ajJf|@;~B@UC##ihm{>otMdk$C>rSg)P$IBOQvT%iFBiGr zzuORfm7hPLu5#JjcDajp@6U1lc=g0SkuN`bPJdCB`*+FNQuN5Hhi~To`fgv`%{24w zoaaxoH6+%qF)I@N<-5&#;n}9UF;cr2pVk{QJbx7bRc1+!>B(Pm5ATKV{IunX^UX|o z&cAJi8WT?cdLdf7;)?#n3-WK`-+b$D`FFCeR`_pS`u>NVb^ABncX_-0_K(ON=8bG` z%|l)m|DP>W>1=NJ#PFkU-jm{b%#ZyXN}vzrC{mqSTY^ zj4|Oxr><=E3|m#msw?{0yZdKla-F83y?T`Y*U;1Dg$dJF*IzFD`(2>_*Zot$JK54V z)!jbwQ}DP%b3Io@byA&RVcn$1M!Wg%M0;MlQz{WZQ(sv5O>WtR{iph-#4kDb{qv6B zZL=8O9^bR>==*1{esJ9GV*1?lk9}>zq_s!#Xh{&uoT^SF)l;(ed_zctVO$Lrm5+5KtF*Q-BQ{ZF~4 z-m`ehrYT`lreES!zN0lIui3aKz4=A#*Qw!){yvX99H5lGYIk7V_s`WSeE);ev%Hq9 z@JL;{;Jo+D1v}0^zSs7-bo0Ny5HClsSz5x?ZM9r`Ys~k#zGC$%2}xT%V|7PeeX54W zwv?%RyjP^?wp`zxH|4&F@5KD~KN3s#Y5dLV`eSFXKmF6Zp2o9J%j3~zUNaWv#1D34RaBdavt#=K2vgozG$cd7=5Q8VbC9d+un(3Fi7T z>#VQd_F&_SAMt4qx67uqe)g!*`KkGB9{267)`{2OEog|1mtC-EjnDO{x?Ai1pN;xg zu%vm5+Z&Vm?4Q;vs-E<cyx`dL=Fhut_95n670RymD-2B9Rb}UCKY#urHqTh_swe+S5;l-GRHY?6PE}EwI1BkVz{h6XU>o89J9A|vwsC&e{Zfo zY13YlZ*!+JE_);WBzx8_`Hg3d!~fRyiG7-Dwa#7p&96npp$=JW`#VqNetZ-A%esCO zV~Z94V~15zmf7a4pP275Su)1^Z<*z=|IZ%ZkeMkzcf9-bXWgarO6|$IyC>=RCq~un zXIZ=a(&DWTZ&oi!>C*kTXwuni=8Uyn@0Y}Cm2YynXZ@>v)iKfkb3dmZdYX7;?fRJu z(_-gkE>#u%)xds0)<;g~{EP6T_qIx&dirpuTWJ0HwNWMCR1c^0Hk?lQqxX03HHFr- zAD-@Bq5djB!JNU{c!KP{BL1244ogc+u}@Xr`cZ$e*Z%Xfp8RiHy1aM4sdwt1w;mUn zPrdg3dM$2eH`nc5kDpIh+Qz@IW}E3rr<~K@wrq^r93phuZ|N=7-5GZ_-4$s&V{-pz zj{fHcSM|!Xe`nQi^R747irF{4>QnW+r@v$_nH7CDHV-?c1+(fM@8!6je4NX7V`|)Kz9%1I zpZ$Iw(R%jMo*m3_cNp_8EPsDMBUm|=_tHMMtutnC);Rt3-_2#QRd?&#j;sCqH!qj< zjgEixtGIb?KhLTClbU~aW%25q_r8mK*1t2oIX_#dU*^xzeMbyZ?)?n?az-!r{Z;G9 zKUYe(7V`%kyzZGN{DZ}#>}K`Tz4j_{IQ z{m81mErjd#4rlhRoq5|nD(>NbXv^_F@SDnS>6P=}Rm^GjsXX8Hd28MDe|NupX!(2M zdgjL(R;IEG#m!gmo@`uKxbD$T&F3M4+czw=o|31TyNBbs?-^GUoB8^eL+z}So)&-D z@wB+J_Rl9zE61NQHjY14{+d564k!}H-`y>>^)B;@$dmPHx>+yv{`SUZM(@0|ZNhaO z{iyKJRe764{%Yuu{rQjt0h?9-?KDw_ty$(%~@-`Y*udaYWT_EroO)B{T7uq`=1}1HhFLK|32IL;K}>n zPgwhI<9^EnTbCa>dGz9+Tk-K4g-iu=tyZ7)-qXKmgKfS2)XQH^^ej_duUK!!e@S9` zyj7Ih`T+ZkqknBSXeS<((`~Me2)fee$^w(RnD9LN_4JU zUue8t@YnXQ*DfcAt>441c>OMCm~vlGPd)F)^|8NZzi_l#btrp&6K`PSw;rR7%*;%d z4`lK^`+C27l&P2SJ`#!?iZ6*MdMVxsq1sQf){5ml7HtFBV5~-wf6an zYjxAI>TS=@uARE=X1n_9d9LRJcKnFsnr_p-%Rca1`7htuFSbOlmo>B4{I9!+S8Gag zAb(NKu9lyYeX4KQ=9vHI<36|Xcfgu_o>iX{Pd|B{zr=H1e!|)CsvQ0Jxy-XQ_dFNa zXnN=`r@iu;EsL*pRKB+|HNLu}`Qiunjhl;)&a=_d?sEKHFaIc9=eJ1zDZb_f%e~&5 z+n01%{^_q7B428iUc7OG}+0U!Rp7sPf3xga@#mYrZb^pCVt-jZ z!S2?h%t`+aMEvUW-2d~=8&}57&(40`R#bi^t?uTHGwWrJr|FrP6_=dhjna>IIZi;zsz2~ z{-M+@iP>ticUk^9yN+MyU)pu`upK|XzUbk3TW@aff8C{ZR(N-X%=KDd-rjXDzs@+j z;Ki-GzqW@R62A9v6W0luL;aR5^VhG8dsUQu=2gl0b>W@Uf(%aP$K2T*{Ym!G-%oS5 zFZeq3)cG`PzRfc0-B0bhFnN|r8KdS#8?hB3_0{##r@k!z`FT>w>j(!{&tK)LOLFH3 z>VE#Z`|S48Q}y>h4*8ZY)NBZ`kemNJTuLSS&u@`#&Tm0RV_>kI*MN?a67S?7=Dhha%@44d4 z4cj|H-M_bWT1yrBJKe3mFIv9tNNu#!(Z}z)j@Dj3B6~PLSNR=#{kk38D(`j+X~}(e z(0P|Ayq&Gke_Qjl_H{d^d)+w<5`GwQXa9^HTRWF+I`Rc-X;b6p6|w8zuOj+`W*cw((-7+$LwldvH#7& z$KH#*pDfVjoHlR6+gb_f-J*N5FRd$I_b^iR*n9rY=;AGG$?yDLIC9u>cqJDc`u;rF z_y0so^W?y&HU9&?6zZJ4`oI6Ok6H4;{XWujT;xB#S}Jq8PJtsWpc2QvQ=KcO}cGy&rR7&XTrIv-(TF{yX<`BMA;YRi;A~>OmK@}m>wTfpIV@5ddhv%Id`t5 zYKPyh&Pn^J9lUl)MBSv%K_zu&0gXQoE=ig@e^Q3zoUQKbZD0MlVy@Y2HZRyOf^E;F zzwxoVB_BHn-cGtYZ-;Gp`^M*efA*)GcRgRY^R>+O`440^);9hRf36_4+5Psbho8PI`tb@m7PzPVpCo%Qd|BPUDhpOzn*_oC)G_mZo6hPqE)#%SAnU;j8^Zr0Urzni#b zzYHvxt9@5rrEb~Jy&8X`HM?iIAHTA)dQtML>1jT;mr{KGo-kcjr>Xcjxu{m>U$}Zt zeR@r<>_Ur_8lQD}Uv_)ApRudo_mA~i-Bs)DGq-+KPMgDdGvb@+2Y zIopS>xySqd)p@0EIzDM?nSWwgU3#8&$7|1NuftESJo&Kd$jPS`w|@DBWypF~p4?Ts zW)V}JuIn4azrODMXUr|%Pn&i3cGQ*1vi#|_g}>|Pt*9-WzEQvL&;6U?c998Q%fl>w z@AJ}fTVN;MrfXsGZRYIv(FwXSc1=YKE1kC%9DBEXvc_NINBt~IzrXU*6)oMF6|?rL zQ|j_))x5Ah&5|)bE4ao6hfDkgeMrsFTREi;8N8o!GWb$ZLQ8tgGMDvge)htd}p{XSzG>%8J0S z46Ra!6)%%tzgs^$Cw9-AsN!pDTb-A`UvIHB`+8i7nBJ*ZWs^T^@2FbWWq_>}%IA7OA?rt?zgf zRLJp))qnkqwM9m47uoo}mB{AS2kjDR=d=EjmU{OF>)!nvX01F^a+N7@-vfm&y^rqP zwf+~pZ7w7Pv(6z-cWLlp5f8nIkWE8!#;aS1X`EB;s&KQeDN`8Lb#}nMxlNt6){_>jo z9Fb^w(SkLn7S3K_$^L}*z{@`ekC#;S{r}Vc-{xq2+>!bEDaG%Vc4)QpN{1Dey0T6> z^xuSg?K8WWqUc|fW6t$FU(@K4iK7{v5{AOO|Ty z1TRfIxb6kpCBr{U7N7L~I(4_B`sev_8SL)D|9?L`zwZBy$LHN=|GBQx<6Zpa{Ga{T z{#-B-+4}tRv(NMEgMa$3yfiadefh(GlQh+ThI1TW7H2Ypdv1NLB>SqR$`>EVK1z^X zWXdcTXYup!smG4x7eqsv?kzBGk3LbK_2YcxpLC`FFHhD-eUyK!p79 z$BXLvltix;J!^kMwD%=X$%dCxQulN}e0SpEu21vdd3}po^7-(ZFTUFKuQZsaH+<{+ zToS3MJ8@Tmz`LdE>JpCKhFHHdMa}QmG#H(+eY42_x|7~Zo^$uyt}2F# zF1hsbL)PQYtL$8}tD+Xlq~F+(xhnhEkL6Fz?|W|b32%J1#p&B^->E^&=}or_Hq0~s zu{_>s`NF63JpVqJw{c%y;`QK+B~!%em#%*lKhx*=wH;mcpDpL|X$Nn7!v1|n$FXpi zW7q9$bB=DX`WMcb_4xm;DCPD=%$xKAb1zL~Xo|PaQQXLSH?{ca4pwflD~12trrma( zJInCfDt~P=-ssYVr`}Wdwui3pS)LcOzBT*Z>aBWfHb(JoZOh%Vsom>bnb*29uiM`1 zSHGy=n)&92ooMXqW8YRSPJ1@5?C`-*~b}la#!%{z+})&&R&sds5GTb(YrDD@UiU_l~|5)IIIm zsudB*(W2VBUOdv%`nT;}SnAf*Z@PA`kq^D|H26;4^E=h|XPZyvxzca&{9g6Do&V3- ze!o-vf6w=M?{+*lmDrJ;{&Lf-yCs#r+7a6q2!&nQw7#)9U-RC-gC@6UIxQ_VjX%b@ zDE-~n%nM6*7d-iD<11aCm6lGJ*T281 zWv~*q-xO`jGvjLCy?t{py1(e<_1M_BrnY6)yy;T7! zvgapp_T93+(>Tdt{raQji6NI~zj|LM_3!Op*5JjmDXkfgqDo$8pIm#QPqRKM+&#}B zp8xChMW>j&0@5e$ynf)(u6cz_CU^KsCa(FVw0C2gn7;NBxp_hIo(rqqSMpl3JWyj} z=DBzBK@{8i!$PmK4C9}za~8jKQz_;RXIXwObI9(6)AlrTK6w>2)%&C zE8nkh+u^Lgk2Sx@6^K>9&a}DzEf-c?fCGWXx`LwNbRJWri(VZha$IqcJpBScj2#w_0osu*@Ft_ zpHY3sZm04m;#$$}Kh8($tK?)d4eF;?1RYBKFL?Xu@7%mS+8Q5N{`~xvtJEH`bGFOR z^BmWIJ^OY%XP4Zu*|(2cr&q*=8O(B4`F~F4-Z9&4@kx7rHx$f&mi~FsAKN$Qv#wwM zBb}!-U)k&*NB#d@9J`MgSB804{=S#^{A4B{=XcL%yKY`T`N^jE-%iQ%_Y=3*uk16~ zP$T71_vRks8L_WQ`xwVWh7-x<3&e^TjEVauE*1q4|&JxRU>v7uqP5&5PD~nHlEVbJ< z*~#f<@oy(LJBEk+78N_(ZK^jPf2W;3C%S%zyudr=GjmzRKi4>%xaoc~_RXICUQ6s% zDoQ;(cHWjV{_T2yU5ou{dkg-s?k%fb_Zlxc`@8jbVaeZ=PWgY5oVF9^ep??sJ)}3s zYu>lXRq9r6@9%gxExPkbyl>6FtBpQE*RL#@eK%)ew;6Iok3281Ht! ziK}Lt->-iYzHF1%p^!-}>(nm2GCi`j>a0LR!HgZ@{LD+TJF0u@H_zX)=A*jl(GTe+ zm)EEHxSA`>wO{G8f|GUi0u4!x8(*K@zTH{Awmv5C__4ERQ!YjciU*f#&+e>VTVG|o z@HI!S>guS;n>4pC+tM;8%k=WiuzP)_ZHxRhe*M~=I`;x^OMRZoyWZF4_q<-H1znuz zu~PEtp1D&>3&NW&#cbd1EX&xS#mmFva_FFg&7J$^LhqEinK^^!2Aq8vXQ|x3(MLU{ z$Xj8yukY%ahX0o=dq1gEce3H_OD zCod%^2uZ%=J^KF5d#Rg3UHv(oU&`5(K7ZU8P+%Yx#jxL2jQ^|HBX&0NbL=0vpC0~T zGykaHp9CEd*_WKT>hls*1iA(Fi`keyTA$pMpkj03rhEqzyVLDQ%6l7gmFp)4+!yOu z#rf?!-)YcO#lx~0VRnlU0V(7iIbjCjy zFXIH6tJcXry0ckheC5)g&+%5h86g$wSn(uB?1$kh$KLQ~i;pQ>pR3^f%*r&`P{4cJ zqpN4`TCfQ}-t_ML-qt@y_eijJ|9!Qhe(J>2X{L8g4t4M=-fB?1|H8+7;oG1;nMY-= zPdZoM5yR$oPtRU{_LFG~)}{V^IVW+^hS{5D8UES+OZ`a*pGeEXlvG(O+n4t(c`s^8 z@Xr2o_JZ&XxAv@)GdE6Q+I8r%=qs5!PP2A7-EBPRQKMpWNtX9Zo7nd+N*x2|l!^X@=;B9|<)5n189?uiX%Qd7v_b+?0aqGV=t6tCQ zo2mAsr(S)FMAXXFIb|zEf3`V#9PJRC(WLl-)A6~hr)7z=ip*}|Ll-ql?j=P}{Pl3Z z<+{TjpC%s@m|eZ$K=1Znff)|(659t;6(G-#k}=PcsSY?=*VlG z+&E=huwSA5nPAIU!GD=Awwo+WRBE+;*#0i;hiiwnOrs{hgT&rwK^KEF3GwD@3J%pq zFLasUcV*tfg#q#!2mh_R;GwW>uf9i~o@i~jf{gsjcl}K*-xR&q`AYB}O|ZDZ+GNL< z^v5BgdWn6)>59!AZGxBhrRJp9hq!DGxge7LCRA)+y+OtEL!PG(|J~y~`|#hHjIq9_ zf0?Yh`@n)H{^+%%bJ9F5wAhZvZsKgxxDaIFwyou})rk+McOAOY8Q@|N;$|w5$(1ot zMI=Gfl6eWglF0tlBRO{*UPZXEUix;efUShjMSV`!{W<5F${K%Guaz(1Kk+rgYjV9J z!=^oK#}ay{o@fzlX)apo5Fs>gOKYglEd_;WU~Gc5|w|aXs)v?96Y=6J?aQ zCupP{xR7vS!L)N(ZLe0uwRi|lc<{X%_CA}kLe}eLwrrK!Z&w` z7Bswn$EV!cq9m8t)PLz+r%Oc<4?~&+N74srwbM#Q@B6}Ltc$wX5hT3k^6isfSpy&P ze2~&v(4)ENeu}&Lk0-ag7F}U|7Vj-_EZI6TZqkCgfoo*j-%YvcXLh!G*QyOh+B;a) z{;hmjR#gA`zx%DSOTV5k%~IF>ER=HD@7_YET=ibFt26Y{k6&%MD8+j9hF{(mQ~-1Vi9JPSon5HU0h6| zTEge$Q_42o=;VoxG#5)ayhB7{s<2m}oS^S%rBcQshik%KtLDZ$cX6A#B~7c(FQe$# zu^UT7T3RpYsVo1Cxhi$(YtJ)}TLC^7@-4hKPc-o0G1%&J_+;;#mCF{~OMd(0tXZB@ zs9JQlXvM*R%u41h=VsOGdps3k^h=Ol_%q3?#NvZZy>-eZHHnw~X07*|>om)>UNz%c^Y z;7*Zm*Y>!{kFH&EZVG--608(=w^r0v!nm-|k|TIR;H($x{!EK`dC{KJO=v~d;;k9= z>$j}9t=YA`@1BFAtF+tF(4M)A3Nr8bU5nLjEYAJRc7lD2=Nt*vpQfvu@3v(<QH#{~v!jpwT@>q~9W+|?wNTUSyOb6rhw zWqYQr!KH%SYpb#p@@Fl4;3fQ)ja%~7`?43evZU|VuT_c-c9c-N*0QdNmCq+C>3GX; zSxJiv8tw+?L@eLxthZ0M`cPREu_GntOHrTSL8fJ<3_TZSo@|bGdK*?95wm>R!nH}) zCo5iz>3+B`f^GR3Rae6^6SCDy{Y>8XR<7Wi(4rJFKdo%BQ^A%U*BpgUJ-PJRP?mLS zflBGL8@<-dZ|hlA+YX+QoE+Tf!lahyzG)h3#uvkFCrSg>&hkIHYzEWsyN*m69IDEuP6EZ+u-e$=7u4lzNRB=k+Xt#UH;> z;yQ8Dic@!^jovvkwP-eDPD^vMdHE0L8tI938$CGEy6M~&hV?&Y%Vfk#`Z5Mb-JIle zUiC&q+%^Wr*A7?ye_nhb?DCI3$Czu!MK&k6+Rwjpsmb&au!ZL`cox%b-i zA7f@^p7zM`-J(Zx#o3d3qsu(&9g}bT+@zZ3aQ$hf$;MpAgcsJS$#d2|7qgy_IKh18 z#wFJppLTW|%7%$|wB|Uu#a*{3(D|+8;_j%rqeZIhx^arrX_cZ~eZP$Zrf->*!E10l ztn8`a#`|GcrC&(D;bVE+d@*23$m4sm_Z@m$+nptL=cX)}FPnIzgWbGLIVE#_R=q?{ zyzSCQfjl`&0z3Abv*L?k2Ub5r3$J7nYt_PVL6Z{jn?cXbuGXE`a zT~zaY$9nr)(j61B*jMe}J$nkTi}T%wxkuWi&tB`Z__bEH@Y!?gM|Nf();8@B5?G$a zx^0ui#e^Nz%giRbGPBN}Xd3omo=#9sTb5P5k7%LWfih7KUz39AH}*VVqut-8w9rAv zWtxjK^FoK1Ic$u~%(vayl2bB641MbCF6PYmv?YGtlyc3pOSV;bx@YuEIlimvgF zF{g`)8cniEX2+dAPWog{tctkHF4iNZO3KP zW_WIFs=szKu=b#m!v~H0kKK>Y%_x1;dt9san3Q|f#SPm!4pyIEGRx$+`DE3jZBolu zmPXu?d7SqAKX0qstQWa@m5+Wzv>aKuQS_1f(RCFG+&eYpc)uKV`15gt`1aR@<>~*} zGZzO6p4^8x&@Ot9E6-U4PvkN-=q0C*-zMlWPyHaDL#HnhVJGru- z`B{D?bgWGgS(q#EDoSJDyrcPrW%ii~I}QblU3%hTbw12vtudQ_h(|J`TKzO7ukLTh znF~b{Y?g}bU}97`oqNZoQTgDH9go(cZ6rgg-9jI-c5R@=d{8|H|d82hVmewQf4}X7caCN+wt7 z^;6b#WlJqN?elxHljVkDnKCX-zc1I1ea&2xcrM^i6kFZmH5?NxOpj*G-!G7mqjX*I z>yoU#->LOnrfrKwXM0%go-XiCY2Lw#?(p;c`Z3d7 zj+Jp5-$bpArZ$JHZt`t>sLfk?Sl=hc`C%SQ;v>l#>B6E-a@aA9d{%+apc*5>(C zVBVq+^$ooYW=)+R1qw9|ZF-=t{~+QZ$KpdgAC9mxI49>=S_#e*>rW8)8rzh0dO9C&Uo2cV;bfmd=B>ai<+8JV=6i&9=N+HFRdjdOantWJUX`DTk2HA` zaYF0v#)hLW?jCeMvHQ=GYu`5Q=A1o8qHIbpdxsg@`BrX$8^#SEdK#|0UgG1opx3R& zDB7ofOU{iA2OsV~`YpfYL4M(n`&|FL-o5ubnaq}ULQnbXiPqb4N0ilzmN~@~35n=8 zTw)JO5p+3TbW=xeO@rLJ3BeVN+(PSxYB$MUd;YrnTj(!`a(|7KpH3=w_|(j&_GNRX zY_8bUbiDGX&5^Tb!+c5m*K zEBcB}O>X-M4?_sW7ebBaGs(B}tCT1G3demKVw0Uu{^ZLFX=T}0X*Hv9r>-zTkgp#yy_%fd}tjoloNpMYS z7QX(pNAZ|fs8W4i(u7wn)4WbbI!jJ4_By4|&3$;0YG_5aXT15TYdh;xm^0>o4f8jA zJ7r?cVw^P1;O}xz^yom8_!4iXGq6t@oT%LyBs9JZZK{CpU+ceeFL1gNlT#np_ zD-=yvW@LP2Tzx}P=+?Ul6J)nAWWN;_*J-(HqhqqoW2uB+#iBDpMn6}H%N?I4m?>cK zyiU+yj`Gi$Ix8Ecn&v!l&t2-EP(S}LM5?mEw7sF5|v&*6$8FqlYEs9Sh}-Jp0t!z!X(%tb#LqOXP-W% zhh7zubQB6cQ!{yXj?8=gbvhgTyx&UfxtBz4g-Qs)) z`>&t6W_UHsmTT@->+`#hwk8I0UzhOMDelnFzpCnCso#zYQ{#fGmD{8=jZQYsS+Qh# zyQ9I0pf7q0x@#NWi@e&w_qXD!@tln+3kv1DLS`%#&f3ZJ_wur&39b*gpKUdEGdSI0 z(qrjzGb>!pUATk5k(=e_uuZA;SxyAL^@ zXjylT@0Lht$0qkXO6eyO-WJTc^XW@#ve4ztz2#5rC(G1@PhV4?BwZga>pUa&;YP;S zRe26c;$`|OpKr8Adv#prwR!hyZ^Y3~mlYG+Wez*L=sXj2U8?-}{S&S^Tfg~z`@rzA zLHtL1>_O4;f5$pJA`UN-QH$ByDo`9Q$^BU|eYGijwv?{T7MCqn>)r+#?Fl%%Dt-I9 zw_$5;nQhQm_v5#&N|^NeP4%ok$GE<*CCa94k~+|G^j3y%jE10KWz@xkMILXG0&}#3 zP0X5vb0_)!{<|yd?jq%>I$1sP22(dXr1gm2(e^J*-}#S0-&l0UeEsgo)&;YSZXbBe zS+UemSncqM<4rf!`vrQAFq#X!%FV6k?uuy7 zJeyQ#*c9gbHKOr?LE%h&mo$z#z<(nem5l?kE>Oi@^phVyYb3B5t|WM}aW`*>31QQHNJl@2Gilw7sg?V@_jU$XvRXTa1et6uNmJk)lC zMMQn2=Z{nm%XPPw{c`sdeOGeRd3Ng>-||O~`->hJa<6<+m-u9rtJOrEQ1NS%uC7Wo zE8gXr)hHRe)QTr#wavBli)_@~j~t$M#^ddU?Rv{N89w;cN9E}D@3{8ha7)G8<3~Lt z>kqI5xOL3^pvst9^7!d9SFQLxoA&ayURm9Cp>x|x7GJ$fCoVi`E#2b3Rn%meR?xKx z+YK&!R;%sJI#Rr0lGnkmWFf)4s|~JMty+G|JF>+yK7NXA%DgAi(J^s?<1X>W(4&=C zminf3=uf@==G_z{uHJe-&6$P*ZvI}rtJM@tCE47)GX5-G*?P6s^^{PD>jUS#+RKh= z2|f{e=eIGELGeAaLxW<-^mj*c9ge#uUz))8xNX&(BBj%p|Kw>EE>3U`D9Ndfnipk0 z%k53Vs$}Y=NH^7%!*7hV>Q@}p;^0}b zKu0YxuO_SSilWS!Cp;VGvxc8C%6M>Kll$$?esRI+W>#J*Hpb1VYcKn4y=GPV`Mg9; z)UjKf3VFKRkAj*n?6eM8_tI1J?G>-H2dk#DHN|nh{uFzFdzrCqq|H;6&@+PRO-D{U z)l5CO=ZzKP%JhGSmc9yNKesbzZQ+CZom!zKC+Em(YJIS)3p4(j6xf$diNjh4`PyhypX}q z%Hh5F@zlw0-#9;i#MqSegrDinq@Rq+&(_Y~wI*q??rO$Y7M;bx*D@`yq?XoP@_EVn z^Fe+1#>TG+o)LE?*gIudW>3#8Eptl$_WI+Tc^khvxn47}&f4@xj^{!{LBoV;#x7kK zJ6QLaXGFzICLs*OAhWr3DE)--Efs7ngoG(9?KmBXirw-{E})z0$X zv-iojSD%*5aJiF{yp>5&W4no_yS!LHtK97+T9(zOedsvD}oFGO?X;`)BQS&AV{F!EzgumH))Er58Ab zS4}fMqN3S9YsdVx#=haly zYLfW5^e1Pn;d)52j>sygqh9|F5B%eTTTU^UvJC#T7f_az%8md-euysrfG1_jQxvwbN(Y_MXu# zecgIhegCYr=Fg(nbbq!uzG>OP8Tm&hrMh{ZzBZBj#F@KE{+spsx0=eVw;X{{P{Ug zP2qdjR>NA+>-d0&k3>7J--}9*yBKjMzsluwk5Auf%~X}yr+N=|$TOC{Id)oX#_GdQbc`+H z_qgRP|L}{)CvnEzS0Z1|{$XFJ$h)8Gaay;uywgIa)jr1`wOyJ0dAe2VvmIGmaunj~ zYqp6-%wpO%yW(|E$SkQ_@7_3xOvuc6zv|Exm*YQuat)GKBuie1bFk_tPi|h(ZL4;> zoxw}@s92z0PHJnWq)GP~r)<&wU7nG8b0;la{Ho|^^UNPVgzp%tns!}0+HI&gz4gjX zm0*XRI`=le4h_~6ZOM09!?P;!?WIX4zHi_^>{S1_QtgMIPH}>3@Ui)<>Tw}|qgLBY zY_Ql^wK_$vaF&C5XSI;}Z`a8d(l>p+ZIISmk~2y7p2D^S!SMcY)-JQVcPchZTzcp% zykwT;Pr;{;6C=`nCUZSIUb*Gqr;j_QzyA|rw8V5>gu*&U*Bgf=TKoj9e7D`L&cB}f z-Q=;GorGaT{hwVXJWrqbo=sxQjl0dGeYn(Mh0yMvm|Ge5OthBsq~;b!on13|bJpi& z(Pry%#mtQqZXWwuzwzI;=vnvuU%Zt3ahH8R|DpHJA7-ltHPvnXV|D9`n&-25UZ;yE zi<;`ab@7i(PQBf`W#hKBy=MOwT-4tyJ;8VOlJ6e(%ePjXey5#XUsHYW{nlEg_`)&QJrfkp?|8{~B4UxvrK{H*q~lqG z0&n|q^KP|fe<8?`+Z8bFQfy)TO|QO}OYiUouP$wSS~)-TY^|^5yqQ{d90iT7anwkvmz-+SPl{NPcy*L3~G-|kz#pI!KVr?|b(%lL2W_|NU&zw5oX`1>XH z7p>JR-+$s^Es$58(zhviXl^~%%LPwP4=J~o>;o^R}*w{Lz}XlTgY2Pdxoyv|kc zDgH~k`BR^r&&uy-{<9Sb9(>4d*zv%qDY>5a!JYWvUO9V)r+a!HFg4loGg`DH@Ev44 z(faOihcd@Hg?A=R#rzk!!k4bDmCS9jZ#jKYe9iI?<@_e;_v>Cw`c~T#^pdG*ay{n* zrKVbW)(=`u&hm>Vy}uCe*nM7k#!rb_dE5Qs^+xs<-r6r_{+$x`W68Ux(Gl}5=)JpU z@1XeG*x%vXbUVJCXKY$;I@>Ny?q{%QesD#RfnDyXksSMnPme`j8L(gZazjyT>h8OL zPrv+BekJ($eP@vwWxpg>T)eq_#S`1o7OzWF{R2;Yf3bYQ8_SM`n;Vvh&rf~d{fk9( z?tjlO9->db)*rs{I!sqJ?*8%(>x$0@TfMe7YyTk9EI!YCn>UqoOFZ*(- z`@~y)W%hkdJ^zfvdJfvgFPac|boGQ8a(|f`f;swRm_Z?@&t&mW)UD*tYKi}@&+PmE z-F$8!)S%*dGL`Gf8;fmU8B!kF{;T{YRPy@yMtA%E{A*K>EvUbzzTuj?z4v}j$=YeD zB~LV#EDMQsoBqBj?Juv;o}wx9TJG4puWYZnblS*Fdw=i7-+hD4rvl@9^t=@-OfB<@OCB@yEZ!FHb$r283*GUae+DZ^^K4I@qU==X29%t{|@IX#k`{P$#uUpcx;cqSJzoQe?!l? zeI?B+P9U{TLk`R-=_+;57UuS$N}zm)4d z_eZ4Kj0aEZFZ6qSIa=?`#UgI_Qs-Bk9KLkl29-@8ftz#VtSH}wJs3R#}ZPgyYilzscw|BToE z9Z2fnPfk>C+;iuvYWE*;AGUdo zUi*IWEV=nzByY(5r>;9{mT&Ah?P2oSrtg^LlP}X%Pg+~6{}sGc z!}&wE@f+hRd&R4#6~r%{;lA`*Vb*!?pw^Io&Szf8A1PS3z3poccZa^*^ixZ^>-9ap z?|kuZy~D%J@veqW2A@seOkeMKZTdy?@)#w(>0g599cP~L?Tq(@Gsf}kNB&J;(!b+H z>+}t+_b1q-2>zUzFtPvFzf-KgUcB(+{CcsnV2`mv%spmxhB=P+?Iw8Mf101TzCos{ zzmENYD9dlg=X-x_pYdq7(8mg!{b$S{7Opw4v;MuK{<*)l+n$;<6fak-cv5{yQGD)C zyMhmA?mIr!uMyWOefOJzZ@y#S?v5vob-OG=-TZwHelg``PU|WZ&|%WontFHA_cvSm z-G( zcU;%7^3uVg`UZc)wO`{6YW)A0``mS}W8c?3onem4{hR;YRzLX8pqKcjyPo0BLv0K9 zn8ufHp4wkrzHocJ|LK3IryYF4`xU;o4!BT&9eNpiC+p*8S?Kpk+ z$lIm~PrmKczH_f@nzm`)`s%{dQLo}II%O%g1$}=#>382YNAUnp-TGCGCJCp*w(WhB z^?Ty#xAHN$|6gSNR-b<5o9?NOnyI~yd$S(j&5h2C=FjcEuxPYwS4cXi9&;;9o<9-L9C@p-Veep12xUK_?g)h*sK?~9Mt zbM2d5#Q5j0>zA-PdBGpGt{5!leGi!4T0}0AUC#Qiy07Au$Moj=g$Mp~+_5aE_ZR>8d-)9W2j}zx^wrtok5%(! z=!sm6)#KW&EtWSw^uB@1btYf_*d4aY4OvY;zH?mvrGC$-=lvhGx&zuubs;;Fne6or zm3J2uMsITc8THhU`_H>Y6{pMdJ4=4E{9D{t@v7s$~IJ^Gb(fYgPKz+;( zf3EMshwcUh-l)%inlz_G1Sw-dteLRldxF@0)1r>#iT&@3i&))doGeyF6h2)q=gBvwtjoWx2kt`TohB z^Y$MX+uu>Rs`WJ6KL76@Zr`X*JSmV;=FYx^)&AP&z2}+sO%ve#Y+1wi>HFtR|K3)f zxP1P;dG{|FdxO@WHeA0>JwE>ZztE?jMqV*n|3n*|SZ;J+`-6%D->!VBvX6E>`JZ|6 z`c@eM28N>4;{4L0Nl%}-ha&5bXH0q7)3`pSA 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 65e705e..8e69c7b 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 8e69c7b..2d75c9a 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 a8ba69d..6a21634 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 ee1c6da..d9f8dc5 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertbase.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertbase.h @@ -33,6 +33,9 @@ protected: 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 91d0681..260604e 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 1ea0ce0..07669a0 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 11dd20e..d06f091 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 9f55a0f..bac0c2c 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 zcmeBE+^Je`vm-^~)cZM8&nn97PguD3?s5hO2G-QP_|RY{1_s6p46NH?7#MixF|d6z zWnkbt%D`?D%D}+)jzMO#1p@>B1_m?HMGOq=FBme)Eg2Y?mN8^$H!(0UhcJ}q9%5kN z7iZ|vbY@^+{KRPa=o|wB9}}a!oC5;`izZ{h(di5fj13BmYhoE07&vz_?rNFDz`&8f z%zagkfr0-c^I@(93=9JL6MGHW*Q}0ZVBk`oxK~jaKA?S*fq~DN`^0`X z1_pL^9;dhv1_ri5o|uqZ3=G_pdFsrsFfi~J@w{Ghl!1ZyFz>|lSquy;*?iKLEes5t zseG$1i!d+o2&MX7lu3fZZZxrqe|x%nxnRtlY&n!vK0P$cE016E@@6<})!~#%| zGkQ(#;7(-?WhiDyoy^9gt*gM033fCn5jo;l%J8Alv$FRvRRC`f*Ak^ C-r^nr delta 341 zcmdm~+NWr0vm-^~)cZM8&nn97PguD3t`h?T;|2!SZ7~cC+;bV&zL_#G@El=aw+Urn z;Cag+v)O`yfp`5xdpRp!F@_#ZX9fnwPmGq2&M`3XFf!W9IWRD=Xfg&IozB3(*uc1} zWfB7eM*=hVRXGL*-Ve-&xfU=m@aavQZ#eOWf_%@OJ_ZI}C-ybHI~W+akFh_~dc?rM zv4Z`(XB-0qPupa9Mo<6zx~U8d9O~Q$v~Myn@Hlaw*zd-`z|PL&6c@t4z*fjpXMTl& zfwz$7^_rs$49tgl-XHB^VBp-zJ28D00|QGopR{EQ0|RF&-|EXE3=Di}llL9OdSZ@3L1|GzesOB?W=H-CW&lY6W6J;l diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.ts b/QtScrcpy/res/i18n/QtScrcpy_en.ts index 9c131b5..9d74059 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_en.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_en.ts @@ -16,22 +16,22 @@ file 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`Kyal!1Zk8iUMc3kC-65(YETMGOpVTnriImJAGx{}{5gn;00F+8D}n4>2%s z{buOVbY@^+EMc^KbdG_6OM%f|&Vhk}S)VcB=yV1KMkxixHL;8g4D8X2yILkOFtDXD zb6=HXVBlWKe3)wi0|WQIi?ya^#a zfxf+#Wi19w3<`d#m5niMfn}u{c7A=+<8rQ+y(??^&l92+BpJe&KBw_juRKUu!iwPc zeoy_*`91aH4mSvQ@Z;{Ujc)!g1^2_!$#tAY`k}?C3TgR83Yj@Msp*M13I(M_1^LCP i#R{3l3dJQwnaL$Nl?nw#`5Bo>nI)+yn=f%5W&!|f5Ap#3 delta 384 zcmdlaGgZ>mW=D#|srPfHo>i3DpRjQ6T_*+x#tjUt+hQ0PI3gL?zL_#GaN01i+k`SO za3(XzY_?!v;9{I;FK5Ns!O)}W%)r1{!f5&E90LQ#T1I<02L=Xaea3*J(-{~Tr5Ja$ zOk!YQOJnA~D#yUUWyXA%YXJiTSIWfsh7)fn$oK5&V_@K1$G)a_2Ll5~9s4t_M+^*X zbJ)Lo#xXE(3QU$~^z_fKo65kzX32d(`z8Ye$35;7``s8ASOa;S;zAf0SYGqgnO|XG z;JnK7dd*P=2B!Nw?~isdFtA7SPE4Q0z`&f$CvDlnz`(A`xB9XO0|VED$@>|NnYd1G z{=yi~9_p2@)1UA6ExIQ#Bc35|LZEN2<$oSO)yl?F4ZptWaXD8@H71Ds=Lt~@k__QX zpVRoNSN=bbe?stkzo+3Ee%#ZO!VSV5{J8sT8LT)M7#JAX7#J94Oy=V@QVK39%1kcF lsZ=N}PEAor%P&&M%*jbjPs~v$C@m_;FHSAqoX>rj2>|%ybiM!p diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index 27ea074..eee9e68 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 0afdc4e..e61704c 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 e5f6827..b7ea3c1 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 20eae6f..bae8044 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 ae80cab..ed900ab 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 bac0c2c..50178f0 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 zcmdm~dQxM8Y(0|(18WZp0|S#J1M9XJ1_r)s3~b*_85sEgGO*i(GB60JF|dQ=*)KA% z&rfGy;8J3c*=)hUAn=00OmqoVRrBgMeL zx{pcU@hSrY(?MqLt8xqsf||^SxfU=m2&S_{I^AMmU`b_ZOxno6z_^xWhUXpz2F3rJjL-A%X2hgA)S-0|UE*v@in$$9Z;l?WYV3%oXfCd-@m{1ghEB^zL9_ z;QP=1OzRN?19vI=ch5Km2LAmVHjc^+3~WUlaYm;Z7#MDF{9~43U|^re>0p$~z`(tc zGw}io0|QqLXKBP21_o9EE^fW83=G`Qx#U(!FfedFdPVw41)9d6^tLRV_@Lu<G6XE89a|KPvd zxs`!|Q6!UrLGat=FN`gW^*jvD42cYR49N_s3^^R2;A3N8U|@4j%u7zqVFxi?G81$1 z(;1l=7#JiNJQy+=QW#PgQW^3X6c{oXQW=UEQW+HC8hJc2Q&Lj%6f#naQWep)^Dy`^ zzsSG6y3JfI-MGT1yl??W90|Y?^DC8F@6sMLbloTaa+Sj8SC%_PlsvWMGBN(a) zU5gk)FheFoE<*uB4nrz~0)sC@J}89X8o7fra|?1(6@2qkFg!WAg4te2j3JaEn8Arb zfx(#}pCOMSk0F&I8Oh(=p}|fH&iQ$HsmUdimoq!p%Q1w4eW^geX8zFPR0TY?OE82p zcrv&#c;d90C*0G`6J|He6kJl2 onOu@nsZd&+nxc@FU!;(klardBn4?foT2zon;00FLm0|)4>2(Ci!=0SIx{dZeqywIbdG_6kBQM<&Vhk}MUye$ z=yV1K#shmE5$0|WDCjyR*!3=9l6IQ}uqFfg#nb2=EMGB9vF;Y_^1 z!oa}3p0hOK3j+hw1unT&5+Lii9FFZ{U|`$ERn^SJz`(MTJ2~Ym0|VDY?){vw{&YmPE7Fdycf zm_CbvfhC(y+Oma#fisnF^<@zT27z>bg~#g{7+9b4XJ$QSU|_G|&s-tJz`#0_zxH(i z0|QeE|E|lk7#LWC`0sXZWnf?w$z)&<*tgk-sfBT}FRT6J6RgIYo7j37H~Vs2VchJ& zrNy*)CeJ*^$&2~s)idZZBr}vU6fqPrq%!0&lrShTWHY2PR5Ii;Br+5*C@|zQhK|Hy%s3e6AvZrI)k;Ctkez{nLBKPwptQs} zKd&scsKh-nHSJJF z%gZmyP0YcqZ5Bf?Lo!1VLncE3*juFx1q>++i3}wSsSGI$It&UR=hPGBb0qu08Uw(g zRm@P#pukWE4>1L>&lDJn8Oj+l8A=$E88YBT-6fxv8WH4khB!L~D%8)YoHUC7$rITv~ zjPyf`Qx($kixe_*a#GV1a})|niwg3KQ;QWcixrAXiZYW+aw-)Hit;lulQK(EQ#M}| Hs9**FtE3i` diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.ts b/QtScrcpy/res/i18n/QtScrcpy_en.ts index 9d74059..e2ae0fe 100644 --- a/QtScrcpy/res/i18n/QtScrcpy_en.ts +++ b/QtScrcpy/res/i18n/QtScrcpy_en.ts @@ -49,93 +49,129 @@ 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 zcmdlab47lFY(0|(18WZp0|Vni2G(sc3=G_23~b*_85p>KGO*i(GBEHcFtCH<*-RPO z=ch9;u)kxF*=)hUz_X9ROmqSSP0|Sc(+XOxr z1_sXcY{y@<)H5(JxUs!xaAII!U|@HU7G_{z`_AsJ{gi=$=@fg5`e4lb_DW%)r1P z7Tp;f93L9vU&S5QKW#$m`EZT?oBtW%+IT!NQ&Lj%6f#naQWeoPvm{@rw1O*T^UE*E zOiqO8tHL7Eiu zixi4eOB70q5-aV|O%QEenjw@Sn8B(4Uh-0ep3q;S8P(ZVa9%7Vw07x_QEE*c`*`#;C~Ey)TkIgtzho10wi& zQc}w@lT#J)5_40nHcw)yVH8FS`NGo7lFk2EmoW;nq}d@w97}OVe);Ap>|u-^JZWE2 zH+1i-&jT5b@M38}N@5AbX!Jm2=}rsrf~#aNPb|vI%uAnqi=#5sD_y5Q-|t&=Phdto zL*9fy-(JiAJbtQ`jinlXebeJ|u9j*{5c$s&q820>!k0d$@l~(isnZX>1OlA_GylAKC~(&E$VXJuD#lXN6#WsP@g@J+X6Wf~Au?!6C>)DRKYGGht zaASMX;Kabdz`*VxEzH2cvX$Lk`zZqhqbGaV3=CY&95#;13=GVy9C1dc85kIraQtJIVPIf>&*@;4%D}*OiZk&7 z3j+h|P0rGYFANM!YFu)wBp4W2>$n__?PFkImEo#t=3-!Ap2?k@@|A&sgP%LUZYl!< znTbo1c6aSC(2-;+~kB%E-*Xz`)k{DteZm zC)@LEHti*gflu&IYAw`!D1*NOd<_9b=0e;#xX{Fee5 z_Ma!2lc8>^UJ!e760-N;miPuI7iAXImnf7Lq$HN4rsybO^C!e5Itl?rsl~+#b)Ya% zD9KkSF3&7U&H(XXAp&wNn|ErZZ(;#B45GT9_UHS3OVf|;3CxIR$eR$-6X@G(S=M5} z#Gv4(TG<%G7FbrQVdvL3Juc^J*}Jlq|2!dTL6RYS>2n%i^~!^kC9DX3@AuU2oZnMF z?r?)}2S4uq+UVy0QgA;koxGRFNI$eVRUs|ENFg&PCpA4WN1>p!s35;MwOApuSfRM2 aC^NYvr&6JyC_f`JDYGOsWwRjfVI}|}@*H&l diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index eee9e68..9315d44 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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 SL2e8T3=E#GelF{r5}E+=iT)%2 literal 0 HcmV?d00001 diff --git a/QtScrcpy/res/res.qrc b/QtScrcpy/res/res.qrc index 840268d..d2f53a4 100644 --- a/QtScrcpy/res/res.qrc +++ b/QtScrcpy/res/res.qrc @@ -2,7 +2,7 @@ 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 8966a7d..b9393ad 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 e0e7f51..af3104d 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 @@ public: QString getLogLevel(); QString getCodecOptions(); QString getCodecName(); + QStringList getConnectedGroups(); + void deleteGroup(const QString &serial); // user data QString getRecordPath(); @@ -38,6 +52,10 @@ public: 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 ed900ab..fa80082 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 3c7a9c2..70e0620 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 23b6cff..843c835 100644 --- a/QtScrcpy/uibase/magneticwidget.h +++ b/QtScrcpy/uibase/magneticwidget.h @@ -46,6 +46,9 @@ private: 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 | 206 ++++++++++++++++++------------------- QtScrcpy/dialog.h | 12 +-- QtScrcpy/util/config.cpp | 183 ++++++++++++-------------------- QtScrcpy/util/config.h | 34 +++--- 6 files changed, 185 insertions(+), 258 deletions(-) diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 78c5164..fc0b959 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 bbbf693..e07ee76 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -26,6 +26,7 @@ public: struct DeviceParams { QString recordFileName = ""; // 视频录制文件名 + QString recordPath = ""; // 视频保存路径 QString serial = ""; // 设备序列号 quint16 localPort = 27183; // reverse时本地监听端口 quint16 maxSize = 720; // 视频分辨率 @@ -37,7 +38,8 @@ public: 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 b7ea3c1..e18bdac 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(); - - ui->serialBox->addItem(ui->deviceIpEdt->text()+":5555"); - - on_startServerBtn_clicked(); + on_updateDevice_clicked(); delayMs(200); - updateConnectedList(); + // 找到第一个无线设备 + 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) { + qWarning() << "No wifi device is found!"; + return; + } + + on_startServerBtn_clicked(); } 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 bae8044..6db054a 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 b9393ad..ee7397a 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 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 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 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 - -// 最大尺寸 录制格式 +#define SERIAL_NICK_NAME_KEY "NickName" +#define SERIAL_NICK_NAME_DEF "Phone" QString Config::s_configPath = ""; @@ -110,101 +125,47 @@ 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->beginGroup(GROUP_COMMON); + 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; } -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); - m_userData->endGroup(); -} - void Config::setRect(const QString &serial, const QRect &rc) { m_userData->beginGroup(serial); @@ -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 af3104d..2049c4f 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 @@ public: 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 9315d44..7b9c6cb 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 e18bdac..4678922 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); + 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 6db054a..9a5ec57 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -82,6 +82,7 @@ private: 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 fa80082..b098da0 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 zcmX@9u|;!&T)hDUYYz(p15*tH>$VsM27XHhwr{2k3<3!Z>^7kc3<8}D>>zm#69)GA z=?n~9tqd}oEf^RCy&23z7cnq!OE6@VTQV@PdNO2bH!(1MVLr^YfPq2q6icMjEd~ab6D*BM8yOgw1XyNx?qOhHY+z+i z(_&y?OJEf@ea*nYevwsg&Qb;j7CzRn^-~!bnEYAWUkfoXFic~sc-+OnzKPak*j_X^F)%PNuscW#Gca%(vAb(OWnf^w&EB)8kAXqp9{ZZ!9SjWo zN$k(G9x*U*-(dgl8OOjNpu}P0sLa5?c9kQ}=rjWZ!wrsq%rXoN9E_X}MyU)8JR+Ql z7g!h=xbAb7MtoslU@hk2*4xU!z~jXww@QM6fyz#Gcj8DP%9z|+P%F?|*T1G_KpuLn;U7+Avi zq%B(*7PN4&aDg#j3Su~3__8ceV8^f1{*NAFyu3oG9)qNFr+dlFeEeNFk~_$Gh{O; zFqAOlgGKTg@)+_MQW=sNN*Js-KtavMz`!8kl3$vXld6!MlbM{YP?E2ZoS&DMnp|SF J`7Lvd003FcpiTe) delta 579 zcmdm@c~WD7T)hSZYYz(p1Ct~J>$VsM2EJ3JfXgV`6 zuoy8~K03$1z;DH9FXzC(z_xk$J3cPaaK&o~AK{{0*_j>-%SY(*S#MyDAV7;bR237$sZoRDx4BXGTe?54@z`*>OPujADfq`cp z-|EXE3=D$v`4t`;tz%%|=;hDMdd$GU<;kD9LW+Tb!=AtPbpQhc%M$)wmuE3Bu>at{ a+qsp2fl(xrfkE)w<`AZhjGHr9d;|dWuaay4 diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.ts b/QtScrcpy/res/i18n/QtScrcpy_en.ts index e2ae0fe..2161044 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 zcmca2KT~0XT)hDUYYz(p1LHjg)@?Bi4BWvCY~M^779YJTQD&2{AMr{UBtk^v4$a|+>(KTMUx>*yNQ8;Wg|m*?jZ&So+gGKO=kuM z=J|}4kIpeLa4%=HmvdlXVC`mfH_T^XU}9tpI69qyp`K|8#>{(<2Ll6l z1^Y9tM+^*{cI@9h;}{rtxHxPal^GaV6FK6HPBSntEaCXaEW^OS=FRC~l*+)snZucQ zfrWvAqnfic;tK-DgN5m0SpYxKlyiE zp2fhxHkJQw=T-&=hAWv247^7-`!G2(>T(623H;rEFL~*IDGpE|vN13)NVw#eCgr3m bB%w-^|hJ6Rf&HZm|U+Oy2?+{3`YD8oZopIZGKBn7*=xt)I%kz*xiD{#uBEfuV%0;&B%P1B(aS1U?rA z2F~?t$6vKD)H5)+vAt+;VqjokV0VxfW?*3Z&hD=Ll!1Zi6noE}J_ZJ!Z1y$1I~W+a zt=XSxJz`+sWMu#D8OOlDeU`(uqIV;0))ITP4B3z1D z7#LW0aOc-eWnkc#$9+KiCIbWaZ0-~L-540yC-XSPg)lI%>++=Um1JOGV&kbZzrw)4 zGl}Q*nxhO1EdO}kAMIjb;F93&3@~S4;Oyj`m_CbvfwhVE*Mlbv49wbm(v~d@44jYn zR$mrjVBq!TS9ol+j)8&gIDcl=V+IBeLH^7YQVa}iz5KPW0~i>X_wet!Jd1&WErS1U a=T-&=hAWv247?qiLztWyH(z0P 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 4678922..e9b56e3 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 9a5ec57..b29d123 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 e9b56e3..71a4a07 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 ee7397a..e5897ef 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 2049c4f..e978be4 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 9a3327b..fb92d82 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 b16147a..32a3c65 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 261eeb9..c37d81f 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 fb92d82..8d6ea46 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 32a3c65..4aba9f1 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.