From 5c592679d4ba254b1074abf6425b4cf6f2b4f0b6 Mon Sep 17 00:00:00 2001 From: rankun Date: Thu, 30 Jan 2020 19:03:25 +0800 Subject: [PATCH 01/14] feat: optimize keymap --- .../inputconvert/inputconvertgame.cpp | 38 +-- .../controller/inputconvert/keymap/keymap.cpp | 297 +++++++++--------- .../controller/inputconvert/keymap/keymap.h | 83 ++--- 3 files changed, 219 insertions(+), 199 deletions(-) diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp index c550e51..dc13ead 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp @@ -73,10 +73,10 @@ void InputConvertGame::keyEvent(const QKeyEvent *from, const QSize& frameSize, c // 处理特殊按键:可以在按键映射和普通映射间切换的按键 if (m_needSwitchGameAgain && KeyMap::KMT_CLICK == node.type - && node.click.switchMap) { + && node.data.click.switchMap) { updateSize(frameSize, showSize); // Qt::Key_Tab Qt::Key_M for PUBG mobile - processKeyClick(node.click.keyNode.pos, false, node.click.switchMap, from); + processKeyClick(node.data.click.keyNode.pos, false, node.data.click.switchMap, from); return; } @@ -93,13 +93,13 @@ void InputConvertGame::keyEvent(const QKeyEvent *from, const QSize& frameSize, c return; // 处理普通按键 case KeyMap::KMT_CLICK: - processKeyClick(node.click.keyNode.pos, false, node.click.switchMap, from); + processKeyClick(node.data.click.keyNode.pos, false, node.data.click.switchMap, from); return; case KeyMap::KMT_CLICK_TWICE: - processKeyClick(node.clickTwice.keyNode.pos, true, false, from); + processKeyClick(node.data.clickTwice.keyNode.pos, true, false, from); return; case KeyMap::KMT_DRAG: - processKeyDrag(node.drag.startPos, node.drag.endPos, from); + processKeyDrag(node.data.drag.keyNode.pos, node.data.drag.keyNode.extendPos, from); return; default: break; @@ -115,7 +115,7 @@ void InputConvertGame::loadKeyMap(const QString &json) if (m_keyMap.isValidMouseMoveMap()) { m_ctrlMouseMove.valid = true; m_ctrlMouseMove.touching = false; - m_ctrlMouseMove.startPosRel = m_keyMap.getMouseMoveMap().startPos; + m_ctrlMouseMove.startPosRel = m_keyMap.getMouseMoveMap().data.mouseMove.startPos; m_ctrlMouseMove.startPosPixel = calcFrameAbsolutePos(m_ctrlMouseMove.startPosRel); } if(m_keyMap.isValidSteerWheelMap()){ @@ -222,11 +222,11 @@ void InputConvertGame::processSteerWheel(const KeyMap::KeyMapNode &node, const Q int key = from->key(); bool flag = from->type() == QEvent::KeyPress; // identify keys - if(key == node.steerWheel.up.key){ + if(key == node.data.steerWheel.up.key){ m_ctrlSteerWheel.pressedUp = flag; - }else if(key == node.steerWheel.right.key){ + }else if(key == node.data.steerWheel.right.key){ m_ctrlSteerWheel.pressedRight = flag; - }else if(key == node.steerWheel.down.key){ + }else if(key == node.data.steerWheel.down.key){ m_ctrlSteerWheel.pressedDown = flag; }else{ // left m_ctrlSteerWheel.pressedLeft = flag; @@ -235,36 +235,36 @@ void InputConvertGame::processSteerWheel(const KeyMap::KeyMapNode &node, const Q int nPressed = 0; if(m_ctrlSteerWheel.pressedUp){ ++nPressed; - offset.ry() -= node.steerWheel.up.offset; + offset.ry() -= node.data.steerWheel.up.extendOffset; } if(m_ctrlSteerWheel.pressedRight){ ++nPressed; - offset.rx() += node.steerWheel.right.offset; + offset.rx() += node.data.steerWheel.right.extendOffset; } if(m_ctrlSteerWheel.pressedDown){ ++nPressed; - offset.ry() += node.steerWheel.down.offset; + offset.ry() += node.data.steerWheel.down.extendOffset; } if(m_ctrlSteerWheel.pressedLeft){ ++nPressed; - offset.rx() -= node.steerWheel.left.offset; + offset.rx() -= node.data.steerWheel.left.extendOffset; } // action //qDebug()<key())<<"-"<type()<<"-"<key(); id = attachTouchID(m_ctrlSteerWheel.touchKey); - sendTouchDownEvent(id, node.steerWheel.centerPos); + sendTouchDownEvent(id, node.data.steerWheel.centerPos); }else{ id = getTouchID(m_ctrlSteerWheel.touchKey); } - sendTouchMoveEvent(id, node.steerWheel.centerPos + offset); + sendTouchMoveEvent(id, node.data.steerWheel.centerPos + offset); } m_ctrlSteerWheel.lastOffset = offset; return; @@ -319,9 +319,9 @@ bool InputConvertGame::processMouseClick(const QMouseEvent *from) if (QEvent::MouseButtonPress == from->type() || QEvent::MouseButtonDblClick == from->type()) { int id = attachTouchID(from->button()); - sendTouchDownEvent(id, node.click.keyNode.pos); + sendTouchDownEvent(id, node.data.click.keyNode.pos); } else if (QEvent::MouseButtonRelease == from->type()) { - sendTouchUpEvent(getTouchID(from->button()), node.click.keyNode.pos); + sendTouchUpEvent(getTouchID(from->button()), node.data.click.keyNode.pos); detachTouchID(from->button()); } else { return false; @@ -346,7 +346,7 @@ bool InputConvertGame::processMouseMove(const QMouseEvent *from) mouseMoveStopTouch(); mouseMoveStartTouch(from); } - offset /= m_keyMap.getMouseMoveMap().speedRatio; + offset /= m_keyMap.getMouseMoveMap().data.mouseMove.speedRatio; m_ctrlMouseMove.lastPosRel = m_ctrlMouseMove.startPosRel + offset; mouseMoveMovingTouch(m_ctrlMouseMove.lastPosRel); } else { diff --git a/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp b/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp index ce1dd84..68faf39 100644 --- a/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp +++ b/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include @@ -40,51 +39,57 @@ void KeyMap::loadKeyMap(const QString &json) QJsonParseError jsonError; QJsonDocument jsonDoc; QJsonObject rootObj; + QPair switchKey; jsonDoc = QJsonDocument::fromJson(json.toUtf8(), &jsonError); - if(jsonError.error != QJsonParseError::NoError) - { + if(jsonError.error != QJsonParseError::NoError) { errorString = QString("json error: %1").arg(jsonError.errorString()); goto parseError; } // switchKey rootObj = jsonDoc.object(); - if (rootObj.contains("switchKey") && rootObj.value("switchKey").isString()) { - QPair p = getItemKey(rootObj, "switchKey"); - if(p.first == AT_INVALID){ - errorString = QString("json error: switchKey invalid"); - goto parseError; - } - m_switchType = p.first; - m_switchKey = p.second; - } else { + + if (!checkItemString(rootObj, "switchKey")) { errorString = QString("json error: no find switchKey"); goto parseError; } + switchKey = getItemKey(rootObj, "switchKey"); + if(switchKey.first == AT_INVALID) { + errorString = QString("json error: switchKey invalid"); + goto parseError; + } + + m_switchKey.type = switchKey.first; + m_switchKey.key= switchKey.second; + // mouseMoveMap - if (rootObj.contains("mouseMoveMap") && rootObj.value("mouseMoveMap").isObject()) { - QJsonObject mouseMoveMap = rootObj.value("mouseMoveMap").toObject(); - if (mouseMoveMap.contains("speedRatio") && mouseMoveMap.value("speedRatio").isDouble()) { - m_mouseMoveMap.speedRatio = mouseMoveMap.value("speedRatio").toInt(); - } else { + if (checkItemObject(rootObj, "mouseMoveMap")) { + QJsonObject mouseMoveMap = getItemObject(rootObj, "mouseMoveMap"); + KeyMapNode keyMapNode; + keyMapNode.type = KMT_MOUSE_MOVE; + + if (!checkItemDouble(mouseMoveMap, "speedRatio")) { errorString = QString("json error: mouseMoveMap on find speedRatio"); goto parseError; } - if (mouseMoveMap.contains("startPos") && mouseMoveMap.value("startPos").isObject()) { - QJsonObject startPos = mouseMoveMap.value("startPos").toObject(); - if (startPos.contains("x") && startPos.value("x").isDouble()) { - m_mouseMoveMap.startPos.setX(startPos.value("x").toDouble()); - } - if (startPos.contains("y") && startPos.value("y").isDouble()) { - m_mouseMoveMap.startPos.setY(startPos.value("y").toDouble()); - } - } else { + keyMapNode.data.mouseMove.speedRatio = static_cast(getItemDouble(mouseMoveMap, "speedRatio")); + + if (!checkItemObject(mouseMoveMap, "startPos")) { errorString = QString("json error: mouseMoveMap on find startPos"); goto parseError; } + QJsonObject startPos = mouseMoveMap.value("startPos").toObject(); + if (checkItemDouble(startPos, "x")) { + keyMapNode.data.mouseMove.startPos.setX(getItemDouble(startPos, "x")); + } + if (checkItemDouble(startPos, "y")) { + keyMapNode.data.mouseMove.startPos.setY(getItemDouble(startPos, "y")); + } + m_idxMouseMove = m_keyMapNodes.size(); + m_keyMapNodes.push_back(keyMapNode); } // keyMapNodes @@ -103,7 +108,7 @@ void KeyMap::loadKeyMap(const QString &json) goto parseError; } - KeyMap::KeyMapType type = getItemType(node, "type"); + KeyMap::KeyMapType type = getItemKeyMapType(node, "type"); switch (type) { case KeyMap::KMT_CLICK: { @@ -113,16 +118,16 @@ void KeyMap::loadKeyMap(const QString &json) break; } QPair key = getItemKey(node, "key"); - if(key.first == AT_INVALID){ + if (key.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("key").toString(); break; } KeyMapNode keyMapNode; keyMapNode.type = type; - keyMapNode.click.keyNode.type = key.first; - keyMapNode.click.keyNode.key = key.second; - keyMapNode.click.keyNode.pos = getItemPos(node, "pos"); - keyMapNode.click.switchMap = getItemSwitchMap(node, "switchMap"); + keyMapNode.data.click.keyNode.type = key.first; + keyMapNode.data.click.keyNode.key = key.second; + keyMapNode.data.click.keyNode.pos = getItemPos(node, "pos"); + keyMapNode.data.click.switchMap = getItemBool(node, "switchMap"); m_keyMapNodes.push_back(keyMapNode); } break; @@ -135,23 +140,23 @@ void KeyMap::loadKeyMap(const QString &json) } QPair key = getItemKey(node, "key"); - if(key.first == AT_INVALID){ + if (key.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("key").toString(); break; } KeyMapNode keyMapNode; keyMapNode.type = type; - keyMapNode.click.keyNode.type = key.first; - keyMapNode.click.keyNode.key = key.second; - keyMapNode.click.keyNode.pos = getItemPos(node, "pos"); - keyMapNode.click.switchMap = getItemSwitchMap(node, "switchMap"); + keyMapNode.data.click.keyNode.type = key.first; + keyMapNode.data.click.keyNode.key = key.second; + keyMapNode.data.click.keyNode.pos = getItemPos(node, "pos"); + keyMapNode.data.click.switchMap = getItemBool(node, "switchMap"); m_keyMapNodes.push_back(keyMapNode); } break; case KeyMap::KMT_STEER_WHEEL: { // safe check - if(!checkForSteerWhell(node)){ + if (!checkForSteerWhell(node)) { qWarning() << "json error: keyMapNodes node format error"; break; } @@ -159,33 +164,40 @@ void KeyMap::loadKeyMap(const QString &json) QPair rightKey = getItemKey(node, "rightKey"); QPair upKey = getItemKey(node, "upKey"); QPair downKey = getItemKey(node, "downKey"); - if(leftKey.first == AT_INVALID || rightKey.first == AT_INVALID - || upKey.first == AT_INVALID || downKey.first == AT_INVALID) - { - if(leftKey.first == AT_INVALID) + if (leftKey.first == AT_INVALID || rightKey.first == AT_INVALID + || upKey.first == AT_INVALID || downKey.first == AT_INVALID) { + if (leftKey.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("leftKey").toString(); - if(rightKey.first == AT_INVALID) + } + if (rightKey.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("rightKey").toString(); - if(upKey.first == AT_INVALID) + } + if (upKey.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("upKey").toString(); - if(downKey.first == AT_INVALID) + } + if (downKey.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("downKey").toString(); + } break; } KeyMapNode keyMapNode; keyMapNode.type = type; - keyMapNode.steerWheel.left = { leftKey.first, leftKey.second, - getItemNumber(node, "leftOffset") }; - keyMapNode.steerWheel.right = { rightKey.first, rightKey.second, - getItemNumber(node, "rightOffset") }; - keyMapNode.steerWheel.up = { upKey.first, upKey.second, - getItemNumber(node, "upOffset") }; - keyMapNode.steerWheel.down = { downKey.first, downKey.second, - getItemNumber(node, "downOffset") }; + keyMapNode.data.steerWheel.left = { leftKey.first, leftKey.second, + QPointF(0, 0), QPointF(0, 0), + getItemDouble(node, "leftOffset") }; + keyMapNode.data.steerWheel.right = { rightKey.first, rightKey.second, + QPointF(0, 0), QPointF(0, 0), + getItemDouble(node, "rightOffset") }; + keyMapNode.data.steerWheel.up = { upKey.first, upKey.second, + QPointF(0, 0), QPointF(0, 0), + getItemDouble(node, "upOffset") }; + keyMapNode.data.steerWheel.down = { downKey.first, downKey.second, + QPointF(0, 0), QPointF(0, 0), + getItemDouble(node, "downOffset") }; - keyMapNode.steerWheel.centerPos = getItemPos(node, "centerPos"); + keyMapNode.data.steerWheel.centerPos = getItemPos(node, "centerPos"); m_idxSteerWheel = m_keyMapNodes.size(); m_keyMapNodes.push_back(keyMapNode); } @@ -193,22 +205,22 @@ void KeyMap::loadKeyMap(const QString &json) case KeyMap::KMT_DRAG: { // safe check - if(!checkForDrag(node)){ + if (!checkForDrag(node)) { qWarning() << "json error: keyMapNodes node format error"; break; } QPair key = getItemKey(node, "key"); - if(key.first == AT_INVALID){ + if (key.first == AT_INVALID) { qWarning() << "json error: keyMapNodes node invalid key: " << node.value("key").toString(); break; } KeyMapNode keyMapNode; keyMapNode.type = type; - keyMapNode.drag.type = key.first; - keyMapNode.drag.key = key.second; - keyMapNode.drag.startPos = getItemPos(node, "startPos"); - keyMapNode.drag.endPos = getItemPos(node, "endPos"); + keyMapNode.data.drag.keyNode.type = key.first; + keyMapNode.data.drag.keyNode.key = key.second; + keyMapNode.data.drag.keyNode.pos = getItemPos(node, "startPos"); + keyMapNode.data.drag.keyNode.extendPos = getItemPos(node, "endPos"); m_keyMapNodes.push_back(keyMapNode); break; } @@ -220,7 +232,7 @@ void KeyMap::loadKeyMap(const QString &json) } // this must be called after m_keyMapNodes is stable makeReverseMap(); - qWarning() << "Script updated."; + qInfo() << "Script updated."; parseError: if (!errorString.isEmpty()) { @@ -231,45 +243,41 @@ parseError: const KeyMap::KeyMapNode& KeyMap::getKeyMapNode(int key) { - auto p = rmapKey.value(key, &m_invalidNode); - if(p == &m_invalidNode) - return *rmapMouse.value(key, &m_invalidNode); + auto p = m_rmapKey.value(key, &m_invalidNode); + if (p == &m_invalidNode) { + return *m_rmapMouse.value(key, &m_invalidNode); + } return *p; } const KeyMap::KeyMapNode& KeyMap::getKeyMapNodeKey(int key) { - return *rmapKey.value(key, &m_invalidNode); + return *m_rmapKey.value(key, &m_invalidNode); } const KeyMap::KeyMapNode& KeyMap::getKeyMapNodeMouse(int key) { - return *rmapMouse.value(key, &m_invalidNode); + return *m_rmapMouse.value(key, &m_invalidNode); } bool KeyMap::isSwitchOnKeyboard() { - return m_switchType == AT_KEY; + return m_switchKey.type == AT_KEY; } int KeyMap::getSwitchKey() { - return m_switchKey; + return m_switchKey.key; } -const KeyMap::MouseMoveMap& KeyMap::getMouseMoveMap() +const KeyMap::KeyMapNode& KeyMap::getMouseMoveMap() { - return m_mouseMoveMap; -} - -const KeyMap::KeyMapNode& KeyMap::getSteerWheelMap() -{ - return m_keyMapNodes[m_idxSteerWheel]; + return m_keyMapNodes[m_idxMouseMove]; } bool KeyMap::isValidMouseMoveMap() { - return !m_mouseMoveMap.startPos.isNull(); + return m_idxMouseMove != -1; } bool KeyMap::isValidSteerWheelMap() @@ -279,39 +287,39 @@ bool KeyMap::isValidSteerWheelMap() void KeyMap::makeReverseMap() { - rmapKey.clear(); - rmapMouse.clear(); - for(int i = 0 ;i < m_keyMapNodes.size(); ++i) { + m_rmapKey.clear(); + m_rmapMouse.clear(); + for (int i = 0 ; i < m_keyMapNodes.size(); ++i) { auto& node = m_keyMapNodes[i]; switch (node.type) { case KMT_CLICK: { - QMultiHash& m = node.click.keyNode.type == AT_KEY ? rmapKey : rmapMouse; - m.insert(node.click.keyNode.key, &node); + QMultiHash& m = node.data.click.keyNode.type == AT_KEY ? m_rmapKey : m_rmapMouse; + m.insert(node.data.click.keyNode.key, &node); } break; case KMT_CLICK_TWICE: { - QMultiHash& m = node.clickTwice.keyNode.type == AT_KEY ? rmapKey : rmapMouse; - m.insert(node.clickTwice.keyNode.key, &node); + QMultiHash& m = node.data.clickTwice.keyNode.type == AT_KEY ? m_rmapKey : m_rmapMouse; + m.insert(node.data.clickTwice.keyNode.key, &node); } break; case KMT_STEER_WHEEL: { - QMultiHash& ml = node.steerWheel.left.type == AT_KEY ? rmapKey : rmapMouse; - ml.insert(node.steerWheel.left.key, &node); - QMultiHash& mr = node.steerWheel.right.type == AT_KEY ? rmapKey : rmapMouse; - mr.insert(node.steerWheel.right.key, &node); - QMultiHash& mu = node.steerWheel.up.type == AT_KEY ? rmapKey : rmapMouse; - mu.insert(node.steerWheel.up.key, &node); - QMultiHash& md = node.steerWheel.down.type == AT_KEY ? rmapKey : rmapMouse; - md.insert(node.steerWheel.down.key, &node); + QMultiHash& ml = node.data.steerWheel.left.type == AT_KEY ? m_rmapKey : m_rmapMouse; + ml.insert(node.data.steerWheel.left.key, &node); + QMultiHash& mr = node.data.steerWheel.right.type == AT_KEY ? m_rmapKey : m_rmapMouse; + mr.insert(node.data.steerWheel.right.key, &node); + QMultiHash& mu = node.data.steerWheel.up.type == AT_KEY ? m_rmapKey : m_rmapMouse; + mu.insert(node.data.steerWheel.up.key, &node); + QMultiHash& md = node.data.steerWheel.down.type == AT_KEY ? m_rmapKey : m_rmapMouse; + md.insert(node.data.steerWheel.down.key, &node); } break; case KMT_DRAG: { - QMultiHash& m = node.drag.type == AT_KEY ? rmapKey : rmapMouse; - m.insert(node.drag.key, &node); + QMultiHash& m = node.data.drag.keyNode.type == AT_KEY ? m_rmapKey : m_rmapMouse; + m.insert(node.data.drag.keyNode.key, &node); } break; default: @@ -320,51 +328,24 @@ void KeyMap::makeReverseMap() } } -// ---- check and get of json item ---- - -bool KeyMap::checkItemKey(const QJsonObject& node, const QString& name) +QString KeyMap::getItemString(const QJsonObject &node, const QString &name) { - return node.contains(name) && node.value(name).isString(); + return node.value(name).toString(); } -bool KeyMap::checkItemPos(const QJsonObject& node, const QString& name) +double KeyMap::getItemDouble(const QJsonObject& node, const QString& name) { - if(node.contains(name) && node.value(name).isObject()){ - QJsonObject pos = node.value(name).toObject(); - return pos.contains("x") && pos.value("x").isDouble() - && pos.contains("y") && pos.value("y").isDouble(); - } - return false; + return node.value(name).toDouble(); } -bool KeyMap::checkItemDouble(const QJsonObject& node, const QString& name) +bool KeyMap::getItemBool(const QJsonObject& node, const QString& name) { - return node.contains(name) && node.value(name).isDouble(); + return node.value(name).toBool(false); } -bool KeyMap::checkItemSwitchMap(const QJsonObject& node, const QString& name) +QJsonObject KeyMap::getItemObject(const QJsonObject &node, const QString &name) { - return !node.contains(name) || node.value(name).isBool(); -} - -KeyMap::KeyMapType KeyMap::getItemType(const QJsonObject& node, const QString& name) -{ - QString value = node.value(name).toString(); - return static_cast(m_metaEnumKeyMapType.keyToValue(value.toStdString().c_str())); -} - -QPair KeyMap::getItemKey(const QJsonObject& node, const QString& name) -{ - QString value = node.value(name).toString(); - int key = m_metaEnumKey.keyToValue(value.toStdString().c_str()); - int btn = m_metaEnumMouseButtons.keyToValue(value.toStdString().c_str()); - if(key == -1 && btn == -1){ - return {AT_INVALID, -1}; - }else if(key != -1){ - return {AT_KEY, key}; - }else{ - return {AT_MOUSE, btn}; - } + return node.value(name).toObject(); } QPointF KeyMap::getItemPos(const QJsonObject& node, const QString& name) @@ -373,23 +354,60 @@ QPointF KeyMap::getItemPos(const QJsonObject& node, const QString& name) return QPointF(pos.value("x").toDouble(), pos.value("y").toDouble()); } -double KeyMap::getItemNumber(const QJsonObject& node, const QString& name) +QPair KeyMap::getItemKey(const QJsonObject& node, const QString& name) { - return node.value(name).toDouble(); + QString value = getItemString(node, name); + int key = m_metaEnumKey.keyToValue(value.toStdString().c_str()); + int btn = m_metaEnumMouseButtons.keyToValue(value.toStdString().c_str()); + if (key == -1 && btn == -1) { + return {AT_INVALID, -1}; + } else if (key != -1) { + return {AT_KEY, key}; + } else { + return {AT_MOUSE, btn}; + } } -bool KeyMap::getItemSwitchMap(const QJsonObject& node, const QString& name) +KeyMap::KeyMapType KeyMap::getItemKeyMapType(const QJsonObject& node, const QString& name) { - return node.value(name).toBool(false); + QString value = getItemString(node, name); + return static_cast(m_metaEnumKeyMapType.keyToValue(value.toStdString().c_str())); } +bool KeyMap::checkItemString(const QJsonObject& node, const QString& name) +{ + return node.contains(name) && node.value(name).isString(); +} -// ---- check for key-map node ---- +bool KeyMap::checkItemDouble(const QJsonObject& node, const QString& name) +{ + return node.contains(name) && node.value(name).isDouble(); +} + +bool KeyMap::checkItemBool(const QJsonObject& node, const QString& name) +{ + return !node.contains(name) || node.value(name).isBool(); +} + +bool KeyMap::checkItemObject(const QJsonObject &node, const QString &name) +{ + return !node.contains(name) || node.value(name).isObject(); +} + +bool KeyMap::checkItemPos(const QJsonObject& node, const QString& name) +{ + if (node.contains(name) && node.value(name).isObject()) { + QJsonObject pos = node.value(name).toObject(); + return pos.contains("x") && pos.value("x").isDouble() + && pos.contains("y") && pos.value("y").isDouble(); + } + return false; +} bool KeyMap::checkForClick(const QJsonObject& node) { - return checkItemKey(node, "key") && checkItemPos(node, "pos") - && checkItemSwitchMap(node, "switchMap"); + return checkItemString(node, "key") && checkItemPos(node, "pos") + && checkItemBool(node, "switchMap"); } bool KeyMap::checkForClickDouble(const QJsonObject& node) @@ -399,8 +417,8 @@ bool KeyMap::checkForClickDouble(const QJsonObject& node) bool KeyMap::checkForSteerWhell(const QJsonObject& node) { - return checkItemKey(node, "leftKey") && checkItemKey(node, "rightKey") - && checkItemKey(node, "upKey") && checkItemKey(node, "downKey") + return checkItemString(node, "leftKey") && checkItemString(node, "rightKey") + && checkItemString(node, "upKey") && checkItemString(node, "downKey") && checkItemDouble(node, "leftOffset") && checkItemDouble(node, "rightOffset") && checkItemDouble(node, "upOffset") && checkItemDouble(node, "downOffset") && checkItemPos(node, "centerPos"); @@ -408,8 +426,7 @@ bool KeyMap::checkForSteerWhell(const QJsonObject& node) bool KeyMap::checkForDrag(const QJsonObject& node) { - return checkItemKey(node, "key") - && checkItemPos(node, "startPos") && checkItemPos(node, "endPos") - && checkItemSwitchMap(node, "switchMap"); + return checkItemString(node, "key") + && checkItemPos(node, "startPos") && checkItemPos(node, "endPos"); } diff --git a/QtScrcpy/device/controller/inputconvert/keymap/keymap.h b/QtScrcpy/device/controller/inputconvert/keymap/keymap.h index bfcd83f..dd607aa 100644 --- a/QtScrcpy/device/controller/inputconvert/keymap/keymap.h +++ b/QtScrcpy/device/controller/inputconvert/keymap/keymap.h @@ -7,8 +7,7 @@ #include #include #include - -class QJsonObject; +#include class KeyMap : public QObject { @@ -20,6 +19,7 @@ public: KMT_CLICK_TWICE, KMT_STEER_WHEEL, KMT_DRAG, + KMT_MOUSE_MOVE }; Q_ENUM(KeyMapType) @@ -33,12 +33,14 @@ public: struct KeyNode { ActionType type = AT_INVALID; int key = Qt::Key_unknown; - QPointF pos = QPointF(0, 0); + QPointF pos = QPointF(0, 0); // normal key + QPointF extendPos = QPointF(0, 0); // for drag + double extendOffset = 0.0; // for steerWheel }; struct KeyMapNode { KeyMapType type = KMT_INVALID; - union { + union DATA { struct { KeyNode keyNode; bool switchMap = false; @@ -48,29 +50,22 @@ public: } clickTwice; struct { QPointF centerPos = {0.0, 0.0}; - struct DirInfo{ - ActionType type = AT_KEY; // keyboard/mouse - int key = Qt::Key_unknown; // key/button - double offset = 0.0; - }; - DirInfo left, right, up, down; + KeyNode left, right, up, down; } steerWheel; struct { - ActionType type = AT_KEY; - int key = Qt::Key_unknown; - QPointF startPos = QPointF(0, 0); - QPointF endPos = QPointF(0, 0); + KeyNode keyNode; } drag; - }; + struct { + QPointF startPos = {0.0, 0.0}; + int speedRatio = 1; + } mouseMove; + DATA() {} + ~DATA() {} + } data; KeyMapNode() {} ~KeyMapNode() {} }; - struct MouseMoveMap { - QPointF startPos = {0.0, 0.0}; - int speedRatio = 1; - }; - KeyMap(QObject *parent = Q_NULLPTR); virtual ~KeyMap(); @@ -83,8 +78,7 @@ public: bool isValidMouseMoveMap(); bool isValidSteerWheelMap(); - const MouseMoveMap& getMouseMoveMap(); - const KeyMapNode& getSteerWheelMap(); + const KeyMap::KeyMapNode& getMouseMoveMap(); static const QString& getKeyMapPath(); @@ -92,41 +86,50 @@ private: // set up the reverse map from key/event event to keyMapNode void makeReverseMap(); - // parse json of the mapping script - bool checkItemKey(const QJsonObject& node, const QString& name="key"); - bool checkItemPos(const QJsonObject& node, const QString& name="pos"); + // safe check for base + bool checkItemString(const QJsonObject& node, const QString& name); bool checkItemDouble(const QJsonObject& node, const QString& name); - bool checkItemSwitchMap(const QJsonObject& node, const QString& name="switchMap"); + bool checkItemBool(const QJsonObject& node, const QString& name); + bool checkItemObject(const QJsonObject& node, const QString& name); + bool checkItemPos(const QJsonObject& node, const QString& name); - KeyMapType getItemType(const QJsonObject& node, const QString& name="type"); - QPair getItemKey(const QJsonObject& node, const QString& name="key"); - QPointF getItemPos(const QJsonObject& node, const QString& name="pos"); - double getItemNumber(const QJsonObject& node, const QString& name); - bool getItemSwitchMap(const QJsonObject& node, const QString& name="switchMap"); - -private: + // safe check for KeyMapNode bool checkForClick(const QJsonObject& node); bool checkForClickDouble(const QJsonObject& node); bool checkForSteerWhell(const QJsonObject& node); bool checkForDrag(const QJsonObject& node); + // get keymap from json object + QString getItemString(const QJsonObject& node, const QString& name); + double getItemDouble(const QJsonObject& node, const QString& name); + bool getItemBool(const QJsonObject& node, const QString& name); + QJsonObject getItemObject(const QJsonObject& node, const QString& name); + QPointF getItemPos(const QJsonObject& node, const QString& name); + QPair getItemKey(const QJsonObject& node, const QString& name); + KeyMapType getItemKeyMapType(const QJsonObject& node, const QString& name); + private: - QVector m_keyMapNodes; - KeyMapNode m_invalidNode; - ActionType m_switchType = AT_KEY; - int m_switchKey = Qt::Key_QuoteLeft; - MouseMoveMap m_mouseMoveMap; static QString s_keyMapPath; + QVector m_keyMapNodes; + KeyNode m_switchKey = { AT_KEY, Qt::Key_QuoteLeft }; + + // just for return + KeyMapNode m_invalidNode; + + // steer wheel index int m_idxSteerWheel = -1; + // mouse move index + int m_idxMouseMove = -1; + // mapping of key/mouse event name to index QMetaEnum m_metaEnumKey = QMetaEnum::fromType(); QMetaEnum m_metaEnumMouseButtons = QMetaEnum::fromType(); QMetaEnum m_metaEnumKeyMapType = QMetaEnum::fromType(); // reverse map of key/mouse event - QMultiHash rmapKey; - QMultiHash rmapMouse; + QMultiHash m_rmapKey; + QMultiHash m_rmapMouse; }; #endif // KEYMAP_H From 9338dcae7f73b5d43322a9cfdbe42aecd4aaa156 Mon Sep 17 00:00:00 2001 From: rankun Date: Thu, 30 Jan 2020 19:40:46 +0800 Subject: [PATCH 02/14] feat: optimize process steer wheel --- .../inputconvert/inputconvertgame.cpp | 42 ++++++++++--------- .../inputconvert/inputconvertgame.h | 16 +++---- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp index dc13ead..3c8f922 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp @@ -119,7 +119,6 @@ void InputConvertGame::loadKeyMap(const QString &json) m_ctrlMouseMove.startPosPixel = calcFrameAbsolutePos(m_ctrlMouseMove.startPosRel); } if(m_keyMap.isValidSteerWheelMap()){ - m_ctrlSteerWheel.valid = true; m_ctrlMouseMove.touching = false; } } @@ -222,46 +221,51 @@ void InputConvertGame::processSteerWheel(const KeyMap::KeyMapNode &node, const Q int key = from->key(); bool flag = from->type() == QEvent::KeyPress; // identify keys - if(key == node.data.steerWheel.up.key){ + if (key == node.data.steerWheel.up.key) { m_ctrlSteerWheel.pressedUp = flag; - }else if(key == node.data.steerWheel.right.key){ + } else if (key == node.data.steerWheel.right.key) { m_ctrlSteerWheel.pressedRight = flag; - }else if(key == node.data.steerWheel.down.key){ + } else if (key == node.data.steerWheel.down.key) { m_ctrlSteerWheel.pressedDown = flag; - }else{ // left + } else { // left m_ctrlSteerWheel.pressedLeft = flag; } + + // calc offset and pressed number QPointF offset(0.0, 0.0); - int nPressed = 0; - if(m_ctrlSteerWheel.pressedUp){ - ++nPressed; + int pressedNum = 0; + if (m_ctrlSteerWheel.pressedUp) { + ++pressedNum; offset.ry() -= node.data.steerWheel.up.extendOffset; } - if(m_ctrlSteerWheel.pressedRight){ - ++nPressed; + if (m_ctrlSteerWheel.pressedRight) { + ++pressedNum; offset.rx() += node.data.steerWheel.right.extendOffset; } - if(m_ctrlSteerWheel.pressedDown){ - ++nPressed; + if (m_ctrlSteerWheel.pressedDown) { + ++pressedNum; offset.ry() += node.data.steerWheel.down.extendOffset; } - if(m_ctrlSteerWheel.pressedLeft){ - ++nPressed; + if (m_ctrlSteerWheel.pressedLeft) { + ++pressedNum; offset.rx() -= node.data.steerWheel.left.extendOffset; } + // action - //qDebug()<key())<<"-"<type()<<"-"<key(); id = attachTouchID(m_ctrlSteerWheel.touchKey); sendTouchDownEvent(id, node.data.steerWheel.centerPos); - }else{ + } else { + // jsut get touch id and move id = getTouchID(m_ctrlSteerWheel.touchKey); } sendTouchMoveEvent(id, node.data.steerWheel.centerPos + offset); diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.h b/QtScrcpy/device/controller/inputconvert/inputconvertgame.h index acf585b..8ded936 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.h @@ -67,14 +67,14 @@ private: int multiTouchID[MULTI_TOUCH_MAX_NUM] = { 0 }; // steer wheel - struct{ - bool valid = false; - bool touching = false; - int touchKey = Qt::Key_unknown; // the first key pressed - int nKeyPressed = 0; - bool pressedUp = false, pressedDown = false; - bool pressedLeft = false, pressedRight = false; - QPointF centerPos; + struct { + // the first key pressed + int touchKey = Qt::Key_unknown; + bool pressedUp = false; + bool pressedDown = false; + bool pressedLeft = false; + bool pressedRight = false; + // for last up QPointF lastOffset; } m_ctrlSteerWheel; From afa5e508f85937c90d9b99aa3fe232c72579b7fa Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 12:58:38 +0800 Subject: [PATCH 03/14] feat: optimize process mouse move 1. revert process mouse move 2. optimize process mouse move 3. fix grabCursor bug --- QtScrcpy/device/controller/controller.cpp | 9 +- .../inputconvert/inputconvertgame.cpp | 149 ++++++++++-------- .../inputconvert/inputconvertgame.h | 25 +-- QtScrcpy/device/ui/toolform.cpp | 2 + QtScrcpy/device/ui/videoform.cpp | 2 - 5 files changed, 102 insertions(+), 85 deletions(-) diff --git a/QtScrcpy/device/controller/controller.cpp b/QtScrcpy/device/controller/controller.cpp index 9874c99..0ec988e 100644 --- a/QtScrcpy/device/controller/controller.cpp +++ b/QtScrcpy/device/controller/controller.cpp @@ -39,7 +39,10 @@ void Controller::postControlMsg(ControlMsg *controlMsg) void Controller::test(QRect rc) { ControlMsg* controlMsg = new ControlMsg(ControlMsg::CMT_INJECT_TOUCH); - controlMsg->setInjectTouchMsgData(POINTER_ID_MOUSE, AMOTION_EVENT_ACTION_DOWN, AMOTION_EVENT_BUTTON_PRIMARY, rc, 1.0f); + controlMsg->setInjectTouchMsgData(POINTER_ID_MOUSE, + AMOTION_EVENT_ACTION_DOWN, + AMOTION_EVENT_BUTTON_PRIMARY, + rc, 1.0f); postControlMsg(controlMsg); } @@ -192,7 +195,7 @@ void Controller::keyEvent(const QKeyEvent *from, const QSize &frameSize, const Q bool Controller::event(QEvent *event) { - if (event && event->type() == ControlMsg::Control) { + if (event && static_cast(event->type()) == ControlMsg::Control) { ControlMsg* controlMsg = dynamic_cast(event); if (controlMsg) { sendControl(controlMsg->serializeData()); @@ -209,7 +212,7 @@ bool Controller::sendControl(const QByteArray &buffer) } qint32 len = 0; if (m_controlSocket) { - len = m_controlSocket->write(buffer.data(), buffer.length()); + len = static_cast(m_controlSocket->write(buffer.data(), buffer.length())); } return len == buffer.length() ? true : false; } diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp index 3c8f922..8dd7d0b 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp @@ -112,24 +112,18 @@ void InputConvertGame::keyEvent(const QKeyEvent *from, const QSize& frameSize, c void InputConvertGame::loadKeyMap(const QString &json) { m_keyMap.loadKeyMap(json); - if (m_keyMap.isValidMouseMoveMap()) { - m_ctrlMouseMove.valid = true; - m_ctrlMouseMove.touching = false; - m_ctrlMouseMove.startPosRel = m_keyMap.getMouseMoveMap().data.mouseMove.startPos; - m_ctrlMouseMove.startPosPixel = calcFrameAbsolutePos(m_ctrlMouseMove.startPosRel); - } - if(m_keyMap.isValidSteerWheelMap()){ - m_ctrlMouseMove.touching = false; - } } void InputConvertGame::updateSize(const QSize &frameSize, const QSize &showSize) { + if (showSize != m_showSize) { + if (m_gameMap) { + // show size change, resize grab cursor + emit grabCursor(true); + } + } m_frameSize = frameSize; m_showSize = showSize; - if(m_ctrlMouseMove.valid){ - m_ctrlMouseMove.startPosPixel = calcScreenAbsolutePos(m_ctrlMouseMove.startPosRel); - } } void InputConvertGame::sendTouchDownEvent(int id, QPointF pos) @@ -158,7 +152,11 @@ void InputConvertGame::sendTouchEvent(int id, QPointF pos, AndroidMotioneventAct if (!controlMsg) { return; } - controlMsg->setInjectTouchMsgData(id, action, (AndroidMotioneventButtons)0, QRect(calcFrameAbsolutePos(pos).toPoint(), m_frameSize), 1.0f); + controlMsg->setInjectTouchMsgData(static_cast(id), + action, + static_cast(0), + QRect(calcFrameAbsolutePos(pos).toPoint(),m_frameSize), + 1.0f); sendControlMsg(controlMsg); } @@ -180,25 +178,20 @@ QPointF InputConvertGame::calcScreenAbsolutePos(QPointF relativePos) int InputConvertGame::attachTouchID(int key) { - //QMetaEnum map = QMetaEnum::fromType(); for (int i = 0; i < MULTI_TOUCH_MAX_NUM; i++) { - if (0 == multiTouchID[i]) { - multiTouchID[i] = key; - //qDebug() << "attach "<(); for (int i = 0; i < MULTI_TOUCH_MAX_NUM; i++) { - if (key == multiTouchID[i]) { - multiTouchID[i] = 0; - //qDebug() << "detach "<type()) { return false; } - if (m_ctrlMouseMove.touching) { - QPointF mousePos = from->localPos(); - mousePos.rx() /= m_showSize.width(); - mousePos.ry() /= m_showSize.height(); - QPointF offset = mousePos - m_ctrlMouseMove.startPosRel; - //qDebug()<localPos()<<" - "<0.95 || mousePos.y()<0.05 || mousePos.y()>0.95) { - //qDebug()<<"reset"; + if (checkCursorPos(from)) { + m_ctrlMouseMove.lastPos = QPointF(0.0, 0.0); + return true; + } + + if (!m_ctrlMouseMove.lastPos.isNull()) { + QPointF distance = from->localPos() - m_ctrlMouseMove.lastPos; + distance /= m_keyMap.getMouseMoveMap().data.mouseMove.speedRatio; + + mouseMoveStartTouch(from); + startMouseMoveTimer(); + + m_ctrlMouseMove.lastConverPos.setX(m_ctrlMouseMove.lastConverPos.x() + distance.x() / m_showSize.width()); + m_ctrlMouseMove.lastConverPos.setY(m_ctrlMouseMove.lastConverPos.y() + distance.y() / m_showSize.height()); + + if (m_ctrlMouseMove.lastConverPos.x() < 0.1 + || m_ctrlMouseMove.lastConverPos.x() > 0.8 + || m_ctrlMouseMove.lastConverPos.y() < 0.1 + || m_ctrlMouseMove.lastConverPos.y() > 0.8) { mouseMoveStopTouch(); mouseMoveStartTouch(from); } - offset /= m_keyMap.getMouseMoveMap().data.mouseMove.speedRatio; - m_ctrlMouseMove.lastPosRel = m_ctrlMouseMove.startPosRel + offset; - mouseMoveMovingTouch(m_ctrlMouseMove.lastPosRel); - } else { - m_ctrlMouseMove.touching = true; - mouseMoveStartTouch(from); - int left = from->globalX() - from->x(); - int top = from->globalY() - from->y(); + + sendTouchMoveEvent(getTouchID(Qt::ExtraButton24), m_ctrlMouseMove.lastConverPos); } + m_ctrlMouseMove.lastPos = from->localPos(); return true; } +bool InputConvertGame::checkCursorPos(const QMouseEvent *from) +{ + bool moveCursor = false; + QPoint pos = from->pos(); + if (pos.x() < CURSOR_POS_CHECK) { + pos.setX(m_showSize.width() - CURSOR_POS_CHECK); + moveCursor = true; + } else if (pos.x() > m_showSize.width() - CURSOR_POS_CHECK) { + pos.setX(CURSOR_POS_CHECK); + moveCursor = true; + } else if (pos.y() < CURSOR_POS_CHECK) { + pos.setY(m_showSize.height() - CURSOR_POS_CHECK); + moveCursor = true; + } else if (pos.y() > m_showSize.height() - CURSOR_POS_CHECK) { + pos.setY(CURSOR_POS_CHECK); + moveCursor = true; + } + + if (moveCursor) { + moveCursorTo(from, pos); + } + + return moveCursor; +} + void InputConvertGame::moveCursorTo(const QMouseEvent *from, const QPoint &localPosPixel) { QPoint posOffset = from->pos() - localPosPixel; @@ -373,37 +397,36 @@ void InputConvertGame::moveCursorTo(const QMouseEvent *from, const QPoint &local void InputConvertGame::mouseMoveStartTouch(const QMouseEvent* from) { - moveCursorTo(from, m_ctrlMouseMove.startPosPixel.toPoint()); - int id = attachTouchID(m_ctrlMouseMove.touchKey); - sendTouchDownEvent(id, m_ctrlMouseMove.startPosRel); - m_ctrlMouseMove.lastPosRel = m_ctrlMouseMove.startPosRel; - m_ctrlMouseMove.touching = true; -} - -void InputConvertGame::mouseMoveMovingTouch(const QPointF& target) -{ - sendTouchMoveEvent(getTouchID(m_ctrlMouseMove.touchKey), target); + Q_UNUSED(from) + if (!m_ctrlMouseMove.touching) { + QPointF mouseMoveStartPos = m_keyMap.getMouseMoveMap().data.mouseMove.startPos; + int id = attachTouchID(Qt::ExtraButton24); + sendTouchDownEvent(id, mouseMoveStartPos); + m_ctrlMouseMove.lastConverPos = mouseMoveStartPos; + m_ctrlMouseMove.touching = true; + } } void InputConvertGame::mouseMoveStopTouch() { - int id = getTouchID(m_ctrlMouseMove.touchKey); - sendTouchUpEvent(id, m_ctrlMouseMove.lastPosRel); - detachTouchID(m_ctrlMouseMove.touchKey); - m_ctrlMouseMove.touching = false; + if (m_ctrlMouseMove.touching) { + sendTouchUpEvent(getTouchID(Qt::ExtraButton24), m_ctrlMouseMove.lastConverPos); + detachTouchID(Qt::ExtraButton24); + m_ctrlMouseMove.touching = false; + } } void InputConvertGame::startMouseMoveTimer() { stopMouseMoveTimer(); - m_mouseMoveTimer = startTimer(1000); + m_ctrlMouseMove.timer = startTimer(1000); } void InputConvertGame::stopMouseMoveTimer() { - if (0 != m_mouseMoveTimer) { - killTimer(m_mouseMoveTimer); - m_mouseMoveTimer = 0; + if (0 != m_ctrlMouseMove.timer) { + killTimer(m_ctrlMouseMove.timer); + m_ctrlMouseMove.timer = 0; } } @@ -412,11 +435,11 @@ bool InputConvertGame::switchGameMap() m_gameMap = !m_gameMap; emit grabCursor(m_gameMap); if (m_gameMap) { - #ifdef QT_NO_DEBUG - QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); - #else - QGuiApplication::setOverrideCursor(QCursor(Qt::CrossCursor)); - #endif +#ifdef QT_NO_DEBUG + QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor)); +#else + QGuiApplication::setOverrideCursor(QCursor(Qt::CrossCursor)); +#endif } else { if(m_ctrlMouseMove.touching) mouseMoveStopTouch(); @@ -427,7 +450,7 @@ bool InputConvertGame::switchGameMap() void InputConvertGame::timerEvent(QTimerEvent *event) { - if (m_mouseMoveTimer == event->timerId()) { + if (m_ctrlMouseMove.timer == event->timerId()) { stopMouseMoveTimer(); mouseMoveStopTouch(); } diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.h b/QtScrcpy/device/controller/inputconvert/inputconvertgame.h index 8ded936..1e32813 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.h +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.h @@ -48,13 +48,12 @@ protected: bool processMouseMove(const QMouseEvent* from); void moveCursorTo(const QMouseEvent* from, const QPoint& localPosPixel); void mouseMoveStartTouch(const QMouseEvent* from); - void mouseMoveMovingTouch(const QPointF& target); void mouseMoveStopTouch(); - void startMouseMoveTimer(); void stopMouseMoveTimer(); bool switchGameMap(); + bool checkCursorPos(const QMouseEvent *from); protected: void timerEvent(QTimerEvent *event); @@ -63,8 +62,9 @@ private: QSize m_frameSize; QSize m_showSize; bool m_gameMap = false; - - int multiTouchID[MULTI_TOUCH_MAX_NUM] = { 0 }; + bool m_needSwitchGameAgain = false; + int m_multiTouchID[MULTI_TOUCH_MAX_NUM] = { 0 }; + KeyMap m_keyMap; // steer wheel struct { @@ -79,21 +79,12 @@ private: } m_ctrlSteerWheel; // mouse move - struct{ - bool valid = false; + struct { + QPointF lastConverPos; + QPointF lastPos = {0.0, 0.0}; bool touching = false; - const int touchKey = Qt::ExtraButton24; - QPointF startPosRel; // in [0, 1) - QPointF startPosPixel; // in [0, size) - QPointF lastPosRel; - //QPointF lastPosPixel; + int timer = 0; } m_ctrlMouseMove; - - int m_mouseMoveTimer = 0; - - bool m_needSwitchGameAgain = false; - - KeyMap m_keyMap; }; #endif // INPUTCONVERTGAME_H diff --git a/QtScrcpy/device/ui/toolform.cpp b/QtScrcpy/device/ui/toolform.cpp index 2b38cc3..c0aeb07 100644 --- a/QtScrcpy/device/ui/toolform.cpp +++ b/QtScrcpy/device/ui/toolform.cpp @@ -69,11 +69,13 @@ void ToolForm::mouseMoveEvent(QMouseEvent *event) void ToolForm::showEvent(QShowEvent *event) { + Q_UNUSED(event) qDebug() << "show event"; } void ToolForm::hideEvent(QHideEvent *event) { + Q_UNUSED(event) qDebug() << "hide event"; } diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 78be624..2ae6496 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -349,7 +349,6 @@ void VideoForm::keyPressEvent(QKeyEvent *event) return; } - //qDebug() << "keyPressEvent" << event->isAutoRepeat(); m_controller->keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size()); } @@ -358,7 +357,6 @@ void VideoForm::keyReleaseEvent(QKeyEvent *event) if (!m_controller) { return; } - //qDebug() << "keyReleaseEvent" << event->isAutoRepeat(); m_controller->keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size()); } From 06cdb345bb7bb2611b4f427372918d4069269692 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 14:35:12 +0800 Subject: [PATCH 04/14] feat: optimize drag and add tiktok map --- .../inputconvert/inputconvertgame.cpp | 34 ++++++---- .../controller/inputconvert/keymap/keymap.cpp | 13 ++-- .../controller/inputconvert/keymap/keymap.h | 2 +- README.md | 2 +- README_zh.md | 2 +- docs/按键映射说明.md | 12 +++- keymap/gameforpeace.json | 3 +- keymap/tiktok.json | 67 +++++++++++++++++++ 8 files changed, 109 insertions(+), 26 deletions(-) create mode 100644 keymap/tiktok.json diff --git a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp index 8dd7d0b..4bc3831 100644 --- a/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp +++ b/QtScrcpy/device/controller/inputconvert/inputconvertgame.cpp @@ -42,9 +42,8 @@ void InputConvertGame::mouseEvent(const QMouseEvent *from, const QSize &frameSiz if (processMouseClick(from)) { return; } - } else { - InputConvertNormal::mouseEvent(from, frameSize, showSize); } + InputConvertNormal::mouseEvent(from, frameSize, showSize); } void InputConvertGame::wheelEvent(const QWheelEvent *from, const QSize &frameSize, const QSize &showSize) @@ -117,7 +116,7 @@ void InputConvertGame::loadKeyMap(const QString &json) void InputConvertGame::updateSize(const QSize &frameSize, const QSize &showSize) { if (showSize != m_showSize) { - if (m_gameMap) { + if (m_gameMap && m_keyMap.isValidMouseMoveMap()) { // show size change, resize grab cursor emit grabCursor(true); } @@ -296,10 +295,14 @@ void InputConvertGame::processKeyClick( void InputConvertGame::processKeyDrag(const QPointF& startPos, QPointF endPos, const QKeyEvent* from) { - if(QEvent::KeyPress == from->type()){ + if (QEvent::KeyPress == from->type()){ int id = attachTouchID(from->key()); sendTouchDownEvent(id, startPos); sendTouchMoveEvent(id, endPos); + } + + if (QEvent::KeyRelease == from->type()) { + int id = getTouchID(from->key()); sendTouchUpEvent(id, endPos); detachTouchID(from->key()); } @@ -314,16 +317,19 @@ bool InputConvertGame::processMouseClick(const QMouseEvent *from) return false; } + qDebug() << "mouse event " << from->type(); if (QEvent::MouseButtonPress == from->type() || QEvent::MouseButtonDblClick == from->type()) { int id = attachTouchID(from->button()); sendTouchDownEvent(id, node.data.click.keyNode.pos); - } else if (QEvent::MouseButtonRelease == from->type()) { - sendTouchUpEvent(getTouchID(from->button()), node.data.click.keyNode.pos); - detachTouchID(from->button()); - } else { - return false; + return true; } - return true; + if (QEvent::MouseButtonRelease == from->type()) { + int id = getTouchID(from->button()); + sendTouchUpEvent(id, node.data.click.keyNode.pos); + detachTouchID(from->button()); + return true; + } + return false; } bool InputConvertGame::processMouseMove(const QMouseEvent *from) @@ -433,6 +439,12 @@ void InputConvertGame::stopMouseMoveTimer() bool InputConvertGame::switchGameMap() { m_gameMap = !m_gameMap; + + if (!m_keyMap.isValidMouseMoveMap()) { + return m_gameMap; + } + + // grab cursor and set cursor only mouse move map emit grabCursor(m_gameMap); if (m_gameMap) { #ifdef QT_NO_DEBUG @@ -441,8 +453,6 @@ bool InputConvertGame::switchGameMap() QGuiApplication::setOverrideCursor(QCursor(Qt::CrossCursor)); #endif } else { - if(m_ctrlMouseMove.touching) - mouseMoveStopTouch(); QGuiApplication::restoreOverrideCursor(); } return m_gameMap; diff --git a/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp b/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp index 68faf39..184c4d1 100644 --- a/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp +++ b/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp @@ -134,7 +134,7 @@ void KeyMap::loadKeyMap(const QString &json) case KeyMap::KMT_CLICK_TWICE: { // safe check - if (!checkForClickDouble(node)) { + if (!checkForClickTwice(node)) { qWarning() << "json error: keyMapNodes node format error"; break; } @@ -386,12 +386,12 @@ bool KeyMap::checkItemDouble(const QJsonObject& node, const QString& name) bool KeyMap::checkItemBool(const QJsonObject& node, const QString& name) { - return !node.contains(name) || node.value(name).isBool(); + return node.contains(name) && node.value(name).isBool(); } bool KeyMap::checkItemObject(const QJsonObject &node, const QString &name) { - return !node.contains(name) || node.value(name).isObject(); + return node.contains(name) && node.value(name).isObject(); } bool KeyMap::checkItemPos(const QJsonObject& node, const QString& name) @@ -406,13 +406,12 @@ bool KeyMap::checkItemPos(const QJsonObject& node, const QString& name) bool KeyMap::checkForClick(const QJsonObject& node) { - return checkItemString(node, "key") && checkItemPos(node, "pos") - && checkItemBool(node, "switchMap"); + return checkForClickTwice(node) && checkItemBool(node, "switchMap"); } -bool KeyMap::checkForClickDouble(const QJsonObject& node) +bool KeyMap::checkForClickTwice(const QJsonObject& node) { - return checkForClick(node); + return checkItemString(node, "key") && checkItemPos(node, "pos"); } bool KeyMap::checkForSteerWhell(const QJsonObject& node) diff --git a/QtScrcpy/device/controller/inputconvert/keymap/keymap.h b/QtScrcpy/device/controller/inputconvert/keymap/keymap.h index dd607aa..f21f814 100644 --- a/QtScrcpy/device/controller/inputconvert/keymap/keymap.h +++ b/QtScrcpy/device/controller/inputconvert/keymap/keymap.h @@ -95,7 +95,7 @@ private: // safe check for KeyMapNode bool checkForClick(const QJsonObject& node); - bool checkForClickDouble(const QJsonObject& node); + bool checkForClickTwice(const QJsonObject& node); bool checkForSteerWhell(const QJsonObject& node); bool checkForDrag(const QJsonObject& node); diff --git a/README.md b/README.md index 9a00f29..4e8ca9f 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ It focuses on: ## Customized key mapping (Windows only) You can write your own script to map keyboard and mouse actions to touches and clicks of the mobile phone according to your needs. [Here](docs/按键映射说明.md) are the rules. -A script for "PUBG mobile" mapping is provided by default. Once enabled, you can play the game with your keyboard and mouse as the PC version. You can also write your own mapping files for other games according to [writing rules](docs/按键映射说明.md). The default key mapping is as follows: +A script for "PUBG mobile" and TikTok mapping is provided by default. Once enabled, you can play the game with your keyboard and mouse as the PC version. or you can use up/down/left/right direction keys to simulate up/down/left/right sliding. You can also write your own mapping files for other games according to [writing rules](docs/按键映射说明.md). The default key mapping is as follows: ![game](screenshot/game.jpg) diff --git a/README_zh.md b/README_zh.md index 2216598..a2864ff 100644 --- a/README_zh.md +++ b/README_zh.md @@ -33,7 +33,7 @@ QtScrcpy可以通过USB(或通过TCP/IP)连接Android设备,并进行显示和 ## 自定义按键映射(仅windows平台开启) 可以根据需要,自己编写脚本将PC键盘按键映射为手机的触摸点击,编写规则在[这里](docs/按键映射说明.md)。 -默认自带了针对和平精英手游进行键鼠映射的映射脚本,开启后可以用键鼠像玩端游一样玩和平精英手游,你也可以按照[编写规则](docs/按键映射说明.md)编写其他游戏的映射文件,默认按键映射如下: +默认自带了针对和平精英手游和抖音进行键鼠映射的映射脚本,开启平精英手游后可以用键鼠像玩端游一样玩和平精英手游,开启抖音映射以后可以使用上下左右方向键模拟上下左右滑动,你也可以按照[编写规则](docs/按键映射说明.md)编写其他游戏的映射文件,默认按键映射如下: ![game](screenshot/game.jpg) diff --git a/docs/按键映射说明.md b/docs/按键映射说明.md index 35dd042..4c2e447 100644 --- a/docs/按键映射说明.md +++ b/docs/按键映射说明.md @@ -16,7 +16,7 @@ - switchKey:切换自定义按键映射的开关键,默认为普通映射,需要使用这个按键在普通映射和自定义映射之间切换。 -- mouseMoveMap:鼠标移动映射,鼠标的移动将被映射为以startPos为起点,以鼠标移动方向为移动方向的手指拖动操作。 +- mouseMoveMap:鼠标移动映射,鼠标的移动将被映射为以startPos为起点,以鼠标移动方向为移动方向的手指拖动操作(开启鼠标移动映射以后会隐藏鼠标,限制鼠标移动范围)。 一般在FPS手游中用来调整人物视野。 - startPos 手指拖动起始点 - speedRatio 鼠标移动映射为手指拖动的比例,可以控制鼠标灵敏度 @@ -26,8 +26,9 @@ 一般按键映射有如下几种类型: - type 按键映射的类型,每个keyMapNodes中的元素都需要指明,可以是如下类型: - - KMT_CLICK 普通点击,键盘按下模拟为手指按下,键盘抬起映射为手指抬起 - - KMT_CLICK_TWICE 两次点击,键盘按下模拟为手指按下再抬起,键盘抬起映射为手指按下再抬起 + - KMT_CLICK 普通点击,按键按下模拟为手指按下,按键抬起模拟为手指抬起 + - KMT_CLICK_TWICE 两次点击,按键按下模拟为手指按下再抬起,按键抬起模拟为手指按下再抬起 + - KMT_DRAG 拖拽,按键按下模拟为手指按下并拖动一段距离,按键抬起模拟为手指抬起 - KMT_STEER_WHEEL 方向盘映射,专用于FPS游戏中移动人物脚步的方向盘的映射,需要4个按键来配合。 不同按键映射类型的专有属性说明: @@ -41,6 +42,11 @@ - key 要映射的按键码 - pos 模拟触摸的位置 +- KMT_DRAG + - key 要映射的按键码 + - startPos 模拟触摸拖动的开始位置 + - endPos 模拟触摸拖动的结束位置 + - KMT_STEER_WHEEL - centerPos 方向盘中心点 - leftKey 左方向的按键控制 diff --git a/keymap/gameforpeace.json b/keymap/gameforpeace.json index 045d390..884324e 100644 --- a/keymap/gameforpeace.json +++ b/keymap/gameforpeace.json @@ -7,7 +7,8 @@ }, "speedRatio": 10 }, - "keyMapNodes": [{ + "keyMapNodes": [ + { "comment": "方向盘", "type": "KMT_STEER_WHEEL", "centerPos": { diff --git a/keymap/tiktok.json b/keymap/tiktok.json new file mode 100644 index 0000000..7d2ab5f --- /dev/null +++ b/keymap/tiktok.json @@ -0,0 +1,67 @@ +{ + "switchKey": "Key_QuoteLeft", + "keyMapNodes": [ + { + "comment": "暂停/继续", + "type": "KMT_CLICK", + "key": "Key_Space", + "pos": { + "x": 0.5, + "y": 0.5 + }, + "switchMap": false + }, + { + "comment": "上滑", + "type": "KMT_DRAG", + "key": "Key_Up", + "startPos": { + "x": 0.5, + "y": 0.7 + }, + "endPos": { + "x": 0.5, + "y": 0.3 + } + }, + { + "comment": "下滑", + "type": "KMT_DRAG", + "key": "Key_Down", + "startPos": { + "x": 0.5, + "y": 0.3 + }, + "endPos": { + "x": 0.5, + "y": 0.7 + } + }, + { + "comment": "左滑", + "type": "KMT_DRAG", + "key": "Key_Left", + "startPos": { + "x": 0.7, + "y": 0.5 + }, + "endPos": { + "x": 0.3, + "y": 0.5 + } + }, + { + "comment": "右滑", + "type": "KMT_DRAG", + "key": "Key_Right", + "startPos": { + "x": 0.3, + "y": 0.5 + }, + "endPos": { + "x": 0.7, + "y": 0.5 + } + } + ] +} \ No newline at end of file From 71ea24c1d79014b7f07b95653c0b90daabd3672a Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 14:46:17 +0800 Subject: [PATCH 05/14] feat: remove auto map --- QtScrcpy/devicemanage/devicemanage.cpp | 4 ++++ QtScrcpy/dialog.cpp | 15 ++------------- QtScrcpy/dialog.ui | 7 ------- QtScrcpy/main.cpp | 4 ++-- README.md | 3 ++- README_zh.md | 3 ++- 6 files changed, 12 insertions(+), 24 deletions(-) diff --git a/QtScrcpy/devicemanage/devicemanage.cpp b/QtScrcpy/devicemanage/devicemanage.cpp index 646becc..decce90 100644 --- a/QtScrcpy/devicemanage/devicemanage.cpp +++ b/QtScrcpy/devicemanage/devicemanage.cpp @@ -49,6 +49,10 @@ bool DeviceManage::connectDevice(Device::DeviceParams params) void DeviceManage::updateScript(QString script) { + if (m_devices.isEmpty()) { + qWarning() << "no device connect!!!"; + return; + } QMapIterator> i(m_devices); while (i.hasNext()) { i.next(); diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index da5991d..c9d8be0 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -91,11 +91,6 @@ void Dialog::initUI() ui->formatBox->addItem("mp4"); ui->formatBox->addItem("mkv"); -#ifndef Q_OS_WIN32 - // game only windows - ui->gameCheck->setEnabled(false); -#endif - ui->recordPathEdt->setText(Config::getInstance().getRecordPath()); } @@ -155,18 +150,12 @@ void Dialog::on_startServerBtn_clicked() params.maxSize = videoSize; params.bitRate = bitRate; // on devices with Android >= 10, the capture frame rate can be limited - params.maxFps = Config::getInstance().getMaxFps(); + params.maxFps = static_cast(Config::getInstance().getMaxFps()); params.recordFileName = absFilePath; params.closeScreen = ui->closeScreenCheck->isChecked(); params.useReverse = ui->useReverseCheck->isChecked(); params.display = !ui->notDisplayCheck->isChecked(); - if (ui->gameCheck->isChecked()) { - if (ui->gameBox->currentText().isEmpty()) { - outLog("no keymap script selected", true); - } else { - params.gameScript = getGameScript(ui->gameBox->currentText()); - } - } + m_deviceManage.connectDevice(params); /* diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index 514e54e..98ce0c8 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -304,13 +304,6 @@ - - - - auto enable - - - diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index 63d9ad1..833b047 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -10,9 +10,9 @@ #include "mousetap/mousetap.h" #include "config.h" -Dialog* g_mainDlg = Q_NULLPTR; +static Dialog* g_mainDlg = Q_NULLPTR; -QtMessageHandler g_oldMessageHandler = Q_NULLPTR; +static QtMessageHandler g_oldMessageHandler = Q_NULLPTR; void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg); void installTranslator(); diff --git a/README.md b/README.md index 4e8ca9f..0f6b2cd 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,9 @@ Here is the instruction of adding new customized mapping files. - Write a customized script and put it in the `keymap` directory - Click `refresh script` to check whether it can be found +- Select your script - Connect your phone, start service and click `apply` -- Start the game and press `~` key (left side of the number key 1) to switch to the mapping mode (It can be changed in the script as `switchkey`) +- Press `~` key (left side of the number key 1) to switch to the custom mapping mode (It can be changed in the script as `switchkey`) - Press the ~ key again to switch back to normal mode - (For PUBG and similar games) If you want to drive cars with WASD, you need to check the `single rocker mode` in the game setting. diff --git a/README_zh.md b/README_zh.md index a2864ff..4afff4c 100644 --- a/README_zh.md +++ b/README_zh.md @@ -42,8 +42,9 @@ QtScrcpy可以通过USB(或通过TCP/IP)连接Android设备,并进行显示和 自定义按键映射操作方法如下: - 编写自定义脚本放入keymap目录 - 点击刷新脚本,确保脚本可以被检测到 +- 选择需要的脚本 - 连接手机并启动服务之后,点击应用脚本 -- 进入游戏场景,按~键(数字键1左边)切换为游戏映射模式即可体验(具体按什么键要看你按键脚本定义的switchKey) +- 按~键(数字键1左边)切换为自定义映射模式即可体验(具体按什么键要看你按键脚本定义的switchKey) - 再次按~键切换为正常控制模式 - 要想wasd控制开车记得在载具设置中设置为单摇杆模式 From a8f79db1727848604744eb197f40d710416b0a5e Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 16:30:28 +0800 Subject: [PATCH 06/14] feat: update ui --- QtScrcpy/dialog.cpp | 18 +- QtScrcpy/dialog.h | 2 +- QtScrcpy/dialog.ui | 812 +++++++++++++++++++++++++++----------------- QtScrcpy/main.cpp | 3 +- 4 files changed, 519 insertions(+), 316 deletions(-) diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index c9d8be0..ccab826 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -221,6 +221,17 @@ void Dialog::outLog(const QString &log, bool newLine) }); } +bool Dialog::filterLog(const QString &log) +{ + if (log.contains("app_proces")) { + return true; + } + if (log.contains("Unable to set geometry")) { + return true; + } + return false; +} + bool Dialog::checkAdbRun() { if (m_adb.isRuning()) { @@ -328,10 +339,3 @@ void Dialog::on_applyScriptBtn_clicked() { m_deviceManage.updateScript(getGameScript(ui->gameBox->currentText())); } - -void Dialog::on_gameCheck_clicked(bool checked) -{ - if (checked) { - on_refreshGameScriptBtn_clicked(); - } -} diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 7f58fa8..31bf7d8 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -21,6 +21,7 @@ public: ~Dialog(); void outLog(const QString& log, bool newLine = true); + bool filterLog(const QString & log); private slots: void on_updateDevice_clicked(); @@ -51,7 +52,6 @@ private slots: void on_refreshGameScriptBtn_clicked(); void on_applyScriptBtn_clicked(); - void on_gameCheck_clicked(bool checked); private: bool checkAdbRun(); diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index 98ce0c8..a768349 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -6,119 +6,424 @@ 0 0 - 513 - 495 + 420 + 471 + + + 420 + 0 + + - 600 - 2160 + 420 + 16777215 QtScrcpy - - - + + + - USB line + Start Config - - - - - - - - device serial: - + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + bit rate: + + + + + + + + + + + + + + + + + max size: + + + + + + + + + + + + + + record format: + + + + + + + - - - - get device IP - - - false - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + record save path: + + + recordPathEdt + + + + + + + true + + + + + + + select path + + + false + + + + - - - - refresh devices - - - false - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + refresh script + + + + + + + apply + + + + - - - - start server - - - false - - - - - - - stop all server - - - - - - - stop server - - - false - - - - - - - start adbd - - - false - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + background record + + + false + + + + + + + + 0 + 0 + + + + always on top + + + false + + + + + + + + 0 + 0 + + + + screen-off + + + + + + + + 0 + 0 + + + + reverse connection + + + true + + + + - + + + + USB line + + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + device serial: + + + + + + + + + + start server + + + false + + + + + + + stop server + + + false + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + stop all server + + + + + + + refresh devices + + + false + + + + + + + get device IP + + + false + + + + + + + start adbd + + + false + + + + + + + + + + Wireless - - - - - - 0 - 0 - - - - wireless connect - - - false - - - - + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + @@ -131,7 +436,7 @@ - + @@ -150,8 +455,20 @@ - + + + + 0 + 0 + + + + + 40 + 16777215 + + @@ -163,10 +480,26 @@ - + + + + + 0 + 0 + + + + wireless connect + + + false + + + + - + 0 0 @@ -182,7 +515,87 @@ - + + + + adb + + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + adb command: + + + adbCommandEdt + + + + + + + devices + + + + + + + + 0 + 0 + + + + execute + + + + + + + + 0 + 0 + + + + terminate + + + + + + + + 0 + 0 + + + + clear + + + + + + + @@ -201,225 +614,10 @@ - - - - adb - - - - - - adb command: - - - adbCommandEdt - - - - - - - terminate - - - - - - - execute - - - - - - - devices - - - - - - - clear - - - - - - - - - - Start Config - - - - - - bit rate: - - - - - - - record format: - - - - - - - record save path: - - - recordPathEdt - - - - - - - - - - refresh script - - - - - - - - 0 - 0 - - - - reverse connection - - - true - - - - - - - - 0 - 0 - - - - always on top - - - false - - - - - - - - 0 - 0 - - - - background record - - - false - - - - - - - - 0 - 0 - - - - screen-off - - - - - - - select path - - - false - - - - - - - - - - true - - - - - - - - - - - - - - - - - apply - - - - - - - - - - - - - - max size: - - - - - - - bitRateBox - formatBox - recordPathEdt - selectRecordPathBtn - gameBox - refreshGameScriptBtn - serialBox - startServerBtn - stopServerBtn - stopAllServerBtn - updateDevice - getIPBtn - startAdbdBtn deviceIpEdt devicePortEdt wirelessConnectBtn diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index 833b047..2ccb6a6 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -91,6 +91,7 @@ void installTranslator() { static QTranslator translator; QLocale locale; QLocale::Language language = locale.language(); + //language = QLocale::English; QString languagePath = ":/i18n/"; switch (language) { case QLocale::Chinese: @@ -112,7 +113,7 @@ void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QS } if (QtDebugMsg < type) { - if (g_mainDlg && g_mainDlg->isVisible() && !msg.contains("app_proces")) { + if (g_mainDlg && g_mainDlg->isVisible() && !g_mainDlg->filterLog(msg)) { g_mainDlg->outLog(msg); } } From 15909bc27d83849e801c2bf2e154e05111c0bf53 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 17:48:33 +0800 Subject: [PATCH 07/14] feat: sdcard path to config --- QtScrcpy/device/device.cpp | 2 +- QtScrcpy/device/filehandler/filehandler.cpp | 15 ++------------- QtScrcpy/device/ui/videoform.cpp | 3 ++- QtScrcpy/util/config.cpp | 12 ++++++++++++ QtScrcpy/util/config.h | 1 + config/config.ini | 4 +++- 6 files changed, 21 insertions(+), 16 deletions(-) diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 7b9e58a..19548bc 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -120,7 +120,7 @@ void Device::initSignals() QMessageBox::warning(m_videoForm, "QtScrcpy", tr("wait current %1 to complete").arg(tips), QMessageBox::Ok); } if (FileHandler::FAR_SUCCESS_EXEC == processResult && m_videoForm) { - QMessageBox::information(m_videoForm, "QtScrcpy", tr("%1 complete, save in %2").arg(tips).arg(m_fileHandler->getDevicePath()), QMessageBox::Ok); + QMessageBox::information(m_videoForm, "QtScrcpy", tr("%1 complete, save in %2").arg(tips).arg(Config::getInstance().getPushFilePath()), QMessageBox::Ok); } if (FileHandler::FAR_ERROR_EXEC == processResult && m_videoForm) { QMessageBox::information(m_videoForm, "QtScrcpy", tr("%1 failed").arg(tips), QMessageBox::Ok); diff --git a/QtScrcpy/device/filehandler/filehandler.cpp b/QtScrcpy/device/filehandler/filehandler.cpp index c2b269f..c92425d 100644 --- a/QtScrcpy/device/filehandler/filehandler.cpp +++ b/QtScrcpy/device/filehandler/filehandler.cpp @@ -1,7 +1,5 @@ #include "filehandler.h" -#define DEVICE_SDCARD_PATH "/sdcard/" - FileHandler::FileHandler(QObject *parent) : QObject (parent) { @@ -32,12 +30,9 @@ void FileHandler::pushFileRequest(const QString &serial, const QString &file, co emit fileHandlerResult(FAR_IS_RUNNING, false); return; } - m_devicePath = devicePath; - if (m_devicePath.isEmpty()) { - m_devicePath = DEVICE_SDCARD_PATH; - } + m_isApk = false; - m_adb.push(serial, file, m_devicePath); + m_adb.push(serial, file, devicePath); } void FileHandler::installApkRequest(const QString &serial, const QString &apkFile) @@ -46,12 +41,6 @@ void FileHandler::installApkRequest(const QString &serial, const QString &apkFil emit fileHandlerResult(FAR_IS_RUNNING, true); return; } - m_devicePath = ""; m_isApk = true; m_adb.install(serial, apkFile); } - -const QString &FileHandler::getDevicePath() -{ - return m_devicePath; -} diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 2ae6496..ac3124b 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -16,6 +16,7 @@ #include "toolform.h" #include "controller.h" #include "filehandler.h" +#include "config.h" extern "C" { #include "libavutil/frame.h" @@ -410,5 +411,5 @@ void VideoForm::dropEvent(QDropEvent *event) m_fileHandler->installApkRequest(m_serial, file); return; } - m_fileHandler->pushFileRequest(m_serial, file); + m_fileHandler->pushFileRequest(m_serial, file, Config::getInstance().getPushFilePath()); } diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index aa7fc8a..649dccb 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -12,6 +12,9 @@ #define COMMON_RECORD_KEY "RecordPath" #define COMMON_RECORD_DEF "" +#define COMMON_PUSHFILE_KEY "PushFilePath" +#define COMMON_PUSHFILE_DEF "/sdcard/" + #define COMMON_SERVER_VERSION_KEY "ServerVersion" #define COMMON_SERVER_VERSION_DEF "1.12.1" @@ -102,6 +105,15 @@ int Config::getSkin() return skin; } +QString Config::getPushFilePath() +{ + QString pushFile; + m_settings->beginGroup(GROUP_COMMON); + pushFile = m_settings->value(COMMON_PUSHFILE_KEY, COMMON_PUSHFILE_DEF).toString(); + m_settings->endGroup(); + return pushFile; +} + QString Config::getTitle() { QString title; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index 8279a9b..cde8c02 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -17,6 +17,7 @@ public: int getMaxFps(); int getDesktopOpenGL(); int getSkin(); + QString getPushFilePath(); private: explicit Config(QObject *parent = nullptr); diff --git a/config/config.ini b/config/config.ini index b2dbe42..eea3e35 100644 --- a/config/config.ini +++ b/config/config.ini @@ -1,8 +1,10 @@ [common] # 窗口标题 WindowTitle=QtScrcpy -# 录制文件路径 +# 录制文件保存路径 RecordPath= +# 推送到安卓设备的文件保存路径 +PushFilePath=/sdcard/ # 最大fps(仅支持Android 10以上) MaxFps=60 # scrcpy-server的版本号(不要修改) From 2ef4904a5d75f38130365cb89580f80a871e9791 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 18:37:59 +0800 Subject: [PATCH 08/14] fix: push file for chinese --- QtScrcpy/adb/adbprocess.cpp | 8 ++++---- QtScrcpy/device/ui/videoform.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/QtScrcpy/adb/adbprocess.cpp b/QtScrcpy/adb/adbprocess.cpp index 68ff7d9..f060a81 100644 --- a/QtScrcpy/adb/adbprocess.cpp +++ b/QtScrcpy/adb/adbprocess.cpp @@ -62,16 +62,16 @@ void AdbProcess::initSignals() connect(this, &QProcess::readyReadStandardError, this, [this](){ - QString tmp = QString::fromLocal8Bit(readAllStandardError()).trimmed(); + QString tmp = QString::fromUtf8(readAllStandardError()).trimmed(); m_errorOutput += tmp; - qWarning(QString("AdbProcess::error:%1").arg(tmp).toUtf8()); + qWarning(QString("AdbProcess::error:%1").arg(tmp).toStdString().data()); }); connect(this, &QProcess::readyReadStandardOutput, this, [this](){ - QString tmp = QString::fromLocal8Bit(readAllStandardOutput()).trimmed(); + QString tmp = QString::fromUtf8(readAllStandardOutput()).trimmed(); m_standardOutput += tmp; - qInfo(QString("AdbProcess::out:%1").arg(tmp).toUtf8()); + qInfo(QString("AdbProcess::out:%1").arg(tmp).toStdString().data()); }); connect(this, &QProcess::started, this, diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index ac3124b..3f6bbc9 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -411,5 +411,5 @@ void VideoForm::dropEvent(QDropEvent *event) m_fileHandler->installApkRequest(m_serial, file); return; } - m_fileHandler->pushFileRequest(m_serial, file, Config::getInstance().getPushFilePath()); + m_fileHandler->pushFileRequest(m_serial, file, Config::getInstance().getPushFilePath() + fileInfo.fileName()); } From 269781a822c0d1a8d0240e2c628a7f1068f847b2 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 18:45:21 +0800 Subject: [PATCH 09/14] feat: get ip failed tips --- QtScrcpy/dialog.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index ccab826..d947d03 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -46,14 +46,18 @@ Dialog::Dialog(QWidget *parent) : } } else if (args.contains("show") && args.contains("wlan0")) { QString ip = m_adb.getDeviceIPFromStdOut(); - if (!ip.isEmpty()) { - ui->deviceIpEdt->setText(ip); + if (ip.isEmpty()) { + log = "ip not find, connect to wifi?"; + break; } + ui->deviceIpEdt->setText(ip); } else if (args.contains("ifconfig") && args.contains("wlan0")) { QString ip = m_adb.getDeviceIPFromStdOut(); - if (!ip.isEmpty()) { - ui->deviceIpEdt->setText(ip); + if (ip.isEmpty()) { + log = "ip not find, connect to wifi?"; + break; } + ui->deviceIpEdt->setText(ip); } break; } From 460c21f9d68795ab5b0183665b8c2ea8d4fb078f Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 19:00:10 +0800 Subject: [PATCH 10/14] feat: add server path to config --- QtScrcpy/device/server/server.cpp | 5 ++--- QtScrcpy/device/server/server.h | 4 ++-- QtScrcpy/util/config.cpp | 12 ++++++++++++ QtScrcpy/util/config.h | 1 + config/config.ini | 10 ++++++---- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/QtScrcpy/device/server/server.cpp b/QtScrcpy/device/server/server.cpp index 4f04f7f..c79ad83 100644 --- a/QtScrcpy/device/server/server.cpp +++ b/QtScrcpy/device/server/server.cpp @@ -8,7 +8,6 @@ #include "server.h" #include "config.h" -#define DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar" #define DEVICE_NAME_FIELD_LENGTH 64 #define SOCKET_NAME "scrcpy" #define MAX_CONNECT_COUNT 30 @@ -68,7 +67,7 @@ bool Server::pushServer() if (m_workProcess.isRuning()) { m_workProcess.kill(); } - m_workProcess.push(m_params.serial, getServerPath(), DEVICE_SERVER_PATH); + m_workProcess.push(m_params.serial, getServerPath(), Config::getInstance().getServerPath()); return true; } @@ -126,7 +125,7 @@ bool Server::execute() } QStringList args; args << "shell"; - args << QString("CLASSPATH=%1").arg(DEVICE_SERVER_PATH); + args << QString("CLASSPATH=%1").arg(Config::getInstance().getServerPath()); args << "app_process"; args << "/"; // unused; args << "com.genymobile.scrcpy.Server"; diff --git a/QtScrcpy/device/server/server.h b/QtScrcpy/device/server/server.h index 33a13ba..8c21172 100644 --- a/QtScrcpy/device/server/server.h +++ b/QtScrcpy/device/server/server.h @@ -82,8 +82,8 @@ private: QPointer m_controlSocket = Q_NULLPTR; bool m_tunnelEnabled = false; bool m_tunnelForward = false; // use "adb forward" instead of "adb reverse" - quint32 m_acceptTimeoutTimer = 0; - quint32 m_connectTimeoutTimer = 0; + int m_acceptTimeoutTimer = 0; + int m_connectTimeoutTimer = 0; quint32 m_connectCount = 0; quint32 m_restartCount = 0; QString m_deviceName = ""; diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index 649dccb..6009223 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -18,6 +18,9 @@ #define COMMON_SERVER_VERSION_KEY "ServerVersion" #define COMMON_SERVER_VERSION_DEF "1.12.1" +#define COMMON_SERVER_PATH_KEY "ServerPath" +#define COMMON_SERVER_PATH_DEF "/data/local/tmp/scrcpy-server.jar" + #define COMMON_MAX_FPS_KEY "MaxFps" #define COMMON_MAX_FPS_DEF 60 @@ -114,6 +117,15 @@ QString Config::getPushFilePath() return pushFile; } +QString Config::getServerPath() +{ + QString serverPath; + m_settings->beginGroup(GROUP_COMMON); + serverPath = m_settings->value(COMMON_SERVER_PATH_KEY, COMMON_SERVER_PATH_DEF).toString(); + m_settings->endGroup(); + return serverPath; +} + QString Config::getTitle() { QString title; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index cde8c02..f23d3b6 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -18,6 +18,7 @@ public: int getDesktopOpenGL(); int getSkin(); QString getPushFilePath(); + QString getServerPath(); private: explicit Config(QObject *parent = nullptr); diff --git a/config/config.ini b/config/config.ini index eea3e35..ae2f632 100644 --- a/config/config.ini +++ b/config/config.ini @@ -1,15 +1,17 @@ [common] # 窗口标题 WindowTitle=QtScrcpy -# 录制文件保存路径 +# 录制文件保存路径(必须以/作为分隔符) RecordPath= -# 推送到安卓设备的文件保存路径 +# 推送到安卓设备的文件保存路径(必须以/结尾) PushFilePath=/sdcard/ # 最大fps(仅支持Android 10以上) MaxFps=60 -# scrcpy-server的版本号(不要修改) -ServerVersion=1.12.1 # 是否显示手机皮肤,0不显示 UseSkin=1 # 视频解码方式:-1 自动,0 软解,1 dx硬解,2 opengl硬解 UseDesktopOpenGL=-1 +# scrcpy-server的版本号(不要修改) +ServerVersion=1.12.1 +# scrcpy-server推送到安卓设备的路径 +ServerPath=/data/local/tmp/scrcpy-server.jar From e81d36bcf652002ab7bc0cb9c0ca3dbffa5837b6 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 20:47:49 +0800 Subject: [PATCH 11/14] feat: add screenshot --- QtScrcpy/device/decoder/avframeconvert.cpp | 15 +++-- QtScrcpy/device/decoder/avframeconvert.h | 18 +++--- QtScrcpy/device/device.cpp | 73 +++++++++++++++++++++- QtScrcpy/device/device.h | 7 +++ QtScrcpy/device/ui/toolform.cpp | 5 +- QtScrcpy/device/ui/toolform.h | 3 + QtScrcpy/device/ui/videoform.cpp | 2 + QtScrcpy/device/ui/videoform.h | 3 + QtScrcpy/dialog.h | 1 + 9 files changed, 107 insertions(+), 20 deletions(-) diff --git a/QtScrcpy/device/decoder/avframeconvert.cpp b/QtScrcpy/device/decoder/avframeconvert.cpp index 837497f..31930d1 100644 --- a/QtScrcpy/device/decoder/avframeconvert.cpp +++ b/QtScrcpy/device/decoder/avframeconvert.cpp @@ -12,7 +12,7 @@ AVFrameConvert::~AVFrameConvert() } -void AVFrameConvert::setSrcFrameInfo(quint32 srcWidth, quint32 srcHeight, AVPixelFormat srcFormat) +void AVFrameConvert::setSrcFrameInfo(int srcWidth, int srcHeight, AVPixelFormat srcFormat) { m_srcWidth = srcWidth; m_srcHeight = srcHeight; @@ -20,21 +20,21 @@ void AVFrameConvert::setSrcFrameInfo(quint32 srcWidth, quint32 srcHeight, AVPixe qDebug() << "Convert::src frame info " << srcWidth << "x" << srcHeight; } -void AVFrameConvert::getSrcFrameInfo(quint32& srcWidth, quint32& srcHeight, AVPixelFormat& srcFormat) +void AVFrameConvert::getSrcFrameInfo(int& srcWidth, int& srcHeight, AVPixelFormat& srcFormat) { srcWidth = m_srcWidth; srcHeight = m_srcHeight; srcFormat = m_srcFormat; } -void AVFrameConvert::setDstFrameInfo(quint32 dstWidth, quint32 dstHeight, AVPixelFormat dstFormat) +void AVFrameConvert::setDstFrameInfo(int dstWidth, int dstHeight, AVPixelFormat dstFormat) { m_dstWidth = dstWidth; m_dstHeight = dstHeight; m_dstFormat = dstFormat; } -void AVFrameConvert::getDstFrameInfo(quint32& dstWidth, quint32& dstHeight, AVPixelFormat& dstFormat) +void AVFrameConvert::getDstFrameInfo(int& dstWidth, int& dstHeight, AVPixelFormat& dstFormat) { dstWidth = m_dstWidth; dstHeight = m_dstHeight; @@ -67,12 +67,15 @@ void AVFrameConvert::deInit() } } -bool AVFrameConvert::convert(AVFrame* srcFrame, AVFrame* dstFrame) +bool AVFrameConvert::convert(const AVFrame* srcFrame, AVFrame* dstFrame) { if(!m_convertCtx || !srcFrame || !dstFrame) { return false; } - qint32 ret = sws_scale(m_convertCtx, (const uint8_t* const*)srcFrame->data, srcFrame->linesize, 0, m_srcHeight, dstFrame->data, dstFrame->linesize); + qint32 ret = sws_scale(m_convertCtx, + static_cast(srcFrame->data), + srcFrame->linesize, 0, m_srcHeight, dstFrame->data, + dstFrame->linesize); if (0 == ret) { return false; } diff --git a/QtScrcpy/device/decoder/avframeconvert.h b/QtScrcpy/device/decoder/avframeconvert.h index 105221c..d32338e 100644 --- a/QtScrcpy/device/decoder/avframeconvert.h +++ b/QtScrcpy/device/decoder/avframeconvert.h @@ -16,22 +16,22 @@ public: virtual ~AVFrameConvert(); public: - void setSrcFrameInfo(quint32 srcWidth, quint32 srcHeight, AVPixelFormat srcFormat); - void getSrcFrameInfo(quint32& srcWidth, quint32& srcHeight, AVPixelFormat& srcFormat); - void setDstFrameInfo(quint32 dstWidth, quint32 dstHeight, AVPixelFormat dstFormat); - void getDstFrameInfo(quint32& dstWidth, quint32& dstHeight, AVPixelFormat& dstFormat); + void setSrcFrameInfo(int srcWidth, int srcHeight, AVPixelFormat srcFormat); + void getSrcFrameInfo(int& srcWidth, int& srcHeight, AVPixelFormat& srcFormat); + void setDstFrameInfo(int dstWidth, int dstHeight, AVPixelFormat dstFormat); + void getDstFrameInfo(int& dstWidth, int& dstHeight, AVPixelFormat& dstFormat); bool init(); bool isInit(); void deInit(); - bool convert(AVFrame* srcFrame, AVFrame* dstFrame); + bool convert(const AVFrame* srcFrame, AVFrame* dstFrame); private: - quint32 m_srcWidth = 0; - quint32 m_srcHeight = 0; + int m_srcWidth = 0; + int m_srcHeight = 0; AVPixelFormat m_srcFormat = AV_PIX_FMT_NONE; - quint32 m_dstWidth = 0; - quint32 m_dstHeight = 0; + int m_dstWidth = 0; + int m_dstHeight = 0; AVPixelFormat m_dstFormat = AV_PIX_FMT_NONE; struct SwsContext *m_convertCtx = Q_NULLPTR; diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 19548bc..1532438 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "device.h" #include "recorder.h" @@ -11,6 +12,11 @@ #include "videoform.h" #include "controller.h" #include "config.h" +#include "avframeconvert.h" +extern "C" +{ +#include "libavutil/imgutils.h" +} Device::Device(DeviceParams params, QObject *parent) : QObject(parent) @@ -97,10 +103,16 @@ void Device::updateScript(QString script) } } +void Device::onScreenshot() +{ + m_screenshot = true; +} + void Device::initSignals() { if (m_controller && m_videoForm) { connect(m_controller, &Controller::grabCursor, m_videoForm, &VideoForm::onGrabCursor); + connect(m_videoForm, &VideoForm::screenshot, this, &Device::onScreenshot); } if (m_videoForm) { connect(m_videoForm, &VideoForm::destroyed, this, [this](QObject *obj){ @@ -138,7 +150,7 @@ void Device::initSignals() }); connect(m_server, &Server::connectToResult, this, [this](bool success, const QString &deviceName, const QSize &size){ if (success) { - float diff = m_startTimeCount.elapsed() / 1000.0f; + double diff = m_startTimeCount.elapsed() / 1000.0; qInfo(QString("server start finish in %1s").arg(diff).toStdString().c_str()); // update ui @@ -189,6 +201,12 @@ void Device::initSignals() if (m_videoForm) { m_videoForm->updateRender(frame); } + + // screenshot + if (m_screenshot) { + saveFrame(frame); + m_screenshot = false; + } m_vb->unLock(); },Qt::QueuedConnection); } @@ -216,3 +234,56 @@ void Device::startServer() m_server->start(params); }); } + +bool Device::saveFrame(const AVFrame* frame) +{ + if (!frame) { + return false; + } + + // create buffer + QImage rgbImage(frame->width, frame->height, QImage::Format_RGB32); + AVFrame* rgbFrame = av_frame_alloc(); + if (!rgbFrame) { + return false; + } + + // bind buffer to AVFrame + av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, rgbImage.bits(), AV_PIX_FMT_RGB32, frame->width, frame->height, 4); + + // convert + AVFrameConvert convert; + convert.setSrcFrameInfo(frame->width, frame->height, AV_PIX_FMT_YUV420P); + convert.setDstFrameInfo(frame->width, frame->height, AV_PIX_FMT_RGB32); + bool ret = false; + ret = convert.init(); + if (!ret) { + return false; + } + ret = convert.convert(frame, rgbFrame); + if (!ret) { + return false; + } + convert.deInit(); + av_free(rgbFrame); + + // save + QString absFilePath; + QString fileDir(Config::getInstance().getRecordPath()); + if (fileDir.isEmpty()) { + qWarning() << "please select record path!!!"; + return false; + } + QDateTime dateTime = QDateTime::currentDateTime(); + QString fileName = dateTime.toString("_yyyyMMdd_hhmmss_zzz"); + fileName = Config::getInstance().getTitle() + fileName + ".jpg"; + QDir dir(fileDir); + absFilePath = dir.absoluteFilePath(fileName); + ret = rgbImage.save(absFilePath); + if (!ret) { + return false; + } + + qInfo() << "screenshot save to " << absFilePath; + return true; +} diff --git a/QtScrcpy/device/device.h b/QtScrcpy/device/device.h index b7573ee..424ddfd 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -12,6 +12,7 @@ class FileHandler; class Stream; class VideoForm; class Controller; +struct AVFrame; class Device : public QObject { Q_OBJECT @@ -40,9 +41,13 @@ public: signals: void deviceDisconnect(QString serial); +public slots: + void onScreenshot(); + private: void initSignals(); void startServer(); + bool saveFrame(const AVFrame* frame); private: // server relevant @@ -59,6 +64,8 @@ private: QTime m_startTimeCount; DeviceParams m_params; + + bool m_screenshot = false; }; #endif // DEVICE_H diff --git a/QtScrcpy/device/ui/toolform.cpp b/QtScrcpy/device/ui/toolform.cpp index c0aeb07..a95763c 100644 --- a/QtScrcpy/device/ui/toolform.cpp +++ b/QtScrcpy/device/ui/toolform.cpp @@ -20,9 +20,6 @@ ToolForm::ToolForm(QWidget* adsorbWidget, AdsorbPositions adsorbPos) m_videoForm = dynamic_cast(adsorbWidget); initStyle(); - - // TODO - ui->screenShotBtn->hide(); } ToolForm::~ToolForm() @@ -123,7 +120,7 @@ void ToolForm::on_powerBtn_clicked() void ToolForm::on_screenShotBtn_clicked() { - // TODO + emit screenshot(); } void ToolForm::on_volumeUpBtn_clicked() diff --git a/QtScrcpy/device/ui/toolform.h b/QtScrcpy/device/ui/toolform.h index 01e18bf..0f3deca 100644 --- a/QtScrcpy/device/ui/toolform.h +++ b/QtScrcpy/device/ui/toolform.h @@ -27,6 +27,9 @@ protected: void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event); +signals: + void screenshot(); + private slots: void on_fullScreenBtn_clicked(); diff --git a/QtScrcpy/device/ui/videoform.cpp b/QtScrcpy/device/ui/videoform.cpp index 3f6bbc9..ba990b0 100644 --- a/QtScrcpy/device/ui/videoform.cpp +++ b/QtScrcpy/device/ui/videoform.cpp @@ -93,6 +93,8 @@ void VideoForm::showToolForm(bool show) if (!m_toolForm) { m_toolForm = new ToolForm(this, ToolForm::AP_OUTSIDE_RIGHT); m_toolForm->move(pos().x() + geometry().width(), pos().y() + 30); + + connect(m_toolForm, &ToolForm::screenshot, this, &VideoForm::screenshot); } m_toolForm->setVisible(show); } diff --git a/QtScrcpy/device/ui/videoform.h b/QtScrcpy/device/ui/videoform.h index 31229d8..0ffa4be 100644 --- a/QtScrcpy/device/ui/videoform.h +++ b/QtScrcpy/device/ui/videoform.h @@ -29,6 +29,9 @@ public: void setFileHandler(FileHandler *fileHandler); void setSerial(const QString &serial); +signals: + void screenshot(); + public slots: void onGrabCursor(bool grab); diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 31bf7d8..2f036de 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -51,6 +51,7 @@ private slots: void on_stopAllServerBtn_clicked(); void on_refreshGameScriptBtn_clicked(); + void on_applyScriptBtn_clicked(); private: From 447ddb99d60614dd572b722f406ae55d1da14f03 Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 21:14:33 +0800 Subject: [PATCH 12/14] feat: add record screen checkbox --- QtScrcpy/device/device.cpp | 2 +- QtScrcpy/dialog.cpp | 32 +++++++++---- QtScrcpy/dialog.h | 2 + QtScrcpy/dialog.ui | 71 ++++++++++++++++------------- QtScrcpy/res/i18n/QtScrcpy_en.qm | Bin 3280 -> 3286 bytes QtScrcpy/res/i18n/QtScrcpy_en.ts | 76 ++++++++++++++++--------------- QtScrcpy/res/i18n/QtScrcpy_zh.qm | Bin 2587 -> 2585 bytes QtScrcpy/res/i18n/QtScrcpy_zh.ts | 76 ++++++++++++++++--------------- 8 files changed, 146 insertions(+), 113 deletions(-) diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index 1532438..cebc2f6 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -271,7 +271,7 @@ bool Device::saveFrame(const AVFrame* frame) QString absFilePath; QString fileDir(Config::getInstance().getRecordPath()); if (fileDir.isEmpty()) { - qWarning() << "please select record path!!!"; + qWarning() << "please select record save path!!!"; return false; } QDateTime dateTime = QDateTime::currentDateTime(); diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index d947d03..095aa96 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "dialog.h" #include "ui_dialog.h" @@ -136,14 +137,16 @@ void Dialog::on_startServerBtn_clicked() outLog("start server...", false); QString absFilePath; - QString fileDir(ui->recordPathEdt->text().trimmed()); - if (!fileDir.isEmpty()) { - QDateTime dateTime = QDateTime::currentDateTime(); - QString fileName = dateTime.toString("_yyyyMMdd_hhmmss_zzz"); - QString ext = ui->formatBox->currentText().trimmed(); - fileName = windowTitle() + fileName + "." + ext; - QDir dir(fileDir); - absFilePath = dir.absoluteFilePath(fileName); + if (ui->recordScreenCheck->isChecked()) { + QString fileDir(ui->recordPathEdt->text().trimmed()); + if (!fileDir.isEmpty()) { + QDateTime dateTime = QDateTime::currentDateTime(); + QString fileName = dateTime.toString("_yyyyMMdd_hhmmss_zzz"); + QString ext = ui->formatBox->currentText().trimmed(); + fileName = windowTitle() + fileName + "." + ext; + QDir dir(fileDir); + absFilePath = dir.absoluteFilePath(fileName); + } } quint32 bitRate = ui->bitRateBox->currentText().trimmed().toUInt(); @@ -343,3 +346,16 @@ void Dialog::on_applyScriptBtn_clicked() { m_deviceManage.updateScript(getGameScript(ui->gameBox->currentText())); } + +void Dialog::on_recordScreenCheck_clicked(bool checked) +{ + if (!checked) { + return; + } + + QString fileDir(ui->recordPathEdt->text().trimmed()); + if (fileDir.isEmpty()) { + qWarning() << "please select record save path!!!"; + ui->recordScreenCheck->setChecked(false); + } +} diff --git a/QtScrcpy/dialog.h b/QtScrcpy/dialog.h index 2f036de..d04645e 100644 --- a/QtScrcpy/dialog.h +++ b/QtScrcpy/dialog.h @@ -54,6 +54,8 @@ private slots: void on_applyScriptBtn_clicked(); + void on_recordScreenCheck_clicked(bool checked); + private: bool checkAdbRun(); void initUI(); diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index a768349..0d4440a 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -195,7 +195,7 @@ - + 0 @@ -208,7 +208,27 @@ 0 - + + + + record screen + + + + + + + + 0 + 0 + + + + screen-off + + + + @@ -224,36 +244,7 @@ - - - - - 0 - 0 - - - - always on top - - - false - - - - - - - - 0 - 0 - - - - screen-off - - - - + @@ -269,6 +260,22 @@ + + + + + 0 + 0 + + + + always on top + + + false + + + diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.qm b/QtScrcpy/res/i18n/QtScrcpy_en.qm index f8fd86bfdaf2232076c0a3c768c1747e6b7f9213..f73b8033ea1af9c87da3872d716401e3e5f5a63d 100644 GIT binary patch delta 438 zcmca0c};SHOnn*y+c#4N2F_jvcAHQJ2F`5^W}=H27}(A+WRzPnFfc7+$kJ|NU|3=ABn813a87#Ns8GX@-;&cMLfz__br5(5J}FEjU5 zIR*x<3g*LH3m6!MM l7*auE3`GnnlOJ%2^YIp?Cg&HWC=@3brKaX>-o+-y1^^|?Y?uH5 delta 450 zcmca6c|mf5Onm|a+c#4N2F`W{cAHQJ2F?u(W}=H27}!oQWRzPnFfc7+$kJ|NU|^oW zP@a2;fr0ZTLyx920|VnHM$1R%7#KK?FxtyGFfg#_GX@-;&cMLfz__br5(5J}J2UrH zIR*xjKd(q&;z`(%3_GXJI0|V0qb_Z!;1_o9ec6aTk3=E9>*?ac%F)(nk zvajjg!N9=L$^K015d#CeKl^vjI0gnzPmVaF(+ms@H#i-PQW+T7yEzjturM&N^>da+ zd|_Z<=HZfCCBab7z!uEqaBLp~1M5kys%9<*29`qZ1KKwk7&uwEPwaPNU|^Htaf%CJ zU|?>xGXMYp diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.ts b/QtScrcpy/res/i18n/QtScrcpy_en.ts index 10a7b53..77a9daa 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 @@ -49,128 +49,132 @@ 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 + + + stop all server stop all server - + adb command: adb command: - + terminate terminate - + execute execute - + clear clear - + reverse connection reverse connection - auto enable - auto enable + 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: @@ -179,17 +183,17 @@ Config - + bit rate: bit rate: - + start adbd start adbd - + refresh devices refresh devices @@ -280,7 +284,7 @@ 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 b5d1de87d7e8e6b3f3488372ce884bea844c3a7c..2a1e8848f5d1e72c9dd218c3312990f606430e85 100644 GIT binary patch delta 438 zcmbO&GE-!NOnndo+c#4N26j^hcAHQJ2KE>RGtos146J$#8ReD?42=I6vb38R7?|1^ z%5x7fFt9f=^k_OWFff)dT0T0*z`(YQ(O%Ahfq|KoG2rNQ1_nkc#$7Fw7#LV%nYpjZ zF)*8A{kH9(OS?Fh#LV;B#SM zV13DU{8bAB1A`meiv}kK1_lOp2Wepj1{OAUckQPP42+)aJ$w2X7}%Gyuj$>vz`$0{ z{!Hr;0|V=1_V1o?3=Hg?9C1dc85kIra5@;JGBB_nPc#C|sh29`xUPH`a&3@kQ0b>>$X z7}(G8yk2vZfr05hpR{EQ0|T23-|EXE3=AA0{0fiPF)%P+cAHQJ2KG1xGtos146FtW8ReD?42=I6vb38R7?^f2 zl;<8|U|?@%=+Sg$U|=j^w0v}qfq`uWqrIF10|T=;W5Chr3=E7?jJsMUF)*;kGjm^+ zV_;za!+e-)0Rsbv7E7elEd~b0*{tkoS_}+KcUaqB3o$U%GnBAZJnmv(VCrF;z~{oi z!1|i)_^TEM1_n2_7Y$Af3=9lxZ?>2+FfggHJ4g#NFtBj2yK6sXU|{rQ@7dGGz`(we zeNFEU1_riD_Gem;7#LWmvVZrCV_;zC=7=*o&A`C0gww$&m4SivFlXWg76t~E|D2@} zUlw0|SEygDQhzbWf51gU;rejM7X>JdLk{R)px6 mYBWlv>u`WQ&A`CGomg6uuaKITn3R*cc?t6Y#?4b$TUh|AU2Sv# diff --git a/QtScrcpy/res/i18n/QtScrcpy_zh.ts b/QtScrcpy/res/i18n/QtScrcpy_zh.ts index c79f3af..7f9721d 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 失败 @@ -49,128 +49,132 @@ Dialog - + Wireless 无线 - + wireless connect 无线连接 - + wireless disconnect 无线断开 - + Start Config 启动配置 - + record save path: 录像保存路径: - - + + select path 选择路径 - + record format: 录制格式: - + + record screen + 录制屏幕 + + + stop all server 停止所有服务 - + adb command: adb命令: - + terminate 终止 - + execute 执行 - + clear 清理 - + reverse connection 反向连接 - auto enable - 自动启用脚本 + 自动启用脚本 - + 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: 设备序列号: @@ -179,17 +183,17 @@ 配置 - + bit rate: 比特率: - + start adbd 启动adbd - + refresh devices 刷新设备列表 @@ -280,7 +284,7 @@ 文件传输失败 - + file does not exist 文件不存在 From eeeeae97fbe288f013f5b4cda7887b026812493a Mon Sep 17 00:00:00 2001 From: rankun Date: Fri, 31 Jan 2020 21:16:49 +0800 Subject: [PATCH 13/14] docs: update TODO.md --- docs/TODO.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/TODO.md b/docs/TODO.md index 4488032..4fc5d00 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -9,12 +9,11 @@ - ui提供show touch设置 ## 中优先级 -- [截屏保存为jpg](https://blog.csdn.net/m0_37684310/article/details/77950390) - 自动打包脚本 - 脚本 - 群控 - 竖屏全屏不拉伸画面 -- 分辨率码率可自定义 +- 软解 - opengles 3.0 ## 高优先级 From e43b3bc0adf5a3b33831944202d9a17a24dc0274 Mon Sep 17 00:00:00 2001 From: rankun Date: Sat, 1 Feb 2020 10:50:26 +0800 Subject: [PATCH 14/14] feat: add config option to render expired frames Replace the compilation flag SKIP_FRAMES by a config flag to force rendering of expired frames. By default, the expired frames are skipped. --- QtScrcpy/device/decoder/decoder.pri | 3 -- QtScrcpy/device/decoder/videobuffer.cpp | 47 +++++++++++++------------ QtScrcpy/device/decoder/videobuffer.h | 10 +++--- QtScrcpy/device/device.cpp | 2 +- QtScrcpy/device/device.h | 21 +++++------ QtScrcpy/dialog.cpp | 1 + QtScrcpy/util/config.cpp | 12 +++++++ QtScrcpy/util/config.h | 1 + config/config.ini | 2 ++ docs/TODO.md | 1 - 10 files changed, 58 insertions(+), 42 deletions(-) diff --git a/QtScrcpy/device/decoder/decoder.pri b/QtScrcpy/device/decoder/decoder.pri index d5366e7..8618ad0 100644 --- a/QtScrcpy/device/decoder/decoder.pri +++ b/QtScrcpy/device/decoder/decoder.pri @@ -9,6 +9,3 @@ SOURCES += \ $$PWD/fpscounter.cpp \ $$PWD/avframeconvert.cpp \ $$PWD/videobuffer.cpp - -#DEFINES += SKIP_FRAMES - diff --git a/QtScrcpy/device/decoder/videobuffer.cpp b/QtScrcpy/device/decoder/videobuffer.cpp index 23fb0bf..d9c0c05 100644 --- a/QtScrcpy/device/decoder/videobuffer.cpp +++ b/QtScrcpy/device/decoder/videobuffer.cpp @@ -15,8 +15,9 @@ VideoBuffer::~VideoBuffer() } -bool VideoBuffer::init() +bool VideoBuffer::init(bool renderExpiredFrames) { + m_renderExpiredFrames = renderExpiredFrames; m_decodingFrame = av_frame_alloc(); if (!m_decodingFrame) { goto error; @@ -71,17 +72,17 @@ void VideoBuffer::offerDecodedFrame(bool& previousFrameSkipped) { m_mutex.lock(); -#ifndef SKIP_FRAMES - // if SKIP_FRAMES is disabled, then the decoder must wait for the current - // frame to be consumed - while (!m_renderingFrameConsumed && !m_interrupted) { - m_renderingFrameConsumedCond.wait(&m_mutex); - } -#else - if (m_fpsCounter.isStarted() && !m_renderingFrameConsumed) { - m_fpsCounter.addSkippedFrame(); + if (m_renderExpiredFrames) { + // if m_renderExpiredFrames is enable, then the decoder must wait for the current + // frame to be consumed + while (!m_renderingFrameConsumed && !m_interrupted) { + m_renderingFrameConsumedCond.wait(&m_mutex); + } + } else { + if (m_fpsCounter.isStarted() && !m_renderingFrameConsumed) { + m_fpsCounter.addSkippedFrame(); + } } -#endif swap(); previousFrameSkipped = !m_renderingFrameConsumed; @@ -96,23 +97,23 @@ const AVFrame *VideoBuffer::consumeRenderedFrame() if (m_fpsCounter.isStarted()) { m_fpsCounter.addRenderedFrame(); } -#ifndef SKIP_FRAMES - // if SKIP_FRAMES is disabled, then notify the decoder the current frame is - // consumed, so that it may push a new one - m_renderingFrameConsumedCond.wakeOne(); -#endif + if (m_renderExpiredFrames) { + // if m_renderExpiredFrames is enable, then notify the decoder the current frame is + // consumed, so that it may push a new one + m_renderingFrameConsumedCond.wakeOne(); + } return m_renderingframe; } void VideoBuffer::interrupt() { -#ifndef SKIP_FRAMES - m_mutex.lock(); - m_interrupted = true; - m_mutex.unlock(); - // wake up blocking wait - m_renderingFrameConsumedCond.wakeOne(); -#endif + if (m_renderExpiredFrames) { + m_mutex.lock(); + m_interrupted = true; + m_mutex.unlock(); + // wake up blocking wait + m_renderingFrameConsumedCond.wakeOne(); + } } void VideoBuffer::swap() diff --git a/QtScrcpy/device/decoder/videobuffer.h b/QtScrcpy/device/decoder/videobuffer.h index 7138fd1..5cdef2b 100644 --- a/QtScrcpy/device/decoder/videobuffer.h +++ b/QtScrcpy/device/decoder/videobuffer.h @@ -15,7 +15,7 @@ public: VideoBuffer(); virtual ~VideoBuffer(); - bool init(); + bool init(bool renderExpiredFrames = false); void deInit(); void lock(); void unLock(); @@ -45,10 +45,12 @@ private: bool m_renderingFrameConsumed = true; FpsCounter m_fpsCounter; -#ifndef SKIP_FRAMES + bool m_renderExpiredFrames = false; QWaitCondition m_renderingFrameConsumedCond; - bool m_interrupted = true; -#endif + + // interrupted is not used if expired frames are not rendered + // since offering a frame will never block + bool m_interrupted = false; }; #endif // VIDEO_BUFFER_H diff --git a/QtScrcpy/device/device.cpp b/QtScrcpy/device/device.cpp index cebc2f6..1bf9bad 100644 --- a/QtScrcpy/device/device.cpp +++ b/QtScrcpy/device/device.cpp @@ -30,7 +30,7 @@ Device::Device(DeviceParams params, QObject *parent) if (params.display) { m_vb = new VideoBuffer(); - m_vb->init(); + m_vb->init(params.renderExpiredFrames); m_decoder = new Decoder(m_vb, this); m_fileHandler = new FileHandler(this); m_controller = new Controller(params.gameScript, this); diff --git a/QtScrcpy/device/device.h b/QtScrcpy/device/device.h index 424ddfd..d5c7f20 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -18,16 +18,17 @@ class Device : public QObject Q_OBJECT public: struct DeviceParams { - QString recordFileName = ""; // 视频录制文件名 - QString serial = ""; // 设备序列号 - quint16 localPort = 27183; // reverse时本地监听端口 - quint16 maxSize = 720; // 视频分辨率 - quint32 bitRate = 8000000; // 视频比特率 - quint32 maxFps = 60; // 视频最大帧率 - bool closeScreen = false; // 启动时自动息屏 - bool useReverse = true; // true:先使用adb reverse,失败后自动使用adb forward;false:直接使用adb forward - bool display = true; // 是否显示画面(或者仅仅后台录制) - QString gameScript = ""; // 游戏映射脚本 + QString recordFileName = ""; // 视频录制文件名 + QString serial = ""; // 设备序列号 + quint16 localPort = 27183; // reverse时本地监听端口 + quint16 maxSize = 720; // 视频分辨率 + quint32 bitRate = 8000000; // 视频比特率 + quint32 maxFps = 60; // 视频最大帧率 + bool closeScreen = false; // 启动时自动息屏 + bool useReverse = true; // true:先使用adb reverse,失败后自动使用adb forward;false:直接使用adb forward + bool display = true; // 是否显示画面(或者仅仅后台录制) + QString gameScript = ""; // 游戏映射脚本 + bool renderExpiredFrames = false; // 是否渲染延迟视频帧 }; explicit Device(DeviceParams params, QObject *parent = nullptr); virtual ~Device(); diff --git a/QtScrcpy/dialog.cpp b/QtScrcpy/dialog.cpp index 095aa96..d5cf11a 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -162,6 +162,7 @@ void Dialog::on_startServerBtn_clicked() params.closeScreen = ui->closeScreenCheck->isChecked(); params.useReverse = ui->useReverseCheck->isChecked(); params.display = !ui->notDisplayCheck->isChecked(); + params.renderExpiredFrames = Config::getInstance().getRenderExpiredFrames(); m_deviceManage.connectDevice(params); diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index 6009223..e5fb9de 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -30,6 +30,9 @@ #define COMMON_SKIN_KEY "UseSkin" #define COMMON_SKIN_DEF 1 +#define COMMON_RENDER_EXPIRED_FRAMES_KEY "RenderExpiredFrames" +#define COMMON_RENDER_EXPIRED_FRAMES_DEF 0 + QString Config::s_configPath = ""; Config::Config(QObject *parent) : QObject(parent) @@ -108,6 +111,15 @@ int Config::getSkin() return skin; } +int Config::getRenderExpiredFrames() +{ + int renderExpiredFrames = 1; + m_settings->beginGroup(GROUP_COMMON); + renderExpiredFrames = m_settings->value(COMMON_RENDER_EXPIRED_FRAMES_KEY, COMMON_RENDER_EXPIRED_FRAMES_DEF).toInt(); + m_settings->endGroup(); + return renderExpiredFrames; +} + QString Config::getPushFilePath() { QString pushFile; diff --git a/QtScrcpy/util/config.h b/QtScrcpy/util/config.h index f23d3b6..9d8514c 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -17,6 +17,7 @@ public: int getMaxFps(); int getDesktopOpenGL(); int getSkin(); + int getRenderExpiredFrames(); QString getPushFilePath(); QString getServerPath(); diff --git a/config/config.ini b/config/config.ini index ae2f632..d13d8cd 100644 --- a/config/config.ini +++ b/config/config.ini @@ -9,6 +9,8 @@ PushFilePath=/sdcard/ MaxFps=60 # 是否显示手机皮肤,0不显示 UseSkin=1 +# 是否渲染过期视频帧(跳过过期视频帧意味着更低的延迟) +RenderExpiredFrames=0 # 视频解码方式:-1 自动,0 软解,1 dx硬解,2 opengl硬解 UseDesktopOpenGL=-1 # scrcpy-server的版本号(不要修改) diff --git a/docs/TODO.md b/docs/TODO.md index 4fc5d00..d4de436 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -3,7 +3,6 @@ # TODO ## 低优先级 - 中文输入(server需要改为apk,作为一个输入法,暂不实现)(或者有其他方式案件注入方式,例如搜狗手机输入法可以监听当前注入?) -- [跳过帧改为动态配置,而不是静态编译](https://github.com/Genymobile/scrcpy/commit/ebccb9f6cc111e8acfbe10d656cac5c1f1b744a0) - [单独线程统计帧率](https://github.com/Genymobile/scrcpy/commit/e2a272bf99ecf48fcb050177113f903b3fb323c4) - text转换 https://github.com/Genymobile/scrcpy/commit/c916af0984f72a60301d13fa8ef9a85112f54202?tdsourcetag=s_pctim_aiomsg - ui提供show touch设置