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