You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

545 lines
16 KiB

#include <QDebug>
#include <QCursor>
#include <QGuiApplication>
#include <QTimer>
#include "inputconvertgame.h"
#define CURSOR_POS_CHECK 50
InputConvertGame::InputConvertGame(Controller *controller) : InputConvertNormal(controller) {}
InputConvertGame::~InputConvertGame() {}
void InputConvertGame::mouseEvent(const QMouseEvent *from, const QSize &frameSize, const QSize &showSize)
{
// 处理开关按键
if (m_keyMap.isSwitchOnKeyboard() == false && m_keyMap.getSwitchKey() == static_cast<int>(from->button())) {
if (from->type() != QEvent::MouseButtonPress) {
return;
}
if (!switchGameMap()) {
m_needBackMouseMove = false;
}
return;
}
if (!m_needBackMouseMove && m_gameMap) {
updateSize(frameSize, showSize);
// mouse move
if (m_keyMap.isValidMouseMoveMap()) {
if (processMouseMove(from)) {
return;
}
}
// mouse click
if (processMouseClick(from)) {
return;
}
}
InputConvertNormal::mouseEvent(from, frameSize, showSize);
}
void InputConvertGame::wheelEvent(const QWheelEvent *from, const QSize &frameSize, const QSize &showSize)
{
if (m_gameMap) {
updateSize(frameSize, showSize);
} else {
InputConvertNormal::wheelEvent(from, frameSize, showSize);
}
}
void InputConvertGame::keyEvent(const QKeyEvent *from, const QSize &frameSize, const QSize &showSize)
{
// 处理开关按键
if (m_keyMap.isSwitchOnKeyboard() && m_keyMap.getSwitchKey() == from->key()) {
if (QEvent::KeyPress != from->type()) {
return;
}
if (!switchGameMap()) {
m_needBackMouseMove = false;
}
return;
}
const KeyMap::KeyMapNode &node = m_keyMap.getKeyMapNodeKey(from->key());
// 处理特殊按键:可以释放出鼠标的按键
if (m_needBackMouseMove && KeyMap::KMT_CLICK == node.type && node.data.click.switchMap) {
updateSize(frameSize, showSize);
// Qt::Key_Tab Qt::Key_M for PUBG mobile
processKeyClick(node.data.click.keyNode.pos, false, node.data.click.switchMap, from);
return;
}
if (m_gameMap) {
updateSize(frameSize, showSize);
if (!from || from->isAutoRepeat()) {
return;
}
// small eyes
if (m_keyMap.isValidMouseMoveMap() && from->key() == m_keyMap.getMouseMoveMap().data.mouseMove.smallEyes.key) {
m_ctrlMouseMove.smallEyes = (QEvent::KeyPress == from->type());
if (QEvent::KeyPress == from->type()) {
m_processMouseMove = false;
int delay = 30;
QTimer::singleShot(delay, this, [this]() { mouseMoveStopTouch(); });
QTimer::singleShot(delay * 2, this, [this]() {
mouseMoveStartTouch(nullptr);
m_processMouseMove = true;
});
stopMouseMoveTimer();
} else {
mouseMoveStopTouch();
mouseMoveStartTouch(nullptr);
}
return;
}
switch (node.type) {
// 处理方向盘
case KeyMap::KMT_STEER_WHEEL:
processSteerWheel(node, from);
return;
// 处理普通按键
case KeyMap::KMT_CLICK:
processKeyClick(node.data.click.keyNode.pos, false, node.data.click.switchMap, from);
return;
case KeyMap::KMT_CLICK_TWICE:
processKeyClick(node.data.clickTwice.keyNode.pos, true, false, from);
return;
case KeyMap::KMT_CLICK_MULTI:
processKeyClickMulti(node.data.clickMulti.keyNode.delayClickNodes, node.data.clickMulti.keyNode.delayClickNodesCount, from);
return;
case KeyMap::KMT_DRAG:
processKeyDrag(node.data.drag.keyNode.pos, node.data.drag.keyNode.extendPos, from);
return;
default:
break;
}
} else {
InputConvertNormal::keyEvent(from, frameSize, showSize);
}
}
bool InputConvertGame::isCurrentCustomKeymap()
{
return m_gameMap;
}
void InputConvertGame::loadKeyMap(const QString &json)
{
m_keyMap.loadKeyMap(json);
}
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;
}
void InputConvertGame::sendTouchDownEvent(int id, QPointF pos)
{
sendTouchEvent(id, pos, AMOTION_EVENT_ACTION_DOWN);
}
void InputConvertGame::sendTouchMoveEvent(int id, QPointF pos)
{
sendTouchEvent(id, pos, AMOTION_EVENT_ACTION_MOVE);
}
void InputConvertGame::sendTouchUpEvent(int id, QPointF pos)
{
sendTouchEvent(id, pos, AMOTION_EVENT_ACTION_UP);
}
void InputConvertGame::sendTouchEvent(int id, QPointF pos, AndroidMotioneventAction action)
{
if (0 > id || MULTI_TOUCH_MAX_NUM - 1 < id) {
Q_ASSERT(0);
return;
}
//qDebug() << "id:" << id << " pos:" << pos << " action" << action;
ControlMsg *controlMsg = new ControlMsg(ControlMsg::CMT_INJECT_TOUCH);
if (!controlMsg) {
return;
}
QPoint absolutePos = calcFrameAbsolutePos(pos).toPoint();
static QPoint lastAbsolutePos = absolutePos;
if (AMOTION_EVENT_ACTION_MOVE == action && lastAbsolutePos == absolutePos) {
delete controlMsg;
return;
}
lastAbsolutePos = absolutePos;
controlMsg->setInjectTouchMsgData(static_cast<quint64>(id), action,
static_cast<AndroidMotioneventButtons>(0),
QRect(absolutePos, m_frameSize),
AMOTION_EVENT_ACTION_DOWN == action? 1.0f : 0.0f);
sendControlMsg(controlMsg);
}
QPointF InputConvertGame::calcFrameAbsolutePos(QPointF relativePos)
{
QPointF absolutePos;
absolutePos.setX(m_frameSize.width() * relativePos.x());
absolutePos.setY(m_frameSize.height() * relativePos.y());
return absolutePos;
}
QPointF InputConvertGame::calcScreenAbsolutePos(QPointF relativePos)
{
QPointF absolutePos;
absolutePos.setX(m_showSize.width() * relativePos.x());
absolutePos.setY(m_showSize.height() * relativePos.y());
return absolutePos;
}
int InputConvertGame::attachTouchID(int key)
{
for (int i = 0; i < MULTI_TOUCH_MAX_NUM; i++) {
if (0 == m_multiTouchID[i]) {
m_multiTouchID[i] = key;
return i;
}
}
return -1;
}
void InputConvertGame::detachTouchID(int key)
{
for (int i = 0; i < MULTI_TOUCH_MAX_NUM; i++) {
if (key == m_multiTouchID[i]) {
m_multiTouchID[i] = 0;
return;
}
}
}
int InputConvertGame::getTouchID(int key)
{
for (int i = 0; i < MULTI_TOUCH_MAX_NUM; i++) {
if (key == m_multiTouchID[i]) {
return i;
}
}
return -1;
}
// -------- steer wheel event --------
void InputConvertGame::processSteerWheel(const KeyMap::KeyMapNode &node, const QKeyEvent *from)
{
int key = from->key();
bool flag = from->type() == QEvent::KeyPress;
// identify keys
if (key == node.data.steerWheel.up.key) {
m_ctrlSteerWheel.pressedUp = flag;
} else if (key == node.data.steerWheel.right.key) {
m_ctrlSteerWheel.pressedRight = flag;
} else if (key == node.data.steerWheel.down.key) {
m_ctrlSteerWheel.pressedDown = flag;
} else { // left
m_ctrlSteerWheel.pressedLeft = flag;
}
// calc offset and pressed number
QPointF offset(0.0, 0.0);
int pressedNum = 0;
if (m_ctrlSteerWheel.pressedUp) {
++pressedNum;
offset.ry() -= node.data.steerWheel.up.extendOffset;
}
if (m_ctrlSteerWheel.pressedRight) {
++pressedNum;
offset.rx() += node.data.steerWheel.right.extendOffset;
}
if (m_ctrlSteerWheel.pressedDown) {
++pressedNum;
offset.ry() += node.data.steerWheel.down.extendOffset;
}
if (m_ctrlSteerWheel.pressedLeft) {
++pressedNum;
offset.rx() -= node.data.steerWheel.left.extendOffset;
}
// action
if (pressedNum == 0) {
// touch up release all
int id = getTouchID(m_ctrlSteerWheel.touchKey);
sendTouchUpEvent(id, node.data.steerWheel.centerPos + m_ctrlSteerWheel.lastOffset);
detachTouchID(m_ctrlSteerWheel.touchKey);
} else {
int id;
// first press, get key and touch down
if (pressedNum == 1 && flag) {
m_ctrlSteerWheel.touchKey = from->key();
id = attachTouchID(m_ctrlSteerWheel.touchKey);
sendTouchDownEvent(id, node.data.steerWheel.centerPos);
} else {
// jsut get touch id and move
id = getTouchID(m_ctrlSteerWheel.touchKey);
}
sendTouchMoveEvent(id, node.data.steerWheel.centerPos + offset);
}
m_ctrlSteerWheel.lastOffset = offset;
return;
}
// -------- key event --------
void InputConvertGame::processKeyClick(const QPointF &clickPos, bool clickTwice, bool switchMap, const QKeyEvent *from)
{
if (switchMap && QEvent::KeyRelease == from->type()) {
m_needBackMouseMove = !m_needBackMouseMove;
hideMouseCursor(!m_needBackMouseMove);
}
if (QEvent::KeyPress == from->type()) {
int id = attachTouchID(from->key());
sendTouchDownEvent(id, clickPos);
if (clickTwice) {
sendTouchUpEvent(getTouchID(from->key()), clickPos);
detachTouchID(from->key());
}
} else if (QEvent::KeyRelease == from->type()) {
if (clickTwice) {
int id = attachTouchID(from->key());
sendTouchDownEvent(id, clickPos);
}
sendTouchUpEvent(getTouchID(from->key()), clickPos);
detachTouchID(from->key());
}
}
void InputConvertGame::processKeyClickMulti(const KeyMap::DelayClickNode *nodes, const int count, const QKeyEvent *from)
{
if (QEvent::KeyPress != from->type()) {
return;
}
int key = from->key();
int delay = 0;
QPointF clickPos;
for (int i = 0; i < count; i++) {
delay += nodes[i].delay;
clickPos = nodes[i].pos;
QTimer::singleShot(delay, this, [this, key, clickPos]() {
int id = attachTouchID(key);
sendTouchDownEvent(id, clickPos);
});
// Don't up it too fast
delay += 20;
QTimer::singleShot(delay, this, [this, key, clickPos]() {
int id = getTouchID(key);
sendTouchUpEvent(id, clickPos);
detachTouchID(key);
});
}
}
void InputConvertGame::processKeyDrag(const QPointF &startPos, QPointF endPos, const QKeyEvent *from)
{
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());
}
}
// -------- mouse event --------
bool InputConvertGame::processMouseClick(const QMouseEvent *from)
{
const KeyMap::KeyMapNode &node = m_keyMap.getKeyMapNodeMouse(from->button());
if (KeyMap::KMT_INVALID == node.type) {
return false;
}
if (QEvent::MouseButtonPress == from->type() || QEvent::MouseButtonDblClick == from->type()) {
int id = attachTouchID(from->button());
sendTouchDownEvent(id, node.data.click.keyNode.pos);
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)
{
if (QEvent::MouseMove != from->type()) {
return false;
}
if (checkCursorPos(from)) {
m_ctrlMouseMove.lastPos = QPointF(0.0, 0.0);
return true;
}
if (!m_ctrlMouseMove.lastPos.isNull() && m_processMouseMove) {
QPointF distance_raw{from->localPos() - m_ctrlMouseMove.lastPos};
QPointF speedRatio {m_keyMap.getMouseMoveMap().data.mouseMove.speedRatio};
QPointF distance {distance_raw.x() / speedRatio.x(), distance_raw.y() / speedRatio.y()};
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.05 || m_ctrlMouseMove.lastConverPos.x() > 0.95 || m_ctrlMouseMove.lastConverPos.y() < 0.05
|| m_ctrlMouseMove.lastConverPos.y() > 0.95) {
if (m_ctrlMouseMove.smallEyes) {
m_processMouseMove = false;
int delay = 30;
QTimer::singleShot(delay, this, [this]() { mouseMoveStopTouch(); });
QTimer::singleShot(delay * 2, this, [this]() {
mouseMoveStartTouch(nullptr);
m_processMouseMove = true;
});
} else {
mouseMoveStopTouch();
mouseMoveStartTouch(from);
}
}
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;
QPoint globalPos = from->globalPos();
globalPos -= posOffset;
//qDebug()<<"move cursor to "<<globalPos<<" offset "<<posOffset;
QCursor::setPos(globalPos);
}
void InputConvertGame::mouseMoveStartTouch(const QMouseEvent *from)
{
Q_UNUSED(from)
if (!m_ctrlMouseMove.touching) {
QPointF mouseMoveStartPos
= m_ctrlMouseMove.smallEyes ? m_keyMap.getMouseMoveMap().data.mouseMove.smallEyes.pos : 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()
{
if (m_ctrlMouseMove.touching) {
sendTouchUpEvent(getTouchID(Qt::ExtraButton24), m_ctrlMouseMove.lastConverPos);
detachTouchID(Qt::ExtraButton24);
m_ctrlMouseMove.touching = false;
}
}
void InputConvertGame::startMouseMoveTimer()
{
stopMouseMoveTimer();
m_ctrlMouseMove.timer = startTimer(500);
}
void InputConvertGame::stopMouseMoveTimer()
{
if (0 != m_ctrlMouseMove.timer) {
killTimer(m_ctrlMouseMove.timer);
m_ctrlMouseMove.timer = 0;
}
}
bool InputConvertGame::switchGameMap()
{
m_gameMap = !m_gameMap;
qInfo() << tr("current keymap mode: %1").arg(m_gameMap ? tr("custom") : tr("normal"));
if (!m_keyMap.isValidMouseMoveMap()) {
return m_gameMap;
}
// grab cursor and set cursor only mouse move map
emit grabCursor(m_gameMap);
hideMouseCursor(m_gameMap);
if (!m_gameMap) {
stopMouseMoveTimer();
mouseMoveStopTouch();
}
return m_gameMap;
}
void InputConvertGame::hideMouseCursor(bool hide)
{
if (hide) {
#ifdef QT_NO_DEBUG
QGuiApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
#else
QGuiApplication::setOverrideCursor(QCursor(Qt::CrossCursor));
#endif
} else {
QGuiApplication::restoreOverrideCursor();
}
}
void InputConvertGame::timerEvent(QTimerEvent *event)
{
if (m_ctrlMouseMove.timer == event->timerId()) {
stopMouseMoveTimer();
mouseMoveStopTouch();
}
}