Add double-buffering - No more flicker!

This commit is contained in:
Grayson Riffe 2026-06-07 21:38:49 -05:00
parent 1763bf72da
commit f11b6abeec
Signed by: grayson
SSH Key Fingerprint: SHA256:23HJg9tnL2m6u0uUb26QIrOTFymFZ+xgSH/2UPEBB/I
2 changed files with 49 additions and 32 deletions

View File

@ -39,6 +39,8 @@ namespace watchfuleye {
, m_greenBrush(nullptr)
, m_yellowBrush(nullptr)
, m_redBrush(nullptr)
, m_offscreenDC(nullptr)
, m_offscreenBitmap(nullptr)
{
std::cout << std::format("{} {}\n", m_appName, m_appVersion);
}
@ -114,9 +116,9 @@ namespace watchfuleye {
app->m_yellowBrush = CreateSolidBrush(RGB(255, 255, 150));
app->m_redBrush = CreateSolidBrush(RGB(255, 150, 150));
// App will update (repaint) every second
// App will update (repaint) at 5 FPS
SendMessage(hWnd, WM_UPDATE, NULL, NULL);
SetTimer(hWnd, IDT_UPDATE, 1000, nullptr);
SetTimer(hWnd, IDT_UPDATE, 200, nullptr);
return 0;
}
@ -167,7 +169,7 @@ namespace watchfuleye {
if (HIWORD(wParam) == 0 && LOWORD(wParam) == IDM_ABOUT) {
MessageBox(hWnd, std::format("{} {}\nCopyright Grayson Riffe 2026\ngraysonriffe.com\n\n"
"\t{} is a simple countdown application\n"
"specifically designed for this electronics testing labratory.\n\n"
"specifically designed for this electronics testing labratory.\n"
"Hopefully, it's working!", app->m_appName, app->m_appVersion,
app->m_appName).c_str(), std::format("About {}", app->m_appName).c_str(), MB_OK);
@ -198,12 +200,32 @@ namespace watchfuleye {
return 0;
}
case WM_ERASEBKGND: {
HDC hDC = reinterpret_cast<HDC>(wParam);
case WM_ERASEBKGND: { // Don't erase the background, it is already done in WM_PAINT
return 1;
}
case WM_PAINT: {
PAINTSTRUCT ps = {};
HDC hDC = BeginPaint(hWnd, &ps);
RECT clientRect = {};
GetClientRect(hWnd, &clientRect);
// Create offscreen buffer if not already created
if (!app->m_offscreenDC) {
app->m_offscreenDC = CreateCompatibleDC(hDC);
app->m_offscreenBitmap = CreateCompatibleBitmap(hDC, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top);
}
// Setup RECTs
RECT timeRect = clientRect;
timeRect.top -= 25, timeRect.bottom -= 25;
RECT lowerRect = { .top = WINDOW_HEIGHT - 65, .right = WINDOW_WIDTH, .bottom = WINDOW_HEIGHT };
HANDLE prevBitmap = SelectObject(app->m_offscreenDC, app->m_offscreenBitmap);
// Fill background
HBRUSH backgroundBrush = nullptr;
if (!app->m_active)
@ -215,25 +237,11 @@ namespace watchfuleye {
else
backgroundBrush = app->m_yellowBrush;
FillRect(hDC, &clientRect, backgroundBrush);
return 1;
}
case WM_PAINT: {
PAINTSTRUCT ps = {};
HDC hDC = BeginPaint(hWnd, &ps);
// Setup RECTs
RECT timeRect = {};
GetClientRect(hWnd, &timeRect);
timeRect.top -= 25, timeRect.bottom -= 25;
RECT lowerRect = { .top = WINDOW_HEIGHT - 65, .right = WINDOW_WIDTH, .bottom = WINDOW_HEIGHT };
FillRect(app->m_offscreenDC, &clientRect, backgroundBrush);
// Setup text options
SetTextColor(hDC, RGB(0, 0, 0));
SetBkMode(hDC, TRANSPARENT);
SetTextColor(app->m_offscreenDC, RGB(0, 0, 0));
SetBkMode(app->m_offscreenDC, TRANSPARENT);
// Calculate time
Seconds elapsed = Duration(Clock::now() - app->m_startTime).count();
@ -242,29 +250,33 @@ namespace watchfuleye {
remaining -= elapsed;
if (!app->m_active || (app->m_active && !app->m_overTime)) { // Draw time
SelectObject(hDC, app->m_timeFont);
SelectObject(app->m_offscreenDC, app->m_timeFont);
unsigned int minutes = remaining % (3600) / 60, seconds = remaining % 60;
DrawText(hDC, std::format("{:02}:{:02}", minutes, seconds).c_str(), -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
DrawText(app->m_offscreenDC, std::format("{:02}:{:02}", minutes, seconds).c_str(), -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
if (!app->m_active) { // Ready (green)
SelectObject(hDC, app->m_lowerFont);
DrawText(hDC, "Ready", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Ready", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
else if (!app->m_overTime) { // Counting (yellow)
SelectObject(hDC, app->m_lowerFont);
DrawText(hDC, "Time Remaining on Tester", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Time Remaining on Tester", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
else { // Over time (red)
SelectObject(hDC, app->m_lowerFont);
DrawText(hDC, "Please Eject Your USB", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
SelectObject(hDC, app->m_timeFont);
DrawText(hDC, "STOP", -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
SelectObject(app->m_offscreenDC, app->m_lowerFont);
DrawText(app->m_offscreenDC, "Please Eject Your USB", -1, &lowerRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
SelectObject(app->m_offscreenDC, app->m_timeFont);
DrawText(app->m_offscreenDC, "STOP", -1, &timeRect, DT_SINGLELINE | DT_NOCLIP | DT_CENTER | DT_VCENTER);
}
if (remaining <= 0)
app->m_overTime = true;
BitBlt(hDC, 0, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, app->m_offscreenDC, 0, 0, SRCCOPY);
SelectObject(app->m_offscreenDC, prevBitmap);
EndPaint(hWnd, &ps);
return 0;
}
@ -335,5 +347,8 @@ namespace watchfuleye {
DeleteObject(m_greenBrush);
DeleteObject(m_yellowBrush);
DeleteObject(m_redBrush);
DeleteObject(m_offscreenBitmap);
DeleteDC(m_offscreenDC);
}
}

View File

@ -38,5 +38,7 @@ namespace watchfuleye {
HRGN m_region;
HFONT m_timeFont, m_lowerFont;
HBRUSH m_greenBrush, m_yellowBrush, m_redBrush;
HDC m_offscreenDC;
HBITMAP m_offscreenBitmap;
};
}