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/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 c550e51..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) @@ -73,10 +72,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 +92,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; @@ -112,25 +111,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().startPos; - m_ctrlMouseMove.startPosPixel = calcFrameAbsolutePos(m_ctrlMouseMove.startPosRel); - } - if(m_keyMap.isValidSteerWheelMap()){ - m_ctrlSteerWheel.valid = true; - m_ctrlMouseMove.touching = false; - } } void InputConvertGame::updateSize(const QSize &frameSize, const QSize &showSize) { + if (showSize != m_showSize) { + if (m_gameMap && m_keyMap.isValidMouseMoveMap()) { + // 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) @@ -159,7 +151,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); } @@ -181,25 +177,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 "<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 + } 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; - offset.ry() -= node.steerWheel.up.offset; + int pressedNum = 0; + if (m_ctrlSteerWheel.pressedUp) { + ++pressedNum; + offset.ry() -= node.data.steerWheel.up.extendOffset; } - if(m_ctrlSteerWheel.pressedRight){ - ++nPressed; - offset.rx() += node.steerWheel.right.offset; + if (m_ctrlSteerWheel.pressedRight) { + ++pressedNum; + offset.rx() += node.data.steerWheel.right.extendOffset; } - if(m_ctrlSteerWheel.pressedDown){ - ++nPressed; - offset.ry() += node.steerWheel.down.offset; + if (m_ctrlSteerWheel.pressedDown) { + ++pressedNum; + offset.ry() += node.data.steerWheel.down.extendOffset; } - if(m_ctrlSteerWheel.pressedLeft){ - ++nPressed; - offset.rx() -= node.steerWheel.left.offset; + 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.steerWheel.centerPos); - }else{ + sendTouchDownEvent(id, node.data.steerWheel.centerPos); + } else { + // jsut get touch id and move id = getTouchID(m_ctrlSteerWheel.touchKey); } - sendTouchMoveEvent(id, node.steerWheel.centerPos + offset); + sendTouchMoveEvent(id, node.data.steerWheel.centerPos + offset); } m_ctrlSteerWheel.lastOffset = offset; return; @@ -299,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()); } @@ -317,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.click.keyNode.pos); - } else if (QEvent::MouseButtonRelease == from->type()) { - sendTouchUpEvent(getTouchID(from->button()), node.click.keyNode.pos); - detachTouchID(from->button()); - } else { - return false; + sendTouchDownEvent(id, node.data.click.keyNode.pos); + 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) @@ -334,30 +337,61 @@ bool InputConvertGame::processMouseMove(const QMouseEvent *from) if (QEvent::MouseMove != from->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().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; @@ -369,53 +403,56 @@ 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; } } 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 - 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(); QGuiApplication::restoreOverrideCursor(); } return m_gameMap; @@ -423,7 +460,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 acf585b..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,37 +62,29 @@ 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{ - 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; // 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/controller/inputconvert/keymap/keymap.cpp b/QtScrcpy/device/controller/inputconvert/keymap/keymap.cpp index ce1dd84..184c4d1 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,45 +118,45 @@ 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; case KeyMap::KMT_CLICK_TWICE: { // safe check - if (!checkForClickDouble(node)) { + if (!checkForClickTwice(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.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,34 +354,70 @@ 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 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) { - 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 +425,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..f21f814 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 checkForClickTwice(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 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/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 7b9e58a..1bf9bad 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) @@ -24,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); @@ -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){ @@ -120,7 +132,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); @@ -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 save 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..d5c7f20 100644 --- a/QtScrcpy/device/device.h +++ b/QtScrcpy/device/device.h @@ -12,21 +12,23 @@ class FileHandler; class Stream; class VideoForm; class Controller; +struct AVFrame; 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(); @@ -40,9 +42,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 +65,8 @@ private: QTime m_startTimeCount; DeviceParams m_params; + + bool m_screenshot = false; }; #endif // DEVICE_H 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/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/device/ui/toolform.cpp b/QtScrcpy/device/ui/toolform.cpp index 2b38cc3..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() @@ -69,11 +66,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"; } @@ -121,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 78be624..ba990b0 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" @@ -92,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); } @@ -349,7 +352,6 @@ void VideoForm::keyPressEvent(QKeyEvent *event) return; } - //qDebug() << "keyPressEvent" << event->isAutoRepeat(); m_controller->keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size()); } @@ -358,7 +360,6 @@ void VideoForm::keyReleaseEvent(QKeyEvent *event) if (!m_controller) { return; } - //qDebug() << "keyReleaseEvent" << event->isAutoRepeat(); m_controller->keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size()); } @@ -412,5 +413,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() + fileInfo.fileName()); } 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/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..d5cf11a 100644 --- a/QtScrcpy/dialog.cpp +++ b/QtScrcpy/dialog.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "dialog.h" #include "ui_dialog.h" @@ -46,14 +47,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; } @@ -91,11 +96,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()); } @@ -137,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(); @@ -155,18 +157,13 @@ 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()); - } - } + params.renderExpiredFrames = Config::getInstance().getRenderExpiredFrames(); + m_deviceManage.connectDevice(params); /* @@ -232,6 +229,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()) { @@ -340,9 +348,15 @@ void Dialog::on_applyScriptBtn_clicked() m_deviceManage.updateScript(getGameScript(ui->gameBox->currentText())); } -void Dialog::on_gameCheck_clicked(bool checked) +void Dialog::on_recordScreenCheck_clicked(bool checked) { - if (checked) { - on_refreshGameScriptBtn_clicked(); + 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 7f58fa8..d04645e 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(); @@ -50,8 +51,10 @@ private slots: void on_stopAllServerBtn_clicked(); void on_refreshGameScriptBtn_clicked(); + void on_applyScriptBtn_clicked(); - void on_gameCheck_clicked(bool checked); + + void on_recordScreenCheck_clicked(bool checked); private: bool checkAdbRun(); diff --git a/QtScrcpy/dialog.ui b/QtScrcpy/dialog.ui index 514e54e..0d4440a 100644 --- a/QtScrcpy/dialog.ui +++ b/QtScrcpy/dialog.ui @@ -6,119 +6,431 @@ 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 + + + + + record screen + + + + + + + + 0 + 0 + + + + screen-off + + + + + + + + 0 + 0 + + + + background record + + + false + + + + + + + + 0 + 0 + + + + reverse connection + + + true + + + + + + + + 0 + 0 + + + + always on top + + + false + + + + - + + + + 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 +443,7 @@ - + @@ -150,8 +462,20 @@ - + + + + 0 + 0 + + + + + 40 + 16777215 + + @@ -163,10 +487,26 @@ - + + + + + 0 + 0 + + + + wireless connect + + + false + + + + - + 0 0 @@ -182,7 +522,87 @@ - + + + + adb + + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + + + + adb command: + + + adbCommandEdt + + + + + + + devices + + + + + + + + 0 + 0 + + + + execute + + + + + + + + 0 + 0 + + + + terminate + + + + + + + + 0 + 0 + + + + clear + + + + + + + @@ -201,232 +621,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 - - - - - - - auto enable - - - - - - - - 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 63d9ad1..2ccb6a6 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(); @@ -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); } } diff --git a/QtScrcpy/res/i18n/QtScrcpy_en.qm b/QtScrcpy/res/i18n/QtScrcpy_en.qm index f8fd86b..f73b803 100644 Binary files a/QtScrcpy/res/i18n/QtScrcpy_en.qm and b/QtScrcpy/res/i18n/QtScrcpy_en.qm differ 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 b5d1de8..2a1e884 100644 Binary files a/QtScrcpy/res/i18n/QtScrcpy_zh.qm and b/QtScrcpy/res/i18n/QtScrcpy_zh.qm differ 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 文件不存在 diff --git a/QtScrcpy/util/config.cpp b/QtScrcpy/util/config.cpp index aa7fc8a..e5fb9de 100644 --- a/QtScrcpy/util/config.cpp +++ b/QtScrcpy/util/config.cpp @@ -12,9 +12,15 @@ #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" +#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 @@ -24,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) @@ -102,6 +111,33 @@ 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; + m_settings->beginGroup(GROUP_COMMON); + pushFile = m_settings->value(COMMON_PUSHFILE_KEY, COMMON_PUSHFILE_DEF).toString(); + m_settings->endGroup(); + 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 8279a9b..9d8514c 100644 --- a/QtScrcpy/util/config.h +++ b/QtScrcpy/util/config.h @@ -17,6 +17,9 @@ public: int getMaxFps(); int getDesktopOpenGL(); int getSkin(); + int getRenderExpiredFrames(); + QString getPushFilePath(); + QString getServerPath(); private: explicit Config(QObject *parent = nullptr); diff --git a/README.md b/README.md index 9a00f29..0f6b2cd 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) @@ -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 2216598..4afff4c 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) @@ -42,8 +42,9 @@ QtScrcpy可以通过USB(或通过TCP/IP)连接Android设备,并进行显示和 自定义按键映射操作方法如下: - 编写自定义脚本放入keymap目录 - 点击刷新脚本,确保脚本可以被检测到 +- 选择需要的脚本 - 连接手机并启动服务之后,点击应用脚本 -- 进入游戏场景,按~键(数字键1左边)切换为游戏映射模式即可体验(具体按什么键要看你按键脚本定义的switchKey) +- 按~键(数字键1左边)切换为自定义映射模式即可体验(具体按什么键要看你按键脚本定义的switchKey) - 再次按~键切换为正常控制模式 - 要想wasd控制开车记得在载具设置中设置为单摇杆模式 diff --git a/config/config.ini b/config/config.ini index b2dbe42..d13d8cd 100644 --- a/config/config.ini +++ b/config/config.ini @@ -1,13 +1,19 @@ [common] # 窗口标题 WindowTitle=QtScrcpy -# 录制文件路径 +# 录制文件保存路径(必须以/作为分隔符) RecordPath= +# 推送到安卓设备的文件保存路径(必须以/结尾) +PushFilePath=/sdcard/ # 最大fps(仅支持Android 10以上) MaxFps=60 -# scrcpy-server的版本号(不要修改) -ServerVersion=1.12.1 # 是否显示手机皮肤,0不显示 UseSkin=1 +# 是否渲染过期视频帧(跳过过期视频帧意味着更低的延迟) +RenderExpiredFrames=0 # 视频解码方式:-1 自动,0 软解,1 dx硬解,2 opengl硬解 UseDesktopOpenGL=-1 +# scrcpy-server的版本号(不要修改) +ServerVersion=1.12.1 +# scrcpy-server推送到安卓设备的路径 +ServerPath=/data/local/tmp/scrcpy-server.jar diff --git a/docs/TODO.md b/docs/TODO.md index 4488032..d4de436 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -3,18 +3,16 @@ # 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设置 ## 中优先级 -- [截屏保存为jpg](https://blog.csdn.net/m0_37684310/article/details/77950390) - 自动打包脚本 - 脚本 - 群控 - 竖屏全屏不拉伸画面 -- 分辨率码率可自定义 +- 软解 - opengles 3.0 ## 高优先级 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