Win32: Add workaround for DWM flicker
And some other minor tweaks. Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
parent
6068944657
commit
fb8f061091
|
@ -73,7 +73,7 @@ target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
QT_NO_CAST_TO_ASCII
|
QT_NO_CAST_TO_ASCII
|
||||||
QT_NO_KEYWORDS
|
QT_NO_KEYWORDS
|
||||||
QT_DEPRECATED_WARNINGS
|
QT_DEPRECATED_WARNINGS
|
||||||
QT_DISABLE_DEPRECATED_BEFORE=0x060100
|
QT_DISABLE_DEPRECATED_BEFORE=0x060200
|
||||||
FRAMELESSHELPER_BUILD_LIBRARY
|
FRAMELESSHELPER_BUILD_LIBRARY
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
target_link_libraries(${PROJECT_NAME} PRIVATE
|
target_link_libraries(${PROJECT_NAME} PRIVATE
|
||||||
dwmapi
|
dwmapi winmm
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
@ -5,12 +5,12 @@ DEFINES += \
|
||||||
QT_NO_CAST_TO_ASCII \
|
QT_NO_CAST_TO_ASCII \
|
||||||
QT_NO_KEYWORDS \
|
QT_NO_KEYWORDS \
|
||||||
QT_DEPRECATED_WARNINGS \
|
QT_DEPRECATED_WARNINGS \
|
||||||
QT_DISABLE_DEPRECATED_BEFORE=0x060100
|
QT_DISABLE_DEPRECATED_BEFORE=0x060200
|
||||||
RESOURCES += $$PWD/images.qrc
|
RESOURCES += $$PWD/images.qrc
|
||||||
win32 {
|
win32 {
|
||||||
CONFIG += windeployqt
|
CONFIG += windeployqt
|
||||||
CONFIG -= embed_manifest_exe
|
CONFIG -= embed_manifest_exe
|
||||||
LIBS += -luser32 -lshell32 -ldwmapi
|
LIBS += -luser32 -lshell32 -ldwmapi -lwinmm
|
||||||
RC_FILE = $$PWD/example.rc
|
RC_FILE = $$PWD/example.rc
|
||||||
OTHER_FILES += $$PWD/example.manifest
|
OTHER_FILES += $$PWD/example.manifest
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* MIT License
|
* MIT License
|
||||||
*
|
*
|
||||||
|
@ -23,6 +21,7 @@
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import QtQuick 2.0
|
import QtQuick 2.0
|
||||||
import QtQuick.Window 2.0
|
import QtQuick.Window 2.0
|
||||||
import QtQuick.Controls 2.0
|
import QtQuick.Controls 2.0
|
||||||
|
@ -48,13 +47,13 @@ Window {
|
||||||
interval: 500
|
interval: 500
|
||||||
running: true
|
running: true
|
||||||
repeat: true
|
repeat: true
|
||||||
onTriggered: label.text = Qt.formatTime(new Date(), "hh:mm:ss")
|
onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss")
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: titleBar
|
id: titleBar
|
||||||
height: framelessHelper.titleBarHeight
|
height: framelessHelper.titleBarHeight
|
||||||
color: "transparent"
|
color: "white"
|
||||||
anchors {
|
anchors {
|
||||||
top: parent.top
|
top: parent.top
|
||||||
topMargin: window._flh_margin
|
topMargin: window._flh_margin
|
||||||
|
@ -106,7 +105,7 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
id: label
|
id: timeLabel
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
font {
|
font {
|
||||||
pointSize: 70
|
pointSize: 70
|
||||||
|
@ -115,9 +114,10 @@ Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
|
id: fullScreenButton
|
||||||
anchors {
|
anchors {
|
||||||
horizontalCenter: parent.horizontalCenter
|
horizontalCenter: parent.horizontalCenter
|
||||||
top: label.bottom
|
top: timeLabel.bottom
|
||||||
topMargin: 15
|
topMargin: 15
|
||||||
}
|
}
|
||||||
property bool _full: window.visibility === Window.FullScreen
|
property bool _full: window.visibility === Window.FullScreen
|
||||||
|
|
|
@ -245,10 +245,12 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
const auto clientRect = ((static_cast<BOOL>(msg->wParam) == FALSE)
|
const auto clientRect = ((static_cast<BOOL>(msg->wParam) == FALSE)
|
||||||
? reinterpret_cast<LPRECT>(msg->lParam)
|
? reinterpret_cast<LPRECT>(msg->lParam)
|
||||||
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam))->rgrc[0]);
|
: &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam))->rgrc[0]);
|
||||||
|
const bool max = IsMaximized(msg->hwnd);
|
||||||
|
const bool full = window->windowState() == Qt::WindowFullScreen;
|
||||||
// We don't need this correction when we're fullscreen. We will
|
// We don't need this correction when we're fullscreen. We will
|
||||||
// have the WS_POPUP size, so we don't have to worry about
|
// have the WS_POPUP size, so we don't have to worry about
|
||||||
// borders, and the default frame will be fine.
|
// borders, and the default frame will be fine.
|
||||||
if (IsMaximized(msg->hwnd) && (window->windowState() != Qt::WindowFullScreen)) {
|
if (max && !full) {
|
||||||
// When a window is maximized, its size is actually a little bit more
|
// When a window is maximized, its size is actually a little bit more
|
||||||
// than the monitor's work area. The window is positioned and sized in
|
// than the monitor's work area. The window is positioned and sized in
|
||||||
// such a way that the resize handles are outside of the monitor and
|
// such a way that the resize handles are outside of the monitor and
|
||||||
|
@ -267,7 +269,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
// Make sure to use MONITOR_DEFAULTTONEAREST, so that this will
|
// Make sure to use MONITOR_DEFAULTTONEAREST, so that this will
|
||||||
// still find the right monitor even when we're restoring from
|
// still find the right monitor even when we're restoring from
|
||||||
// minimized.
|
// minimized.
|
||||||
if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) {
|
if (max || full) {
|
||||||
APPBARDATA abd;
|
APPBARDATA abd;
|
||||||
SecureZeroMemory(&abd, sizeof(abd));
|
SecureZeroMemory(&abd, sizeof(abd));
|
||||||
abd.cbSize = sizeof(abd);
|
abd.cbSize = sizeof(abd);
|
||||||
|
@ -308,8 +310,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
left = hasAutohideTaskbar(ABE_LEFT);
|
left = hasAutohideTaskbar(ABE_LEFT);
|
||||||
right = hasAutohideTaskbar(ABE_RIGHT);
|
right = hasAutohideTaskbar(ABE_RIGHT);
|
||||||
} else {
|
} else {
|
||||||
// The following code is copied from Mozilla Firefox,
|
|
||||||
// with some modifications.
|
|
||||||
int edge = -1;
|
int edge = -1;
|
||||||
APPBARDATA _abd;
|
APPBARDATA _abd;
|
||||||
SecureZeroMemory(&_abd, sizeof(_abd));
|
SecureZeroMemory(&_abd, sizeof(_abd));
|
||||||
|
@ -361,7 +361,7 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#if 1
|
#if 0
|
||||||
// Fix the flickering issue while resizing.
|
// Fix the flickering issue while resizing.
|
||||||
// "clientRect->right += 1;" also works.
|
// "clientRect->right += 1;" also works.
|
||||||
// This small technique is known to have two draw backs:
|
// This small technique is known to have two draw backs:
|
||||||
|
@ -376,6 +376,63 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
// is not correct. It confuses QPA's internal logic.
|
// is not correct. It confuses QPA's internal logic.
|
||||||
clientRect->bottom += 1;
|
clientRect->bottom += 1;
|
||||||
#endif
|
#endif
|
||||||
|
// Dirty hack to workaround the DWM flicker.
|
||||||
|
LARGE_INTEGER freq = {};
|
||||||
|
if (QueryPerformanceFrequency(&freq) == FALSE) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceFrequency"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
TIMECAPS tc = {};
|
||||||
|
if (timeGetDevCaps(&tc, sizeof(tc)) != MMSYSERR_NOERROR) {
|
||||||
|
qWarning() << "timeGetDevCaps() failed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const UINT ms_granularity = tc.wPeriodMin;
|
||||||
|
if (timeBeginPeriod(ms_granularity) != TIMERR_NOERROR) {
|
||||||
|
qWarning() << "timeBeginPeriod() failed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LARGE_INTEGER now0 = {};
|
||||||
|
if (QueryPerformanceCounter(&now0) == FALSE) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// ask DWM where the vertical blank falls
|
||||||
|
DWM_TIMING_INFO dti;
|
||||||
|
SecureZeroMemory(&dti, sizeof(dti));
|
||||||
|
dti.cbSize = sizeof(dti);
|
||||||
|
const HRESULT hr = DwmGetCompositionTimingInfo(nullptr, &dti);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("DwmGetCompositionTimingInfo"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LARGE_INTEGER now1 = {};
|
||||||
|
if (QueryPerformanceCounter(&now1) == FALSE) {
|
||||||
|
qWarning() << Utilities::getSystemErrorMessage(QStringLiteral("QueryPerformanceCounter"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// - DWM told us about SOME vertical blank
|
||||||
|
// - past or future, possibly many frames away
|
||||||
|
// - convert that into the NEXT vertical blank
|
||||||
|
const LONGLONG period = dti.qpcRefreshPeriod;
|
||||||
|
const LONGLONG dt = dti.qpcVBlank - now1.QuadPart;
|
||||||
|
LONGLONG w = 0, m = 0;
|
||||||
|
if (dt >= 0) {
|
||||||
|
w = dt / period;
|
||||||
|
} else {
|
||||||
|
// reach back to previous period
|
||||||
|
// - so m represents consistent position within phase
|
||||||
|
w = -1 + dt / period;
|
||||||
|
}
|
||||||
|
m = dt - (period * w);
|
||||||
|
Q_ASSERT(m >= 0);
|
||||||
|
Q_ASSERT(m < period);
|
||||||
|
const qreal m_ms = 1000.0 * static_cast<qreal>(m) / static_cast<qreal>(freq.QuadPart);
|
||||||
|
Sleep(static_cast<DWORD>(qRound(m_ms)));
|
||||||
|
if (timeEndPeriod(ms_granularity) != TIMERR_NOERROR) {
|
||||||
|
qWarning() << "timeEndPeriod() failed.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
// We cannot return WVR_REDRAW otherwise Windows exhibits bugs where
|
// We cannot return WVR_REDRAW otherwise Windows exhibits bugs where
|
||||||
// client pixels and child windows are mispositioned by the width/height
|
// client pixels and child windows are mispositioned by the width/height
|
||||||
// of the upper-left nonclient area.
|
// of the upper-left nonclient area.
|
||||||
|
@ -502,7 +559,8 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true);
|
const int resizeBorderThickness = Utilities::getSystemMetric(window, SystemMetric::ResizeBorderThickness, true);
|
||||||
const int titleBarHeight = Utilities::getSystemMetric(window, SystemMetric::TitleBarHeight, true);
|
const int titleBarHeight = Utilities::getSystemMetric(window, SystemMetric::TitleBarHeight, true);
|
||||||
bool isTitleBar = false;
|
bool isTitleBar = false;
|
||||||
if (IsMaximized(msg->hwnd) || (window->windowState() == Qt::WindowFullScreen)) {
|
const bool max = IsMaximized(msg->hwnd);
|
||||||
|
if (max || (window->windowState() == Qt::WindowFullScreen)) {
|
||||||
isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight)
|
isTitleBar = (localMouse.y() >= 0) && (localMouse.y() <= titleBarHeight)
|
||||||
&& (localMouse.x() >= 0) && (localMouse.x() <= windowWidth)
|
&& (localMouse.x() >= 0) && (localMouse.x() <= windowWidth)
|
||||||
&& !Utilities::isHitTestVisible(window);
|
&& !Utilities::isHitTestVisible(window);
|
||||||
|
@ -513,8 +571,8 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
&& !Utilities::isHitTestVisible(window);
|
&& !Utilities::isHitTestVisible(window);
|
||||||
}
|
}
|
||||||
const bool isTop = localMouse.y() <= resizeBorderThickness;
|
const bool isTop = localMouse.y() <= resizeBorderThickness;
|
||||||
const LRESULT hitTestResult = [clientRect, msg, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window]{
|
*result = [clientRect, isTitleBar, &localMouse, resizeBorderThickness, windowWidth, isTop, window, max](){
|
||||||
if (IsMaximized(msg->hwnd)) {
|
if (max) {
|
||||||
if (isTitleBar) {
|
if (isTitleBar) {
|
||||||
return HTCAPTION;
|
return HTCAPTION;
|
||||||
}
|
}
|
||||||
|
@ -559,7 +617,6 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
}
|
}
|
||||||
return HTCLIENT;
|
return HTCLIENT;
|
||||||
}();
|
}();
|
||||||
*result = hitTestResult;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case WM_SETICON:
|
case WM_SETICON:
|
||||||
|
@ -591,14 +648,14 @@ bool FramelessHelperWin::nativeEventFilter(const QByteArray &eventType, void *me
|
||||||
*result = ret;
|
*result = ret;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case WM_WINDOWPOSCHANGING: {
|
|
||||||
#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 2))
|
#if (QT_VERSION < QT_VERSION_CHECK(6, 2, 2))
|
||||||
|
case WM_WINDOWPOSCHANGING: {
|
||||||
// Tell Windows to discard the entire contents of the client area, as re-using
|
// Tell Windows to discard the entire contents of the client area, as re-using
|
||||||
// parts of the client area would lead to jitter during resize.
|
// parts of the client area would lead to jitter during resize.
|
||||||
const auto windowPos = reinterpret_cast<LPWINDOWPOS>(msg->lParam);
|
const auto windowPos = reinterpret_cast<LPWINDOWPOS>(msg->lParam);
|
||||||
windowPos->flags |= SWP_NOCOPYBITS;
|
windowPos->flags |= SWP_NOCOPYBITS;
|
||||||
#endif
|
|
||||||
} break;
|
} break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@
|
||||||
#include <QtCore/qt_windows.h>
|
#include <QtCore/qt_windows.h>
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <dwmapi.h>
|
#include <dwmapi.h>
|
||||||
|
#include <timeapi.h>
|
||||||
|
|
||||||
#ifndef WM_NCUAHDRAWCAPTION
|
#ifndef WM_NCUAHDRAWCAPTION
|
||||||
#define WM_NCUAHDRAWCAPTION (0x00AE)
|
#define WM_NCUAHDRAWCAPTION (0x00AE)
|
||||||
|
|
4
lib.pro
4
lib.pro
|
@ -9,7 +9,7 @@ DEFINES += \
|
||||||
QT_NO_CAST_TO_ASCII \
|
QT_NO_CAST_TO_ASCII \
|
||||||
QT_NO_KEYWORDS \
|
QT_NO_KEYWORDS \
|
||||||
QT_DEPRECATED_WARNINGS \
|
QT_DEPRECATED_WARNINGS \
|
||||||
QT_DISABLE_DEPRECATED_BEFORE=0x060100 \
|
QT_DISABLE_DEPRECATED_BEFORE=0x060200 \
|
||||||
FRAMELESSHELPER_BUILD_LIBRARY
|
FRAMELESSHELPER_BUILD_LIBRARY
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
framelesshelper_global.h \
|
framelesshelper_global.h \
|
||||||
|
@ -32,6 +32,6 @@ win32 {
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
utilities_win32.cpp \
|
utilities_win32.cpp \
|
||||||
framelesshelper_win32.cpp
|
framelesshelper_win32.cpp
|
||||||
LIBS += -luser32 -lshell32 -ldwmapi
|
LIBS += -luser32 -lshell32 -ldwmapi -lwinmm
|
||||||
RC_FILE = framelesshelper.rc
|
RC_FILE = framelesshelper.rc
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue