diff --git a/QtScrcpy/main.cpp b/QtScrcpy/main.cpp index 1b80d9b..bc09362 100644 --- a/QtScrcpy/main.cpp +++ b/QtScrcpy/main.cpp @@ -11,7 +11,7 @@ #include "dialog.h" #include "mousetap/mousetap.h" #include "stream.h" -#include "windowframelesshelper.h" +#include "windowframelesshelper/windowframelesshelper.h" static Dialog *g_mainDlg = Q_NULLPTR; diff --git a/QtScrcpy/uibase/uibase.pri b/QtScrcpy/uibase/uibase.pri index d64b681..1af7572 100644 --- a/QtScrcpy/uibase/uibase.pri +++ b/QtScrcpy/uibase/uibase.pri @@ -2,24 +2,22 @@ FORMS += HEADERS += \ $$PWD/keepratiowidget.h \ - $$PWD/magneticwidget.h \ - $$PWD/windowframelesshelper.h \ - $$PWD/windownativeeventfilter.h - + $$PWD/magneticwidget.h SOURCES += \ $$PWD/keepratiowidget.cpp \ - $$PWD/magneticwidget.cpp \ - $$PWD/windownativeeventfilter.cpp + $$PWD/magneticwidget.cpp win32 { - SOURCES += $$PWD/windowframelesshelper.cpp + SOURCES += } mac { - SOURCES += $$PWD/windowframelesshelper.mm + SOURCES += } linux { - SOURCES += $$PWD/windowframelesshelper.cpp + SOURCES += } + +include ($$PWD/windowframelesshelper/windowframelesshelper.pri) diff --git a/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.cpp b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.cpp new file mode 100644 index 0000000..8a6a366 --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.cpp @@ -0,0 +1,5 @@ +#include "nativewindowutils.h" + +quint64 NativeWindowUtils::GetHandleByWId(WId id) { + return (quint64)id; +} diff --git a/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.h b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.h new file mode 100644 index 0000000..a2a462e --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.h @@ -0,0 +1,8 @@ +#pragma once +#include + +class NativeWindowUtils +{ +public: + static quint64 GetHandleByWId(WId id); +}; diff --git a/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.mm b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.mm new file mode 100644 index 0000000..beed24e --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/nativewindowutils.mm @@ -0,0 +1,15 @@ +#include "nativewindowutils.h" +#include + +quint64 NativeWindowUtils::GetHandleByWId(WId id) { + NSView* view = (NSView*)id; + if (nullptr == view) { + return 0; + } + + NSWindow *window = view.window; + if (nullptr == window) { + return 0; + } + return (quint64)window; +} diff --git a/QtScrcpy/uibase/windowframelesshelper.cpp b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.cpp similarity index 61% rename from QtScrcpy/uibase/windowframelesshelper.cpp rename to QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.cpp index d00d75e..0ccdcef 100644 --- a/QtScrcpy/uibase/windowframelesshelper.cpp +++ b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.cpp @@ -1,9 +1,17 @@ #include "windowframelesshelper.h" -#include "windownativeeventfilter.h" +#include "windowframelessmanager.h" WindowFramelessHelper::WindowFramelessHelper(QObject *parent) : QObject(parent) { + WindowFramelessManager::Instance()->addWindow(this); +#ifdef Q_OS_WIN32 + WindowNativeEventFilter::Instance()->Init(); +#endif +} +WindowFramelessHelper::~WindowFramelessHelper() +{ + WindowFramelessManager::Instance()->removeWindow(this); } QQuickWindow *WindowFramelessHelper::target() const @@ -13,18 +21,10 @@ QQuickWindow *WindowFramelessHelper::target() const void WindowFramelessHelper::setTarget(QQuickWindow *target) { - if (target == m_target) { + if (target == nullptr || target == m_target) { return; } m_target = target; -#ifdef Q_OS_WIN32 - WindowNativeEventFilter::Instance()->Init(); -#endif - emit targetChanged(); } - -void WindowFramelessHelper::updateStyle() { - -} diff --git a/QtScrcpy/uibase/windowframelesshelper.h b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.h similarity index 75% rename from QtScrcpy/uibase/windowframelesshelper.h rename to QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.h index b16459f..e85aa33 100644 --- a/QtScrcpy/uibase/windowframelesshelper.h +++ b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.h @@ -1,5 +1,4 @@ -#ifndef WindowFramelessHelper_H -#define WindowFramelessHelper_H +#pragma once #include @@ -10,18 +9,14 @@ class WindowFramelessHelper : public QObject public: explicit WindowFramelessHelper(QObject *parent = nullptr); + virtual ~WindowFramelessHelper(); QQuickWindow *target() const; void setTarget(QQuickWindow *target); -protected: - void updateStyle(); - signals: void targetChanged(); private: QQuickWindow* m_target = nullptr; }; - -#endif // WindowFramelessHelper_H diff --git a/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.pri b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.pri new file mode 100644 index 0000000..1b9c349 --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/windowframelesshelper.pri @@ -0,0 +1,24 @@ +HEADERS += \ + $$PWD/nativewindowutils.h \ + $$PWD/windowframelesshelper.h \ + $$PWD/windowframelessmanager.h + +SOURCES += \ + $$PWD/windowframelesshelper.cpp \ + $$PWD/windowframelessmanager.cpp + +win32 { + HEADERS += $$PWD/windownativeeventfilterwin.h + SOURCES += $$PWD/windownativeeventfilterwin.cpp \ + $$PWD/nativewindowutils.cpp \ +} + +mac { + HEADERS += $$PWD/windownativeeventfiltermac.h + SOURCES += $$PWD/windownativeeventfiltermac.mm \ + $$PWD/nativewindowutils.mm +} + +linux { + +} diff --git a/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.cpp b/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.cpp new file mode 100644 index 0000000..0c19f22 --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.cpp @@ -0,0 +1,54 @@ +#include "windowframelessmanager.h" +#include "windowframelesshelper.h" +#include "nativewindowutils.h" + +static QVector s_windowFramelessHelpers; + +WindowFramelessManager::WindowFramelessManager() +{ + +} + +WindowFramelessManager::~WindowFramelessManager() +{ + +} + +WindowFramelessManager *WindowFramelessManager::Instance() +{ + static WindowFramelessManager windowNativeEventFilter; + return &windowNativeEventFilter; +} + +void WindowFramelessManager::addWindow(WindowFramelessHelper* win) +{ + if (nullptr == win) { + return; + } + s_windowFramelessHelpers.push_back(win); +} + +void WindowFramelessManager::removeWindow(WindowFramelessHelper* win) +{ + if (nullptr == win) { + return; + } + s_windowFramelessHelpers.removeOne(win); +} + +WindowFramelessHelper* WindowFramelessManager::getWindowByHandle(quint64 handle) +{ + quint64 targetHandle = 0; + for (auto i = s_windowFramelessHelpers.begin(); i != s_windowFramelessHelpers.end(); i++) { + if ((*i)->target() == nullptr) { + continue; + } + + targetHandle = NativeWindowUtils::GetHandleByWId((*i)->target()->winId()); + if (targetHandle == handle) { + return (*i); + } + } + + return nullptr; +} diff --git a/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.h b/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.h new file mode 100644 index 0000000..d3ccfeb --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/windowframelessmanager.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include + +class WindowFramelessHelper; +class WindowFramelessManager +{ +private: + WindowFramelessManager(); + +public: + virtual ~WindowFramelessManager(); + +public: + static WindowFramelessManager* Instance(); + + void addWindow(WindowFramelessHelper* win); + void removeWindow(WindowFramelessHelper* win); + WindowFramelessHelper* getWindowByHandle(quint64 handle); +}; diff --git a/QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.h b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.h new file mode 100644 index 0000000..7cfc4d1 --- /dev/null +++ b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.h @@ -0,0 +1,7 @@ +#pragma once + +class windownativeeventfiltermac +{ +public: + windownativeeventfiltermac(); +}; diff --git a/QtScrcpy/uibase/windowframelesshelper.mm b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.mm similarity index 50% rename from QtScrcpy/uibase/windowframelesshelper.mm rename to QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.mm index cf166de..0d7ba2e 100644 --- a/QtScrcpy/uibase/windowframelesshelper.mm +++ b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfiltermac.mm @@ -1,9 +1,15 @@ -#include "windowframelesshelper.h" +#include "windownativeeventfiltermac.h" #include #include #import #include +#include "windowframelessmanager.h" + +windownativeeventfiltermac::windownativeeventfiltermac() +{ + +} void SwizzleSelector(Class originalCls, SEL originalSelector, Class swizzledCls, SEL swizzledSelector) { Method originalMethod = class_getInstanceMethod(originalCls, originalSelector); @@ -48,16 +54,20 @@ void SwizzleSelector(Class originalCls, SEL originalSelector, Class swizzledCls, { [self filter_setStyleMask:styleMask]; - //设置标题文字和图标为不可见 - self.titleVisibility = NSWindowTitleHidden; - //设置标题栏为透明 - self.titlebarAppearsTransparent = YES; - //设置不可由标题栏拖动,避免与自定义拖动冲突 - self.movable = NO; - self.hasShadow = YES; - //设置view扩展到标题栏 - if (!(self.styleMask & NSWindowStyleMaskFullSizeContentView)) { - self.styleMask |= NSWindowStyleMaskFullSizeContentView; + WindowFramelessHelper* win = WindowFramelessManager::Instance()->getWindowByHandle((quint64)self); + + if (win) { + //设置标题文字和图标为不可见 + self.titleVisibility = NSWindowTitleHidden; + //设置标题栏为透明 + self.titlebarAppearsTransparent = YES; + //设置不可由标题栏拖动,避免与自定义拖动冲突 + self.movable = NO; + self.hasShadow = YES; + //设置view扩展到标题栏 + if (!(self.styleMask & NSWindowStyleMaskFullSizeContentView)) { + self.styleMask |= NSWindowStyleMaskFullSizeContentView; + } } } @@ -66,58 +76,3 @@ void SwizzleSelector(Class originalCls, SEL originalSelector, Class swizzledCls, [self filter_setTitlebarAppearsTransparent:titlebarAppearsTransparent]; } @end - -WindowFramelessHelper::WindowFramelessHelper(QObject *parent) : QObject(parent) -{ - -} - -QQuickWindow *WindowFramelessHelper::target() const -{ - return m_target; -} - -void WindowFramelessHelper::setTarget(QQuickWindow *target) -{ - if (target == m_target) { - return; - } - m_target = target; - - //updateStyle(); - - emit targetChanged(); -} -/* -void WindowFramelessHelper::updateStyle() { - if (!m_target) { - return; - } - - //如果当前osx版本老于10.9,则后续代码不可用。转为使用定制的系统按钮,不支持自由缩放窗口及窗口阴影 - if (QOperatingSystemVersion::current() < QOperatingSystemVersion::OSXYosemite) { - return; - } - - NSView* view = (NSView*)m_target->winId(); - if (nullptr == view) { - return; - } - - NSWindow *window = view.window; - if (nullptr == window) { - return; - } - - //设置标题文字和图标为不可见 - window.titleVisibility = NSWindowTitleHidden; - //设置标题栏为透明 - window.titlebarAppearsTransparent = YES; - //设置不可由标题栏拖动,避免与自定义拖动冲突 - window.movable = NO; - window.hasShadow = YES; - //设置view扩展到标题栏 - window.styleMask |= NSWindowStyleMaskFullSizeContentView; -} - -*/ diff --git a/QtScrcpy/uibase/windownativeeventfilter.cpp b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfilterwin.cpp similarity index 100% rename from QtScrcpy/uibase/windownativeeventfilter.cpp rename to QtScrcpy/uibase/windowframelesshelper/windownativeeventfilterwin.cpp diff --git a/QtScrcpy/uibase/windownativeeventfilter.h b/QtScrcpy/uibase/windowframelesshelper/windownativeeventfilterwin.h similarity index 100% rename from QtScrcpy/uibase/windownativeeventfilter.h rename to QtScrcpy/uibase/windowframelesshelper/windownativeeventfilterwin.h diff --git a/QtScrcpy/uibase/windownativeeventfilterwin.cpp b/QtScrcpy/uibase/windownativeeventfilterwin.cpp new file mode 100644 index 0000000..3adefe4 --- /dev/null +++ b/QtScrcpy/uibase/windownativeeventfilterwin.cpp @@ -0,0 +1,254 @@ +#include "windownativeeventfilter.h" + +#if defined(Q_OS_WIN) +#include +#include +#include +#include +#include + +WindowNativeEventFilter::WindowNativeEventFilter() {} + +WindowNativeEventFilter::~WindowNativeEventFilter() +{ + // do nothing, because this object is static instance +} + +WindowNativeEventFilter *WindowNativeEventFilter::Instance() +{ + static WindowNativeEventFilter g_windowNativeEventFilter; + return &g_windowNativeEventFilter; +} + +void WindowNativeEventFilter::Init() +{ + if (!m_inited) { + m_inited = true; + QGuiApplication::instance()->installNativeEventFilter(this); + } +} + +bool WindowNativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, long *result) +{ + Q_UNUSED(eventType); + + MSG *param = static_cast(message); + if (!param) { + return false; + } + + switch (param->message) { + case WM_NCHITTEST: { + if (processNcHitTest(message, result)) { + return true; + } + } break; + case WM_NCLBUTTONDOWN: { + if (processNcLButtonDown(message, result)) { + return true; + } + } break; + case WM_SETCURSOR: { + if (processSetCursor(message, result)) { + return true; + } + } break; + default: + break; + } + + return false; +} + +bool WindowNativeEventFilter::processNcHitTest(void *message, long *result) +{ + MSG *param = static_cast(message); + if (!param) { + return false; + } + + QWindow *window = getWindow((WId)param->hwnd); + if (!window) { + return false; + } + + // 没有最大化&全屏按钮,则认为是固定尺寸窗口 + if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) { + return false; + } + + qreal dpi = window->devicePixelRatio(); + if (dpi <= 0.99) { + dpi = 1.0; + } + + QPoint ptCursor = QCursor::pos(); + int nX = ptCursor.x() - window->geometry().x(); + int nY = ptCursor.y() - window->geometry().y(); + + *result = HTCLIENT; + + if (Qt::WindowMaximized == window->windowState()) { + return false; + } + + if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) { + return false; + } + + qDebug() << "processNcHitTest:" << ptCursor << window->geometry() << "d:" << nX; + int borderWidth = 5; + if ((nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth) && (nY > m_windowMargin.top()) + && (nY < m_windowMargin.top() + borderWidth)) { + *result = HTTOPLEFT; + } else if ( + (nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right()) && (nY > m_windowMargin.top()) + && (nY < m_windowMargin.top() + borderWidth)) { + *result = HTTOPRIGHT; + } else if ( + (nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth) && (nY > window->height() - m_windowMargin.bottom() - borderWidth) + && (nY < window->height() - m_windowMargin.bottom())) { + *result = HTBOTTOMLEFT; + } else if ( + (nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right()) + && (nY > window->height() - m_windowMargin.bottom() - borderWidth) && (nY < window->height() - m_windowMargin.bottom())) { + *result = HTBOTTOMRIGHT; + } else if ((nX > m_windowMargin.left()) && (nX < m_windowMargin.left() + borderWidth)) { + *result = HTLEFT; + } else if ((nX > window->width() - m_windowMargin.right() - borderWidth) && (nX < window->width() - m_windowMargin.right())) { + *result = HTRIGHT; + } else if ((nY > m_windowMargin.top()) && (nY < m_windowMargin.top() + borderWidth)) { + *result = HTTOP; + } else if ((nY > window->height() - m_windowMargin.bottom() - borderWidth) && (nY < window->height() - m_windowMargin.bottom())) { + *result = HTBOTTOM; + } + + return true; +} + +bool WindowNativeEventFilter::processNcLButtonDown(void *message, long *result) +{ + Q_UNUSED(result); + + MSG *param = static_cast(message); + if (!param) { + return false; + } + + QWindow *window = getWindow((WId)param->hwnd); + if (!window) { + return false; + } + + if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) { + return false; + } + + if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) { + return false; + } + + HWND hwnd = param->hwnd; + WPARAM nHitTest = param->wParam; + if (nHitTest == HTTOP) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOP, param->lParam); + } else if (nHitTest == HTBOTTOM) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOM, param->lParam); + } else if (nHitTest == HTLEFT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_LEFT, param->lParam); + } else if (nHitTest == HTRIGHT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_RIGHT, param->lParam); + } else if (nHitTest == HTTOPLEFT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPLEFT, param->lParam); + } else if (nHitTest == HTTOPRIGHT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_TOPRIGHT, param->lParam); + } else if (nHitTest == HTBOTTOMLEFT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMLEFT, param->lParam); + } else if (nHitTest == HTBOTTOMRIGHT) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_SIZE | WMSZ_BOTTOMRIGHT, param->lParam); + } else if (nHitTest == HTCAPTION) { + ::PostMessage(hwnd, WM_SYSCOMMAND, SC_MOVE + 1, 0); + } + + return false; +} + +bool WindowNativeEventFilter::processSetCursor(void *message, long *result) +{ + Q_UNUSED(result); + + MSG *param = static_cast(message); + if (!param) { + return false; + } + + QWindow *window = getWindow((WId)param->hwnd); + if (!window) { + return false; + } + + if (!(window->flags() & Qt::WindowMaximizeButtonHint) && !(window->flags() & Qt::WindowFullscreenButtonHint)) { + return false; + } + + if ((window->windowStates() & Qt::WindowMaximized) || (window->windowStates() & Qt::WindowFullScreen)) { + return false; + } + + // is invisible window + HWND hwnd = param->hwnd; + if (!::IsWindowVisible(hwnd)) { + return true; + } + + // is not enabled window + if (!::IsWindowEnabled(hwnd)) { + return true; + } + + // set cursor + HCURSOR hCursor = nullptr; + LPARAM hittest = LOWORD(param->lParam); + switch (hittest) { + case HTRIGHT: + case HTLEFT: + hCursor = LoadCursor(nullptr, IDC_SIZEWE); + break; + case HTTOP: + case HTBOTTOM: + hCursor = LoadCursor(nullptr, IDC_SIZENS); + break; + case HTTOPLEFT: + case HTBOTTOMRIGHT: + hCursor = LoadCursor(nullptr, IDC_SIZENWSE); + break; + case HTTOPRIGHT: + case HTBOTTOMLEFT: + hCursor = LoadCursor(nullptr, IDC_SIZENESW); + break; + default: + break; + } + + if (!hCursor) { + return false; + } + + ::SetCursor(hCursor); + ::DestroyCursor(hCursor); + + return true; +} + +QWindow *WindowNativeEventFilter::getWindow(WId wndId) +{ + QWindowList windows = QGuiApplication::topLevelWindows(); + for (int i = 0; i < windows.size(); ++i) { + if (windows.at(i)->winId() == wndId) { + return windows.at(i); + } + } + return nullptr; +} + +#endif //(Q_OS_WIN) diff --git a/QtScrcpy/uibase/windownativeeventfilterwin.h b/QtScrcpy/uibase/windownativeeventfilterwin.h new file mode 100644 index 0000000..0fe1215 --- /dev/null +++ b/QtScrcpy/uibase/windownativeeventfilterwin.h @@ -0,0 +1,32 @@ +#pragma once +#include + +#if defined(Q_OS_WIN) +#include +#include + +class WindowNativeEventFilter : public QAbstractNativeEventFilter +{ +protected: + WindowNativeEventFilter(); + ~WindowNativeEventFilter() override; + +public: + static WindowNativeEventFilter *Instance(); + + void Init(); + bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; + +private: + bool processNcHitTest(void *message, long *result); + bool processNcLButtonDown(void *message, long *result); + bool processSetCursor(void *message, long *result); + + QWindow *getWindow(WId wndId); + +private: + bool m_inited = false; + QMargins m_windowMargin = QMargins(0, 0, 0, 0); +}; + +#endif // Q_OS_WIN