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.

428 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <QDesktopWidget>
#include <QMouseEvent>
#include <QTimer>
#include <QStyle>
#include <QStyleOption>
#include <QPainter>
#ifdef Q_OS_WIN32
#include <Windows.h>
#endif
#include <QQuickWidget>
#include <QMimeData>
#include <QFileInfo>
#include <QMessageBox>
#include "videoform.h"
#include "ui_videoform.h"
#include "iconhelper.h"
#include "toolform.h"
#include "controlevent.h"
VideoForm::VideoForm(const QString& serial, quint16 maxSize, quint32 bitRate,QWidget *parent) :
QWidget(parent),
ui(new Ui::videoForm),
m_serial(serial),
m_maxSize(maxSize),
m_bitRate(bitRate)
{
ui->setupUi(this);
initUI();
m_server = new Server();
m_frames.init();
m_decoder.setFrames(&m_frames);
initSignals();
// fix: macos cant recv finished signel, timer is ok
QTimer::singleShot(0, this, [this](){
// max size support 480p 720p 1080p 设备原生分辨率
// support wireless connect, example:
//m_server->start("192.168.0.174:5555", 27183, m_maxSize, m_bitRate, "");
// only one devices, serial can be null
// mark: crop input format: "width:height:x:y" or - for no crop, for example: "100:200:0:0"
m_server->start(m_serial, 27183, m_maxSize, m_bitRate, "-");
});
updateShowSize(size());
bool vertical = size().height() > size().width();
updateStyleSheet(vertical);
}
VideoForm::~VideoForm()
{
m_server->stop();
m_decoder.stopDecode();
delete m_server;
m_frames.deInit();
delete ui;
}
void VideoForm::initUI()
{
QPixmap phone;
if (phone.load(":/res/phone.png")) {
m_widthHeightRatio = 1.0f * phone.width() / phone.height();
}
setAttribute(Qt::WA_DeleteOnClose);
// 去掉标题栏
setWindowFlags(Qt::FramelessWindowHint);
// 根据图片构造异形窗口
setAttribute(Qt::WA_TranslucentBackground);
setMouseTracking(true);
ui->loadingWidget->setAttribute(Qt::WA_DeleteOnClose);
ui->videoWidget->setMouseTracking(true);
ui->videoWidget->hide();
// 最后绘制不设置最后绘制会影响父窗体异形异常quickWidget的透明通道会形成穿透
ui->quickWidget->setAttribute(Qt::WA_AlwaysStackOnTop);
// 背景透明
ui->quickWidget->setClearColor(QColor(Qt::transparent));
}
void VideoForm::initSignals()
{
connect(&m_fileHandler, &FileHandler::fileHandlerResult, this, [this](FileHandler::FILE_HANDLER_RESULT processResult){
if (FileHandler::FAR_IS_RUNNING == processResult) {
QMessageBox::warning(this, "QtScrcpy", tr("wait current file transfer to complete"), QMessageBox::Ok);
}
if (FileHandler::FAR_SUCCESS_EXEC == processResult) {
QMessageBox::information(this, "QtScrcpy", tr("file transfer complete"), QMessageBox::Ok);
}
if (FileHandler::FAR_ERROR_EXEC == processResult) {
QMessageBox::information(this, "QtScrcpy", tr("file transfer failed"), QMessageBox::Ok);
}
});
connect(&m_inputConvert, &InputConvertGame::grabCursor, this, [this](bool grab){
#ifdef Q_OS_WIN32
if(grab) {
QRect rc(mapToGlobal(ui->videoWidget->pos())
, ui->videoWidget->size());
RECT mainRect;
mainRect.left = (LONG)rc.left();
mainRect.right = (LONG)rc.right();
mainRect.top = (LONG)rc.top();
mainRect.bottom = (LONG)rc.bottom();
ClipCursor(&mainRect);
} else {
ClipCursor(Q_NULLPTR);
}
#endif
});
connect(m_server, &Server::serverStartResult, this, [this](bool success){
if (success) {
m_server->connectTo();
} else {
close();
}
});
connect(m_server, &Server::connectToResult, this, [this](bool success, const QString &deviceName, const QSize &size){
if (success) {
// update ui
setWindowTitle(deviceName);
updateShowSize(size);
// init decode
m_decoder.setDeviceSocket(m_server->getDeviceSocket());
m_decoder.startDecode();
// init controller
m_inputConvert.setDeviceSocket(m_server->getDeviceSocket());
}
});
connect(m_server, &Server::onServerStop, this, [this](){
close();
qDebug() << "server process stop";
});
connect(&m_decoder, &Decoder::onDecodeStop, this, [this](){
close();
qDebug() << "decoder thread stop";
});
// must be Qt::QueuedConnection, ui update must be main thread
connect(&m_decoder, &Decoder::onNewFrame, this, [this](){
if (ui->videoWidget->isHidden()) {
ui->loadingWidget->close();
ui->videoWidget->show();
}
m_frames.lock();
const AVFrame *frame = m_frames.consumeRenderedFrame();
//qDebug() << "widthxheight:" << frame->width << "x" << frame->height;
updateShowSize(QSize(frame->width, frame->height));
ui->videoWidget->setFrameSize(QSize(frame->width, frame->height));
ui->videoWidget->updateTextures(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
m_frames.unLock();
},Qt::QueuedConnection);
}
void VideoForm::showToolFrom(bool show)
{
if (!m_toolForm) {
m_toolForm = new ToolForm(this, ToolForm::AP_OUTSIDE_RIGHT);
m_toolForm->move(pos().x() + geometry().width(), pos().y() + 30);
}
m_toolForm->setVisible(show);
}
void VideoForm::updateStyleSheet(bool vertical)
{
if (vertical) {
setStyleSheet(R"(
#videoForm {
border-image: url(:/res/phone-v.png) 150px 142px 85px 142px;
border-width: 150px 142px 85px 142px;
}
)");
layout()->setContentsMargins(10, 68, 12, 62);
} else {
setStyleSheet(R"(
#videoForm {
border-image: url(:/res/phone-h.png) 142px 85px 142px 150px;
border-width: 142px 85px 142px 150px;
}
)");
layout()->setContentsMargins(68, 12, 62, 10);
}
}
void VideoForm::updateShowSize(const QSize &newSize)
{
if (frameSize != newSize) {
frameSize = newSize;
bool vertical = newSize.height() > newSize.width();
QSize showSize = newSize;
QDesktopWidget* desktop = QApplication::desktop();
if (desktop) {
QRect screenRect = desktop->availableGeometry();
if (vertical) {
showSize.setHeight(qMin(newSize.height(), screenRect.height() - 200));
showSize.setWidth(showSize.height() * m_widthHeightRatio);
} else {
showSize.setWidth(qMin(newSize.width(), screenRect.width()));
showSize.setHeight(showSize.width() * m_widthHeightRatio);
}
if (isFullScreen()) {
switchFullScreen();
}
// 窗口居中
move(screenRect.center() - QRect(0, 0, showSize.width(), showSize.height()).center());
}
// 减去标题栏高度 (mark:已经没有标题栏了)
//int titleBarHeight = style()->pixelMetric(QStyle::PM_TitleBarHeight);
//showSize.setHeight(showSize.height() - titleBarHeight);
if (showSize != size()) {
resize(showSize);
updateStyleSheet(vertical);
}
}
}
void VideoForm::switchFullScreen()
{
if (isFullScreen()) {
showNormal();
updateStyleSheet(height() > width());
showToolFrom(true);
} else {
showToolFrom(false);
layout()->setContentsMargins(0, 0, 0, 0);
showFullScreen();
}
}
void VideoForm::postGoMenu()
{
postKeyCodeClick(AKEYCODE_MENU);
}
void VideoForm::postGoBack()
{
postKeyCodeClick(AKEYCODE_BACK);
}
void VideoForm::postAppSwitch()
{
postKeyCodeClick(AKEYCODE_APP_SWITCH);
}
void VideoForm::postPower()
{
postKeyCodeClick(AKEYCODE_POWER);
}
void VideoForm::postVolumeUp()
{
postKeyCodeClick(AKEYCODE_VOLUME_UP);
}
void VideoForm::postVolumeDown()
{
postKeyCodeClick(AKEYCODE_VOLUME_DOWN);
}
void VideoForm::postTurnOn()
{
ControlEvent* controlEvent = new ControlEvent(ControlEvent::CET_COMMAND);
if (!controlEvent) {
return;
}
controlEvent->setCommandEventData(CONTROL_EVENT_COMMAND_BACK_OR_SCREEN_ON);
m_inputConvert.sendControlEvent(controlEvent);
}
void VideoForm::postTextInput(const QString& text)
{
ControlEvent* controlEvent = new ControlEvent(ControlEvent::CET_TEXT);
if (!controlEvent) {
return;
}
controlEvent->setTextEventData(text);
m_inputConvert.sendControlEvent(controlEvent);
}
void VideoForm::postGoHome()
{
postKeyCodeClick(AKEYCODE_HOME);
}
void VideoForm::postKeyCodeClick(AndroidKeycode keycode)
{
ControlEvent* controlEventDown = new ControlEvent(ControlEvent::CET_KEYCODE);
if (!controlEventDown) {
return;
}
controlEventDown->setKeycodeEventData(AKEY_EVENT_ACTION_DOWN, keycode, AMETA_NONE);
m_inputConvert.sendControlEvent(controlEventDown);
ControlEvent* controlEventUp = new ControlEvent(ControlEvent::CET_KEYCODE);
if (!controlEventUp) {
return;
}
controlEventUp->setKeycodeEventData(AKEY_EVENT_ACTION_UP, keycode, AMETA_NONE);
m_inputConvert.sendControlEvent(controlEventUp);
}
void VideoForm::mousePressEvent(QMouseEvent *event)
{
if (ui->videoWidget->geometry().contains(event->pos())) {
event->setLocalPos(ui->videoWidget->mapFrom(this, event->localPos().toPoint()));
m_inputConvert.mouseEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size());
} else {
if (event->button() == Qt::LeftButton) {
m_dragPosition = event->globalPos() - frameGeometry().topLeft();
event->accept();
}
}
}
void VideoForm::mouseReleaseEvent(QMouseEvent *event)
{
if (ui->videoWidget->geometry().contains(event->pos())) {
event->setLocalPos(ui->videoWidget->mapFrom(this, event->localPos().toPoint()));
m_inputConvert.mouseEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size());
}
}
void VideoForm::mouseMoveEvent(QMouseEvent *event)
{
if (ui->videoWidget->geometry().contains(event->pos())) {
event->setLocalPos(ui->videoWidget->mapFrom(this, event->localPos().toPoint()));
m_inputConvert.mouseEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size());
} else {
if (event->buttons() & Qt::LeftButton) {
move(event->globalPos() - m_dragPosition);
event->accept();
}
}
}
void VideoForm::wheelEvent(QWheelEvent *event)
{
if (ui->videoWidget->geometry().contains(event->pos())) {
QPointF pos = ui->videoWidget->mapFrom(this, event->pos());
/*
QWheelEvent(const QPointF &pos, const QPointF& globalPos, int delta,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers,
Qt::Orientation orient = Qt::Vertical);
*/
QWheelEvent wheelEvent(pos, event->globalPosF(), event->delta(),
event->buttons(), event->modifiers(), event->orientation());
m_inputConvert.wheelEvent(&wheelEvent, ui->videoWidget->frameSize(), ui->videoWidget->size());
}
}
void VideoForm::keyPressEvent(QKeyEvent *event)
{
if (Qt::Key_Escape == event->key()
&& !event->isAutoRepeat()
&& isFullScreen()) {
switchFullScreen();
}
//qDebug() << "keyPressEvent" << event->isAutoRepeat();
m_inputConvert.keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size());
}
void VideoForm::keyReleaseEvent(QKeyEvent *event)
{
//qDebug() << "keyReleaseEvent" << event->isAutoRepeat();
m_inputConvert.keyEvent(event, ui->videoWidget->frameSize(), ui->videoWidget->size());
}
void VideoForm::paintEvent(QPaintEvent *paint)
{
Q_UNUSED(paint);
QStyleOption opt;
opt.init(this);
QPainter p(this);
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
void VideoForm::showEvent(QShowEvent *event)
{
Q_UNUSED(event);
showToolFrom();
}
void VideoForm::dragEnterEvent(QDragEnterEvent *event)
{
event->acceptProposedAction();
}
void VideoForm::dragMoveEvent(QDragMoveEvent *event)
{
Q_UNUSED(event);
}
void VideoForm::dragLeaveEvent(QDragLeaveEvent *event)
{
Q_UNUSED(event);
}
void VideoForm::dropEvent(QDropEvent *event)
{
const QMimeData* qm = event->mimeData();
QString file = qm->urls()[0].toLocalFile();
QFileInfo fileInfo(file);
if (!fileInfo.exists()) {
QMessageBox::warning(this, "QtScrcpy", tr("file does not exist"), QMessageBox::Ok);
return;
}
if (fileInfo.isFile() && fileInfo.suffix() == "apk") {
m_fileHandler.installApkRequest(m_serial, file);
return;
}
m_fileHandler.pushFileRequest(m_serial, file);
}