147 lines
5.0 KiB
C++
147 lines
5.0 KiB
C++
#include "VideoWindow.h"
|
||
#include <QVBoxLayout>
|
||
|
||
enum : uint8_t {
|
||
CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT = 2,
|
||
ACTION_DOWN = 0,
|
||
ACTION_UP = 1,
|
||
ACTION_MOVE = 2,
|
||
};
|
||
|
||
// Вспомогательная запись BE-целых
|
||
static void write32BE(QByteArray &buf, uint32_t v) {
|
||
buf.append(char((v >> 24) & 0xFF));
|
||
buf.append(char((v >> 16) & 0xFF));
|
||
buf.append(char((v >> 8) & 0xFF));
|
||
buf.append(char((v) & 0xFF));
|
||
}
|
||
|
||
static void write16BE(QByteArray &buf, uint16_t v) {
|
||
buf.append(char((v >> 8) & 0xFF));
|
||
buf.append(char((v) & 0xFF));
|
||
}
|
||
|
||
static constexpr uint8_t SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT = 2;
|
||
static constexpr uint32_t AMOTION_EVENT_BUTTON_PRIMARY = 1;
|
||
|
||
void VideoWindow::sendTouch(uint8_t action, const QPoint &pos) {
|
||
if (m_controlSocket->state() != QAbstractSocket::ConnectedState) {
|
||
return;
|
||
}
|
||
|
||
// 1) Подготовка буфера нужного размера
|
||
QByteArray ba;
|
||
ba.reserve(32);
|
||
|
||
// 2) Хелперы для записи
|
||
auto write8 = [&](uint8_t v) { ba.append(char(v)); };
|
||
auto write16 = [&](uint16_t v) {
|
||
ba.append(char((v >> 8) & 0xFF));
|
||
ba.append(char(v & 0xFF));
|
||
};
|
||
auto write32 = [&](uint32_t v) {
|
||
ba.append(char((v >> 24) & 0xFF));
|
||
ba.append(char((v >> 16) & 0xFF));
|
||
ba.append(char((v >> 8) & 0xFF));
|
||
ba.append(char(v & 0xFF));
|
||
};
|
||
auto write64 = [&](quint64 v) {
|
||
for (int shift = 56; shift >= 0; shift -= 8) {
|
||
ba.append(char((v >> shift) & 0xFF));
|
||
}
|
||
};
|
||
|
||
// 3) Нормализация координат от QLabel → реальные
|
||
QSize lbl = m_videoLabel->size();
|
||
quint32 x = quint32(double(pos.x()) * m_originalWidth / lbl.width());
|
||
quint32 y = quint32(double(pos.y()) * m_originalHeight / lbl.height());
|
||
|
||
// 4) Сборка пакета по спецификации (32 байта):
|
||
write8(SC_CONTROL_MSG_TYPE_INJECT_TOUCH_EVENT); // 1 байт: тип сообщения
|
||
write8(action); // 1 байт: action (DOWN=0, UP=1, MOVE=2)
|
||
write64(0); // 8 байт: pointerId = 0
|
||
write32(x); // 4 байта: position.x
|
||
write32(y); // 4 байта: position.y
|
||
write16(uint16_t(m_originalWidth)); // 2 байта: screenWidth
|
||
write16(uint16_t(m_originalHeight)); // 2 байта: screenHeight
|
||
|
||
// pressure: float [0;1] → int16 [0;0x7FFF]
|
||
{
|
||
int16_t p16 = int16_t(1.0f * 0x7FFF);
|
||
write16(uint16_t(p16)); // 2 байта: pressure
|
||
}
|
||
|
||
write32(AMOTION_EVENT_BUTTON_PRIMARY); // 4 байта: action_button
|
||
write32(AMOTION_EVENT_BUTTON_PRIMARY); // 4 байта: buttons
|
||
|
||
// 5) Проверка длины и отправка
|
||
const int len = ba.size();
|
||
qDebug() << "Packed touch event size =" << len;
|
||
Q_ASSERT(len == 32);
|
||
if (len != 32) {
|
||
qWarning() << "Wrong packet length, aborting send.";
|
||
return;
|
||
}
|
||
|
||
m_controlSocket->write(ba);
|
||
m_controlSocket->flush();
|
||
}
|
||
|
||
|
||
void VideoWindow::mousePressEvent(QMouseEvent *ev) {
|
||
sendTouch(ACTION_DOWN, ev->pos());
|
||
}
|
||
|
||
void VideoWindow::mouseMoveEvent(QMouseEvent *ev) {
|
||
sendTouch(ACTION_MOVE, ev->pos());
|
||
}
|
||
|
||
void VideoWindow::mouseReleaseEvent(QMouseEvent *ev) {
|
||
sendTouch(ACTION_UP, ev->pos());
|
||
}
|
||
|
||
VideoWindow::VideoWindow(QWidget *parent): QMainWindow(parent) {
|
||
setFixedSize(360, 800);
|
||
|
||
|
||
QWidget *central = new QWidget(this);
|
||
setCentralWidget(central);
|
||
QVBoxLayout *layout = new QVBoxLayout(central);
|
||
m_videoLabel = new QLabel(this);
|
||
m_videoLabel->setAlignment(Qt::AlignCenter);
|
||
m_videoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||
m_videoLabel->setScaledContents(true);
|
||
layout->addWidget(m_videoLabel);
|
||
layout->setContentsMargins(0, 0, 0, 0);
|
||
|
||
m_receiver = new VideoReceiver(this);
|
||
connect(m_receiver, &VideoReceiver::frameReady, this, [&](const QImage &img) {
|
||
if (m_originalWidth == 0) {
|
||
m_originalWidth = img.width();
|
||
m_originalHeight = img.height();
|
||
}
|
||
|
||
QPixmap scaled = QPixmap::fromImage(img).scaled(m_videoLabel->size(), Qt::KeepAspectRatio,
|
||
Qt::SmoothTransformation);
|
||
m_videoLabel->setPixmap(scaled);
|
||
});
|
||
m_receiver->start();
|
||
|
||
// В конструкторе VideoWindow:
|
||
m_controlSocket = new QTcpSocket(this);
|
||
m_controlSocket->connectToHost("127.0.0.1", 27184);
|
||
if (!m_controlSocket->waitForConnected(3000)) {
|
||
qWarning() << "Cannot connect to control socket";
|
||
} else {
|
||
qDebug() << "Connected to control socket";
|
||
}
|
||
|
||
connect(m_controlSocket, &QTcpSocket::readyRead, this, [this]() {
|
||
m_controlSocket->readAll(); // важно «чистить» буфер
|
||
});
|
||
}
|
||
|
||
VideoWindow::~VideoWindow() {
|
||
m_receiver->wait();
|
||
}
|