1 /*
2 * screen_win32.c - Win32 port specific code
3 *
4 * Copyright (C) 2000 Krzysztof Nikiel
5 * Copyright (C) 2000-2006 Atari800 development team (see DOC/CREDITS)
6 *
7 * This file is part of the Atari800 emulator project which emulates
8 * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
9 *
10 * Atari800 is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * Atari800 is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Atari800; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include "config.h"
26
27 #define DIRECTDRAW_VERSION 0x0500
28 #define DIRECTINPUT_VERSION 0x0500
29 #define WIN32_LEAN_AND_MEAN
30 #define DW_WINDOWSTYLE WS_TILEDWINDOW | WS_SIZEBOX | WS_DLGFRAME
31
32 #define DISP_CHANGE_NOMODE -6
33
34 #include <windows.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 #include <math.h>
38
39 #include "atari.h"
40 #include "colours.h"
41 #include "log.h"
42 #include "util.h"
43 #include "keyboard.h"
44 #include "screen.h"
45 #include "sound.h"
46 #include "main_menu.h"
47 #include "ui.h"
48
49 #include "screen_win32.h"
50 #include "main.h"
51 #include "render_gdi.h"
52 #include "render_gdiplus.h"
53 #include "render_directdraw.h"
54 #include "render_direct3d.h"
55
56 static RECT currentwindowrect = {0,0,0,0};
57 static WINDOWPLACEMENT currentwindowstate;
58 static HCURSOR cursor;
59 static BOOL refreshframeparams = TRUE;
60 static int scaledWidth = 0;
61 static int scaledHeight = 0;
62 static int bltgfx = 0;
63 static int scrwidth = SCREENWIDTH;
64 static int scrheight = SCREENHEIGHT;
65
66 RENDERMODE rendermode = GDI;
67 DISPLAYMODE displaymode = GDI_NEARESTNEIGHBOR;
68 FSRESOLUTION fsresolution = DESKTOP;
69 SCREENMODE screenmode = WINDOW;
70 ASPECTMODE scalingmethod = NORMAL;
71 ASPECTRATIO aspectmode = AUTO;
72 FRAMEPARAMS frameparams;
73 CROP crop;
74 OFFSET offset;
75 BOOL hidecursor = FALSE;
76 BOOL lockaspect = FALSE;
77 BOOL usecustomfsresolution = FALSE;
78 BOOL showmenu = TRUE;
79 int cropamount = 0;
80 int windowscale = 200;
81 int currentscale = 200;
82 int fullscreenWidth = 0;
83 int fullscreenHeight = 0;
84 int origScreenWidth = 0;
85 int origScreenHeight = 0;
86 int origScreenDepth = 0;
87
groff(void)88 void groff(void)
89 {
90 switch (rendermode)
91 {
92 case GDI:
93 DestroyWindow(hWndMain);
94 case DIRECTDRAW:
95 shutdowndirectdraw();
96 break;
97 case GDIPLUS:
98 DestroyWindow(hWndMain);
99 shutdowngdiplus();
100 break;
101 case DIRECT3D:
102 DestroyWindow(hWndMain);
103 shutdowndirect3d();
104 break;
105 }
106 }
107
refreshframe()108 void refreshframe()
109 {
110 refreshframeparams = TRUE;
111 }
112
setcursor()113 void setcursor()
114 {
115 if (screenmode == WINDOW)
116 while (ShowCursor(TRUE) < 0) {}
117 else if (hidecursor)
118 while (ShowCursor(FALSE) > -1) {}
119 else if (UI_is_active)
120 while (ShowCursor(TRUE) < 0) {}
121 else
122 while (ShowCursor(FALSE) > -1) {}
123 }
124
GetRenderMode()125 RENDERMODE GetRenderMode() {
126 return rendermode;
127 }
128
GetDisplayMode()129 DISPLAYMODE GetDisplayMode() {
130 return displaymode;
131 }
132
GetScreenMode()133 SCREENMODE GetScreenMode() {
134 return screenmode;
135 }
136
GetDisplayModeName(char * name)137 void GetDisplayModeName(char *name) {
138
139 switch (displaymode) {
140 case GDI_NEARESTNEIGHBOR:
141 sprintf(name, "%s", "GDI");
142 break;
143 case GDIPLUS_NEARESTNEIGHBOR:
144 sprintf(name, "%s", "GDI+");
145 break;
146 case GDIPLUS_BILINEAR:
147 sprintf(name, "%s", "GDI+/Bilinear");
148 break;
149 case GDIPLUS_HQBICUBIC:
150 sprintf(name, "%s", "GDI+/Bicubic(HQ)");
151 break;
152 case DIRECT3D_NEARESTNEIGHBOR:
153 sprintf(name, "%s", "Direct3D");
154 break;
155 case DIRECT3D_BILINEAR:
156 sprintf(name, "%s", "Direct3D/Bilinear");
157 break;
158 case GDIPLUS_HQBILINEAR:
159 sprintf(name, "%s", "GDI+/Bilinear(HQ)");
160 break;
161 case GDIPLUS_BICUBIC:
162 sprintf(name, "%s", "GDI+/Bicubic");
163 break;
164 }
165 }
166
GetActiveDisplayMode()167 DISPLAYMODE GetActiveDisplayMode() {
168
169 DISPLAYMODE retval;
170
171 if (rendermode == GDI && frameparams.filter == NEARESTNEIGHBOR)
172 retval = GDI_NEARESTNEIGHBOR;
173 else if (rendermode == GDIPLUS && frameparams.filter == NEARESTNEIGHBOR)
174 retval = GDIPLUS_NEARESTNEIGHBOR;
175 else if (rendermode == GDIPLUS && frameparams.filter == BILINEAR)
176 retval = GDIPLUS_BILINEAR;
177 else if (rendermode == GDIPLUS && frameparams.filter == HQBICUBIC)
178 retval = GDIPLUS_HQBICUBIC;
179 else if (rendermode == DIRECT3D && frameparams.filter == NEARESTNEIGHBOR)
180 retval = DIRECT3D_NEARESTNEIGHBOR;
181 else if (rendermode == DIRECT3D && frameparams.filter == BILINEAR)
182 retval = DIRECT3D_BILINEAR;
183 else if (rendermode == GDIPLUS && frameparams.filter == HQBILINEAR)
184 retval = GDIPLUS_HQBILINEAR;
185 else if (rendermode == GDIPLUS && frameparams.filter == BICUBIC)
186 retval = GDIPLUS_BICUBIC;
187
188 return retval;
189 }
190
SetDisplayMode(DISPLAYMODE dm)191 void SetDisplayMode(DISPLAYMODE dm) {
192
193 displaymode = dm;
194
195 switch (dm) {
196 case GDI_NEARESTNEIGHBOR:
197 rendermode = GDI;
198 frameparams.filter = NEARESTNEIGHBOR;
199 break;
200 case GDIPLUS_NEARESTNEIGHBOR:
201 rendermode = GDIPLUS;
202 frameparams.filter = NEARESTNEIGHBOR;
203 break;
204 case GDIPLUS_BILINEAR:
205 rendermode = GDIPLUS;
206 frameparams.filter = BILINEAR;
207 break;
208 case GDIPLUS_HQBICUBIC:
209 rendermode = GDIPLUS;
210 frameparams.filter = HQBICUBIC;
211 break;
212 case DIRECT3D_NEARESTNEIGHBOR:
213 rendermode = DIRECT3D;
214 frameparams.filter = NEARESTNEIGHBOR;
215 break;
216 case DIRECT3D_BILINEAR:
217 rendermode = DIRECT3D;
218 frameparams.filter = BILINEAR;
219 break;
220 case GDIPLUS_HQBILINEAR:
221 rendermode = GDIPLUS;
222 frameparams.filter = HQBILINEAR;
223 break;
224 case GDIPLUS_BICUBIC:
225 rendermode = GDIPLUS;
226 frameparams.filter = BICUBIC;
227 break;
228 }
229 }
230
initwin(void)231 static BOOL initwin(void)
232 {
233 LONG retval;
234 RECT windowRect = {0,0,0,0};
235 int xpos, ypos;
236 static DEVMODE dmDevMode;
237 char msgbuffer[200];
238 WNDCLASS wc;
239
240 wc.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;
241 wc.lpfnWndProc = Atari_WindowProc;
242 wc.cbClsExtra = 0;
243 wc.cbWndExtra = 0;
244 wc.hInstance = myInstance;
245 wc.hIcon = LoadIcon(myInstance, IDI_APPLICATION);
246 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
247 wc.hbrBackground = GetStockObject(BLACK_BRUSH);
248 wc.lpszMenuName = myname;
249 wc.lpszClassName = myname;
250
251 RegisterClass(&wc);
252 setmenu();
253
254 // save the original screen resolution settings
255 if (!origScreenWidth && !origScreenHeight)
256 {
257 // Get current display settings
258 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmDevMode);
259 origScreenWidth = dmDevMode.dmPelsWidth;
260 origScreenHeight = dmDevMode.dmPelsHeight;
261 origScreenDepth = dmDevMode.dmBitsPerPel;
262 }
263
264 // display to a window
265 if (rendermode != DIRECTDRAW && screenmode == WINDOW)
266 {
267 // If the screen resolution has changed (due, perhaps, to switching into
268 // fullscreen mode), return to the original screen resolution.
269 retval = ChangeDisplaySettings(NULL, 0);
270
271 // compute a properly scaled client rect for our new window
272 getscaledrect(&windowRect);
273 scaledWidth = windowRect.right - windowRect.left;
274 scaledHeight = windowRect.bottom - windowRect.top;
275
276 // get the coordinates necessary to center the rectangle
277 getcenteredcoords(windowRect, &xpos, &ypos);
278
279 hWndMain = CreateWindowEx(0, myname, myname, DW_WINDOWSTYLE, xpos, ypos,
280 scaledWidth, scaledHeight, NULL, hMainMenu, myInstance, NULL );
281 }
282 else // display as fullscreen
283 {
284 // User has specified a custom fullscreen screen resolution.
285 // Scan all supported graphics mode for the one that best
286 // matches the requested resolution.
287 if (usecustomfsresolution) {
288 int i, nModeExists;
289
290 for (i=0; ;i++) {
291 nModeExists = EnumDisplaySettings(NULL, i, &dmDevMode);
292
293 if (!nModeExists) {
294 // we've reached the end of the list
295 retval = DISP_CHANGE_NOMODE;
296 break;
297 }
298 else {
299 // look for a graphics mode that matches our requested resolution
300 // and has a pixel depth equal to the original screen mode.
301 if (dmDevMode.dmPelsWidth == fullscreenWidth &&
302 dmDevMode.dmPelsHeight == fullscreenHeight &&
303 dmDevMode.dmBitsPerPel == origScreenDepth) {
304
305 // Change the resolution now.
306 // Using CDS_FULLSCREEN means that this resolution is temporary, so Windows
307 // rather than us, will take care of resetting this back to the default when we
308 // exit the application.
309 retval = ChangeDisplaySettings(&dmDevMode, CDS_FULLSCREEN);
310 break;
311 }
312 }
313 }
314 }
315 else // no custom screen resolution was specified.
316 {
317 // Check current resolution and change it back to the default
318 // if it is different from the current fullscreen settings.
319 EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmDevMode);
320 if (dmDevMode.dmPelsWidth != fullscreenWidth ||
321 dmDevMode.dmPelsHeight != fullscreenHeight ||
322 dmDevMode.dmBitsPerPel != origScreenDepth) {
323 retval = ChangeDisplaySettings(NULL, 0);
324 fullscreenWidth = dmDevMode.dmPelsWidth;
325 fullscreenHeight = dmDevMode.dmPelsHeight;
326 }
327 else
328 retval = DISP_CHANGE_SUCCESSFUL;
329 }
330
331 // create an empty fullscreen window
332 hWndMain = CreateWindowEx(
333 WS_EX_TOPMOST, myname, myname, WS_POPUP, 0, 0,
334 GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
335 NULL, NULL, myInstance, NULL);
336
337 // If we were unsuccessful in changing the resolution, report this to the user,
338 // Then attempt to recover by returning to window mode.
339 if (retval != DISP_CHANGE_SUCCESSFUL) {
340 sprintf(msgbuffer, "Error %d.\n\nCould not change resolution\nto requested size: %dx%d.\n\nReturning to window mode.\nPlease check your settings.",
341 retval, fullscreenWidth, fullscreenHeight);
342 MessageBox(hWndMain, msgbuffer, myname, MB_OK);
343
344 // Restore screen to window state
345 screenmode = WINDOW;
346 retval = ChangeDisplaySettings(NULL, 0);
347
348 getcenteredcoords(windowRect, &xpos, &ypos);
349 hWndMain = CreateWindowEx(0, myname, myname, DW_WINDOWSTYLE, xpos, ypos,
350 scaledWidth, scaledHeight, NULL, hMainMenu, myInstance, NULL );
351 }
352
353 setcursor();
354 }
355
356 if (!hWndMain)
357 return 1;
358
359 return 0;
360 }
361
362 // initialize menu the first time for a given window
initmenu(void)363 void initmenu(void)
364 {
365 hMainMenu = CreateMenu();
366 hFileMenu = CreateMenu();
367 hManageMenu = CreateMenu();
368 hSystemMenu = CreateMenu();
369 hDisplayMenu = CreateMenu();
370 hHelpMenu = CreateMenu();
371 SetMenu(hWndMain, hMainMenu);
372
373 InsertMenu(hMainMenu, ID_FILE, MF_POPUP, (UINT)hFileMenu, "File");
374 AppendMenu(hFileMenu, MF_STRING, ID_RUN_ATARI_PROGRAM, "&Run Atari Program...\tAlt+R");
375 AppendMenu(hFileMenu, MF_SEPARATOR, 0, "");
376 AppendMenu(hFileMenu, MF_STRING, ID_RESET, "Reset (Warm Start)\tF5");
377 AppendMenu(hFileMenu, MF_STRING, ID_REBOOT, "Reboot (Cold Start)\tShift+F5");
378 AppendMenu(hFileMenu, MF_SEPARATOR, 0, "");
379 AppendMenu(hFileMenu, MF_STRING, ID_CONFIGURATION, "Configuration...\tF1");
380 AppendMenu(hFileMenu, MF_STRING, ID_SAVE_CONFIG, "Save Configuration");
381 AppendMenu(hFileMenu, MF_SEPARATOR, 0, "");
382 AppendMenu(hFileMenu, MF_STRING, ID_BACK, "Back\tEsc");
383 AppendMenu(hFileMenu, MF_SEPARATOR, 0, "");
384 AppendMenu(hFileMenu, MF_STRING, ID_EXIT, "Exit\tF9");
385
386 InsertMenu(hMainMenu, ID_MANAGE, MF_POPUP, (UINT)hManageMenu, "Manage");
387 AppendMenu(hManageMenu, MF_STRING, ID_DISK_MANAGEMENT, "&Disks...\tAlt+D");
388 AppendMenu(hManageMenu, MF_STRING, ID_CART_MANAGEMENT, "&Cartridges...\tAlt+C");
389 AppendMenu(hManageMenu, MF_STRING, ID_TAPE_MANAGEMENT, "Tapes...");
390 AppendMenu(hManageMenu, MF_SEPARATOR, 0, "");
391 AppendMenu(hManageMenu, MF_STRING, ID_EMULATOR_BIOS, "Emulator/BIOS...");
392
393 InsertMenu(hMainMenu, ID_SYSTEM, MF_POPUP, (UINT)hSystemMenu, "System");
394 AppendMenu(hSystemMenu, MF_STRING, ID_NTSC, "NTSC");
395 AppendMenu(hSystemMenu, MF_STRING, ID_PAL, "PAL");
396 AppendMenu(hSystemMenu, MF_SEPARATOR, 0, "");
397 AppendMenu(hSystemMenu, MF_STRING, ID_SELECT_MACHINE, "Select Machine...\tAlt+Y");
398 AppendMenu(hSystemMenu, MF_SEPARATOR, 0, "");
399 AppendMenu(hSystemMenu, MF_STRING, ID_SOUND, "Sound...\tAlt+O");
400 AppendMenu(hSystemMenu, MF_STRING, ID_CONTROLLERS, "Controllers...");
401 if (Atari800_tv_mode == Atari800_TV_NTSC) {
402 CheckMenuItem(hSystemMenu, ID_NTSC, MF_CHECKED);
403 CheckMenuItem(hSystemMenu, ID_PAL, MF_UNCHECKED);
404 }
405 else {
406 CheckMenuItem(hSystemMenu, ID_NTSC, MF_UNCHECKED);
407 CheckMenuItem(hSystemMenu, ID_PAL, MF_CHECKED);
408 }
409
410 InsertMenu(hMainMenu, ID_DISPLAY, MF_POPUP, (UINT)hDisplayMenu, "Display");
411 AppendMenu(hDisplayMenu, MF_STRING, ID_FULLSCREEN, "Toggle Fullscreen\tAlt+Enter");
412 AppendMenu(hDisplayMenu, MF_SEPARATOR, 0, "");
413 AppendMenu(hDisplayMenu, MF_STRING, ID_WINDOW_SIZE_UP, "Increase Window Size\tAlt+PgUp");
414 AppendMenu(hDisplayMenu, MF_STRING, ID_WINDOW_SIZE_DOWN, "Decrease Window Size\tAlt+PgDn");
415 AppendMenu(hDisplayMenu, MF_SEPARATOR, 0, "");
416 AppendMenu(hDisplayMenu, MF_STRING, ID_ATARI_DISPLAY, "General Display Settings...");
417 AppendMenu(hDisplayMenu, MF_STRING, ID_WINDOWS_DISPLAY, "Windows Display Options...");
418
419 InsertMenu(hMainMenu, ID_HELP, MF_POPUP, (UINT)hHelpMenu, "Help");
420 AppendMenu(hHelpMenu, MF_STRING, ID_FUNCTION_KEY_HELP, "Function Key List...");
421 AppendMenu(hHelpMenu, MF_STRING, ID_HOT_KEY_HELP, "Hot Key List...");
422 AppendMenu(hHelpMenu, MF_SEPARATOR, 0, "");
423 AppendMenu(hHelpMenu, MF_STRING, ID_ABOUT_ATARI800, "About Atari800...\tAlt+A");
424 }
425
426 // restore a previously initialized menu to the window
restoremenu(void)427 void restoremenu(void)
428 {
429 // menus are never shown in fullscreen mode
430 if (screenmode == FULLSCREEN)
431 return;
432
433 LoadMenu((HINSTANCE)hWndMain, (LPCSTR)hMainMenu);
434 SetMenu(hWndMain, hMainMenu);
435 }
436
437 // remove menu from the window
destroymenu(void)438 void destroymenu(void)
439 {
440 SetMenu(hWndMain, NULL);
441 DestroyMenu((HMENU)hWndMain);
442 }
443
444 // set the menu based on the current state
setmenu(void)445 void setmenu(void)
446 {
447 if (showmenu && !hMainMenu)
448 initmenu();
449 else if (showmenu)
450 restoremenu();
451 else if (!showmenu)
452 destroymenu();
453
454 changewindowsize(RESET, 0);
455 }
456
457 // switch menu on or off
togglemenustate(void)458 void togglemenustate(void)
459 {
460 showmenu = !showmenu;
461 setmenu();
462 }
463
464 // helper function that creates a scaled rectangle with the exact client area that we want.
getscaledrect(RECT * rect)465 void getscaledrect(RECT *rect)
466 {
467 // This function is intended for windowed mode only
468 if (screenmode == FULLSCREEN)
469 return;
470
471 // Set base window size based on selected aspect mode
472 if (aspectmode == WIDE || aspectmode == AUTO) {
473
474 // Enforce strict 7:5 aspect dimensions for the window
475 // if the aspect ratio is locked.
476 if (lockaspect) {
477 scrwidth = SCREENWIDTH;
478 scrheight = SCREENHEIGHT;
479 }
480 else
481 {
482 // Otherwise allow dimensions of the window to dynamically
483 // vary based on the crop.horizontal and crop.vertical values.
484 scrwidth = SCREENWIDTH - crop.horizontal * 2;
485 scrheight = SCREENHEIGHT - crop.vertical * 2;
486 }
487 }
488 else { // a cropped mode was selected
489 if (lockaspect) {
490 // Enforce strict 4:3 aspect dimensions for the window
491 scrwidth = CROPPEDWIDTH;
492 scrheight = SCREENHEIGHT;
493 }
494 else {
495 // Allow dimensions of the window to dynamically vary
496 // based on the crop.horizontal and crop.vertical values.
497 scrwidth = CROPPEDWIDTH - crop.horizontal * 2;
498 scrheight = SCREENHEIGHT - crop.vertical * 2;
499 }
500 }
501
502 // now scale the window dimensions based on the windowscale
503 scaledWidth = (int)(scrwidth * windowscale / 100.0f);
504 scaledHeight = (int)(scrheight * windowscale / 100.0f);
505
506 // prevent the scaled size from exceeding the screen resolution
507 if (scaledWidth > GetSystemMetrics(SM_CXSCREEN) || scaledHeight > GetSystemMetrics(SM_CYSCREEN))
508 {
509 scaledWidth = GetSystemMetrics(SM_CXSCREEN);
510 scaledHeight = GetSystemMetrics(SM_CYSCREEN);
511 }
512
513 // set a preliminary scaled rectangle
514 // to the calculated size...
515 rect->right = scaledWidth;
516 rect->bottom = scaledHeight;
517
518 // To avoid screen artifacts in windowed mode, especially when using scanlines, it is important
519 // that the client area of the window is of the exact scaled dimensions. CreateWindow/MoveWindow by itself
520 // does not do this, therefore, we need the AdjustWindowRectEx() method to help enforce the dimensions.
521 AdjustWindowRectEx(rect, DW_WINDOWSTYLE, showmenu, 0);
522 }
523
cmdlineFail(char msg[])524 static void cmdlineFail(char msg[])
525 {
526 char txt[256];
527 sprintf(txt, "There was an invalid or missing argument\nfor commandline parameter: %s\nThe emulator may not run as expected.", msg);
528 MessageBox(NULL, txt, "Commandline Error", MB_OK | MB_ICONWARNING);
529 }
530
531 // switches to the next scanline mode
changescanlinemode()532 void changescanlinemode()
533 {
534 frameparams.scanlinemode++;
535 if (frameparams.scanlinemode == HIGH + 1)
536 frameparams.scanlinemode = NONE;
537
538 // Force rewrite frame parameters
539 refreshframe();
540
541 return;
542 }
543
544 // Change the 3D tilt effect to the next mode
545 // This only has an effect in Direct3D mode
changetiltlevel()546 void changetiltlevel()
547 {
548 frameparams.tiltlevel++;
549 if (frameparams.tiltlevel > TILTLEVEL3)
550 frameparams.tiltlevel = TILTLEVEL0;
551 }
552
553 // Turns the 3D screensaver on or off.
554 // This only has an effect in Direct3D mode
togglescreensaver()555 void togglescreensaver()
556 {
557 if (rendermode == DIRECT3D)
558 {
559 frameparams.screensaver = !frameparams.screensaver;
560 frameparams.d3dRefresh = TRUE;
561 }
562 }
563
564 // toggle between fullscreen and windowed modes
togglewindowstate()565 void togglewindowstate()
566 {
567 int showCmd = SW_RESTORE;
568
569 // not supported in DirectDraw mode
570 if (rendermode == DIRECTDRAW)
571 return;
572
573 // when the window is destroyed, we lose its keyboard handler
574 // so let's uninit it here and reinit after we create the new window.
575 uninitinput();
576
577 if (screenmode == FULLSCREEN) {
578 screenmode = WINDOW; /* switch to windowed mode */
579 }
580 else { // switch to fullscreen
581
582 // ...also save the current windowed state
583 // in case we need to return to it from fullscreen.
584 GetWindowRect(hWndMain, ¤twindowrect);
585 currentwindowstate.length = sizeof(WINDOWPLACEMENT);
586 GetWindowPlacement(hWndMain, ¤twindowstate);
587
588 screenmode = FULLSCREEN; /* keep screenmode sync'ed with mode */
589 }
590
591 SuppressNextQuitMessage(); // prevents DestroyWindow from closing the application
592 destroymenu();
593 DestroyWindow(hWndMain);
594 initwin();
595
596 /* if we're in Direct3D rendering mode, we need to do a full
597 shutdown and restart of the Direct3D engine. */
598 if (rendermode == DIRECT3D) {
599 shutdowndirect3d();
600
601 if (screenmode == FULLSCREEN)
602 startupdirect3d(fullscreenWidth, fullscreenHeight, FALSE, &frameparams);
603 else
604 startupdirect3d(currentwindowrect.right - currentwindowrect.left,
605 currentwindowrect.bottom - currentwindowrect.top, TRUE, &frameparams);
606 }
607
608 // if we're returning to windowed mode, restore it to its previous size and location.
609 // If not, use the default metrics.
610 if (screenmode == WINDOW && currentwindowrect.bottom && currentwindowrect.right)
611 {
612 MoveWindow(hWndMain, currentwindowrect.left, currentwindowrect.top,
613 currentwindowrect.right - currentwindowrect.left,
614 currentwindowrect.bottom - currentwindowrect.top, TRUE);
615
616 if (currentwindowstate.showCmd == SW_SHOWMAXIMIZED)
617 showCmd = SW_MAXIMIZE;
618
619 changewindowsize(RESET, 0);
620 }
621
622 ShowWindow(hWndMain, showCmd);
623 setmenu();
624
625 // keep sound muted in UI
626 if (UI_is_active)
627 Sound_Pause();
628
629 // reinit the keyboard input and clear it
630 initinput();
631 clearkb();
632
633 setcursor();
634
635 return;
636 }
637
638 /* 'scale' is expressed as a percentage (100 = 100%) */
639 /* CHANGEWINDOWSIZE indicates whether to STEPUP or STEPDOWN */
640 /* by the scale percentage, or to just simply SET or RESET it. */
641 /* Note that when the RESET parameter is used, the scale parameter */
642 /* is ignored. */
changewindowsize(CHANGEWINDOWSIZE cws,int scale)643 void changewindowsize(CHANGEWINDOWSIZE cws, int scale)
644 {
645 RECT rect = {0,0,0,0};
646 WINDOWPLACEMENT wndpl;
647 int xpos, ypos;
648 int originalwindowscale = windowscale;
649
650 // not supported in DirectDraw mode
651 if (rendermode == DIRECTDRAW)
652 return;
653
654 // in simple mode, force scale stepping to 100% jumps
655 if (scalingmethod == SIMPLE && (cws == STEPUP || cws == STEPDOWN))
656 scale = 100;
657
658 // don't change window size if window is maximized, minimized, or fullscreen
659 wndpl.length = sizeof(WINDOWPLACEMENT);
660 GetWindowPlacement(hWndMain, &wndpl);
661 if (wndpl.showCmd == SW_SHOWMAXIMIZED || wndpl.showCmd == SW_SHOWMINIMIZED || screenmode == FULLSCREEN)
662 return;
663
664 switch (cws)
665 {
666 case STEPUP:
667 // adjust current windowscale as an even multiple of the scale parameter
668 windowscale = currentscale / scale * scale;
669 windowscale += scale;
670 break;
671
672 case STEPDOWN:
673 windowscale = currentscale / scale * scale;
674 if (windowscale == currentscale)
675 windowscale -= scale;
676 if (windowscale < 100)
677 {
678 windowscale = 100;
679 return;
680 }
681 break;
682
683 case SET:
684 windowscale = scale;
685 break;
686
687 case RESET:
688 windowscale = currentscale;
689 }
690
691 getscaledrect(&rect);
692
693 // we've oversized, so back off and get out
694 if ((rect.bottom - rect.top) > GetSystemMetrics(SM_CYSCREEN))
695 {
696 windowscale = originalwindowscale;
697 return;
698 }
699
700 // Position the new resized window relative to previous window position
701 xpos = wndpl.rcNormalPosition.left+
702 (((wndpl.rcNormalPosition.right-wndpl.rcNormalPosition.left)-
703 (rect.right-rect.left)) / 2);
704
705 ypos = wndpl.rcNormalPosition.top+
706 (((wndpl.rcNormalPosition.bottom-wndpl.rcNormalPosition.top)-
707 (rect.bottom-rect.top)) / 2);
708
709 MoveWindow(hWndMain, xpos, ypos,
710 rect.right - rect.left, rect.bottom - rect.top, TRUE);
711
712 return;
713 }
714
715 // Short helper function that computes an x,y position that can be used
716 // by MoveWindow to center a window having the given rectangle.
getcenteredcoords(RECT rect,int * x,int * y)717 void getcenteredcoords(RECT rect, int* x, int* y)
718 {
719 *x = (GetSystemMetrics(SM_CXSCREEN) / 2) - ((rect.right - rect.left) / 2);
720 *y = (GetSystemMetrics(SM_CYSCREEN) / 2) - ((rect.bottom - rect.top) / 2);
721 }
722
723 // Returns the native atari coordinates given a set of windows coordinates.
724 // These coordinates are relative to the full 384x240 screen buffer.
725 // a coordinate of -1,-1 indicates a click outside the screen buffer.
getnativecoords(int mx,int my,int * nx,int * ny)726 void getnativecoords(int mx, int my, int* nx, int* ny)
727 {
728 RECT rect;
729 float fx;
730 float fy;
731
732 GetClientRect(hWndMain, &rect);
733
734 // adjust input mouse coordinates based on client rectangle size
735 my = my - ((rect.bottom - rect.top) - frameparams.height) / 2;
736 mx = mx - ((rect.right - rect.left) - frameparams.width) / 2;
737
738 // transform absolute mouse coordinates into relative coordinates
739 fx = (float)mx/frameparams.width;
740 fy = (float)my/frameparams.height;
741
742 // compute the absolute atari coordinates relative to the
743 // 384x240 screen buffer accounting for offsets and cropping
744 *nx = (int)((frameparams.view.right - frameparams.view.left) * fx
745 + frameparams.view.left);
746
747 *ny = (int)((frameparams.view.bottom - frameparams.view.top) * fy
748 + frameparams.view.top);
749
750 // force any points determined to be outside the screen buffer to -1,-1
751 *nx = (*nx < frameparams.view.left || *nx >= frameparams.view.right) ? -1 : *nx;
752 *ny = (*ny < frameparams.view.top || *ny >= frameparams.view.bottom) ? -1 : *ny;
753
754 if (*nx <= -1 || *ny <= -1 || *nx >= Screen_WIDTH || *ny >= Screen_HEIGHT)
755 *nx = *ny = -1;
756 }
757
758 // Parse the width and height from a string formated as
759 // widthxheight (such as 800x600, 1280x720, etc.)
getres(char res[],int * width,int * height)760 BOOL getres(char res[], int *width, int *height)
761 {
762 size_t pos;
763 char sep[] = "x";
764
765 pos = strcspn(res, sep);
766 if (pos == strlen(res))
767 return FALSE; // missing x delimiter
768
769 *width = atoi(strtok(res, sep));
770 *height = atoi(strtok(NULL, sep));
771
772 if (*width == 0 || *height == 0)
773 return FALSE; // value could not be converted
774
775 return TRUE;
776 }
777
778 // Process the commandline, then initialize the selected renderer
gron(int * argc,char * argv[])779 int gron(int *argc, char *argv[])
780 {
781 int i, j;
782 int retvalue = 0;
783 RECT rect = {0,0,0,0};
784 int xpos, ypos;
785
786 // set some defaults
787 int width = 0;
788 int help_only = FALSE;
789
790 // process commandline parameters and arguments
791 for (i = j = 1; i < *argc; i++) {
792 if (strcmp(argv[i], "-windowed") == 0)
793 screenmode = WINDOW;
794 else if (strcmp(argv[i], "-width") == 0) {
795 rendermode = DIRECTDRAW;
796 width = Util_sscandec(argv[++i]);
797 }
798 else if (strcmp(argv[i], "-blt") == 0) {
799 rendermode = DIRECTDRAW;
800 bltgfx = TRUE;
801 }
802 else if (strcmp(argv[i], "-hidecursor") == 0)
803 hidecursor = TRUE;
804 else if (strcmp(argv[i], "-lockaspect") == 0)
805 lockaspect = TRUE;
806 else if (strcmp(argv[i], "-fullscreen") == 0)
807 screenmode = FULLSCREEN;
808 else if (strcmp(argv[i], "-hidemenu") == 0)
809 showmenu = FALSE;
810 else if (strcmp(argv[i], "-winscale") == 0)
811 windowscale = Util_sscandec(argv[++i]);
812 else if (strcmp(argv[i], "-hcrop") == 0)
813 crop.horizontal = atoi(argv[++i]);
814 else if (strcmp(argv[i], "-vcrop") == 0)
815 crop.vertical = atoi(argv[++i]);
816 else if (strcmp(argv[i], "-crop") == 0) {
817 crop.horizontal = atoi(argv[++i]);
818 crop.vertical = atoi(argv[++i]);
819 }
820 else if (strcmp(argv[i], "-hoffset") == 0)
821 offset.horizontal = atoi(argv[++i]);
822 else if (strcmp(argv[i], "-voffset") == 0)
823 offset.vertical = atoi(argv[++i]);
824 else if (strcmp(argv[i], "-scanlines") == 0)
825 {
826 if (!checkparamarg(argv[++i]))
827 cmdlineFail(argv[i-1]);
828 else if (strcmp(argv[i], "low") == 0)
829 frameparams.scanlinemode = LOW;
830 else if (strcmp(argv[i], "medium") == 0)
831 frameparams.scanlinemode = MEDIUM;
832 else if (strcmp(argv[i], "high") == 0)
833 frameparams.scanlinemode = HIGH;
834 }
835 else if (strcmp(argv[i], "-render") == 0)
836 {
837 if (!checkparamarg(argv[++i]))
838 cmdlineFail(argv[i-1]);
839 else if (strcmp(argv[i], "ddraw") == 0)
840 {
841 rendermode = DIRECTDRAW;
842 bltgfx = TRUE;
843 }
844 else if (strcmp(argv[i], "gdi") == 0) {
845 frameparams.filter = NEARESTNEIGHBOR;
846 rendermode = GDI;
847 }
848 else if (strcmp(argv[i], "gdiplus") == 0) {
849 frameparams.filter = NEARESTNEIGHBOR;
850 rendermode = GDIPLUS;
851 }
852 else if (strcmp(argv[i], "direct3d") == 0) {
853 frameparams.filter = NEARESTNEIGHBOR;
854 rendermode = DIRECT3D;
855 }
856 }
857 else if (strcmp(argv[i], "-filter") == 0)
858 {
859 if (!checkparamarg(argv[++i]))
860 cmdlineFail(argv[i-1]);
861 else if (strcmp(argv[i], "pixel") == 0)
862 frameparams.filter = NEARESTNEIGHBOR;
863 else if (strcmp(argv[i], "bilinear") == 0)
864 frameparams.filter = BILINEAR;
865 else if (strcmp(argv[i], "bicubic") == 0)
866 frameparams.filter = BICUBIC;
867 else if (strcmp(argv[i], "hqbilinear") == 0)
868 frameparams.filter = HQBILINEAR;
869 else if (strcmp(argv[i], "hqbicubic") == 0)
870 frameparams.filter = HQBICUBIC;
871 }
872 else if (strcmp(argv[i], "-scaling") == 0)
873 {
874 if (!checkparamarg(argv[++i]))
875 cmdlineFail(argv[i-1]);
876 else if (strcmp(argv[i], "off") == 0)
877 scalingmethod = OFF;
878 else if (strcmp(argv[i], "normal") == 0)
879 scalingmethod = NORMAL;
880 else if (strcmp(argv[i], "simple") == 0)
881 scalingmethod = SIMPLE;
882 else if (strcmp(argv[i], "adaptive") == 0)
883 scalingmethod = ADAPTIVE;
884 }
885 else if (strcmp(argv[i], "-aspect") == 0)
886 {
887 if (!checkparamarg(argv[++i]))
888 cmdlineFail(argv[i-1]);
889 else if (strcmp(argv[i], "auto") == 0)
890 aspectmode = AUTO;
891 else if (strcmp(argv[i], "wide") == 0)
892 aspectmode = WIDE;
893 else if (strcmp(argv[i], "cropped") == 0)
894 aspectmode = CROPPED;
895 else if (strcmp(argv[i], "compressed") == 0)
896 aspectmode = COMPRESSED;
897 }
898 else if (strcmp(argv[i], "-fsres") == 0) {
899 getres(argv[++i], &fullscreenWidth, &fullscreenHeight);
900 usecustomfsresolution = TRUE;
901 }
902 else {
903 if (strcmp(argv[i], "-help") == 0) {
904 Log_print("\t-windowed Run in a window");
905 Log_print("\t-width <num> Set display mode width (ddraw mode only)");
906 Log_print("\t-blt Use blitting to draw graphics (ddraw mode only)");
907 Log_print("\t-hidecursor Always hide mouse cursor in fullscreen");
908 Log_print("\t-fullscreen Run in fullscreen");
909 Log_print("\t-winscale <size> Default window size <100> <200> <300>... etc.");
910 Log_print("\t-scanlines <%%> Scanline mode <low> <medium> <high>");
911 Log_print("\t-render <mode> Render mode <ddraw> <gdi> <gdiplus> <direct3d>");
912 Log_print("\t-filter <mode> Filter <bilinear> <bicubic> <hqbilinear> <hqbicubic>");
913 Log_print("\t-scaling <mode> Scaling method <off> <normal> <simple> <adaptive>");
914 Log_print("\t-aspect <mode> Aspect mode <auto> <wide> <cropped> <compressed>");
915 Log_print("\t-fsres <res> Fullscale resolution <widthxheight> viz. <640x480>");
916 Log_print("\t-vcrop <amt> Crop screen vertically <0..108>");
917 Log_print("\t-hcrop <amt> Crop screen horizontally <-24..150>");
918 Log_print("\t-voffset <amt> Offset screen vertically <-50..50>");
919 Log_print("\t-hoffset <amt> Offset screen horizontally <-24..24>");
920 Log_print("\t-lockaspect Lock aspect mode when trimming");
921 Log_print("\t-hidemenu Hide Windows menu");
922 help_only = TRUE;
923 }
924 argv[j++] = argv[i];
925 }
926 }
927
928 if (help_only)
929 return FALSE;
930
931 *argc = j;
932
933 displaymode = GetActiveDisplayMode();
934
935 initwin(); // initialize a window
936
937 // Do render mode-specific initialization
938 switch (rendermode)
939 {
940 case DIRECTDRAW:
941 retvalue = startupdirectdraw(bltgfx, width);
942 break;
943 case GDIPLUS:
944 startupgdiplus();
945 break;
946 case DIRECT3D:
947 if (screenmode == WINDOW)
948 {
949 startupdirect3d(scaledWidth, scaledHeight, TRUE, &frameparams);
950
951 // Direct3D overrides the creation of the Window, so we must
952 // manually rescale it to ensure it is the exact size we want.
953 getscaledrect(&rect);
954 getcenteredcoords(rect, &xpos, &ypos);
955 MoveWindow(hWndMain, xpos, ypos, rect.right - rect.left,
956 rect.bottom - rect.top, TRUE);
957 }
958 else if (screenmode == FULLSCREEN)
959 {
960 startupdirect3d(fullscreenWidth, fullscreenHeight, FALSE, &frameparams);
961 }
962 break;
963 }
964
965 return retvalue;
966 }
967
968 // Refresh the frame -- called on every frame
refreshv(UBYTE * scr_ptr)969 void refreshv(UBYTE *scr_ptr)
970 {
971 float fHorizAspect, fVertAspect;
972 PAINTSTRUCT ps;
973 INT nRectWidth, nRectHeight;
974 INT nBaseWidth, nBaseHeight;
975 INT i;
976 RECT rt;
977
978 /* DirectDraw mode does not support windowed modes, or aspect ratio processing
979 (nor much else for that matter), so redirect immediately to the DirectDraw renderer. */
980 if (rendermode == DIRECTDRAW)
981 {
982 refreshv_directdraw(scr_ptr, bltgfx);
983 return;
984 }
985
986 GetClientRect(hWndMain, &rt);
987
988 if (refreshframeparams)
989 InvalidateRect(hWndMain, &rt, TRUE); // blank screen to avoid artifacts
990 else
991 InvalidateRect(hWndMain, &rt, FALSE); // don't blank screen
992
993 // open the device context for painting
994 frameparams.hdc = BeginPaint(hWndMain, &ps);
995
996 // Generic frame and window operations are performed inside this section.
997 // In order to avoid performing all of this work on every frame, we only process
998 // when a function has set the refreshframeparams flag to true.
999 if (refreshframeparams)
1000 {
1001 nRectWidth = rt.right - rt.left;
1002 nRectHeight = rt.bottom - rt.top;
1003 frameparams.width = nRectWidth;
1004 frameparams.height = nRectHeight;
1005 frameparams.y_origin = 0;
1006 frameparams.x_origin = 0;
1007 frameparams.d3dRefresh = TRUE;
1008 cropamount = 0;
1009
1010 // Scale and enforce aspect ratio
1011 if (scalingmethod)
1012 {
1013 // Set standard 4:3 crop if we're running in crop mode.
1014 if (aspectmode == CROPPED)
1015 {
1016 // 4:3 aspect ratio in CROPPED and AUTO/FULLSCREEN modes
1017 // are created by specifying an 8 pixel horizontal crop.
1018 cropamount = STD_CROP;
1019 }
1020 else if (aspectmode == AUTO && screenmode == FULLSCREEN)
1021 {
1022 // if we're in AUTO mode and fullscreen, then test the fullscreen
1023 // aspect ratio to see whether we need to crop.
1024 if ((float)fullscreenWidth / (float)fullscreenHeight < 7.0f/5.0f)
1025 cropamount = STD_CROP;
1026 }
1027
1028 // Set the *display* aspect ratio.
1029 // Wide modes are set nominally to an aspect ratio of 7:5
1030 if (aspectmode == WIDE || (aspectmode == AUTO && screenmode == WINDOW) ||
1031 (aspectmode == AUTO && screenmode == FULLSCREEN && cropamount == 0))
1032 {
1033 // Enforce a strict WIDE aspect ratio of 7:5
1034 // if the aspect ratio is locked.
1035 if (lockaspect) {
1036 fHorizAspect = 7.0f;
1037 fVertAspect = 5.0f;
1038 }
1039 else {
1040 // Otherwise, allow aspect ratio to vary dynamically based on the
1041 // crop.horizontal and crop.vertical values.
1042 fHorizAspect = (float)SCREENWIDTH - crop.horizontal * 2;
1043 fVertAspect = (float)SCREENHEIGHT - crop.vertical * 2;
1044 }
1045 }
1046 else // aspect must be COMPRESSED, CROPPED, or AUTO/FULLSCREEN/CROP
1047 {
1048 // Enforce a strict 4:3 aspect ratio if aspect is locked.
1049 if (lockaspect) {
1050 fHorizAspect = 4.0f;
1051 fVertAspect = 3.0f;
1052 }
1053 else {
1054 // Allow aspect ratio to vary dynamically based on the
1055 // crop.horizontal and crop.vertical values if lockaspect is set to false.
1056 fHorizAspect = (float)CROPPEDWIDTH - crop.horizontal * 2;
1057 fVertAspect = (float)SCREENHEIGHT - crop.vertical * 2;
1058 }
1059 }
1060
1061 if (screenmode == FULLSCREEN && scalingmethod == ADAPTIVE)
1062 {
1063 // ADAPTIVE scaling -
1064 // ** Intended for widescreen monitors that stretch non-native resolutions **:
1065 // In fullscreen legacy (4:3) resolutions like VGA running on a
1066 // widescreen monitor, some users may want to squeeze the screen back into a
1067 // more normalized aspect if (and only if) the monitor stretches these resolutions.
1068 // To do this, we create a compensation aspect ratio based on the assumption
1069 // that the original screen resolution is the native resolution --
1070 // or at least one that has square (or nearly square) pixels.
1071
1072 float fOrigAspect = (float)origScreenWidth / (float)origScreenHeight;
1073 float fFullAspect = (float)fullscreenWidth / (float)fullscreenHeight;
1074 float fAdjustedAspect = fFullAspect * fHorizAspect/fVertAspect;
1075 float fCompensationAspect = fAdjustedAspect / fOrigAspect;
1076 float fCompensatedWidth = (float)fullscreenHeight * fCompensationAspect;
1077
1078 frameparams.width = (int)fCompensatedWidth;
1079 frameparams.height = fullscreenHeight;
1080 frameparams.x_origin = (int)((nRectWidth - frameparams.width) / 2.0f);
1081 }
1082 else if (scalingmethod == SIMPLE)
1083 {
1084 // SIMPLE scaling
1085 // This fullscreen mode sizes the Atari screen by a simple even multiple
1086 // of its native size, and finds the largest that will fit within the
1087 // currently set client rectangle size.
1088
1089 // Set the base height/width of Atari screen
1090 // Account for possible cropping if the user has specified it.
1091 if (lockaspect) {
1092 if (aspectmode == WIDE || (aspectmode == AUTO && cropamount == 0))
1093 nBaseWidth = SCREENWIDTH;
1094 else
1095 nBaseWidth = CROPPEDWIDTH;
1096
1097 nBaseHeight = SCREENHEIGHT;
1098 }
1099 else {
1100 if (aspectmode == WIDE || (aspectmode == AUTO && cropamount == 0))
1101 nBaseWidth = SCREENWIDTH - crop.horizontal * 2;
1102 else
1103 nBaseWidth = CROPPEDWIDTH - crop.horizontal * 2;
1104
1105 nBaseHeight = SCREENHEIGHT - crop.vertical * 2;
1106 }
1107
1108 // Find the largest even multiple of the Atari screen
1109 // that will fit inside the client rectangle.
1110 for (i = 1; ;i++)
1111 if (nBaseWidth * i > nRectWidth || nBaseHeight * i > nRectHeight)
1112 break;
1113
1114 // Finally, set the frame size and location
1115 frameparams.height = nBaseHeight * (i-1);
1116 frameparams.width = nBaseWidth * (i-1);
1117 frameparams.y_origin = (int)((nRectHeight - frameparams.height) / 2.0f);
1118 frameparams.x_origin = (int)((nRectWidth - frameparams.width) / 2.0f);
1119 }
1120 else // we're in either a window or fullscreen with NORMAL aspect processing
1121 {
1122 // we have a tall narrow window, so pin to the width and
1123 // adjust the height to force the aspect ratio.
1124 if (nRectHeight >= nRectWidth * fVertAspect/fHorizAspect)
1125 {
1126 frameparams.height = (int)(nRectWidth * fVertAspect/fHorizAspect);
1127 frameparams.width = nRectWidth;
1128 frameparams.y_origin = (int)((nRectHeight - frameparams.height) / 2.0f);
1129 }
1130 // we have a wide, fat window, so pin to the height and
1131 // adjust the width to force the aspect ratio.
1132 else
1133 {
1134 frameparams.width = (int)(nRectHeight * fHorizAspect/fVertAspect);
1135 frameparams.height = nRectHeight;
1136 frameparams.x_origin = (int)((nRectWidth - frameparams.width) / 2.0f);
1137 }
1138 }
1139 }
1140
1141 // Specify the viewport rectangle that will be sent to the renderer. This is a
1142 // rectangle that encloses the portion of the screen buffer that we want to display.
1143 frameparams.view.left = STD_INDENT + cropamount + crop.horizontal;
1144 frameparams.view.top = crop.vertical;
1145 frameparams.view.right = Screen_WIDTH - frameparams.view.left;
1146 frameparams.view.bottom = Screen_HEIGHT - frameparams.view.top;
1147
1148 // If an offset has been set by the user, adjust the viewport by that amount.
1149 // The negative signs reverse the shift direction to make it more natural.
1150 OffsetRect(&frameparams.view, -offset.horizontal, -offset.vertical);
1151
1152 /* When doing a refreshframeparams refresh, perform these direct3d specific */
1153 /* operations. This should be done regardless of aspect ratio settings. */
1154 if (rendermode == DIRECT3D)
1155 {
1156 // Update window dimensions
1157 frameparams.d3dWidth = (float)frameparams.width / nRectWidth;
1158 frameparams.d3dHeight = (float)frameparams.height / nRectHeight;
1159
1160 // Although Direct3D can handle window resizing with no intervention, it is not
1161 // optimal since it does not automatically change the backbuffer size to match the new
1162 // window size. This can result in lower quality after a resize. However, if we take
1163 // the time to manually reset the device and change the backbuffer size, we can maintain
1164 // a high quality image.
1165 resetdevice(nRectWidth, nRectHeight, screenmode, &frameparams);
1166 }
1167
1168 // Update the title bar with resolution, scale, scanline information
1169 if (screenmode == WINDOW)
1170 {
1171 char buffer[100];
1172 char scanlinetxt[25];
1173
1174 // The displayed window scale is actually pixel based rather than screen size
1175 // based to make it more useful. To compute this we use the vertical screen
1176 // dimension, since its relatively constant, and factor out any vertical trim.
1177
1178 currentscale = (int)Util_round(((float)frameparams.height +
1179 (crop.vertical * 2 * ((float)windowscale/100)))
1180 / 240.0f * 100);
1181
1182 switch (frameparams.scanlinemode)
1183 {
1184 case NONE:
1185 strcpy(scanlinetxt, "");
1186 break;
1187 case LOW:
1188 strcpy(scanlinetxt, "; Scanlines: Low (1x)");
1189 break;
1190 case MEDIUM:
1191 strcpy(scanlinetxt, "; Scanlines: Medium (2x)");
1192 break;
1193 case HIGH:
1194 strcpy(scanlinetxt, "; Scanlines: High (3x)");
1195 break;
1196 }
1197
1198 sprintf(buffer, "%s - %dx%d (%d%%)%s",myname, frameparams.width, frameparams.height, currentscale, scanlinetxt);
1199 SetWindowText(hWndMain, buffer);
1200 }
1201
1202 refreshframeparams = FALSE; // we're done refreshing so set the fp_refresh global to FALSE
1203 }
1204
1205 // sanity check to make sure the window has some real estate
1206 if ((!frameparams.width) || (!frameparams.height))
1207 return;
1208
1209 // Now that we've completed our generic screen processing, pass the screen buffer along
1210 // with the calculated frame parameters over to the specific renderer the user has specified
1211 // in order to actually render the frame.
1212 switch (rendermode)
1213 {
1214 case GDI:
1215 refreshv_gdi(scr_ptr, &frameparams);
1216 break;
1217 case GDIPLUS:
1218 refreshv_gdiplus(scr_ptr, &frameparams);
1219 break;
1220 case DIRECT3D:
1221 refreshv_direct3d(scr_ptr, &frameparams);
1222 break;
1223 }
1224
1225 // We are finished writing to the DC
1226 EndPaint(hWndMain, &ps);
1227
1228 ValidateRect(hWndMain, &rt);
1229 }
1230
1231