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