1 #define  _WIN32_WINNT 0x0501
2 #include <SDL2/SDL.h>
3 #include "video.h"
4 #include "wx-sdl2-video.h"
5 #include "wx-utils.h"
6 #include "ibm.h"
7 #include "mouse.h"
8 #include "wx-display.h"
9 #include "plat-keyboard.h"
10 
11 #ifdef _WIN32
12 #define BITMAP WINDOWS_BITMAP
13 #undef UNICODE
14 #include <windows.h>
15 #include <windowsx.h>
16 #undef BITMAP
17 #endif
18 
19 HHOOK hKeyboardHook;
20 
21 static HINSTANCE hinstance = 0;
22 static HWND hwnd = 0;
23 static HMENU menu = 0;
24 
25 #define szClassName "PCemMainWnd"
26 #define szSubClassName "PCemSubWnd"
27 
28 static RAWINPUTDEVICE device;
29 static uint16_t scancode_map[65536];
30 
31 SDL_mutex* rendererMutex;
32 SDL_cond* rendererCond;
33 SDL_Thread* renderthread = NULL;
34 
35 SDL_Window* window = NULL;
36 SDL_Window* dummy_window = NULL;
37 
38 int rendering = 0;
39 
40 int mousecapture = 0;
41 
42 extern int pause;
43 extern int video_scale;
44 extern int take_screenshot;
45 
46 void* window_ptr;
47 void* menu_ptr;
48 
49 SDL_Rect remembered_rect;
50 int remembered_mouse_x = 0;
51 int remembered_mouse_y = 0;
52 
53 int custom_resolution_width = 640;
54 int custom_resolution_height = 480;
55 
56 int win_doresize = 0;
57 int winsizex = 640, winsizey = 480;
58 
59 void renderer_start();
60 void renderer_stop(int timeout);
61 
62 int trigger_fullscreen = 0;
63 int trigger_screenshot = 0;
64 int trigger_togglewindow = 0;
65 int trigger_inputrelease = 0;
66 
67 extern void device_force_redraw();
68 extern void mouse_wheel_update(int);
69 extern void toggle_fullscreen();
70 
display_resize(int width,int height)71 void display_resize(int width, int height)
72 {
73         winsizex = width*(video_scale+1) >> 1;
74         winsizey = height*(video_scale+1) >> 1;
75 
76         SDL_Rect rect;
77         rect.x = rect.y = 0;
78         rect.w = winsizex;
79         rect.h = winsizey;
80         sdl_scale(video_fullscreen_scale, rect, &rect, winsizex, winsizey);
81         winsizex = rect.w;
82         winsizey = rect.h;
83 
84         win_doresize = 1;
85 }
86 
releasemouse()87 void releasemouse()
88 {
89         if (mousecapture)
90         {
91                 SDL_SetWindowGrab(window, SDL_FALSE);
92                 SDL_SetRelativeMouseMode(SDL_FALSE);
93                 mousecapture = 0;
94         }
95 }
96 
is_fullscreen()97 int is_fullscreen()
98 {
99         if (window)
100         {
101                 int flags = SDL_GetWindowFlags(window);
102                 return (flags&SDL_WINDOW_FULLSCREEN) || (flags&SDL_WINDOW_FULLSCREEN_DESKTOP);
103         }
104         return 0;
105 }
106 
107 /* This is so we can disambiguate scan codes that would otherwise conflict and get
108    passed on incorrectly. */
convert_scan_code(UINT16 scan_code)109 UINT16 convert_scan_code(UINT16 scan_code)
110 {
111         switch (scan_code)
112         {
113                 case 0xE001:
114                 return 0xF001;
115                 case 0xE002:
116                 return 0xF002;
117                 case 0xE005:
118                 return 0xF005;
119                 case 0xE006:
120                 return 0xF006;
121                 case 0xE007:
122                 return 0xF007;
123                 case 0xE071:
124                 return 0xF008;
125                 case 0xE072:
126                 return 0xF009;
127                 case 0xE07F:
128                 return 0xF00A;
129                 case 0xE0E1:
130                 return 0xF00B;
131                 case 0xE0EE:
132                 return 0xF00C;
133                 case 0xE0F1:
134                 return 0xF00D;
135                 case 0xE0FE:
136                 return 0xF00E;
137                 case 0xE0EF:
138                 return 0xF00F;
139 
140                 default:
141                 return scan_code;
142         }
143 }
144 
get_registry_key_map()145 void get_registry_key_map()
146 {
147         char *keyName = "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layout";
148         char *valueName = "Scancode Map";
149         char buf[32768];
150         DWORD bufSize;
151         HKEY hKey;
152         int j;
153 
154         /* First, prepare the default scan code map list which is 1:1.
155            Remappings will be inserted directly into it.
156            65536 bytes so scan codes fit in easily and it's easy to find what each maps too,
157            since each array element is a scan code and provides for E0, etc. ones too. */
158         for (j = 0; j < 65536; j++)
159                 scancode_map[j] = convert_scan_code(j);
160 
161         bufSize = 32768;
162         pclog("Preparing scan code map list...\n");
163         /* Get the scan code remappings from:
164            HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout */
165         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName, 0, 1, &hKey) == ERROR_SUCCESS)
166         {
167                 if(RegQueryValueEx(hKey, valueName, NULL, NULL, (LPBYTE)buf, &bufSize) == ERROR_SUCCESS)
168                 {
169                         UINT32 *bufEx2 = (UINT32 *) buf;
170                         int scMapCount = bufEx2[2];
171                         pclog("%lu scan code mappings found!\n", scMapCount);
172                         if ((bufSize != 0) && (scMapCount != 0))
173                         {
174                                 UINT16 *bufEx = (UINT16 *) (buf + 12);
175                                 pclog("More than zero scan code mappings found, processing...\n");
176                                 for (j = 0; j < scMapCount*2; j += 2)
177                                 {
178                                         /* Each scan code is 32-bit: 16 bits of remapped scan code,
179                                            and 16 bits of original scan code. */
180                                         int scancode_unmapped = bufEx[j + 1];
181                                         int scancode_mapped = bufEx[j];
182 
183                                         scancode_mapped = convert_scan_code(scancode_mapped);
184 
185                                         scancode_map[scancode_unmapped] = scancode_mapped;
186                                         pclog("Scan code mapping %u detected: %X -> %X\n", scancode_unmapped, scancode_mapped, scancode_map[scancode_unmapped]);
187                                 }
188                                 pclog("Done processing!\n");
189                         }
190                 }
191                 RegCloseKey(hKey);
192         }
193         pclog("Done preparing!\n");
194 }
195 
LowLevelKeyboardProc(int nCode,WPARAM wParam,LPARAM lParam)196 LRESULT CALLBACK LowLevelKeyboardProc( int nCode, WPARAM wParam, LPARAM lParam )
197 {
198         if (nCode < 0 || nCode != HC_ACTION || (!mousecapture && !video_fullscreen))
199                 return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam);
200 
201         KBDLLHOOKSTRUCT* p = (KBDLLHOOKSTRUCT*)lParam;
202 
203         if (p->vkCode == VK_TAB && p->flags & LLKHF_ALTDOWN) return 1; //disable alt-tab
204         if (p->vkCode == VK_SPACE && p->flags & LLKHF_ALTDOWN) return 1; //disable alt-tab
205         if((p->vkCode == VK_LWIN) || (p->vkCode == VK_RWIN)) return 1;//disable windows keys
206         if (p->vkCode == VK_ESCAPE && p->flags & LLKHF_ALTDOWN) return 1;//disable alt-escape
207         BOOL bControlKeyDown = GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1);//checks ctrl key pressed
208         if (p->vkCode == VK_ESCAPE && bControlKeyDown) return 1; //disable ctrl-escape
209 
210         return CallNextHookEx( hKeyboardHook, nCode, wParam, lParam );
211 }
212 
WindowProcedure(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)213 LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
214 {
215         switch (message)
216         {
217         case WM_COMMAND:
218                 /* pass through commands to wx window */
219                 wx_winsendmessage(window_ptr, message, wParam, lParam);
220                 return 0;
221                 break;
222         case WM_INPUT:
223         {
224                 UINT size;
225                 RAWINPUT *raw;
226 
227                 if (!infocus)
228                         break;
229 
230                 GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER));
231 
232                 raw = malloc(size);
233 
234                 /* Here we read the raw input data for the keyboard */
235                 GetRawInputData((HRAWINPUT)(lParam), RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER));
236 
237                 /* If the input is keyboard, we process it */
238                 if (raw->header.dwType == RIM_TYPEKEYBOARD)
239                 {
240                         const RAWKEYBOARD rawKB = raw->data.keyboard;
241                         USHORT scancode = rawKB.MakeCode;
242 
243                         // pclog("Keyboard input received: S:%X VK:%X F:%X\n", c, d, e);
244 
245                         /* If it's not a scan code that starts with 0xE1 */
246                         if (!(rawKB.Flags & RI_KEY_E1))
247                         {
248                                 if (rawKB.Flags & RI_KEY_E0)
249                                         scancode |= (0xE0 << 8);
250 
251                                 /* Remap it according to the list from the Registry */
252                                 scancode = scancode_map[scancode];
253 
254                                 if ((scancode >> 8) == 0xF0)
255                                         scancode |= 0x100; /* Extended key code in disambiguated format */
256                                 else if ((scancode >> 8) == 0xE0)
257                                         scancode |= 0x80; /* Normal extended key code */
258 
259                                 /* If it's not 0 (therefore not 0xE1, 0xE2, etc),
260                                    then pass it on to the rawinputkey array */
261                                 if (!(scancode & 0xf00))
262                                         rawinputkey[scancode & 0x1ff] = !(rawKB.Flags & RI_KEY_BREAK);
263                         }
264                 }
265                 free(raw);
266 
267         }
268         break;
269         case WM_SETFOCUS:
270                 infocus=1;
271         break;
272         case WM_KILLFOCUS:
273                 infocus=0;
274                 if (is_fullscreen())
275                         window_dowindowed = 1;
276                 window_doinputrelease = 1;
277                 memset(rawinputkey, 0, sizeof(rawinputkey));
278         break;
279         case WM_CLOSE:
280         case WM_DESTROY:
281         case WM_KEYDOWN:
282         case WM_SYSKEYDOWN:
283         case WM_KEYUP:
284         case WM_SYSKEYUP:
285                 return 0;
286 
287         case WM_SYSCOMMAND:
288                 if (wParam == SC_KEYMENU && HIWORD(lParam) <= 0 && (video_fullscreen || mousecapture))
289                         return 0; /*disable ALT key for menu*/
290                 break;
291         default:
292                 break;
293         }
294         return DefWindowProc (hwnd, message, wParam, lParam);
295 }
296 
subWindowProcedure(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)297 LRESULT CALLBACK subWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
298 {
299         switch (message)
300         {
301                 default:
302                 return DefWindowProc(hwnd, message, wParam, lParam);
303         }
304         return 0;
305 }
306 
display_init()307 int display_init()
308 {
309         SDL_SetHint(SDL_HINT_WINDOWS_DISABLE_THREAD_NAMING, "1");
310         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
311         {
312                 printf("SDL could not initialize! Error: %s\n", SDL_GetError());
313                 return 0;
314         }
315 
316         SDL_version ver;
317         SDL_GetVersion(&ver);
318         printf("SDL %i.%i.%i initialized.\n", ver.major, ver.minor, ver.patch);
319 
320         return 1;
321 }
322 
display_close()323 void display_close()
324 {
325         SDL_Quit();
326 }
327 
display_start(void * wnd_ptr)328 void display_start(void* wnd_ptr)
329 {
330         window_ptr = wnd_ptr;
331         menu_ptr = wx_getmenu(wnd_ptr);
332 
333         get_registry_key_map();
334 
335         infocus = 1;
336 
337         atexit(releasemouse);
338         rendererMutex = SDL_CreateMutex();
339         rendererCond = SDL_CreateCond();
340         renderer_start();
341 }
342 
display_stop()343 void display_stop()
344 {
345         renderer_stop(10 * 1000);
346 
347         SDL_DestroyMutex(rendererMutex);
348         SDL_DestroyCond(rendererCond);
349         SDL_DetachThread(renderthread);
350         releasemouse();
351 
352 }
353 
sdl_set_window_title(const char * title)354 void sdl_set_window_title(const char* title) {
355         if (hwnd && !is_fullscreen())
356                 SetWindowText(hwnd, title);
357 }
358 
get_border_size(int * top,int * left,int * bottom,int * right)359 int get_border_size(int* top, int* left, int* bottom, int* right)
360 {
361         int res = SDL_GetWindowBordersSize(window, top, left, bottom, right);
362         if (top) *top -= GetSystemMetrics(SM_CYMENUSIZE);
363 
364         return res;
365 }
366 
367 static const struct {
368         SDL_Scancode sdl;
369         int system;
370 } SDLScancodeToSystemScancode[] = {
371                 { SDL_SCANCODE_A, 0x1e },
372                 { SDL_SCANCODE_B, 0x30 },
373                 { SDL_SCANCODE_C, 0x2e },
374                 { SDL_SCANCODE_D, 0x20 },
375                 { SDL_SCANCODE_E, 0x12 },
376                 { SDL_SCANCODE_F, 0x21 },
377                 { SDL_SCANCODE_G, 0x22 },
378                 { SDL_SCANCODE_H, 0x23 },
379                 { SDL_SCANCODE_I, 0x17 },
380                 { SDL_SCANCODE_J, 0x24 },
381                 { SDL_SCANCODE_K, 0x25 },
382                 { SDL_SCANCODE_L, 0x26 },
383                 { SDL_SCANCODE_M, 0x32 },
384                 { SDL_SCANCODE_N, 0x31 },
385                 { SDL_SCANCODE_O, 0x18 },
386                 { SDL_SCANCODE_P, 0x19 },
387                 { SDL_SCANCODE_Q, 0x10 },
388                 { SDL_SCANCODE_R, 0x13 },
389                 { SDL_SCANCODE_S, 0x1f },
390                 { SDL_SCANCODE_T, 0x14 },
391                 { SDL_SCANCODE_U, 0x16 },
392                 { SDL_SCANCODE_V, 0x2f },
393                 { SDL_SCANCODE_W, 0x11 },
394                 { SDL_SCANCODE_X, 0x2d },
395                 { SDL_SCANCODE_Y, 0x15 },
396                 { SDL_SCANCODE_Z, 0x2c },
397                 { SDL_SCANCODE_0, 0x0B },
398                 { SDL_SCANCODE_1, 0x02 },
399                 { SDL_SCANCODE_2, 0x03 },
400                 { SDL_SCANCODE_3, 0x04 },
401                 { SDL_SCANCODE_4, 0x05 },
402                 { SDL_SCANCODE_5, 0x06 },
403                 { SDL_SCANCODE_6, 0x07 },
404                 { SDL_SCANCODE_7, 0x08 },
405                 { SDL_SCANCODE_8, 0x09 },
406                 { SDL_SCANCODE_9, 0x0A },
407                 { SDL_SCANCODE_GRAVE, 0x29 },
408                 { SDL_SCANCODE_MINUS, 0x0c },
409                 { SDL_SCANCODE_EQUALS, 0x0d },
410                 { SDL_SCANCODE_NONUSBACKSLASH, 0x56 },
411                 { SDL_SCANCODE_BACKSLASH, 0x2b },
412                 { SDL_SCANCODE_BACKSPACE, 0x0e },
413                 { SDL_SCANCODE_SPACE, 0x39 },
414                 { SDL_SCANCODE_TAB, 0x0f },
415                 { SDL_SCANCODE_CAPSLOCK, 0x3a },
416                 { SDL_SCANCODE_LSHIFT, 0x2a },
417                 { SDL_SCANCODE_LCTRL, 0x1d },
418                 { SDL_SCANCODE_LGUI, 0xdb },
419                 { SDL_SCANCODE_LALT, 0x38 },
420                 { SDL_SCANCODE_RSHIFT, 0x36 },
421                 { SDL_SCANCODE_RCTRL, 0x9d },
422                 { SDL_SCANCODE_RGUI, 0xdc },
423                 { SDL_SCANCODE_RALT, 0xb8 },
424                 { SDL_SCANCODE_SYSREQ, 0x54 },
425                 { SDL_SCANCODE_APPLICATION, 0xdd },
426                 { SDL_SCANCODE_RETURN, 0x1c },
427                 { SDL_SCANCODE_ESCAPE, 0x01 },
428                 { SDL_SCANCODE_F1, 0x3B },
429                 { SDL_SCANCODE_F2, 0x3C },
430                 { SDL_SCANCODE_F3, 0x3D },
431                 { SDL_SCANCODE_F4, 0x3e },
432                 { SDL_SCANCODE_F5, 0x3f },
433                 { SDL_SCANCODE_F6, 0x40 },
434                 { SDL_SCANCODE_F7, 0x41 },
435                 { SDL_SCANCODE_F8, 0x42 },
436                 { SDL_SCANCODE_F9, 0x43 },
437                 { SDL_SCANCODE_F10, 0x44 },
438                 { SDL_SCANCODE_F11, 0x57 },
439                 { SDL_SCANCODE_F12, 0x58 },
440                 { SDL_SCANCODE_SCROLLLOCK, 0x46 },
441                 { SDL_SCANCODE_LEFTBRACKET, 0x1a },
442                 { SDL_SCANCODE_RIGHTBRACKET, 0x1b },
443                 { SDL_SCANCODE_INSERT, 0xd2 },
444                 { SDL_SCANCODE_HOME, 0xc7 },
445                 { SDL_SCANCODE_PAGEUP, 0xc9 },
446                 { SDL_SCANCODE_DELETE, 0xd3 },
447                 { SDL_SCANCODE_END, 0xcf },
448                 { SDL_SCANCODE_PAGEDOWN, 0xd1 },
449                 { SDL_SCANCODE_UP, 0xc8 },
450                 { SDL_SCANCODE_LEFT, 0xcb },
451                 { SDL_SCANCODE_DOWN, 0xd0 },
452                 { SDL_SCANCODE_RIGHT, 0xcd },
453                 { SDL_SCANCODE_NUMLOCKCLEAR, 0x45 },
454                 { SDL_SCANCODE_KP_DIVIDE, 0xb5 },
455                 { SDL_SCANCODE_KP_MULTIPLY, 0x37 },
456                 { SDL_SCANCODE_KP_MINUS, 0x4a },
457                 { SDL_SCANCODE_KP_PLUS, 0x4e },
458                 { SDL_SCANCODE_KP_ENTER, 0x9c },
459                 { SDL_SCANCODE_KP_PERIOD, 0x53 },
460                 { SDL_SCANCODE_KP_0, 0x52 },
461                 { SDL_SCANCODE_KP_1, 0x4f },
462                 { SDL_SCANCODE_KP_2, 0x50 },
463                 { SDL_SCANCODE_KP_3, 0x51 },
464                 { SDL_SCANCODE_KP_4, 0x48 },
465                 { SDL_SCANCODE_KP_5, 0x4c },
466                 { SDL_SCANCODE_KP_6, 0x4d },
467                 { SDL_SCANCODE_KP_7, 0x47 },
468                 { SDL_SCANCODE_KP_8, 0x48 },
469                 { SDL_SCANCODE_KP_9, 0x49 },
470                 { SDL_SCANCODE_SEMICOLON, 0x27 },
471                 { SDL_SCANCODE_APOSTROPHE, 0x28 },
472                 { SDL_SCANCODE_COMMA, 0x33 },
473                 { SDL_SCANCODE_PERIOD, 0x34 },
474                 { SDL_SCANCODE_SLASH, 0x35 },
475                 { SDL_SCANCODE_PRINTSCREEN, 0xb7 }
476 };
477 
sdl_scancode(SDL_Scancode scancode)478 int sdl_scancode(SDL_Scancode scancode)
479 {
480         int i;
481         for (i = 0; i < SDL_arraysize(SDLScancodeToSystemScancode); ++i) {
482                 if (SDLScancodeToSystemScancode[i].sdl == scancode) {
483                         return SDLScancodeToSystemScancode[i].system;
484                 }
485         }
486         return -1;
487 }
488 
489 SDL_Event event;
490 SDL_Rect rect;
491 int border_x, border_y = 0;
492 
493 uint64_t render_time = 0;
494 int render_fps = 0;
495 uint32_t render_frame_time = 0;
496 uint32_t render_frames = 0;
497 
window_setup()498 void window_setup()
499 {
500         SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "0");
501         SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
502         SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1");
503 
504         if (start_in_fullscreen)
505         {
506                 start_in_fullscreen = 0;
507                 window_dofullscreen = 1;
508                 window_doinputgrab = 1;
509         }
510 
511         if (window_remember)
512         {
513                 rect.x = window_x;
514                 rect.y = window_y;
515                 rect.w = window_w;
516                 rect.h = window_h;
517         }
518         else
519         {
520                 rect.x = SDL_WINDOWPOS_CENTERED;
521                 rect.y = SDL_WINDOWPOS_CENTERED;
522                 rect.w = 640;
523                 rect.h = 480;
524         }
525 
526         if (vid_resize == 2)
527         {
528                 rect.w = custom_resolution_width;
529                 rect.h = custom_resolution_height;
530         }
531 }
532 
window_create()533 int window_create()
534 {
535         int i;
536         WNDCLASSEX wincl;        /* Data structure for the windowclass */
537 
538         hinstance = (HINSTANCE)GetModuleHandle(NULL);
539         /* The Window structure */
540         wincl.hInstance = hinstance;
541         wincl.lpszClassName = szClassName;
542         wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
543         wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
544         wincl.cbSize = sizeof (WNDCLASSEX);
545 
546         /* Use default icon and mouse-pointer */
547         wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
548         wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
549         wincl.hCursor = NULL;//LoadCursor (NULL, IDC_ARROW);
550         wincl.lpszMenuName = NULL;                 /* No menu */
551         wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
552         wincl.cbWndExtra = 0;                      /* structure or the window instance */
553         /* Use Windows's default color as the background of the window */
554         wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;
555 
556         /* Register the window class, and if it fails quit the program */
557         if (!RegisterClassEx(&wincl))
558                 return 0;
559 
560         wincl.lpszClassName = szSubClassName;
561         wincl.lpfnWndProc = subWindowProcedure;      /* This function is called by windows */
562 
563         if (!RegisterClassEx(&wincl))
564                 return 0;
565 
566         menu = CreateMenu();
567         HMENU native_menu = (HMENU)wx_getnativemenu(menu_ptr);
568         int count = GetMenuItemCount(native_menu);
569         MENUITEMINFO info;
570         for (i = 0; i < count; ++i)
571         {
572                 char label[256];
573                 memset(&info, 0, sizeof(MENUITEMINFO));
574                 info.cbSize = sizeof(MENUITEMINFO);
575                 info.fMask = MIIM_TYPE | MIIM_ID;
576                 info.fType = MFT_STRING;
577                 info.cch = 256;
578                 info.dwTypeData = label;
579                 if (GetMenuItemInfo(native_menu, i, 1, &info))
580                         AppendMenu(menu, MF_STRING | MF_POPUP, (UINT)GetSubMenu(native_menu, i), info.dwTypeData);
581         }
582 
583         /* The class is registered, let's create the program*/
584         hwnd = CreateWindowEx (
585                 0,                   /* Extended possibilites for variation */
586                 szClassName,         /* Classname */
587                 "PCem v14",          /* Title Text */
588                 WS_OVERLAPPEDWINDOW&~WS_SIZEBOX, /* default window */
589                 CW_USEDEFAULT,       /* Windows decides the position */
590                 CW_USEDEFAULT,       /* where the window ends up on the screen */
591                 640+(GetSystemMetrics(SM_CXFIXEDFRAME)*2),                 /* The programs width */
592                 480+(GetSystemMetrics(SM_CYFIXEDFRAME)*2)+GetSystemMetrics(SM_CYMENUSIZE)+GetSystemMetrics(SM_CYCAPTION)+1,                 /* and height in pixels */
593                 HWND_DESKTOP,        /* The window is a child-window to desktop */
594                 menu,                /* Menu */
595                 hinstance,           /* Program Instance handler */
596                 NULL                 /* No Window Creation data */
597         );
598 
599         /* Make the window visible on the screen */
600         ShowWindow(hwnd, 1);
601 
602         memset(rawinputkey, 0, sizeof(rawinputkey));
603         device.usUsagePage = 0x01;
604         device.usUsage = 0x06;
605         device.dwFlags = RIDEV_NOHOTKEYS;
606         device.hwndTarget = hwnd;
607 
608         if (RegisterRawInputDevices(&device, 1, sizeof(device)))
609                 pclog("Raw input registered!\n");
610         else
611                 pclog("Raw input registration failed!\n");
612 
613         hKeyboardHook = SetWindowsHookEx( WH_KEYBOARD_LL,  LowLevelKeyboardProc, GetModuleHandle(NULL), 0 );
614 
615         if (requested_render_driver.sdl_window_params & SDL_WINDOW_OPENGL)
616         {
617                 /* create dummy window for opengl */
618                 dummy_window = SDL_CreateWindow("GL3 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1, 1, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL);
619                 if (dummy_window)
620                 {
621                         char ptr[32];
622                         snprintf(ptr, 31, "%p", dummy_window);
623                         ptr[31] = 0;
624                         SDL_SetHint(SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT, ptr);
625                 }
626         }
627         window = SDL_CreateWindowFrom(hwnd);
628         if (!window)
629         {
630                 char message[200];
631                 sprintf(message,
632                                 "SDL window could not be created! Error: %s\n",
633                                 SDL_GetError());
634                 wx_messagebox(window_ptr, message, "SDL Error", WX_MB_OK);
635                 return 0;
636         }
637 
638         SDL_SetWindowPosition(window, rect.x, rect.y);
639         SDL_SetWindowSize(window, rect.w, rect.h);
640 
641         if (vid_resize)
642                 window_dosetresize = 1;
643 
644         render_time = 0;
645         render_fps = 0;
646         render_frame_time = SDL_GetTicks();
647         render_frames = 0;
648         return 1;
649 }
650 
window_close()651 void window_close()
652 {
653         int i;
654         sdl_renderer_close();
655 
656         int count = GetMenuItemCount(menu);
657         for (i = 0; i < count; ++i)
658                 RemoveMenu(menu, 0, MF_BYPOSITION);
659         DestroyMenu(menu);
660         menu = 0;
661         SetMenu(hwnd, 0);
662 
663         if (window) {
664                 SDL_GetWindowPosition(window, &rect.x, &rect.y);
665                 SDL_GetWindowSize(window, &rect.w, &rect.h);
666                 get_border_size(&border_y, &border_x, 0, 0);
667                 rect.x -= border_x;
668                 rect.y -= border_y;
669 
670                 SDL_DestroyWindow(window);
671         }
672         window = NULL;
673 
674         if (dummy_window)
675         {
676                 SDL_SetHint(SDL_HINT_VIDEO_WINDOW_SHARE_PIXEL_FORMAT, "");
677                 SDL_DestroyWindow(dummy_window);
678         }
679         dummy_window = NULL;
680 
681         UnhookWindowsHookEx( hKeyboardHook );
682         DestroyWindow(hwnd);
683         hwnd = NULL;
684 
685         UnregisterClass(szSubClassName, hinstance);
686         UnregisterClass(szClassName, hinstance);
687 
688 }
689 
render()690 int render()
691 {
692         uint64_t start_time = timer_read();
693         uint64_t end_time;
694 
695         if (window_doreset)
696         {
697                 pclog("window_doreset\n");
698                 window_doreset = 0;
699                 renderer_doreset = 0;
700                 return 0;
701         }
702         if (window_dosetresize)
703         {
704                 window_dosetresize = 0;
705                 SDL_GetWindowSize(window, &rect.w, &rect.h);
706                 SDL_SetWindowResizable(window, vid_resize == 1);
707                 SDL_SetWindowSize(window, rect.w, rect.h);
708 
709                 if (vid_resize == 2)
710                         SDL_SetWindowSize(window, custom_resolution_width, custom_resolution_height);
711 
712                 device_force_redraw();
713         }
714         if (renderer_doreset)
715         {
716                 pclog("renderer_doreset\n");
717                 renderer_doreset = 0;
718                 sdl_renderer_close();
719                 sdl_renderer_init(window);
720 
721                 device_force_redraw();
722                 video_wait_for_blit();
723         }
724         while(SDL_PollEvent(&event))
725         {
726                 switch (event.type) {
727                 case SDL_MOUSEBUTTONUP:
728                         if (!mousecapture)
729                         {
730                                 if (event.button.button == SDL_BUTTON_LEFT && !pause)
731                                 {
732                                         window_doinputgrab = 1;
733                                         if (video_fullscreen)
734                                                 window_dofullscreen = 1;
735                                 }
736                         }
737                         else if (event.button.button == SDL_BUTTON_MIDDLE && !is_fullscreen() && !(mouse_get_type(mouse_type) & MOUSE_TYPE_3BUTTON))
738                                 window_doinputrelease = 1;
739                         break;
740                 case SDL_MOUSEWHEEL:
741                         if (mousecapture) mouse_wheel_update(event.wheel.y);
742                         break;
743                 case SDL_WINDOWEVENT:
744                         if (event.window.event == SDL_WINDOWEVENT_CLOSE)
745                                 wx_stop_emulation(window_ptr);
746                         if (event.window.event == SDL_WINDOWEVENT_RESIZED)
747                                 device_force_redraw();
748 
749                         if (window_remember)
750                         {
751                                 int flags = SDL_GetWindowFlags(window);
752                                 if (!(flags&SDL_WINDOW_FULLSCREEN) && !(flags&SDL_WINDOW_FULLSCREEN_DESKTOP))
753                                 {
754                                         if (event.window.event == SDL_WINDOWEVENT_MOVED)
755                                         {
756                                                 get_border_size(&border_y, &border_x, 0, 0);
757                                                 window_x = event.window.data1-border_x;
758                                                 window_y = event.window.data2-border_y;
759                                         }
760                                         else if (event.window.event == SDL_WINDOWEVENT_RESIZED)
761                                         {
762                                                 window_w = event.window.data1;
763                                                 window_h = event.window.data2;
764                                         }
765                                         //save_window_pos = 1;
766                                 }
767                         }
768 
769                         break;
770                 }
771         }
772         if (rawinputkey[sdl_scancode(SDL_SCANCODE_PAGEDOWN)] &&
773                         (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]) &&
774                         (rawinputkey[sdl_scancode(SDL_SCANCODE_LALT)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RALT)]))
775                 trigger_fullscreen = 1;
776         else if (trigger_fullscreen)
777         {
778                 trigger_fullscreen = 0;
779                 toggle_fullscreen();
780         }
781         else if (rawinputkey[sdl_scancode(SDL_SCANCODE_PAGEUP)] &&
782                         (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]) &&
783                         (rawinputkey[sdl_scancode(SDL_SCANCODE_LALT)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RALT)]))
784                 trigger_screenshot = 1;
785         else if (trigger_screenshot)
786         {
787                 trigger_screenshot = 0;
788                 take_screenshot = 1;
789         }
790         else if (event.key.keysym.scancode == SDL_SCANCODE_END &&
791                         (rawinputkey[sdl_scancode(SDL_SCANCODE_LCTRL)] || rawinputkey[sdl_scancode(SDL_SCANCODE_RCTRL)]))
792                 trigger_inputrelease = 1;
793         else if (trigger_inputrelease)
794         {
795                 trigger_inputrelease = 0;
796                 if (!is_fullscreen())
797                         window_doinputrelease = 1;
798         }
799         if (window_doremember)
800         {
801                 window_doremember = 0;
802                 SDL_GetWindowPosition(window, &window_x, &window_y);
803                 SDL_GetWindowSize(window, &window_w, &window_h);
804                 get_border_size(&border_y, &border_x, 0, 0);
805                 window_x -= border_x;
806                 window_y -= border_y;
807                 saveconfig(NULL);
808         }
809 
810         if (window_dotogglefullscreen)
811         {
812                 window_dotogglefullscreen = 0;
813                 if (SDL_GetWindowGrab(window) || is_fullscreen())
814                 {
815                         window_doinputrelease = 1;
816                         if (is_fullscreen())
817                                 window_dowindowed = 1;
818                 }
819                 else
820                 {
821                         window_doinputgrab = 1;
822                         window_dofullscreen = 1;
823                 }
824         }
825 
826         if (window_dofullscreen)
827         {
828                 window_dofullscreen = 0;
829                 SetMenu(hwnd, 0);
830                 video_wait_for_blit();
831                 SDL_RaiseWindow(window);
832                 SDL_GetGlobalMouseState(&remembered_mouse_x, &remembered_mouse_y);
833                 SDL_GetWindowPosition(window, &remembered_rect.x, &remembered_rect.y);
834                 get_border_size(&border_y, &border_x, 0, 0);
835                 remembered_rect.x -= border_x;
836                 remembered_rect.y -= border_y;
837                 SDL_GetWindowSize(window, &remembered_rect.w, &remembered_rect.h);
838                 SDL_SetWindowFullscreen(window, video_fullscreen_mode == 0 ? SDL_WINDOW_FULLSCREEN_DESKTOP : SDL_WINDOW_FULLSCREEN);
839                 device_force_redraw();
840         }
841         if (window_doinputgrab) {
842                 window_doinputgrab = 0;
843                 mousecapture = 1;
844                 SDL_GetRelativeMouseState(0, 0);
845                 SDL_SetWindowGrab(window, SDL_TRUE);
846                 SDL_SetRelativeMouseMode(SDL_TRUE);
847         }
848         if (mousecapture)
849         {
850                 SDL_Rect rect;
851                 SDL_GetWindowSize(window, &rect.w, &rect.h);
852                 SDL_WarpMouseInWindow(window, rect.w/2, rect.h/2);
853         }
854 
855         if (window_doinputrelease) {
856                 window_doinputrelease = 0;
857                 releasemouse();
858         }
859         if (window_dowindowed)
860         {
861                 window_dowindowed = 0;
862                 SetMenu(hwnd, menu);
863                 SDL_SetWindowFullscreen(window, 0);
864                 SDL_SetWindowSize(window, remembered_rect.w, remembered_rect.h);
865                 SDL_SetWindowPosition(window, remembered_rect.x, remembered_rect.y);
866                 SDL_WarpMouseGlobal(remembered_mouse_x, remembered_mouse_y);
867                 device_force_redraw();
868         }
869 
870         if (win_doresize)
871         {
872                 int flags = SDL_GetWindowFlags(window);
873                 win_doresize = 0;
874                 if (!vid_resize || (flags&SDL_WINDOW_FULLSCREEN)) {
875                         SDL_GetWindowSize(window, &rect.w, &rect.h);
876                         if (rect.w != winsizex || rect.h != winsizey) {
877                                 SDL_GetWindowPosition(window, &rect.x, &rect.y);
878                                 SDL_SetWindowSize(window, winsizex, winsizey);
879                                 SDL_SetWindowPosition(window, rect.x, rect.y);
880                                 device_force_redraw();
881                         }
882                 }
883         }
884 
885         if (sdl_renderer_update(window)) sdl_renderer_present(window);
886 
887         end_time = timer_read();
888         render_time += end_time - start_time;
889 
890         ++render_frames;
891         uint32_t ticks = SDL_GetTicks();
892         if (ticks-render_frame_time >= 1000) {
893                 render_fps = render_frames/((ticks-render_frame_time)/1000.0);
894                 render_frames = 0;
895                 render_frame_time = ticks;
896         }
897 
898         return 1;
899 }
900 
renderer_thread(void * params)901 int renderer_thread(void* params)
902 {
903         int internal_rendering;
904 
905         SDL_LockMutex(rendererMutex);
906         SDL_CondSignal(rendererCond);
907         SDL_UnlockMutex(rendererMutex);
908 
909         window_setup();
910 
911         rendering = 1;
912         while (rendering) {
913 
914                 if (!window_create())
915                         rendering = 0;
916 
917                 renderer_doreset = 1;
918                 internal_rendering = 1;
919                 while (rendering && internal_rendering)
920                 {
921                         if (!render())
922                                 internal_rendering = 0;
923 
924                         SDL_Delay(1);
925                 }
926                 window_close();
927         }
928 
929         SDL_LockMutex(rendererMutex);
930         SDL_CondSignal(rendererCond);
931         SDL_UnlockMutex(rendererMutex);
932 
933         return SDL_TRUE;
934 }
935 
936 void* timer = 0;
937 
render_timer()938 void render_timer()
939 {
940 #ifdef PCEM_RENDER_TIMER_LOOP
941         /* For some reason this while-loop works on OSX, which also fixes missing events. No idea why though. */
942         renderer_thread(0);
943 #else
944         if (rendering && !render())
945         {
946                 window_close();
947                 window_create();
948                 renderer_doreset = 1;
949         }
950 #endif
951 }
952 
render_start_timer()953 void render_start_timer()
954 {
955 #ifdef PCEM_RENDER_TIMER_LOOP
956         timer = wx_createtimer(render_timer);
957         wx_starttimer(timer, 500, 1);
958 #else
959         window_setup();
960         if (window_create())
961         {
962                 rendering = 1;
963                 renderer_doreset = 1;
964                 wx_starttimer(timer, 1, 0);
965         }
966 #endif
967 }
968 
renderer_start()969 void renderer_start()
970 {
971         if (!rendering)
972         {
973 #ifdef PCEM_RENDER_WITH_TIMER
974                 render_start_timer();
975 #else
976                 SDL_LockMutex(rendererMutex);
977                 renderthread = SDL_CreateThread(renderer_thread, "SDL2 Thread", NULL);
978                 SDL_CondWait(rendererCond, rendererMutex);
979                 SDL_UnlockMutex(rendererMutex);
980 #endif
981         }
982 }
983 
renderer_stop(int timeout)984 void renderer_stop(int timeout)
985 {
986 #if defined(PCEM_RENDER_WITH_TIMER) && !defined(PCEM_RENDER_TIMER_LOOP)
987         rendering = 0;
988         window_close();
989         wx_destroytimer(timer);
990 #else
991         if (rendering)
992         {
993                 SDL_LockMutex(rendererMutex);
994                 rendering = 0;
995                 if (timeout)
996                         SDL_CondWaitTimeout(rendererCond, rendererMutex, timeout);
997                 else
998                         SDL_CondWait(rendererCond, rendererMutex);
999                 SDL_UnlockMutex(rendererMutex);
1000                 renderthread = NULL;
1001         }
1002         if (timer)
1003                 wx_destroytimer(timer);
1004 #endif
1005 }
1006