1 /* NetHack 3.6 mhstatus.c $NHDT-Date: 1536411224 2018/09/08 12:53:44 $ $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.29 $ */
2 /* Copyright (C) 2001 by Alex Kompel */
3 /* NetHack may be freely redistributed. See license for details. */
4
5 #include <assert.h>
6 #include "winos.h"
7 #include "winMS.h"
8 #include "mhstatus.h"
9 #include "mhmsg.h"
10 #include "mhfont.h"
11
12 #ifndef STATUS_HILITES
13 #error STATUS_HILITES not defined
14 #endif
15
16 #define MAXWINDOWTEXT BUFSZ
17
18 #define STATUS_BLINK_INTERVAL 500 // milliseconds
19
20
21 extern COLORREF nhcolor_to_RGB(int c); /* from mhmap */
22
23 typedef struct back_buffer {
24 HWND hWnd;
25 HDC hdc;
26 HBITMAP bm;
27 HBITMAP orig_bm;
28 int width;
29 int height;
30 } back_buffer_t;
31
back_buffer_free(back_buffer_t * back_buffer)32 void back_buffer_free(back_buffer_t * back_buffer)
33 {
34 if (back_buffer->bm != NULL) {
35 SelectObject(back_buffer->hdc, back_buffer->orig_bm);
36 DeleteObject(back_buffer->bm);
37 back_buffer->bm = NULL;
38 }
39 }
40
back_buffer_allocate(back_buffer_t * back_buffer,int width,int height)41 void back_buffer_allocate(back_buffer_t * back_buffer, int width, int height)
42 {
43 HDC hdc = GetDC(back_buffer->hWnd);
44 back_buffer->bm = CreateCompatibleBitmap(hdc, width, height);
45 back_buffer->orig_bm = SelectObject(back_buffer->hdc, back_buffer->bm);
46 back_buffer->width = width;
47 back_buffer->height = height;
48 ReleaseDC(back_buffer->hWnd, hdc);
49 }
50
back_buffer_size(back_buffer_t * back_buffer,int width,int height)51 void back_buffer_size(back_buffer_t * back_buffer, int width, int height)
52 {
53 if (back_buffer->bm == NULL || back_buffer->width != width
54 || back_buffer->height != height) {
55 back_buffer_free(back_buffer);
56 back_buffer_allocate(back_buffer, width, height);
57 }
58 }
59
back_buffer_init(back_buffer_t * back_buffer,HWND hWnd,int width,int height)60 void back_buffer_init(back_buffer_t * back_buffer, HWND hWnd, int width, int height)
61 {
62 back_buffer->hWnd = hWnd;
63 back_buffer->hdc = CreateCompatibleDC(NULL);
64 back_buffer->bm = NULL;
65
66 back_buffer_size(back_buffer, width, height);
67 }
68
69
70 typedef struct mswin_nethack_status_window {
71 int index;
72 char window_text[NHSW_LINES][MAXWINDOWTEXT + 1];
73 mswin_status_lines * status_lines;
74 back_buffer_t back_buffer;
75 boolean blink_state; /* true = invert blink text */
76 boolean has_blink_fields; /* true if one or more has blink attriubte */
77 } NHStatusWindow, *PNHStatusWindow;
78
79
80 static TCHAR szStatusWindowClass[] = TEXT("MSNHStatusWndClass");
81 LRESULT CALLBACK StatusWndProc(HWND, UINT, WPARAM, LPARAM);
82 static void register_status_window_class(void);
83 static LRESULT onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam);
84
85 #define DEFAULT_COLOR_BG_STATUS COLOR_WINDOW
86 #define DEFAULT_COLOR_FG_STATUS COLOR_WINDOWTEXT
87
88 HWND
mswin_init_status_window()89 mswin_init_status_window()
90 {
91 static int run_once = 0;
92 HWND ret;
93 NHStatusWindow *data;
94 RECT rt;
95 int width, height;
96
97 if (!run_once) {
98 register_status_window_class();
99 run_once = 1;
100 }
101
102 /* get window position */
103 if (GetNHApp()->bAutoLayout) {
104 SetRect(&rt, 0, 0, 0, 0);
105 } else {
106 mswin_get_window_placement(NHW_STATUS, &rt);
107 }
108
109 /* create status window object */
110 width = rt.right - rt.left;
111 height = rt.bottom - rt.top;
112 ret = CreateWindow(szStatusWindowClass, NULL,
113 WS_CHILD | WS_CLIPSIBLINGS | WS_SIZEBOX,
114 rt.left, /* horizontal position of window */
115 rt.top, /* vertical position of window */
116 width, /* window width */
117 height, /* window height */
118 GetNHApp()->hMainWnd, NULL, GetNHApp()->hApp, NULL);
119 if (!ret)
120 panic("Cannot create status window");
121
122 /* Set window caption */
123 SetWindowText(ret, "Status");
124
125 /* create window data */
126 data = (PNHStatusWindow) malloc(sizeof(NHStatusWindow));
127 if (!data)
128 panic("out of memory");
129
130 ZeroMemory(data, sizeof(NHStatusWindow));
131 SetWindowLongPtr(ret, GWLP_USERDATA, (LONG_PTR) data);
132
133 back_buffer_init(&data->back_buffer, ret, width, height);
134
135 mswin_apply_window_style(ret);
136
137 if (status_bg_brush == NULL) {
138 status_bg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_BG_STATUS);
139 status_bg_brush = CreateSolidBrush(status_bg_color);
140 }
141
142 if (status_fg_brush == NULL) {
143 status_fg_color = (COLORREF)GetSysColor(DEFAULT_COLOR_FG_STATUS);
144 status_fg_brush = CreateSolidBrush(status_fg_color);
145 }
146
147 /* set cursor blink timer */
148 SetTimer(ret, 0, STATUS_BLINK_INTERVAL, NULL);
149
150 return ret;
151 }
152
153 void
register_status_window_class()154 register_status_window_class()
155 {
156 WNDCLASS wcex;
157 ZeroMemory(&wcex, sizeof(wcex));
158
159 wcex.style = CS_NOCLOSE;
160 wcex.lpfnWndProc = (WNDPROC) StatusWndProc;
161 wcex.cbClsExtra = 0;
162 wcex.cbWndExtra = 0;
163 wcex.hInstance = GetNHApp()->hApp;
164 wcex.hIcon = NULL;
165 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
166 wcex.hbrBackground = NULL;
167 wcex.lpszMenuName = NULL;
168 wcex.lpszClassName = szStatusWindowClass;
169
170 RegisterClass(&wcex);
171 }
172
173 LRESULT CALLBACK
StatusWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam)174 StatusWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
175 {
176 PNHStatusWindow data;
177
178 data = (PNHStatusWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
179 switch (message) {
180 case WM_MSNH_COMMAND: {
181 switch (wParam) {
182 case MSNH_MSG_PUTSTR: {
183 PMSNHMsgPutstr msg_data = (PMSNHMsgPutstr) lParam;
184 strncpy(data->window_text[data->index], msg_data->text,
185 MAXWINDOWTEXT);
186 data->index = (data->index + 1) % NHSW_LINES;
187 InvalidateRect(hWnd, NULL, TRUE);
188 } break;
189
190 case MSNH_MSG_CLEAR_WINDOW: {
191 data->index = 0;
192 ZeroMemory(data->window_text, sizeof(data->window_text));
193 InvalidateRect(hWnd, NULL, TRUE);
194 } break;
195
196 case MSNH_MSG_GETTEXT: {
197 PMSNHMsgGetText msg_data = (PMSNHMsgGetText) lParam;
198 msg_data->buffer[0] = '\0';
199 size_t space_remaining = msg_data->max_size;
200
201 for (int line = 0; line < NHSW_LINES; line++) {
202 mswin_status_line *status_line = data->status_lines[line].lines;
203 for (int i = 0; i < status_line->status_strings.count; i++) {
204 mswin_status_string * status_string = status_line->status_strings.status_strings[i];
205 if (status_string->space_in_front) {
206 strncat(msg_data->buffer, " ", space_remaining);
207 space_remaining = msg_data->max_size - strlen(msg_data->buffer);
208 }
209 strncat(msg_data->buffer, status_string->str, space_remaining);
210 space_remaining = msg_data->max_size - strlen(msg_data->buffer);
211 }
212 strncat(msg_data->buffer, "\r\n", space_remaining);
213 space_remaining = msg_data->max_size - strlen(msg_data->buffer);
214 }
215 } break;
216
217 case MSNH_MSG_UPDATE_STATUS: {
218 PMSNHMsgUpdateStatus msg_data = (PMSNHMsgUpdateStatus) lParam;
219 data->status_lines = msg_data->status_lines;
220 InvalidateRect(hWnd, NULL, TRUE);
221 } break;
222
223 case MSNH_MSG_RANDOM_INPUT:
224 nhassert(0); // unexpected
225 break;
226
227 } /* end switch( wParam ) { */
228 } break;
229
230 case WM_PAINT:
231 return onWMPaint(hWnd, wParam, lParam);
232
233 case WM_SIZE: {
234 RECT rt;
235 GetWindowRect(hWnd, &rt);
236 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
237 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
238 mswin_update_window_placement(NHW_STATUS, &rt);
239 }
240 return FALSE;
241
242 case WM_MOVE: {
243 RECT rt;
244 GetWindowRect(hWnd, &rt);
245 ScreenToClient(GetNHApp()->hMainWnd, (LPPOINT) &rt);
246 ScreenToClient(GetNHApp()->hMainWnd, ((LPPOINT) &rt) + 1);
247 mswin_update_window_placement(NHW_STATUS, &rt);
248 }
249 return FALSE;
250
251 case WM_DESTROY:
252 free(data);
253 SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) 0);
254 break;
255
256 case WM_SETFOCUS:
257 SetFocus(GetNHApp()->hMainWnd);
258 break;
259
260 case WM_TIMER:
261 data->blink_state = !data->blink_state;
262 if (data->has_blink_fields)
263 InvalidateRect(hWnd, NULL, TRUE);
264 break;
265
266 default:
267 return DefWindowProc(hWnd, message, wParam, lParam);
268 }
269 return 0;
270 }
271
272 static LRESULT
onWMPaint(HWND hWnd,WPARAM wParam,LPARAM lParam)273 onWMPaint(HWND hWnd, WPARAM wParam, LPARAM lParam)
274 {
275 SIZE sz;
276 WCHAR wbuf[BUFSZ];
277 RECT rt;
278 PAINTSTRUCT ps;
279 PNHStatusWindow data;
280 int width, height;
281 RECT clear_rect;
282 HDC front_buffer_hdc;
283
284 data = (PNHStatusWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
285
286 front_buffer_hdc = BeginPaint(hWnd, &ps);
287 GetClientRect(hWnd, &rt);
288
289 width = rt.right - rt.left;
290 height = rt.bottom - rt.top;
291
292 back_buffer_size(&data->back_buffer, width, height);
293
294 HDC hdc = data->back_buffer.hdc;
295
296 SetBkColor(hdc, status_bg_color);
297 SetTextColor(hdc, status_fg_color);
298
299 clear_rect.left = 0;
300 clear_rect.top = 0;
301 clear_rect.right = width;
302 clear_rect.bottom = height;
303
304 FillRect(hdc, &clear_rect, status_bg_brush);
305
306 if (data->status_lines != NULL) {
307
308 data->has_blink_fields = FALSE;
309
310 for (int line = 0; line < NHSW_LINES; line++) {
311 LONG left = rt.left;
312 LONG cy = 0;
313 int vlen;
314
315 mswin_status_line * status_line = &data->status_lines->lines[line];
316
317 for (int i = 0; i < status_line->status_strings.count; i++) {
318 mswin_status_string * status_string = status_line->status_strings.status_strings[i];
319 int clr, atr;
320 int fntatr = ATR_NONE;
321 COLORREF nFg, nBg;
322
323 if (status_string->str == NULL || status_string->str[0] == '\0')
324 continue;
325
326 clr = status_string->color;
327 atr = status_string->attribute;
328
329 const char *str = status_string->str;
330
331 vlen = strlen(str);
332
333 if (atr & HL_BOLD)
334 fntatr = ATR_BOLD;
335 else if (atr & HL_INVERSE)
336 fntatr = ATR_INVERSE;
337 else if (atr & HL_ULINE)
338 fntatr = ATR_ULINE;
339 else if (atr & HL_BLINK) {
340 data->has_blink_fields = TRUE;
341 if (data->blink_state) {
342 fntatr = ATR_INVERSE;
343 atr |= HL_INVERSE;
344 }
345 }
346 else if (atr & HL_DIM)
347 fntatr = ATR_DIM;
348
349 cached_font * fnt = mswin_get_font(NHW_STATUS, fntatr, hdc, FALSE);
350
351 BOOL useUnicode = fnt->supportsUnicode;
352
353 winos_ascii_to_wide_str(str, wbuf, SIZE(wbuf));
354
355 nFg = (clr == NO_COLOR ? status_fg_color
356 : ((clr >= 0 && clr < CLR_MAX) ? nhcolor_to_RGB(clr)
357 : status_fg_color));
358
359 if (atr & HL_DIM) {
360 /* make a dim representation - this can produce color shift */
361 float redReduction = 0.5f;
362 float greenReduction = 0.5f;
363 float blueReduction = 0.25f;
364 uchar red = (uchar)(GetRValue(nFg) * (1.0f - redReduction));
365 uchar green = (uchar)(GetGValue(nFg) * (1.0f - greenReduction));
366 uchar blue = (uchar)(GetBValue(nFg) * (1.0f - blueReduction));
367 nFg = RGB(red, green, blue);
368 }
369
370 nBg = status_bg_color;
371
372 if (status_string->space_in_front)
373 rt.left += fnt->width;
374
375 sz.cy = -1;
376
377 if (status_string->draw_bar && iflags.wc2_hitpointbar) {
378 /* NOTE: we current don't support bar attributes */
379
380 /* when we are drawing bar we need to look at the hp status
381 * field to get the correct percentage and color */
382 COLORREF bar_color = nhcolor_to_RGB(status_string->bar_color);
383 HBRUSH back_brush = CreateSolidBrush(bar_color);
384 RECT barrect;
385
386 /* prepare for drawing */
387 SelectObject(hdc, fnt->hFont);
388 SetBkMode(hdc, OPAQUE);
389 SetBkColor(hdc, status_bg_color);
390 SetTextColor(hdc, status_fg_color);
391
392 if (useUnicode) {
393 /* get bounding rectangle */
394 GetTextExtentPoint32W(hdc, wbuf, vlen, &sz);
395
396 /* first draw title normally */
397 DrawTextW(hdc, wbuf, vlen, &rt, DT_LEFT);
398 }
399 else {
400 /* get bounding rectangle */
401 GetTextExtentPoint32A(hdc, str, vlen, &sz);
402
403 /* first draw title normally */
404 DrawTextA(hdc, str, vlen, &rt, DT_LEFT);
405 }
406 int bar_percent = status_string->bar_percent;
407 if (bar_percent > 0) {
408 /* calc bar length */
409 barrect.left = rt.left;
410 barrect.top = rt.top;
411 barrect.bottom = sz.cy;
412 if (bar_percent > 0)
413 barrect.right = (int)((bar_percent * sz.cx) / 100);
414 /* else
415 barrect.right = sz.cx; */
416
417 /* then draw hpbar on top of title */
418 FillRect(hdc, &barrect, back_brush);
419 SetBkMode(hdc, TRANSPARENT);
420 SetTextColor(hdc, nBg);
421
422 if (useUnicode)
423 DrawTextW(hdc, wbuf, vlen, &barrect, DT_LEFT);
424 else
425 DrawTextA(hdc, str, vlen, &barrect, DT_LEFT);
426 }
427 DeleteObject(back_brush);
428 }
429 else {
430 if (atr & HL_INVERSE) {
431 COLORREF tmp = nFg;
432 nFg = nBg;
433 nBg = tmp;
434 }
435
436 /* prepare for drawing */
437 SelectObject(hdc, fnt->hFont);
438 SetBkMode(hdc, OPAQUE);
439 SetBkColor(hdc, nBg);
440 SetTextColor(hdc, nFg);
441
442 if (useUnicode) {
443 /* get bounding rectangle */
444 GetTextExtentPoint32W(hdc, wbuf, vlen, &sz);
445
446 /* draw */
447 DrawTextW(hdc, wbuf, vlen, &rt, DT_LEFT);
448 }
449 else {
450 /* get bounding rectangle */
451 GetTextExtentPoint32A(hdc, str, vlen, &sz);
452
453 /* draw */
454 DrawTextA(hdc, str, vlen, &rt, DT_LEFT);
455 }
456 }
457 assert(sz.cy >= 0);
458
459 rt.left += sz.cx;
460 cy = max(cy, sz.cy);
461 }
462
463 rt.left = left;
464 rt.top += cy;
465 }
466 }
467
468 BitBlt(front_buffer_hdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
469
470 EndPaint(hWnd, &ps);
471
472 return 0;
473 }
474
475 void
mswin_status_window_size(HWND hWnd,LPSIZE sz)476 mswin_status_window_size(HWND hWnd, LPSIZE sz)
477 {
478 RECT rt;
479
480 GetClientRect(hWnd, &rt);
481
482 PNHStatusWindow data = (PNHStatusWindow) GetWindowLongPtr(hWnd, GWLP_USERDATA);
483 if (data) {
484 HDC hdc = GetDC(hWnd);
485 cached_font * font = mswin_get_font(NHW_STATUS, ATR_NONE, hdc, FALSE);
486 HGDIOBJ saveFont = SelectObject(hdc, font->hFont);
487
488 SIZE text_sz;
489 GetTextExtentPoint32(hdc, _T("W"), 1, &text_sz);
490
491 TEXTMETRIC tm;
492 GetTextMetrics(hdc, &tm);
493
494 rt.bottom = rt.top + text_sz.cy * NHSW_LINES;
495
496 SelectObject(hdc, saveFont);
497 ReleaseDC(hWnd, hdc);
498 }
499 AdjustWindowRect(&rt, GetWindowLong(hWnd, GWL_STYLE), FALSE);
500 sz->cx = rt.right - rt.left;
501 sz->cy = rt.bottom - rt.top;
502 }
503