1 #if !defined(TILES) && defined(_WIN32)
2 #define UNICODE 1
3 #ifndef CMAKE
4 #define _UNICODE 1
5 #endif
6 #include "cursesport.h" // IWYU pragma: associated
7
8 #include <cstdlib>
9 #include <fstream>
10
11 #include "cached_options.h"
12 #include "cursesdef.h"
13 #include "options.h"
14 #include "output.h"
15 #include "color.h"
16 #include "catacharset.h"
17 #include "get_version.h"
18 #include "init.h"
19 #include "input.h"
20 #include "path_info.h"
21 #include "filesystem.h"
22 #include "debug.h"
23 #include "cata_utility.h"
24 #include "string_formatter.h"
25 #include "color_loader.h"
26 #include "font_loader.h"
27 #include "platform_win.h"
28 #include "mmsystem.h"
29 #include "ui_manager.h"
30 #include "wcwidth.h"
31
32 //***********************************
33 //Globals *
34 //***********************************
35
36 static constexpr int ERR = -1;
37 const wchar_t *szWindowClass = L"CataCurseWindow"; //Class name :D
38 HINSTANCE WindowINST; //the instance of the window
39 HWND WindowHandle; //the handle of the window
40 HDC WindowDC; //Device Context of the window, used for backbuffer
41 int WindowWidth; //Width of the actual window, not the curses window
42 int WindowHeight; //Height of the actual window, not the curses window
43 int lastchar; //the last character that was pressed, resets in getch
44 int inputdelay; //How long getch will wait for a character to be typed
45 HDC backbuffer; //an off-screen DC to prevent flickering, lower CPU
46 HBITMAP backbit; //the bitmap that is used in conjunction with the above
47 int fontwidth; //the width of the font, background is always this size
48 int fontheight; //the height of the font, background is always this size
49 int halfwidth; //half of the font width, used for centering lines
50 int halfheight; //half of the font height, used for centering lines
51 HFONT font; //Handle to the font created by CreateFont
52 std::array<RGBQUAD, color_loader<RGBQUAD>::COLOR_NAMES_COUNT> windowsPalette;
53 unsigned char *dcbits; //the bits of the screen image, for direct access
54 bool CursorVisible = true; // Showcursor is a somewhat weird function
55 bool needs_resize = false; // The window needs to be resized
56 bool initialized = false;
57
58 static int TERMINAL_WIDTH;
59 static int TERMINAL_HEIGHT;
60
61 //***********************************
62 //Non-curses, Window functions *
63 //***********************************
64
65 // Declare this locally, because it's not generally cross-compatible in cursesport.h
66 LRESULT CALLBACK ProcessMessages( HWND__ *hWnd, std::uint32_t Msg, WPARAM wParam, LPARAM lParam );
67
widen(const std::string & s)68 static std::wstring widen( const std::string &s )
69 {
70 if( s.empty() ) {
71 // MultiByteToWideChar can not handle this case
72 return std::wstring();
73 }
74 std::vector<wchar_t> buffer( s.length() );
75 const int newlen = MultiByteToWideChar( CP_UTF8, 0, s.c_str(), s.length(),
76 buffer.data(), buffer.size() );
77 // On failure, newlen is 0, returns an empty strings.
78 return std::wstring( buffer.data(), newlen );
79 }
80
81 static constexpr uint32_t WndStyle = WS_CAPTION | WS_MINIMIZEBOX | WS_SIZEBOX |
82 WS_MAXIMIZEBOX | WS_SYSMENU | WS_VISIBLE;
83
84 // Registers, creates, and shows the Window!!
WinCreate()85 static bool WinCreate()
86 {
87 // Get current process handle
88 WindowINST = GetModuleHandle( nullptr );
89 std::string title = string_format( "Cataclysm: Dark Days Ahead - %s", getVersionString() );
90
91 // Register window class
92 WNDCLASSEXW WindowClassType = WNDCLASSEXW();
93 WindowClassType.cbSize = sizeof( WNDCLASSEXW );
94 // The procedure that gets msgs
95 WindowClassType.lpfnWndProc = ProcessMessages;
96 // hInstance
97 WindowClassType.hInstance = WindowINST;
98 // Get first resource
99 WindowClassType.hIcon = LoadIcon( WindowINST, MAKEINTRESOURCE( 0 ) );
100 WindowClassType.hIconSm = LoadIcon( WindowINST, MAKEINTRESOURCE( 0 ) );
101 WindowClassType.hCursor = LoadCursor( nullptr, IDC_ARROW );
102 WindowClassType.lpszClassName = szWindowClass;
103 if( !RegisterClassExW( &WindowClassType ) ) {
104 return false;
105 }
106
107 // Adjust window size
108 // Basic window, show on creation
109 RECT WndRect;
110 WndRect.left = WndRect.top = 0;
111 WndRect.right = WindowWidth;
112 WndRect.bottom = WindowHeight;
113 AdjustWindowRect( &WndRect, WndStyle, false );
114
115 // Center window
116 RECT WorkArea;
117 SystemParametersInfo( SPI_GETWORKAREA, 0, &WorkArea, 0 );
118 int WindowX = WorkArea.right / 2 - ( WndRect.right - WndRect.left ) / 2;
119 int WindowY = WorkArea.bottom / 2 - ( WndRect.bottom - WndRect.top ) / 2;
120
121 // Magic
122 WindowHandle = CreateWindowExW( 0, szWindowClass, widen( title ).c_str(), WndStyle,
123 WindowX, WindowY,
124 WndRect.right - WndRect.left,
125 WndRect.bottom - WndRect.top,
126 nullptr, nullptr, WindowINST, nullptr );
127 return WindowHandle != nullptr;
128
129 }
130
131 // Unregisters, releases the DC if needed, and destroys the window.
WinDestroy()132 static void WinDestroy()
133 {
134 if( ( WindowDC != nullptr ) && ( ReleaseDC( WindowHandle, WindowDC ) == 0 ) ) {
135 WindowDC = nullptr;
136 }
137 if( WindowHandle != nullptr && ( !( DestroyWindow( WindowHandle ) ) ) ) {
138 WindowHandle = nullptr;
139 }
140 if( !( UnregisterClassW( szWindowClass, WindowINST ) ) ) {
141 WindowINST = nullptr;
142 }
143 }
144
145 // Creates a backbuffer to prevent flickering
create_backbuffer()146 static void create_backbuffer()
147 {
148 if( WindowDC != nullptr ) {
149 ReleaseDC( WindowHandle, WindowDC );
150 }
151 if( backbuffer != nullptr ) {
152 ReleaseDC( WindowHandle, backbuffer );
153 }
154 WindowDC = GetDC( WindowHandle );
155 backbuffer = CreateCompatibleDC( WindowDC );
156
157 BITMAPINFO bmi = BITMAPINFO();
158 bmi.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
159 bmi.bmiHeader.biWidth = WindowWidth;
160 bmi.bmiHeader.biHeight = -WindowHeight;
161 bmi.bmiHeader.biPlanes = 1;
162 bmi.bmiHeader.biBitCount = 8;
163 bmi.bmiHeader.biCompression = BI_RGB; // Raw RGB
164 bmi.bmiHeader.biSizeImage = WindowWidth * WindowHeight * 1;
165 bmi.bmiHeader.biClrUsed = color_loader<RGBQUAD>::COLOR_NAMES_COUNT; // Colors in the palette
166 bmi.bmiHeader.biClrImportant = color_loader<RGBQUAD>::COLOR_NAMES_COUNT; // Colors in the palette
167 backbit = CreateDIBSection( nullptr, &bmi, DIB_RGB_COLORS, reinterpret_cast<void **>( &dcbits ),
168 nullptr,
169 0 );
170 DeleteObject( SelectObject( backbuffer, backbit ) ); //load the buffer into DC
171 }
172
handle_resize(int,int)173 bool handle_resize( int, int )
174 {
175 if( !initialized ) {
176 return false;
177 }
178 needs_resize = false;
179 RECT WndRect;
180 if( GetClientRect( WindowHandle, &WndRect ) ) {
181 TERMINAL_WIDTH = WndRect.right / fontwidth;
182 TERMINAL_HEIGHT = WndRect.bottom / fontheight;
183 WindowWidth = TERMINAL_WIDTH * fontwidth;
184 WindowHeight = TERMINAL_HEIGHT * fontheight;
185 catacurses::stdscr = catacurses::newwin( TERMINAL_HEIGHT, TERMINAL_WIDTH, point_zero );
186 catacurses::resizeterm();
187 create_backbuffer();
188 SetBkMode( backbuffer, TRANSPARENT ); //Transparent font backgrounds
189 SelectObject( backbuffer, font ); //Load our font into the DC
190 color_loader<RGBQUAD>().load( windowsPalette );
191 if( SetDIBColorTable( backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) {
192 throw std::runtime_error( "SetDIBColorTable failed" );
193 }
194 ui_manager::screen_resized();
195 }
196
197 return true;
198 }
199
resize_term(const int cell_w,const int cell_h)200 void resize_term( const int cell_w, const int cell_h )
201 {
202 RECT WndRect;
203 WndRect.left = WndRect.top = 0;
204 WndRect.right = cell_w * fontwidth * get_scaling_factor();
205 WndRect.bottom = cell_h * fontheight * get_scaling_factor();
206 if( !AdjustWindowRect( &WndRect, WndStyle, false ) ) {
207 return;
208 }
209 if( !SetWindowPos( WindowHandle, nullptr, 0, 0,
210 WndRect.right - WndRect.left, WndRect.bottom - WndRect.top,
211 SWP_NOMOVE | SWP_NOZORDER ) ) {
212 return;
213 }
214 GetClientRect( WindowHandle, &WndRect );
215 handle_resize( WndRect.right - WndRect.left, WndRect.bottom - WndRect.top );
216 }
217
218 // Copied from sdlcurses.cpp
219 #define ALT_BUFFER_SIZE 8
220 static char alt_buffer[ALT_BUFFER_SIZE] = {};
221 static int alt_buffer_len = 0;
222 static bool alt_down = false;
223 static bool shift_down = false;
224
begin_alt_code()225 static void begin_alt_code()
226 {
227 alt_buffer[0] = '\0';
228 alt_down = true;
229 alt_buffer_len = 0;
230 }
231
add_alt_code(char c)232 static void add_alt_code( char c )
233 {
234 // Not exactly how it works, but acceptable
235 if( c >= '0' && c <= '9' ) {
236 if( alt_buffer_len < ALT_BUFFER_SIZE - 1 ) {
237 alt_buffer[alt_buffer_len] = c;
238 alt_buffer[++alt_buffer_len] = '\0';
239 }
240 }
241 }
242
end_alt_code()243 static int end_alt_code()
244 {
245 alt_down = false;
246 return atoi( alt_buffer );
247 }
248
249 // This function processes any Windows messages we get. Keyboard, OnClose, etc
ProcessMessages(HWND__ * hWnd,unsigned int Msg,WPARAM wParam,LPARAM lParam)250 LRESULT CALLBACK ProcessMessages( HWND__ *hWnd, unsigned int Msg,
251 WPARAM wParam, LPARAM lParam )
252 {
253 uint16_t MouseOver;
254 switch( Msg ) {
255 case WM_DEADCHAR:
256 case WM_CHAR:
257 lastchar = static_cast<int>( wParam );
258 switch( lastchar ) {
259 case VK_TAB:
260 lastchar = ( shift_down ) ? KEY_BTAB : '\t';
261 break;
262 case VK_RETURN:
263 // Reroute ENTER key for compatibility purposes
264 lastchar = 10;
265 break;
266 case VK_BACK:
267 // Reroute BACKSPACE key for compatibility purposes
268 lastchar = 127;
269 break;
270 }
271 return 0;
272
273 case WM_KEYDOWN:
274 // Here we handle non-character input
275 switch( wParam ) {
276 case VK_SHIFT:
277 shift_down = true;
278 break;
279 case VK_LEFT:
280 lastchar = KEY_LEFT;
281 break;
282 case VK_RIGHT:
283 lastchar = KEY_RIGHT;
284 break;
285 case VK_UP:
286 lastchar = KEY_UP;
287 break;
288 case VK_DOWN:
289 lastchar = KEY_DOWN;
290 break;
291 case VK_NEXT:
292 lastchar = KEY_NPAGE;
293 break;
294 case VK_PRIOR:
295 lastchar = KEY_PPAGE;
296 break;
297 case VK_HOME:
298 lastchar = KEY_HOME;
299 break;
300 case VK_END:
301 lastchar = KEY_END;
302 break;
303 case VK_F1:
304 lastchar = KEY_F( 1 );
305 break;
306 case VK_F2:
307 lastchar = KEY_F( 2 );
308 break;
309 case VK_F3:
310 lastchar = KEY_F( 3 );
311 break;
312 case VK_F4:
313 lastchar = KEY_F( 4 );
314 break;
315 case VK_F5:
316 lastchar = KEY_F( 5 );
317 break;
318 case VK_F6:
319 lastchar = KEY_F( 6 );
320 break;
321 case VK_F7:
322 lastchar = KEY_F( 7 );
323 break;
324 case VK_F8:
325 lastchar = KEY_F( 8 );
326 break;
327 case VK_F9:
328 lastchar = KEY_F( 9 );
329 break;
330 case VK_F10:
331 lastchar = KEY_F( 10 );
332 break;
333 case VK_F11:
334 lastchar = KEY_F( 11 );
335 break;
336 case VK_F12:
337 lastchar = KEY_F( 12 );
338 break;
339 default:
340 break;
341 }
342 return 0;
343
344 case WM_KEYUP:
345 if( wParam == VK_SHIFT ) {
346 shift_down = false;
347 return 0;
348 }
349
350 // LeftAlt hack
351 if( !GetAsyncKeyState( VK_LMENU ) && alt_down ) {
352 if( int code = end_alt_code() ) {
353 lastchar = code;
354 }
355 }
356 return 0;
357
358 case WM_SIZE:
359 WINDOWPLACEMENT win_placement;
360 win_placement.length = sizeof( win_placement );
361 if( GetWindowPlacement( hWnd, &win_placement ) && win_placement.showCmd != SW_SHOWMINIMIZED ) {
362 needs_resize = true;
363 }
364 return 0;
365
366 case WM_SYSCHAR:
367 add_alt_code( static_cast<char>( wParam ) );
368 return 0;
369
370 case WM_SYSKEYDOWN:
371 // LeftAlt hack
372 if( GetAsyncKeyState( VK_LMENU ) && !alt_down ) {
373 begin_alt_code();
374 }
375 break;
376
377 case WM_SETCURSOR:
378 MouseOver = LOWORD( lParam );
379 if( get_option<std::string>( "HIDE_CURSOR" ) == "hide" ) {
380 if( MouseOver == HTCLIENT && CursorVisible ) {
381 CursorVisible = false;
382 ShowCursor( false );
383 } else if( MouseOver != HTCLIENT && !CursorVisible ) {
384 CursorVisible = true;
385 ShowCursor( true );
386 }
387 } else if( !CursorVisible ) {
388 CursorVisible = true;
389 ShowCursor( true );
390 }
391 break;
392
393 case WM_ERASEBKGND:
394 // Don't erase background
395 return 1;
396
397 case WM_PAINT:
398 BitBlt( WindowDC, 0, 0, WindowWidth, WindowHeight, backbuffer, 0, 0, SRCCOPY );
399 ValidateRect( WindowHandle, nullptr );
400 return 0;
401
402 case WM_DESTROY:
403 // A messy exit, but easy way to escape game loop
404 exit( 0 );
405 }
406
407 return DefWindowProcW( hWnd, Msg, wParam, lParam );
408 }
409
410 //The following 3 methods use mem functions for fast drawing
VertLineDIB(int x,int y,int y2,int thickness,unsigned char color)411 inline void VertLineDIB( int x, int y, int y2, int thickness, unsigned char color )
412 {
413 int j;
414 for( j = y; j < y2; j++ ) {
415 memset( &dcbits[x + j * WindowWidth], color, thickness );
416 }
417 }
HorzLineDIB(int x,int y,int x2,int thickness,unsigned char color)418 inline void HorzLineDIB( int x, int y, int x2, int thickness, unsigned char color )
419 {
420 int j;
421 for( j = y; j < y + thickness; j++ ) {
422 memset( &dcbits[x + j * WindowWidth], color, x2 - x );
423 }
424 }
FillRectDIB(int x,int y,int width,int height,unsigned char color)425 inline void FillRectDIB( int x, int y, int width, int height, unsigned char color )
426 {
427 int j;
428 for( j = y; j < y + height; j++ )
429 //NOTE TO FUTURE: this breaks if j is negative. Apparently it doesn't break if j is too large, though?
430 {
431 memset( &dcbits[x + j * WindowWidth], color, width );
432 }
433 }
434
curses_drawwindow(const catacurses::window & w)435 void cata_cursesport::curses_drawwindow( const catacurses::window &w )
436 {
437
438 WINDOW *const win = w.get<WINDOW>();
439 int i = 0;
440 int j = 0;
441 int drawx = 0;
442 int drawy = 0;
443 wchar_t tmp;
444
445 for( j = 0; j < win->height; j++ ) {
446 if( win->line[j].touched ) {
447 win->line[j].touched = false;
448
449 for( i = 0; i < win->width; i++ ) {
450 const cursecell &cell = win->line[j].chars[i];
451 if( cell.ch.empty() ) {
452 // second cell of a multi-cell character
453 continue;
454 }
455 drawx = ( ( win->pos.x + i ) * fontwidth );
456 drawy = ( ( win->pos.y + j ) * fontheight ); //-j;
457 if( drawx + fontwidth > WindowWidth || drawy + fontheight > WindowHeight ) {
458 // Outside of the display area, would not render anyway
459 continue;
460 }
461
462 int FG = cell.FG;
463 int BG = cell.BG;
464 FillRectDIB( drawx, drawy, fontwidth, fontheight, BG );
465 static const std::string space_string = " ";
466 // Spaces don't need any drawing except background
467 if( cell.ch == space_string ) {
468 continue;
469 }
470
471 tmp = UTF8_getch( cell.ch );
472 if( tmp != UNKNOWN_UNICODE ) {
473
474 int color = RGB( windowsPalette[FG].rgbRed, windowsPalette[FG].rgbGreen,
475 windowsPalette[FG].rgbBlue );
476 SetTextColor( backbuffer, color );
477
478 int cw = mk_wcwidth( tmp );
479 if( cw > 1 ) {
480 FillRectDIB( drawx + fontwidth * ( cw - 1 ), drawy, fontwidth, fontheight, BG );
481 i += cw - 1;
482 }
483 if( tmp ) {
484 const std::wstring utf16 = widen( cell.ch );
485 ExtTextOutW( backbuffer, drawx, drawy, 0, nullptr, utf16.c_str(), utf16.length(), nullptr );
486 }
487 } else {
488 switch( static_cast<unsigned char>( win->line[j].chars[i].ch[0] ) ) {
489 // box bottom/top side (horizontal line)
490 case LINE_OXOX_C:
491 HorzLineDIB( drawx, drawy + halfheight, drawx + fontwidth, 1, FG );
492 break;
493 // box left/right side (vertical line)
494 case LINE_XOXO_C:
495 VertLineDIB( drawx + halfwidth, drawy, drawy + fontheight, 2, FG );
496 break;
497 // box top left
498 case LINE_OXXO_C:
499 HorzLineDIB( drawx + halfwidth, drawy + halfheight, drawx + fontwidth, 1, FG );
500 VertLineDIB( drawx + halfwidth, drawy + halfheight, drawy + fontheight, 2, FG );
501 break;
502 // box top right
503 case LINE_OOXX_C:
504 HorzLineDIB( drawx, drawy + halfheight, drawx + halfwidth, 1, FG );
505 VertLineDIB( drawx + halfwidth, drawy + halfheight, drawy + fontheight, 2, FG );
506 break;
507 // box bottom right
508 case LINE_XOOX_C:
509 HorzLineDIB( drawx, drawy + halfheight, drawx + halfwidth, 1, FG );
510 VertLineDIB( drawx + halfwidth, drawy, drawy + halfheight + 1, 2, FG );
511 break;
512 // box bottom left
513 case LINE_XXOO_C:
514 HorzLineDIB( drawx + halfwidth, drawy + halfheight, drawx + fontwidth, 1, FG );
515 VertLineDIB( drawx + halfwidth, drawy, drawy + halfheight + 1, 2, FG );
516 break;
517 // box bottom north T (left, right, up)
518 case LINE_XXOX_C:
519 HorzLineDIB( drawx, drawy + halfheight, drawx + fontwidth, 1, FG );
520 VertLineDIB( drawx + halfwidth, drawy, drawy + halfheight, 2, FG );
521 break;
522 // box bottom east T (up, right, down)
523 case LINE_XXXO_C:
524 VertLineDIB( drawx + halfwidth, drawy, drawy + fontheight, 2, FG );
525 HorzLineDIB( drawx + halfwidth, drawy + halfheight, drawx + fontwidth, 1, FG );
526 break;
527 // box bottom south T (left, right, down)
528 case LINE_OXXX_C:
529 HorzLineDIB( drawx, drawy + halfheight, drawx + fontwidth, 1, FG );
530 VertLineDIB( drawx + halfwidth, drawy + halfheight, drawy + fontheight, 2, FG );
531 break;
532 // box X (left down up right)
533 case LINE_XXXX_C:
534 HorzLineDIB( drawx, drawy + halfheight, drawx + fontwidth, 1, FG );
535 VertLineDIB( drawx + halfwidth, drawy, drawy + fontheight, 2, FG );
536 break;
537 // box bottom east T (left, down, up)
538 case LINE_XOXX_C:
539 VertLineDIB( drawx + halfwidth, drawy, drawy + fontheight, 2, FG );
540 HorzLineDIB( drawx, drawy + halfheight, drawx + halfwidth, 1, FG );
541 break;
542 default:
543 break;
544 }//switch (tmp)
545 }//(tmp < 0)
546 }//for (i=0;i<win->width;i++)
547 }
548 }// for (j=0;j<win->height;j++)
549 // We drew the window, mark it as so
550 win->draw = false;
551 }
552
553 // Check for any window messages (keypress, paint, mousemove, etc)
CheckMessages()554 static void CheckMessages()
555 {
556 MSG msg;
557 while( PeekMessage( &msg, nullptr, 0, 0, PM_REMOVE ) ) {
558 TranslateMessage( &msg );
559 DispatchMessage( &msg );
560 }
561 if( needs_resize ) {
562 restore_on_out_of_scope<int> prev_lastchar( lastchar );
563 handle_resize( 0, 0 );
564 refresh_display();
565 }
566 }
567
568 // Calculates the new width of the window
projected_window_width()569 int projected_window_width()
570 {
571 return get_option<int>( "TERMINAL_X" ) * fontwidth;
572 }
573
574 // Calculates the new height of the window
projected_window_height()575 int projected_window_height()
576 {
577 return get_option<int>( "TERMINAL_Y" ) * fontheight;
578 }
579
get_terminal_width()580 int get_terminal_width()
581 {
582 return TERMINAL_WIDTH;
583 }
584
get_terminal_height()585 int get_terminal_height()
586 {
587 return TERMINAL_HEIGHT;
588 }
589
590 //***********************************
591 //Pseudo-Curses Functions *
592 //***********************************
593
594 // Basic Init, create the font, backbuffer, etc
init_interface()595 void catacurses::init_interface()
596 {
597 lastchar = -1;
598 inputdelay = -1;
599
600 font_loader fl;
601 fl.load();
602 ::fontwidth = fl.fontwidth;
603 ::fontheight = fl.fontheight;
604 halfwidth = fontwidth / 2;
605 halfheight = fontheight / 2;
606 TERMINAL_WIDTH = get_option<int>( "TERMINAL_X" );
607 TERMINAL_HEIGHT = get_option<int>( "TERMINAL_Y" );
608 WindowWidth = TERMINAL_WIDTH * fontwidth;
609 WindowHeight = TERMINAL_HEIGHT * fontheight;
610
611 // Create the actual window, register it, etc
612 WinCreate();
613 // Set Sleep resolution to 1ms
614 timeBeginPeriod( 1 );
615 // Let the message queue handle setting up the window
616 CheckMessages();
617
618 create_backbuffer();
619
620 // Load private fonts
621 if( SetCurrentDirectoryW( L"data\\font" ) ) {
622 WIN32_FIND_DATAW findData;
623 for( HANDLE findFont = FindFirstFileW( L".\\*", &findData ); findFont != INVALID_HANDLE_VALUE; ) {
624 // Skip folders
625 if( !( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ) {
626 AddFontResourceExW( findData.cFileName, FR_PRIVATE, nullptr );
627 }
628 if( !FindNextFileW( findFont, &findData ) ) {
629 FindClose( findFont );
630 break;
631 }
632 }
633 SetCurrentDirectoryW( L"..\\.." );
634 }
635
636 // Use desired font, if possible
637 cata_assert( !fl.typeface.empty() );
638 font = CreateFontW( fontheight, fontwidth, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
639 DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
640 PROOF_QUALITY, FF_MODERN, widen( fl.typeface.front() ).c_str() );
641
642 // Transparent font backgrounds
643 SetBkMode( backbuffer, TRANSPARENT );
644 // Load our font into the DC
645 SelectObject( backbuffer, font );
646
647 color_loader<RGBQUAD>().load( windowsPalette );
648 if( SetDIBColorTable( backbuffer, 0, windowsPalette.size(), windowsPalette.data() ) == 0 ) {
649 throw std::runtime_error( "SetDIBColorTable failed" );
650 }
651 init_colors();
652
653 stdscr = newwin( get_option<int>( "TERMINAL_Y" ), get_option<int>( "TERMINAL_X" ), point_zero );
654 //newwin calls `new WINDOW`, and that will throw, but not return nullptr.
655
656 initialized = true;
657 }
658
659 // A very accurate and responsive timer (NEVER use GetTickCount)
GetPerfCount()660 static uint64_t GetPerfCount()
661 {
662 uint64_t Count;
663 QueryPerformanceCounter( reinterpret_cast<PLARGE_INTEGER>( &Count ) );
664 return Count;
665 }
666
667 // we can probably add support for keycode mode, but wincurse is deprecated
668 // so we just ignore the mode argument.
get_input_event(const keyboard_mode)669 input_event input_manager::get_input_event( const keyboard_mode /*preferred_keyboard_mode*/ )
670 {
671 if( test_mode ) {
672 // input should be skipped in caller's code
673 throw std::runtime_error( "input_manager::get_input_event called in test mode" );
674 }
675
676 // standards note: getch is sometimes required to call refresh
677 // see, e.g., http://linux.die.net/man/3/getch
678 // so although it's non-obvious, that refresh() call (and maybe InvalidateRect?) IS supposed to be there
679 uint64_t Frequency;
680 QueryPerformanceFrequency( reinterpret_cast<PLARGE_INTEGER>( &Frequency ) );
681 wrefresh( catacurses::stdscr );
682 InvalidateRect( WindowHandle, nullptr, true );
683 lastchar = ERR;
684 if( inputdelay < 0 ) {
685 for( ; lastchar == ERR; Sleep( 1 ) ) {
686 CheckMessages();
687 }
688 } else if( inputdelay > 0 ) {
689 for( uint64_t t0 = GetPerfCount(), t1 = 0; t1 < ( t0 + inputdelay * Frequency / 1000 );
690 t1 = GetPerfCount() ) {
691 CheckMessages();
692 if( lastchar != ERR ) {
693 break;
694 }
695 Sleep( 1 );
696 }
697 } else {
698 CheckMessages();
699 }
700
701 if( lastchar != ERR && get_option<std::string>( "HIDE_CURSOR" ) == "hidekb" && CursorVisible ) {
702 CursorVisible = false;
703 ShowCursor( false );
704 }
705
706 previously_pressed_key = 0;
707 input_event rval;
708 if( lastchar == ERR ) {
709 if( input_timeout > 0 ) {
710 rval.type = input_event_t::timeout;
711 } else {
712 rval.type = input_event_t::error;
713 }
714 } else {
715 // == Unicode DELETE
716 if( lastchar == 127 ) {
717 previously_pressed_key = KEY_BACKSPACE;
718 return input_event( KEY_BACKSPACE, input_event_t::keyboard_char );
719 }
720 rval.type = input_event_t::keyboard_char;
721 rval.text = utf32_to_utf8( lastchar );
722 previously_pressed_key = lastchar;
723 // for compatibility only add the first byte, not the code point
724 // as it would conflict with the special keys defined by ncurses
725 rval.add_input( lastchar );
726 }
727
728 return rval;
729 }
730
gamepad_available()731 bool gamepad_available()
732 {
733 return false;
734 }
735
get_coordinates(const catacurses::window &)736 cata::optional<tripoint> input_context::get_coordinates( const catacurses::window & )
737 {
738 // TODO: implement this properly
739 return cata::nullopt;
740 }
741
742 // Ends the terminal, destroy everything
endwin()743 void catacurses::endwin()
744 {
745 DeleteObject( font );
746 WinDestroy();
747 // Unload it
748 RemoveFontResourceExA( "data\\font", FR_PRIVATE, nullptr );
749 }
750
751 template<>
from_rgb(const int r,const int g,const int b)752 RGBQUAD color_loader<RGBQUAD>::from_rgb( const int r, const int g, const int b )
753 {
754 RGBQUAD result;
755 // Blue
756 result.rgbBlue = b;
757 // Green
758 result.rgbGreen = g;
759 // Red
760 result.rgbRed = r;
761 //The Alpha, is not used, so just set it to 0
762 result.rgbReserved = 0;
763 return result;
764 }
765
set_timeout(const int t)766 void input_manager::set_timeout( const int t )
767 {
768 input_timeout = t;
769 inputdelay = t;
770 }
771
handle_additional_window_clear(WINDOW *)772 void cata_cursesport::handle_additional_window_clear( WINDOW * )
773 {
774 }
775
get_scaling_factor()776 int get_scaling_factor()
777 {
778 return 1;
779 }
780
getWindowHandle()781 HWND getWindowHandle()
782 {
783 return WindowHandle;
784 }
785
refresh_display()786 void refresh_display()
787 {
788 RedrawWindow( WindowHandle, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW );
789 }
790
791 #endif
792