Win32GUI: Use a back buffer for all status rendering

This eliminates all flickering when status is updated.
This commit is contained in:
Bart House
2017-10-08 21:13:33 -07:00
committed by Pasi Kallinen
parent 7c24b420a9
commit 1ec7bed529

View File

@@ -12,6 +12,52 @@
extern COLORREF nhcolor_to_RGB(int c); /* from mhmap */
typedef struct back_buffer {
HWND hWnd;
HDC hdc;
HBITMAP bm;
HBITMAP orig_bm;
int width;
int height;
} back_buffer_t;
void back_buffer_free(back_buffer_t * back_buffer)
{
if (back_buffer->bm != NULL) {
SelectObject(back_buffer->hdc, back_buffer->orig_bm);
DeleteObject(back_buffer->bm);
back_buffer->bm = NULL;
}
}
void back_buffer_allocate(back_buffer_t * back_buffer, int width, int height)
{
HDC hdc = GetDC(back_buffer->hWnd);
back_buffer->bm = CreateCompatibleBitmap(hdc, width, height);
back_buffer->orig_bm = SelectObject(back_buffer->hdc, back_buffer->bm);
back_buffer->width = width;
back_buffer->height = height;
ReleaseDC(back_buffer->hWnd, hdc);
}
void back_buffer_size(back_buffer_t * back_buffer, int width, int height)
{
if (back_buffer->bm == NULL || back_buffer->width != width
|| back_buffer->height != height) {
back_buffer_free(back_buffer);
back_buffer_allocate(back_buffer, width, height);
}
}
void back_buffer_init(back_buffer_t * back_buffer, HWND hWnd, int width, int height)
{
back_buffer->hWnd = hWnd;
back_buffer->hdc = CreateCompatibleDC(NULL);
back_buffer->bm = NULL;
back_buffer_size(back_buffer, width, height);
}
typedef struct mswin_nethack_status_window {
int index;
char window_text[NHSW_LINES][MAXWINDOWTEXT + 1];
@@ -20,6 +66,7 @@ typedef struct mswin_nethack_status_window {
boolean *activefields;
int *percents;
int *colors;
back_buffer_t back_buffer;
} NHStatusWindow, *PNHStatusWindow;
static int fieldorder1[] = { BL_TITLE, BL_STR, BL_DX, BL_CO, BL_IN,
@@ -45,6 +92,7 @@ mswin_init_status_window()
HWND ret;
NHStatusWindow *data;
RECT rt;
int width, height;
if (!run_once) {
register_status_window_class();
@@ -59,12 +107,14 @@ mswin_init_status_window()
}
/* create status window object */
width = rt.right - rt.left;
height = rt.bottom - rt.top;
ret = CreateWindow(szStatusWindowClass, NULL,
WS_CHILD | WS_CLIPSIBLINGS | WS_SIZEBOX,
rt.left, /* horizontal position of window */
rt.top, /* vertical position of window */
rt.right - rt.left, /* window width */
rt.bottom - rt.top, /* window height */
rt.left, /* horizontal position of window */
rt.top, /* vertical position of window */
width, /* window width */
height, /* window height */
GetNHApp()->hMainWnd, NULL, GetNHApp()->hApp, NULL);
if (!ret)
panic("Cannot create status window");
@@ -80,8 +130,20 @@ mswin_init_status_window()
ZeroMemory(data, sizeof(NHStatusWindow));
SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR) data);
back_buffer_init(&data->back_buffer, ret, width, height);
mswin_apply_window_style(ret);
if (status_bg_brush == NULL) {
status_bg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_BG_STATUS);
status_bg_brush = CreateSolidBrush(status_bg_color);
}
if (status_fg_brush == NULL) {
status_fg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_FG_STATUS);
status_fg_brush = CreateSolidBrush(status_fg_color);
}
return ret;
}
@@ -98,9 +160,7 @@ register_status_window_class()
wcex.hInstance = GetNHApp()->hApp;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = status_bg_brush
? status_bg_brush
: SYSCLR_TO_BRUSH(DEFAULT_COLOR_BG_STATUS);
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szStatusWindowClass;
@@ -216,30 +276,35 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
int *f;
int **fop;
SIZE sz;
HGDIOBJ oldFont, normalFont, boldFont;
HGDIOBJ normalFont, boldFont;
TCHAR wbuf[BUFSZ];
COLORREF OldBg, OldFg, Bg, Fg;
RECT rt;
PAINTSTRUCT ps;
HDC hdc;
PNHStatusWindow data;
int width, height;
RECT clear_rect;
HDC front_buffer_hdc;
data = (PNHStatusWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
hdc = BeginPaint(hWnd, &ps);
front_buffer_hdc = BeginPaint(hWnd, &ps);
GetClientRect(hWnd, &rt);
width = rt.right - rt.left;
height = rt.bottom - rt.top;
back_buffer_size(&data->back_buffer, width, height);
hdc = data->back_buffer.hdc;
normalFont = mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE);
boldFont = mswin_get_font(NHW_STATUS, ATR_BOLD, hdc, FALSE);
oldFont = SelectObject(hdc, normalFont);
Bg = status_bg_brush ? status_bg_color
: (COLORREF) GetSysColor(DEFAULT_COLOR_BG_STATUS);
OldBg = SetBkColor(hdc, Bg);
SelectObject(hdc, normalFont);
Fg = status_fg_brush ? status_fg_color
: (COLORREF) GetSysColor(DEFAULT_COLOR_FG_STATUS);
OldFg = SetTextColor(hdc, Fg);
SetBkColor(hdc, status_bg_color);
SetTextColor(hdc, status_fg_color);
if (iflags.wc2_hitpointbar && BL_HP < data->n_fields
&& data->activefields[BL_HP]) {
@@ -247,6 +312,13 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
hpbar_color = data->colors[BL_HP] & 0x00ff;
}
clear_rect.left = 0;
clear_rect.top = 0;
clear_rect.right = width;
clear_rect.bottom = height;
FillRect(hdc, &clear_rect, status_bg_brush);
for (fop = fieldorders; *fop; fop++) {
LONG left = rt.left;
LONG cy = 0;
@@ -275,9 +347,10 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
else if (atr & HL_DIM)
fntatr = ATR_DIM;
fnt = mswin_get_font(NHW_STATUS, fntatr, hdc, FALSE);
nFg = (clr == NO_COLOR ? Fg :
((clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr) : Fg));
nBg = Bg;
nFg = (clr == NO_COLOR ? status_fg_color
: ((clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr)
: status_fg_color));
nBg = status_bg_color;
GetTextExtentPoint32(hdc, wbuf, vlen, &sz);
if (*f == BL_TITLE && iflags.wc2_hitpointbar) {
@@ -287,7 +360,7 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
/* first draw title normally */
SelectObject(hdc, fnt);
SetBkMode(hdc, OPAQUE);
SetBkColor(hdc, Bg);
SetBkColor(hdc, status_bg_color);
SetTextColor(hdc, nhcolor_to_RGB(hpbar_color));
DrawText(hdc, wbuf, vlen, &rt, DT_LEFT);
@@ -323,13 +396,13 @@ onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
rt.left += sz.cx;
cy = max(cy, sz.cy);
}
rt.left = left;
rt.top += cy;
}
SelectObject(hdc, oldFont);
SetTextColor(hdc, OldFg);
SetBkColor(hdc, OldBg);
BitBlt(front_buffer_hdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
return 0;