1
0
forked from BRT/arc

Add ScrcpyWindow integration for screen mirroring

Introduced a ScrcpyWindow to enable integration of the scrcpy tool for device screen mirroring within the application. Platform-specific embedding is supported for macOS and Windows, with structure adjustments made in MainWindow and the build system (CMakeLists.txt).
This commit is contained in:
slava 2025-04-26 10:11:29 +07:00
parent c1b34ce7d7
commit 327b206fe8
7 changed files with 165 additions and 0 deletions

View File

@ -19,8 +19,15 @@ elseif (APPLE)
set(CMAKE_PREFIX_PATH "/opt/homebrew/opt/qt") set(CMAKE_PREFIX_PATH "/opt/homebrew/opt/qt")
include_directories("/opt/homebrew/opt/tesseract/include") include_directories("/opt/homebrew/opt/tesseract/include")
link_directories("/opt/homebrew/opt/tesseract/lib") link_directories("/opt/homebrew/opt/tesseract/lib")
set(CMAKE_CXX_STANDARD 17) # или 20, если нужно
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ObjC++")
endif () endif ()
set(SCRCPY
${CMAKE_CURRENT_SOURCE_DIR}/views/scrcpy/ScrcpyWindow_mac.mm
)
find_package(Qt6 REQUIRED COMPONENTS find_package(Qt6 REQUIRED COMPONENTS
Widgets Widgets
Sql Sql
@ -52,6 +59,7 @@ add_executable(ARCDesktopProject
${SERVICES_SOURCES} ${SERVICES_SOURCES}
${DB_SOURCES} ${DB_SOURCES}
${MODELS_SOURCES} ${MODELS_SOURCES}
${SCRCPY}
) )
if (WIN32) if (WIN32)

Binary file not shown.

View File

@ -48,6 +48,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
stackedWidget->setCurrentWidget(accountPage); stackedWidget->setCurrentWidget(accountPage);
deleteDevicePage(); // освобождаем ресурсы deleteDevicePage(); // освобождаем ресурсы
}); });
scrcpyWindow = new ScrcpyWindow();
scrcpyWindow->show();
} }

View File

@ -4,6 +4,7 @@
#include <QStackedWidget> #include <QStackedWidget>
#include <QTimer> #include <QTimer>
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
#include "scrcpy/ScrcpyWindow.h"
#include "widget/FlowLayout.h" #include "widget/FlowLayout.h"
class MainWindow final : public QMainWindow { class MainWindow final : public QMainWindow {
@ -21,6 +22,8 @@ private:
QWidget* devicePage = nullptr; QWidget* devicePage = nullptr;
QWidget* accountPage = nullptr; QWidget* accountPage = nullptr;
ScrcpyWindow *scrcpyWindow;
void createDevicePage(); void createDevicePage();
void deleteDevicePage(); void deleteDevicePage();
}; };

View File

@ -0,0 +1,68 @@
#include "ScrcpyWindow.h"
#include <QDebug>
#include <QTimer>
#ifdef Q_OS_WIN
#include <windows.h>
#endif
ScrcpyWindow::ScrcpyWindow(QWidget *parent) : QWidget(parent), m_process(nullptr) {
setWindowFlag(Qt::WindowStaysOnTopHint);
startScrcpy();
}
ScrcpyWindow::~ScrcpyWindow() {
stopScrcpy();
}
void ScrcpyWindow::startScrcpy() {
m_process = new QProcess(this);
m_process->start("scrcpy", {"--window-title", "scrcpy", "--always-on-top"});
// Подождём немного, чтобы окно scrcpy запустилось
QTimer::singleShot(5000, this, &ScrcpyWindow::embedPlatformSpecific);
}
void ScrcpyWindow::stopScrcpy() {
if (m_process) {
m_process->terminate();
m_process->waitForFinished();
delete m_process;
m_process = nullptr;
}
}
void ScrcpyWindow::embedPlatformSpecific() {
#ifdef Q_OS_WIN
HWND hwndScrcpy = FindWindowW(nullptr, L"scrcpy");
if (!hwndScrcpy) {
qDebug() << "scrcpy window not found";
return;
}
HWND hwndTarget = (HWND)this->winId();
SetParent(hwndScrcpy, hwndTarget);
LONG style = GetWindowLong(hwndScrcpy, GWL_STYLE);
style &= ~(WS_CAPTION | WS_THICKFRAME | WS_BORDER);
SetWindowLong(hwndScrcpy, GWL_STYLE, style);
RECT rect;
GetClientRect(hwndTarget, &rect);
SetWindowPos(hwndScrcpy, HWND_TOP, 0, 0,
rect.right - rect.left, rect.bottom - rect.top,
SWP_SHOWWINDOW);
#elif defined(Q_OS_MAC)
extern void embedScrcpyMac(QWidget *container);
embedScrcpyMac(this);
#else
qDebug() << "Scrcpy embedding not implemented for this OS.";
#endif
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <QWidget>
#include <QProcess>
class ScrcpyWindow final : public QWidget {
Q_OBJECT
public:
explicit ScrcpyWindow(QWidget *parent = nullptr);
~ScrcpyWindow() override;
private:
QProcess *m_process;
void startScrcpy();
void stopScrcpy();
void embedPlatformSpecific();
};

View File

@ -0,0 +1,61 @@
#import <Cocoa/Cocoa.h>
#import <QtCore/QDebug>
#import <ApplicationServices/ApplicationServices.h>
#include <QWidget>
#include <QWindow>
void listAllWindows() {
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
for (NSDictionary *entry in (__bridge NSArray *)windowList) {
NSString *name = entry[(NSString *)kCGWindowName];
NSString *owner = entry[(NSString *)kCGWindowOwnerName];
NSNumber *windowNumber = entry[(NSString *)kCGWindowNumber];
if (name.length > 0) {
qDebug() << "Window:" << owner.UTF8String << "-" << name.UTF8String
<< "ID:" << windowNumber.intValue;
}
}
CFRelease(windowList);
}
void embedScrcpyMac(QWidget *container) {
// Получаем список окон
NSArray *windows = [NSApp windows];
NSWindow *scrcpyWindow = nil;
listAllWindows();
for (NSWindow *window in windows) {
NSString *title = [window title];
qDebug() << "Found window title:" << title.UTF8String;
if ([[window title] isEqualToString:@"scrcpy"]) {
scrcpyWindow = window;
break;
}
}
if (!scrcpyWindow) {
qDebug() << "scrcpy window not found";
return;
}
// Убираем заголовок и рамку
[scrcpyWindow setStyleMask:NSWindowStyleMaskBorderless];
// Получаем координаты окна Qt
NSView *nativeView = (NSView *)container->winId();
NSWindow *qtWindow = [nativeView window];
NSRect frame = [qtWindow frame];
// Задаём новый размер и позицию для окна scrcpy
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGFloat x = frame.origin.x;
CGFloat y = frame.origin.y;
[scrcpyWindow setFrame:NSMakeRect(x, y, width, height) display:YES];
[scrcpyWindow orderFront:nil];
}