1 /* Mednafen - Multi-system Emulator
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17
18
19 #include "main.h"
20
21 #ifdef WIN32
22 #include <mednafen/win32-common.h>
23 #endif
24
25 #include <trio/trio.h>
26
27 #include "video.h"
28 #include "opengl.h"
29 #include "shader.h"
30 #include "nongl.h"
31
32 #include "icon.h"
33 #include "netplay.h"
34 #include "cheat.h"
35
36 #include "nnx.h"
37 #include "debugger.h"
38 #include "fps.h"
39 #include "help.h"
40 #include "video-state.h"
41
42 #ifdef WANT_FANCY_SCALERS
43 #include "scalebit.h"
44 #include "hqxx-common.h"
45 #include "2xSaI.h"
46 #endif
47
48 class SDL_to_MDFN_Surface_Wrapper : public MDFN_Surface
49 {
50 public:
SDL_to_MDFN_Surface_Wrapper(SDL_Surface * sdl_surface)51 INLINE SDL_to_MDFN_Surface_Wrapper(SDL_Surface *sdl_surface) : ss(sdl_surface)
52 {
53 // Locking should be first thing.
54 if(SDL_MUSTLOCK(ss))
55 SDL_LockSurface(ss);
56
57 format.bpp = ss->format->BitsPerPixel;
58 format.colorspace = MDFN_COLORSPACE_RGB;
59 format.Rshift = ss->format->Rshift;
60 format.Gshift = ss->format->Gshift;
61 format.Bshift = ss->format->Bshift;
62 format.Ashift = ss->format->Ashift;
63
64 format.Rprec = 8 - ss->format->Rloss;
65 format.Gprec = 8 - ss->format->Gloss;
66 format.Bprec = 8 - ss->format->Bloss;
67 format.Aprec = 8 - ss->format->Aloss;
68
69 pixels_is_external = true;
70
71 pixels16 = NULL;
72 pixels = NULL;
73
74 if(ss->format->BitsPerPixel == 16)
75 {
76 pixels16 = (uint16*)ss->pixels;
77 pitchinpix = ss->pitch >> 1;
78 }
79 else
80 {
81 pixels = (uint32*)ss->pixels;
82 pitchinpix = ss->pitch >> 2;
83 }
84
85 format = MDFN_PixelFormat(MDFN_COLORSPACE_RGB, sdl_surface->format->Rshift, sdl_surface->format->Gshift, sdl_surface->format->Bshift, sdl_surface->format->Ashift);
86
87 w = ss->w;
88 h = ss->h;
89 }
90
~SDL_to_MDFN_Surface_Wrapper()91 INLINE ~SDL_to_MDFN_Surface_Wrapper()
92 {
93 if(SDL_MUSTLOCK(ss))
94 SDL_UnlockSurface(ss);
95 ss = NULL;
96 }
97 private:
98 SDL_Surface *ss;
99 };
100
101 enum
102 {
103 VDRIVER_OPENGL = 0,
104 VDRIVER_SOFTSDL = 1,
105 VDRIVER__COUNT
106 };
107
108 enum
109 {
110 NTVB_NONE = 0,
111
112 NTVB_HQ2X,
113 NTVB_HQ3X,
114 NTVB_HQ4X,
115
116 NTVB_SCALE2X,
117 NTVB_SCALE3X,
118 NTVB_SCALE4X,
119
120 NTVB_NN2X,
121 NTVB_NN3X,
122 NTVB_NN4X,
123
124 NTVB_NNY2X,
125 NTVB_NNY3X,
126 NTVB_NNY4X,
127
128 NTVB_2XSAI,
129 NTVB_SUPER2XSAI,
130 NTVB_SUPEREAGLE,
131 };
132
133 static const MDFNSetting_EnumList VDriver_List[] =
134 {
135 { "default", VDRIVER__COUNT, gettext_noop("Default"), gettext_noop("Selects the default video driver. Currently, this is OpenGL for all platforms, but may change in the future if better platform-specific drivers are added.") },
136
137 { "opengl", VDRIVER_OPENGL, "OpenGL", gettext_noop("All video-related Mednafen features are available with this video driver.") },
138 { "softfb", VDRIVER_SOFTSDL, "Software Blitting to Framebuffer", gettext_noop("Slower with lower-quality scaling than OpenGL, but if you don't have hardware-accelerated OpenGL rendering, it will probably be faster than software OpenGL rendering. Bilinear interpolation not available. OpenGL shaders do not work with this output method, of course.") },
139
140 // Backwards-compat:
141 { "0", VDRIVER_OPENGL },
142 { "1", VDRIVER_SOFTSDL },
143 { "sdl", VDRIVER_SOFTSDL },
144 { "overlay", VDRIVER__COUNT },
145
146 { NULL, 0 },
147 };
148
149 static const MDFNSetting GlobalVideoSettings[] =
150 {
151 { "video.driver", MDFNSF_NOFLAGS, gettext_noop("Video output driver."), NULL, MDFNST_ENUM, "default", NULL, NULL, NULL, NULL, VDriver_List },
152
153 { "video.fs", MDFNSF_NOFLAGS, gettext_noop("Enable fullscreen mode."), NULL, MDFNST_BOOL, "0", },
154 { "video.fs.display", MDFNSF_NOFLAGS, gettext_noop("Display to use with fullscreen mode."), gettext_noop("Specify -1 to use the display on which the center of the window lies in windowed mode."), MDFNST_INT, "-1", "-1", "32767" },
155
156 //{ "video.window.x", MDFNSF_NOFLAGS, gettext_noop("Window starting X position."), NULL, MDFNST_INT, "0x2FFF0000" },
157 //{ "video.window.y", MDFNSF_NOFLAGS, gettext_noop("Window starting Y position."), NULL, MDFNST_INT, "0x2FFF0000" },
158
159 { "video.glvsync", MDFNSF_NOFLAGS, gettext_noop("Attempt to synchronize OpenGL page flips to vertical retrace period."),
160 gettext_noop("Note: Additionally, if the environment variable \"__GL_SYNC_TO_VBLANK\" does not exist, then it will be created and set to the value specified for this setting. This has the effect of forcibly enabling or disabling vblank synchronization when running under Linux with NVidia's drivers."),
161 MDFNST_BOOL, "1" },
162
163 { "video.disable_composition", MDFNSF_NOFLAGS, gettext_noop("Attempt to disable desktop composition."), gettext_noop("Currently, this setting only has an effect on Windows Vista and Windows 7(and probably the equivalent server versions as well)."), MDFNST_BOOL, "1" },
164 };
165
166 static const MDFNSetting_EnumList StretchMode_List[] =
167 {
168 { "0", 0, gettext_noop("Disabled") },
169 { "off", 0 },
170 { "none", 0 },
171
172 { "1", 1 },
173 { "full", 1, gettext_noop("Full"), gettext_noop("Full-screen stretch, disregarding aspect ratio.") },
174
175 { "2", 2 },
176 { "aspect", 2, gettext_noop("Aspect Preserve"), gettext_noop("Full-screen stretch as far as the aspect ratio(in this sense, the equivalent xscalefs == yscalefs) can be maintained.") },
177
178 { "aspect_int", 3, gettext_noop("Aspect Preserve + Integer Scale"), gettext_noop("Full-screen stretch, same as \"aspect\" except that the equivalent xscalefs and yscalefs are rounded down to the nearest integer.") },
179 { "aspect_mult2", 4, gettext_noop("Aspect Preserve + Integer Multiple-of-2 Scale"), gettext_noop("Full-screen stretch, same as \"aspect_int\", but rounds down to the nearest multiple of 2.") },
180
181 { NULL, 0 },
182 };
183
184 static const MDFNSetting_EnumList VideoIP_List[] =
185 {
186 { "0", VIDEOIP_OFF, gettext_noop("Disabled") },
187
188 { "1", VIDEOIP_BILINEAR, gettext_noop("Bilinear") },
189
190 // Disabled until a fix can be made for rotation.
191 { "x", VIDEOIP_LINEAR_X, gettext_noop("Linear (X)"), gettext_noop("Interpolation only on the X axis.") },
192 { "y", VIDEOIP_LINEAR_Y, gettext_noop("Linear (Y)"), gettext_noop("Interpolation only on the Y axis.") },
193
194 { NULL, 0 },
195 };
196
197 static const MDFNSetting_EnumList Special_List[] =
198 {
199 { "0", NTVB_NONE },
200 { "none", NTVB_NONE, "None/Disabled" },
201
202 #ifdef WANT_FANCY_SCALERS
203 { "hq2x", NTVB_HQ2X, "hq2x" },
204 { "hq3x", NTVB_HQ3X, "hq3x" },
205 { "hq4x", NTVB_HQ4X, "hq4x" },
206 { "scale2x",NTVB_SCALE2X, "scale2x" },
207 { "scale3x",NTVB_SCALE3X, "scale3x" },
208 { "scale4x",NTVB_SCALE4X, "scale4x" },
209
210 { "2xsai", NTVB_2XSAI, "2xSaI" },
211 { "super2xsai", NTVB_SUPER2XSAI, "Super 2xSaI" },
212 { "supereagle", NTVB_SUPEREAGLE, "Super Eagle" },
213 #endif
214
215 { "nn2x", NTVB_NN2X, "Nearest-neighbor 2x" },
216 { "nn3x", NTVB_NN3X, "Nearest-neighbor 3x" },
217 { "nn4x", NTVB_NN4X, "Nearest-neighbor 4x" },
218 { "nny2x", NTVB_NNY2X, "Nearest-neighbor 2x, y axis only" },
219 { "nny3x", NTVB_NNY3X, "Nearest-neighbor 3x, y axis only" },
220 { "nny4x", NTVB_NNY4X, "Nearest-neighbor 4x, y axis only" },
221
222 { NULL, 0 },
223 };
224
225 static const MDFNSetting_EnumList Shader_List[] =
226 {
227 { "none", SHADER_NONE, gettext_noop("None/Disabled") },
228 { "autoip", SHADER_AUTOIP, gettext_noop("Auto Interpolation"), gettext_noop("Will automatically interpolate on each axis if the corresponding effective scaling factor is not an integer.") },
229 { "autoipsharper", SHADER_AUTOIPSHARPER, gettext_noop("Sharper Auto Interpolation"), gettext_noop("Same as \"autoip\", but when interpolation is done, it is done in a manner that will reduce blurriness if possible.") },
230 { "scale2x", SHADER_SCALE2X, "Scale2x" },
231 { "sabr", SHADER_SABR, "SABR v3.0", gettext_noop("GPU-intensive.") },
232 { "ipsharper", SHADER_IPSHARPER, gettext_noop("Sharper bilinear interpolation.") },
233 { "ipxnoty", SHADER_IPXNOTY, gettext_noop("Linear interpolation on X axis only.") },
234 { "ipynotx", SHADER_IPYNOTX, gettext_noop("Linear interpolation on Y axis only.") },
235 { "ipxnotysharper", SHADER_IPXNOTYSHARPER, gettext_noop("Sharper version of \"ipxnoty\".") },
236 { "ipynotxsharper", SHADER_IPYNOTXSHARPER, gettext_noop("Sharper version of \"ipynotx\".") },
237
238 { "goat", SHADER_GOAT, gettext_noop("Simple approximation of a color TV CRT look."), gettext_noop("Intended for fullscreen modes with a vertical resolution of around 1000 to 1500 pixels. Doesn't simulate halation and electron beam energy distribution nuances.") },
239
240 { NULL, 0 },
241 };
242
243 static const MDFNSetting_EnumList GoatPat_List[] =
244 {
245 { "goatron", ShaderParams::GOAT_MASKPAT_GOATRON, gettext_noop("Goatron"), gettext_noop("Brightest.") },
246 { "goattron", ShaderParams::GOAT_MASKPAT_GOATRON },
247 { "goatronprime", ShaderParams::GOAT_MASKPAT_GOATRONPRIME },
248 { "goattronprime", ShaderParams::GOAT_MASKPAT_GOATRONPRIME },
249
250 { "borg", ShaderParams::GOAT_MASKPAT_BORG, gettext_noop("Borg"), gettext_noop("Darkest.") },
251 { "slenderman", ShaderParams::GOAT_MASKPAT_SLENDERMAN, gettext_noop("Slenderman"), gettext_noop("Spookiest?") },
252
253 { NULL, 0 },
254 };
255
Video_MakeSettings(std::vector<MDFNSetting> & settings)256 void Video_MakeSettings(std::vector <MDFNSetting> &settings)
257 {
258 static const char *CSD_xres = gettext_noop("Full-screen horizontal resolution.");
259 static const char *CSD_yres = gettext_noop("Full-screen vertical resolution.");
260 static const char *CSDE_xres = gettext_noop("A value of \"0\" will cause the current desktop horizontal resolution to be used.");
261 static const char *CSDE_yres = gettext_noop("A value of \"0\" will cause the current desktop vertical resolution to be used.");
262
263 static const char *CSD_xscale = gettext_noop("Scaling factor for the X axis in windowed mode.");
264 static const char *CSD_yscale = gettext_noop("Scaling factor for the Y axis in windowed mode.");
265
266 static const char *CSD_xscalefs = gettext_noop("Scaling factor for the X axis in fullscreen mode.");
267 static const char *CSD_yscalefs = gettext_noop("Scaling factor for the Y axis in fullscreen mode.");
268 static const char *CSDE_xyscalefs = gettext_noop("For this setting to have any effect, the \"<system>.stretch\" setting must be set to \"0\".");
269
270 static const char *CSD_scanlines = gettext_noop("Enable scanlines with specified opacity.");
271 static const char *CSDE_scanlines = gettext_noop("Opacity is specified in %; IE a value of \"100\" will give entirely black scanlines.\n\nNegative values are the same as positive values for non-interlaced video, but for interlaced video will cause the scanlines to be overlaid over the previous(if the video.deinterlacer setting is set to \"weave\", the default) field's lines.");
272
273 static const char *CSD_stretch = gettext_noop("Stretch to fill screen.");
274 static const char *CSDvideo_settingsip = gettext_noop("Enable (bi)linear interpolation.");
275
276 static const char *CSD_special = gettext_noop("Enable specified special video scaler.");
277 static const char *CSDE_special = gettext_noop("The destination rectangle is NOT altered by this setting, so if you have xscale and yscale set to \"2\", and try to use a 3x scaling filter like hq3x, the image is not going to look that great. The nearest-neighbor scalers are intended for use with bilinear interpolation enabled, at high resolutions(such as 1280x1024; nn2x(or nny2x) + bilinear interpolation + fullscreen stretching at this resolution looks quite nice).");
278
279 static const char *CSD_shader = gettext_noop("Enable specified OpenGL shader.");
280 static const char *CSDE_shader = gettext_noop("Obviously, this will only work with the OpenGL \"video.driver\" setting, and only on cards and OpenGL implementations that support shaders, otherwise you will get a black screen, or Mednafen may display an error message when starting up. When a shader is enabled, the \"<system>.videoip\" setting is ignored.");
281
282 for(unsigned int i = 0; i < MDFNSystems.size() + 1; i++)
283 {
284 int nominal_width;
285 //int nominal_height;
286 const char* default_videoip;
287 const char *sysname;
288 char default_value[256];
289 MDFNSetting setting;
290 const int default_xres = 0, default_yres = 0;
291 const double default_scalefs = 1.0;
292 double default_scale;
293
294 if(i == MDFNSystems.size())
295 {
296 nominal_width = 384;
297 //nominal_height = 240;
298 default_videoip = "0";
299 sysname = "player";
300 }
301 else
302 {
303 const int mr = MDFNSystems[i]->multires;
304
305 nominal_width = MDFNSystems[i]->nominal_width;
306 //nominal_height = MDFNSystems[i]->nominal_height;
307
308 if(mr < 0)
309 default_videoip = "x";
310 else if(!mr)
311 default_videoip = "0";
312 else
313 default_videoip = "1";
314
315 sysname = (const char *)MDFNSystems[i]->shortname;
316 }
317
318 default_scale = ceil(1024 / nominal_width);
319
320 if(default_scale * nominal_width > 1024)
321 default_scale--;
322
323 if(!default_scale)
324 default_scale = 1;
325
326 trio_snprintf(default_value, 256, "%d", default_xres);
327 BuildSystemSetting(&setting, sysname, "xres", CSD_xres, CSDE_xres, MDFNST_UINT, strdup(default_value), "0", "65536");
328 settings.push_back(setting);
329
330 trio_snprintf(default_value, 256, "%d", default_yres);
331 BuildSystemSetting(&setting, sysname, "yres", CSD_yres, CSDE_yres, MDFNST_UINT, strdup(default_value), "0", "65536");
332 settings.push_back(setting);
333
334 trio_snprintf(default_value, 256, "%f", default_scale);
335 BuildSystemSetting(&setting, sysname, "xscale", CSD_xscale, NULL, MDFNST_FLOAT, strdup(default_value), "0.01", "256");
336 settings.push_back(setting);
337 BuildSystemSetting(&setting, sysname, "yscale", CSD_yscale, NULL, MDFNST_FLOAT, strdup(default_value), "0.01", "256");
338 settings.push_back(setting);
339
340 trio_snprintf(default_value, 256, "%f", default_scalefs);
341 BuildSystemSetting(&setting, sysname, "xscalefs", CSD_xscalefs, CSDE_xyscalefs, MDFNST_FLOAT, strdup(default_value), "0.01", "256");
342 settings.push_back(setting);
343 BuildSystemSetting(&setting, sysname, "yscalefs", CSD_yscalefs, CSDE_xyscalefs, MDFNST_FLOAT, strdup(default_value), "0.01", "256");
344 settings.push_back(setting);
345
346 BuildSystemSetting(&setting, sysname, "scanlines", CSD_scanlines, CSDE_scanlines, MDFNST_INT, "0", "-100", "100");
347 settings.push_back(setting);
348
349 BuildSystemSetting(&setting, sysname, "stretch", CSD_stretch, NULL, MDFNST_ENUM, "aspect_mult2", NULL, NULL, NULL, NULL, StretchMode_List);
350 settings.push_back(setting);
351
352 BuildSystemSetting(&setting, sysname, "videoip", CSDvideo_settingsip, NULL, MDFNST_ENUM, default_videoip, NULL, NULL, NULL, NULL, VideoIP_List);
353 settings.push_back(setting);
354
355 BuildSystemSetting(&setting, sysname, "special", CSD_special, CSDE_special, MDFNST_ENUM, "none", NULL, NULL, NULL, NULL, Special_List);
356 settings.push_back(setting);
357
358 BuildSystemSetting(&setting, sysname, "shader", CSD_shader, CSDE_shader, MDFNST_ENUM, "none", NULL, NULL, NULL, NULL, Shader_List);
359 settings.push_back(setting);
360
361 BuildSystemSetting(&setting, sysname, "shader.goat.hdiv", gettext_noop("Constant RGB horizontal divergence."), nullptr, MDFNST_FLOAT, "0.50", "-2.00", "2.00");
362 settings.push_back(setting);
363
364 BuildSystemSetting(&setting, sysname, "shader.goat.vdiv", gettext_noop("Constant RGB vertical divergence."), nullptr, MDFNST_FLOAT, "0.50", "-2.00", "2.00");
365 settings.push_back(setting);
366
367 BuildSystemSetting(&setting, sysname, "shader.goat.pat", gettext_noop("Mask pattern."), nullptr, MDFNST_ENUM, "goatron", NULL, NULL, NULL, NULL, GoatPat_List);
368 settings.push_back(setting);
369
370 BuildSystemSetting(&setting, sysname, "shader.goat.tp", gettext_noop("Transparency of otherwise-opaque mask areas."), nullptr, MDFNST_FLOAT, "0.50", "0.00", "1.00");
371 settings.push_back(setting);
372
373 BuildSystemSetting(&setting, sysname, "shader.goat.fprog", gettext_noop("Force interlaced video to be treated as progressive."), gettext_noop("When disabled, the default, the \"video.deinterlacer\" setting is effectively ignored with respect to what appears on the screen. When enabled, it may be prudent to disable the scanlines effect controlled by the *.goat.slen setting, or else the scanline effect may look objectionable."), MDFNST_BOOL, "0");
374 settings.push_back(setting);
375
376 BuildSystemSetting(&setting, sysname, "shader.goat.slen", gettext_noop("Enable scanlines effect."), nullptr, MDFNST_BOOL, "1");
377 settings.push_back(setting);
378 }
379
380 for(unsigned i = 0; i < sizeof(GlobalVideoSettings) / sizeof(GlobalVideoSettings[0]); i++)
381 settings.push_back(GlobalVideoSettings[i]);
382 }
383
384 static struct
385 {
386 int fullscreen;
387 int fs_display;
388 int xres, yres;
389 double xscale, xscalefs;
390 double yscale, yscalefs;
391 int videoip;
392 int stretch;
393 int special;
394 int scanlines;
395 ShaderType shader;
396 ShaderParams shader_params;
397
398 std::string special_str, shader_str, goat_pat_str;
399 } video_settings;
400
401 static unsigned vdriver;
402 static bool osd_alpha_blend;
403
404 static const struct ScalerDefinition
405 {
406 int id;
407 int xscale;
408 int yscale;
409 } Scalers[] =
410 {
411 { NTVB_HQ2X, 2, 2 },
412 { NTVB_HQ3X, 3, 3 },
413 { NTVB_HQ4X, 4, 4 },
414
415 { NTVB_SCALE2X, 2, 2 },
416 { NTVB_SCALE3X, 3, 3 },
417 { NTVB_SCALE4X, 4, 4 },
418
419 { NTVB_NN2X, 2, 2 },
420 { NTVB_NN3X, 3, 3 },
421 { NTVB_NN4X, 4, 4 },
422
423 { NTVB_NNY2X, 1, 2 },
424 { NTVB_NNY3X, 1, 3 },
425 { NTVB_NNY4X, 1, 4 },
426
427 { NTVB_2XSAI, 2, 2 },
428 { NTVB_SUPER2XSAI, 2, 2 },
429 { NTVB_SUPEREAGLE, 2, 2 },
430 };
431
432 static MDFNGI *VideoGI;
433
434 static const ScalerDefinition* CurrentScaler = NULL;
435
436 static int winpos_x, winpos_y;
437 static bool winpos_applied;
438 static SDL_Window* window = NULL;
439 static SDL_GLContext glcontext = NULL;
440 static SDL_Surface *screen = NULL;
441 static OpenGL_Blitter *ogl_blitter = NULL;
442 static SDL_Surface *IconSurface=NULL;
443
444 static int32 screen_w, screen_h;
445 static MDFN_Rect screen_dest_rect;
446
447 static MDFN_Surface *HelpSurface = NULL;
448 static MDFN_Rect HelpRect;
449
450 static MDFN_Surface *SMSurface = NULL;
451 static MDFN_Rect SMRect;
452 static MDFN_Rect SMDRect;
453
454 static double exs,eys;
455 static int evideoip;
456
457 static int NeedClear = 0;
458 static uint32 LastBBClearTime = 0;
459
460 static WMInputBehavior CurWMIB;
461
462 static int rotated;
463
464 static MDFN_PixelFormat pf_normal;
465
MarkNeedBBClear(void)466 static INLINE void MarkNeedBBClear(void)
467 {
468 NeedClear = 15;
469 }
470
SyncCleanup(void)471 static void SyncCleanup(void)
472 {
473 if(SMSurface)
474 {
475 delete SMSurface;
476 SMSurface = nullptr;
477 }
478
479 if(HelpSurface)
480 {
481 delete HelpSurface;
482 HelpSurface = nullptr;
483 }
484
485 if(ogl_blitter)
486 {
487 delete ogl_blitter;
488 ogl_blitter = nullptr;
489 }
490
491 if(glcontext)
492 {
493 SDL_GL_DeleteContext(glcontext);
494 glcontext = nullptr;
495 }
496
497 /*
498 */
499 }
500
Video_Kill(void)501 void Video_Kill(void)
502 {
503 SyncCleanup();
504
505 if(window)
506 {
507 if(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
508 SDL_SetWindowFullscreen(window, 0);
509 //
510 SDL_SetRelativeMouseMode(SDL_FALSE);
511 SDL_ShowCursor(SDL_TRUE);
512 SDL_SetWindowGrab(window, SDL_FALSE);
513
514 SDL_DestroyWindow(window);
515 window = nullptr;
516 }
517 //
518 //
519 //
520 if(IconSurface)
521 {
522 //SDL_FreeSurface(IconSurface);
523 //IconSurface = nullptr;
524 }
525
526 screen = nullptr;
527 VideoGI = nullptr;
528 screen_w = 0;
529 screen_h = 0;
530 }
531
532 // May be called before Video_Init(), and after Video_Kill().
Video_ErrorPopup(bool warning,const char * title,const char * text)533 bool Video_ErrorPopup(bool warning, const char* title, const char* text)
534 {
535 bool ret = false;
536
537 #ifdef WIN32
538 if(window)
539 {
540 // Best not to drive the user stark raving mad.
541 SDL_SetRelativeMouseMode(SDL_FALSE);
542 SDL_ShowCursor(SDL_TRUE);
543 SDL_SetWindowGrab(window, SDL_FALSE);
544 // should we or shouldn't we...: SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "0");
545 }
546 #if 0
547 SDL_Window* pw = window;
548
549 // Avoid comically-horrible behavior.
550 if(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
551 pw = nullptr;
552
553 ret = !SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, text, pw);
554 #endif
555 ret = (bool)MessageBoxW(NULL, (wchar_t*)UTF8_to_UTF16(text).c_str(), (wchar_t*)UTF8_to_UTF16(title).c_str(), MB_OK | (warning ? MB_ICONWARNING : MB_ICONERROR) | MB_TASKMODAL | MB_SETFOREGROUND | MB_TOPMOST);
556
557 if(window)
558 Video_SetWMInputBehavior(CurWMIB);
559 #endif
560
561 return ret;
562 }
563
GenerateWindowedDestRect(void)564 static void GenerateWindowedDestRect(void)
565 {
566 exs = video_settings.xscale;
567 eys = video_settings.yscale;
568
569 screen_dest_rect.x = 0;
570 screen_dest_rect.y = 0;
571 screen_dest_rect.w = floor(0.5 + VideoGI->nominal_width * exs);
572 screen_dest_rect.h = floor(0.5 + VideoGI->nominal_height * eys);
573
574 if(rotated == MDFN_ROTATE90 || rotated == MDFN_ROTATE270)
575 std::swap(screen_dest_rect.w, screen_dest_rect.h);
576 }
577
GenerateFullscreenDestRect(void)578 static bool GenerateFullscreenDestRect(void)
579 {
580 if(video_settings.stretch)
581 {
582 int nom_width, nom_height;
583
584 if(rotated)
585 {
586 nom_width = VideoGI->nominal_height;
587 nom_height = VideoGI->nominal_width;
588 }
589 else
590 {
591 nom_width = VideoGI->nominal_width;
592 nom_height = VideoGI->nominal_height;
593 }
594
595 if(video_settings.stretch == 2 || video_settings.stretch == 3 || video_settings.stretch == 4) // Aspect-preserve stretch
596 {
597 exs = (double)screen_w / nom_width;
598 eys = (double)screen_h / nom_height;
599
600 if(video_settings.stretch == 3 || video_settings.stretch == 4) // Round down to nearest int.
601 {
602 double floor_exs = floor(exs);
603 double floor_eys = floor(eys);
604
605 if(!floor_exs || !floor_eys)
606 {
607 MDFN_printf(_("WARNING: Resolution is too low for stretch mode selected. Falling back to \"aspect\" mode.\n"));
608 }
609 else
610 {
611 exs = floor_exs;
612 eys = floor_eys;
613
614 if(video_settings.stretch == 4) // Round down to nearest multiple of 2.
615 {
616 int even_exs = (int)exs & ~1;
617 int even_eys = (int)eys & ~1;
618
619 if(!even_exs || !even_eys)
620 {
621 MDFN_printf(_("WARNING: Resolution is too low for stretch mode selected. Falling back to \"aspect_int\" mode.\n"));
622 }
623 else
624 {
625 exs = even_exs;
626 eys = even_eys;
627 }
628 }
629 }
630 }
631
632 // Check if we are constrained horizontally or vertically
633 if (exs > eys)
634 {
635 // Too tall for screen, fill vertically
636 exs = eys;
637 }
638 else
639 {
640 // Too wide for screen, fill horizontally
641 eys = exs;
642 }
643
644 //printf("%f %f\n", exs, eys);
645
646 screen_dest_rect.w = floor(0.5 + exs * nom_width);
647 screen_dest_rect.h = floor(0.5 + eys * nom_height);
648
649 // Centering:
650 int nx = (int)((screen_w - screen_dest_rect.w) / 2);
651 if(nx < 0) nx = 0;
652 screen_dest_rect.x = nx;
653
654 int ny = (int)((screen_h - screen_dest_rect.h) / 2);
655 if(ny < 0) ny = 0;
656 screen_dest_rect.y = ny;
657 }
658 else // Full-stretch
659 {
660 screen_dest_rect.x = 0;
661 screen_dest_rect.w = screen_w;
662
663 screen_dest_rect.y = 0;
664 screen_dest_rect.h = screen_h;
665
666 exs = (double)screen_w / nom_width;
667 eys = (double)screen_h / nom_height;
668 }
669 }
670 else
671 {
672 exs = video_settings.xscalefs;
673 eys = video_settings.yscalefs;
674
675 screen_dest_rect.w = floor(0.5 + VideoGI->nominal_width * exs);
676 screen_dest_rect.h = floor(0.5 + VideoGI->nominal_height * eys);
677
678 if(rotated == MDFN_ROTATE90 || rotated == MDFN_ROTATE270)
679 std::swap(screen_dest_rect.w, screen_dest_rect.h);
680
681 screen_dest_rect.x = (screen_w - screen_dest_rect.w) / 2;
682 screen_dest_rect.y = (screen_h - screen_dest_rect.h) / 2;
683 }
684
685 // Quick and dirty kludge for VB's "hli" and "vli" 3D modes.
686 screen_dest_rect.x &= ~1;
687 screen_dest_rect.y &= ~1;
688
689 return screen_dest_rect.w < 16384 && screen_dest_rect.h < 16384;
690 }
691
692 static bool weset_glstvb = false;
693 static uint32 real_rs, real_gs, real_bs, real_as;
694
Video_SetWMInputBehavior(const WMInputBehavior & beeeeees)695 void Video_SetWMInputBehavior(const WMInputBehavior& beeeeees)
696 {
697 CurWMIB = beeeeees;
698 //
699 const bool fs = (SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN);
700 const bool grab = CurWMIB.Grab;
701 const bool relm = CurWMIB.MouseRel && !CurWMIB.MouseAbs && !CurWMIB.Cursor && (grab || fs);
702 const bool curse = !relm && CurWMIB.Cursor;
703
704 //printf("Grab: %d, RelM: %d, Curse: %d\n", grab, relm, curse);
705
706 if(grab)
707 {
708 SDL_ShowWindow(window);
709 SDL_RaiseWindow(window);
710 SDL_SetWindowGrab(window, SDL_TRUE);
711 }
712
713 SDL_ShowCursor(SDL_FALSE);
714 SDL_SetRelativeMouseMode(relm ? SDL_TRUE : SDL_FALSE);
715 SDL_ShowCursor(curse ? SDL_TRUE : SDL_FALSE);
716
717 if(!grab)
718 SDL_SetWindowGrab(window, SDL_FALSE);
719
720 SDL_SetHint(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, grab ? "1" : "0");
721 }
722
723 #if 0
724 struct ModeInfo
725 {
726 int32 width = 0;
727 int32 height = 0;
728 MDFN_PixelFormat format(MDFN_COLORSPACE_RGB, 0, 8, 16, 24);
729 bool fullscreen = false;
730 float refresh_rate = 0.0
731 bool vsync = false;
732 };
733
734 static ModeInfo SetMode(const ModeInfo& mode)
735 {
736 if(window && !(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN))
737 SDL_GetWindowPosition(window, &winpos_x, &winpos_y);
738
739 if(window && !(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN))
740 SDL_GetWindowPosition(window, &winpos_x, &winpos_y);
741 //
742 SyncCleanup();
743 }
744
745 if(new_mode == last_tried_mode && new_mode == cur_mode)
746 SetMode(new_mode);
747 #endif
748
Video_Sync(MDFNGI * gi)749 void Video_Sync(MDFNGI *gi)
750 {
751 MDFNI_printf(_("Initializing video...\n"));
752 MDFN_AutoIndent aindv(1);
753
754 if(window && winpos_applied && !(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN))
755 SDL_GetWindowPosition(window, &winpos_x, &winpos_y);
756 //
757 SyncCleanup();
758 //Time::SleepMS(1000);
759
760 VideoGI = gi;
761 rotated = gi->rotated;
762 //
763 #ifdef WIN32
764 if(MDFN_GetSettingB("video.disable_composition"))
765 {
766 static bool dwm_already_try_disable = false;
767
768 if(!dwm_already_try_disable)
769 {
770 HMODULE dwmdll;
771
772 dwm_already_try_disable = true;
773
774 if((dwmdll = LoadLibrary("Dwmapi.dll")) != NULL)
775 {
776 HRESULT WINAPI (*p_DwmEnableComposition)(UINT) = (HRESULT WINAPI (*)(UINT))GetProcAddress(dwmdll, "DwmEnableComposition");
777
778 if(p_DwmEnableComposition != NULL)
779 {
780 p_DwmEnableComposition(0);
781 }
782 }
783 }
784 }
785 #endif
786
787 if(!getenv("__GL_SYNC_TO_VBLANK") || weset_glstvb)
788 {
789 if(MDFN_GetSettingB("video.glvsync"))
790 {
791 #if HAVE_PUTENV
792 static char gl_pe_string[] = "__GL_SYNC_TO_VBLANK=1";
793 putenv(gl_pe_string);
794 #elif HAVE_SETENV
795 setenv("__GL_SYNC_TO_VBLANK", "1", 1);
796 #endif
797 }
798 else
799 {
800 #if HAVE_PUTENV
801 static char gl_pe_string[] = "__GL_SYNC_TO_VBLANK=0";
802 putenv(gl_pe_string);
803 #elif HAVE_SETENV
804 setenv("__GL_SYNC_TO_VBLANK", "0", 1);
805 #endif
806 }
807 weset_glstvb = true;
808 }
809 //
810 osd_alpha_blend = MDFN_GetSettingB("osd.alpha_blend");
811
812 std::string snp = std::string(gi->shortname) + ".";
813
814 if(gi->GameType == GMT_PLAYER)
815 snp = "player.";
816
817 video_settings.xres = MDFN_GetSettingUI(snp + "xres");
818 video_settings.yres = MDFN_GetSettingUI(snp + "yres");
819 video_settings.xscale = MDFN_GetSettingF(snp + "xscale");
820 video_settings.yscale = MDFN_GetSettingF(snp + "yscale");
821 video_settings.xscalefs = MDFN_GetSettingF(snp + "xscalefs");
822 video_settings.yscalefs = MDFN_GetSettingF(snp + "yscalefs");
823 video_settings.videoip = MDFN_GetSettingI(snp + "videoip");
824 video_settings.stretch = MDFN_GetSettingUI(snp + "stretch");
825 video_settings.scanlines = MDFN_GetSettingI(snp + "scanlines");
826
827 video_settings.special = MDFN_GetSettingUI(snp + "special");
828 video_settings.special_str = MDFN_GetSettingS(snp + "special");
829
830 video_settings.shader_params.goat_hdiv = MDFN_GetSettingF(snp + "shader.goat.hdiv");
831 video_settings.shader_params.goat_vdiv = MDFN_GetSettingF(snp + "shader.goat.vdiv");
832 video_settings.shader_params.goat_pat = MDFN_GetSettingI(snp + "shader.goat.pat");
833 video_settings.goat_pat_str = MDFN_GetSettingS(snp + "shader.goat.pat");
834 video_settings.shader_params.goat_tp = MDFN_GetSettingF(snp + "shader.goat.tp");
835 video_settings.shader_params.goat_slen = MDFN_GetSettingB(snp + "shader.goat.slen");
836 video_settings.shader_params.goat_fprog = MDFN_GetSettingB(snp + "shader.goat.fprog");
837 //
838
839 video_settings.fullscreen = MDFN_GetSettingB("video.fs");
840 video_settings.fs_display = MDFN_GetSettingI("video.fs.display");
841 vdriver = MDFN_GetSettingI("video.driver");
842 if(vdriver == VDRIVER__COUNT) // "default"
843 vdriver = VDRIVER_OPENGL;
844
845 video_settings.shader = (ShaderType)MDFN_GetSettingI(snp + "shader");
846 video_settings.shader_str = MDFN_GetSettingS(snp + "shader");
847 //
848 //
849 if(0)
850 {
851 // Gotta keep the compiler on its toes(the floor is SPAGHETTI)!
852 TryWindowed:;
853 MDFNI_SetSettingB("video.fs", false);
854 video_settings.fullscreen = 0;
855 if(window)
856 {
857 if(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN)
858 SDL_SetWindowFullscreen(window, 0);
859 }
860 }
861
862 GenerateWindowedDestRect();
863 screen_w = screen_dest_rect.w;
864 screen_h = screen_dest_rect.h;
865 //
866 //
867
868 if(screen_dest_rect.w > 16383 || screen_dest_rect.h > 16383)
869 throw MDFN_Error(0, _("Window size(%dx%d) is too large!"), screen_dest_rect.w, screen_dest_rect.h);
870
871 SDL_SetWindowFullscreen(window, 0);
872 SDL_PumpEvents();
873 SDL_SetWindowSize(window, screen_dest_rect.w, screen_dest_rect.h);
874 SDL_PumpEvents();
875 SDL_SetWindowPosition(window, winpos_x, winpos_y);
876 winpos_applied = true;
877 SDL_PumpEvents();
878 SDL_SetWindowTitle(window, (gi && gi->name.size()) ? gi->name.c_str() : "Mednafen");
879 SDL_PumpEvents();
880 SDL_ShowWindow(window);
881 SDL_PumpEvents();
882
883 //
884 if(0)
885 {
886 TryNonGL:;
887
888 if(ogl_blitter)
889 {
890 delete ogl_blitter;
891 ogl_blitter = nullptr;
892 }
893
894 if(glcontext)
895 {
896 SDL_GL_DeleteContext(glcontext);
897 glcontext = nullptr;
898 }
899 vdriver = VDRIVER_SOFTSDL;
900 }
901 //
902 if(vdriver == VDRIVER_OPENGL)
903 {
904 if(!(SDL_GetWindowFlags(window) & SDL_WINDOW_OPENGL))
905 {
906 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to soft SDL driver because window is not OpenGL-capable."));
907 goto TryNonGL;
908 }
909 else
910 {
911 SDL_GL_ResetAttributes();
912 SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
913 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
914 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1);
915 //SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
916 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
917
918 if(!(glcontext = SDL_GL_CreateContext(window)))
919 {
920 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to soft SDL driver because of error creating OpenGL context: %s"), SDL_GetError());
921 goto TryNonGL;
922 }
923 else
924 {
925 SDL_GL_SetSwapInterval(MDFN_GetSettingB("video.glvsync"));
926 }
927 }
928 }
929
930 #if 1
931 // Kludge, TODO: figure out where the bug exists in SDL or wherever...
932 {
933 int x, y;
934 SDL_GetWindowPosition(window, &x, &y);
935 SDL_PumpEvents();
936 SDL_SetWindowPosition(window, x, y);
937 }
938 #endif
939
940 //
941 //
942 //
943 CurrentScaler = nullptr;
944 for(auto& scaler : Scalers)
945 if(video_settings.special == scaler.id)
946 CurrentScaler = &scaler;
947 assert(video_settings.special == NTVB_NONE || CurrentScaler);
948
949 evideoip = video_settings.videoip;
950
951 //
952 //
953 //
954 if(video_settings.fullscreen)
955 {
956 const unsigned xres = video_settings.xres;
957 const unsigned yres = video_settings.yres;
958 SDL_DisplayMode trymode;
959 SDL_DisplayMode mode;
960 int dindex;
961
962 if(video_settings.fs_display >= 0)
963 dindex = video_settings.fs_display;
964 else
965 {
966 #if 0
967 if((dindex = SDL_GetWindowDisplayIndex(window)) < 0)
968 {
969 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_GetWindowDisplayIndex() failed: %s"), SDL_GetError());
970 goto TryWindowed;
971 }
972 #endif
973 int num_displays;
974 int wx, wy;
975 int ww, wh;
976 int wcx, wcy;
977
978 dindex = 0;
979
980 SDL_GetWindowPosition(window, &wx, &wy);
981 SDL_GetWindowSize(window, &ww, &wh);
982
983 wcx = wx + ww / 2;
984 wcy = wy + wh / 2;
985
986 if((num_displays = SDL_GetNumVideoDisplays()) < 0)
987 {
988 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_GetNumVideoDisplays() failed: %s"), SDL_GetError());
989 goto TryWindowed;
990 }
991
992 //
993 // Fullscreen with mirrored displays is kind of glitchy(maybe partly SDL2's fault?), but try to force usage of the one with the largest pixel count.
994 //
995 struct DIndexModeBounds
996 {
997 int dindex;
998 SDL_DisplayMode mode;
999 SDL_Rect bounds;
1000 };
1001 std::vector<DIndexModeBounds> dimbs;
1002
1003 for(int d = 0; d < num_displays; d++)
1004 {
1005 DIndexModeBounds z;
1006
1007 z.dindex = d;
1008 if(SDL_GetCurrentDisplayMode(d, &z.mode) < 0)
1009 {
1010 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_GetCurrentDisplayMode() failed: %s"), SDL_GetError());
1011 goto TryWindowed;
1012 }
1013
1014 if(SDL_GetDisplayBounds(d, &z.bounds) < 0)
1015 {
1016 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_DisplayBounds() failed: %s"), SDL_GetError());
1017 goto TryWindowed;
1018 }
1019
1020 bool add_at_end = true;
1021 // Won't perform well if someone has thousands of displays ;)
1022 for(DIndexModeBounds& dimbs_e : dimbs)
1023 {
1024 if(z.bounds.x == dimbs_e.bounds.x && z.bounds.y == dimbs_e.bounds.y)
1025 {
1026 if((z.mode.w * z.mode.h) > (dimbs_e.mode.w * dimbs_e.mode.h))
1027 dimbs_e = z;
1028
1029 add_at_end = false;
1030 break;
1031 }
1032 }
1033
1034 if(add_at_end)
1035 dimbs.push_back(z);
1036 }
1037
1038 int64 last_distance = INT64_MAX;
1039 for(const DIndexModeBounds& e : dimbs)
1040 {
1041 const int bcx = e.bounds.x + (e.bounds.w / 2);
1042 const int bcy = e.bounds.y + (e.bounds.h / 2);
1043 const int dx = wcx - bcx;
1044 const int dy = wcy - bcy;
1045 const int64 distance = (int64)dx * dx + (int64)dy * dy;
1046
1047 if(distance < last_distance)
1048 {
1049 dindex = e.dindex;
1050 last_distance = distance;
1051 }
1052 }
1053 }
1054
1055 //
1056 //
1057 {
1058 SDL_Rect dbr;
1059
1060 if(SDL_GetDisplayBounds(dindex, &dbr) < 0)
1061 {
1062 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_GetDisplayBounds() failed: %s"), SDL_GetError());
1063 goto TryWindowed;
1064 }
1065
1066 SDL_SetWindowPosition(window, dbr.x, dbr.y);
1067 SDL_SetWindowSize(window, dbr.w, dbr.h);
1068 }
1069 //
1070 //
1071
1072 if(SDL_GetCurrentDisplayMode(dindex, &trymode) < 0)
1073 {
1074 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_GetCurrentDisplayMode() failed: %s"), SDL_GetError());
1075 goto TryWindowed;
1076 }
1077
1078 if(xres > 0)
1079 trymode.w = xres;
1080
1081 if(yres > 0)
1082 trymode.h = yres;
1083
1084 if(!SDL_GetClosestDisplayMode(dindex, &trymode, &mode))
1085 {
1086 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because no modes big enough for %dx%d."), trymode.w, trymode.h);
1087 goto TryWindowed;
1088 }
1089
1090 if(SDL_SetWindowDisplayMode(window, &mode) < 0)
1091 {
1092 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_SetWindowDisplayMode() failed: %s"), SDL_GetError());
1093 goto TryWindowed;
1094 }
1095
1096 #if 0
1097 int old_mousex = 0;
1098 int old_mousey = 0;
1099 SDL_GetGlobalMouseState(&old_mousex, &old_mousey);
1100 #endif
1101
1102 if(SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN) < 0)
1103 {
1104 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because SDL_SetWindowFullscreen() failed: %s"), SDL_GetError());
1105 goto TryWindowed;
1106 }
1107
1108 #if 0
1109 // ugggh
1110 SDL_PumpEvents();
1111 SDL_Delay(20);
1112 SDL_PumpEvents();
1113 SDL_WarpMouseGlobal(old_mousex, old_mousey);
1114 #endif
1115
1116 screen_w = mode.w;
1117 screen_h = mode.h;
1118 if(!GenerateFullscreenDestRect())
1119 {
1120 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to windowed mode because screen destination rectangle(%dx%d) is too large!"), screen_dest_rect.w, screen_dest_rect.h);
1121 goto TryWindowed;
1122 }
1123 }
1124 //
1125 //
1126 MDFN_printf(_("Driver: %s\n"), (vdriver == VDRIVER_OPENGL) ? _("OpenGL") : _("Software SDL") );
1127 //
1128 //
1129 int rs, gs, bs, as;
1130
1131 screen = nullptr;
1132
1133 {
1134 SDL_DisplayMode mode;
1135 int dindex;
1136
1137 if((dindex = SDL_GetWindowDisplayIndex(window)) < 0)
1138 throw MDFN_Error(0, "SDL_GetWindowDisplayIndex() failed: %s", SDL_GetError());
1139
1140 if(SDL_GetCurrentDisplayMode(dindex, &mode) < 0)
1141 throw MDFN_Error(0, "SDL_GetCurrentDisplayMode() failed: %s", SDL_GetError());
1142
1143 if(mode.refresh_rate)
1144 MDFN_printf(_("Display Mode: %u x %u x %u bpp @ %dHz (Window: %u x %u)\n"), mode.w, mode.h, SDL_BITSPERPIXEL(mode.format), mode.refresh_rate, screen_w, screen_h);
1145 else
1146 MDFN_printf(_("Display Mode: %u x %u x %u bpp (Window: %u x %u)\n"), mode.w, mode.h, SDL_BITSPERPIXEL(mode.format), screen_w, screen_h);
1147
1148 if(vdriver != VDRIVER_OPENGL)
1149 {
1150 if(!(screen = SDL_GetWindowSurface(window)))
1151 throw MDFN_Error(0, _("SDL_GetWindowSurface() failed: %s"), SDL_GetError());
1152
1153 if(screen->format->BitsPerPixel != 32)
1154 throw MDFN_Error(0, _("Window surface bit depth(%ubpp) is not supported by Mednafen."), screen->format->BitsPerPixel);
1155
1156 rs = screen->format->Rshift;
1157 gs = screen->format->Gshift;
1158 bs = screen->format->Bshift;
1159
1160 as = 0;
1161 while(as == rs || as == gs || as == bs) // Find unused 8-bits to use as our alpha channel
1162 as += 8;
1163 }
1164 }
1165
1166 if(vdriver == VDRIVER_OPENGL)
1167 {
1168 MDFN_AutoIndent aindps;
1169 char sp[128] = { 0 };
1170
1171 if(video_settings.shader == SHADER_GOAT)
1172 {
1173 trio_snprintf(sp, sizeof(sp), " (pat=%s, hdiv=%f, vdiv=%f, tp=%f, slen=%d, fprog=%d)",
1174 video_settings.goat_pat_str.c_str(), video_settings.shader_params.goat_hdiv, video_settings.shader_params.goat_vdiv, video_settings.shader_params.goat_tp, video_settings.shader_params.goat_slen, video_settings.shader_params.goat_fprog);
1175 }
1176
1177 MDFN_printf(_("Shader: %s%s\n"), video_settings.shader_str.c_str(), sp);
1178 }
1179
1180 MDFN_printf(_("Fullscreen: %s\n"), video_settings.fullscreen ? _("Yes") : _("No"));
1181 MDFN_printf(_("Special Scaler: %s\n"), (video_settings.special == NTVB_NONE) ? _("None") : video_settings.special_str.c_str());
1182
1183 if(!video_settings.scanlines)
1184 MDFN_printf(_("Scanlines: Off\n"));
1185 else
1186 MDFN_printf(_("Scanlines: %d%% opacity%s\n"), abs(video_settings.scanlines), (video_settings.scanlines < 0) ? _(" (with interlace field obscure)") : "");
1187
1188 MDFN_printf(_("Destination Rectangle: X=%d, Y=%d, W=%d, H=%d\n"), screen_dest_rect.x, screen_dest_rect.y, screen_dest_rect.w, screen_dest_rect.h);
1189 if(screen_dest_rect.x < 0 || screen_dest_rect.y < 0 || (screen_dest_rect.x + screen_dest_rect.w) > screen_w || (screen_dest_rect.y + screen_dest_rect.h) > screen_h)
1190 {
1191 MDFN_AutoIndent ainddr;
1192 MDFN_printf(_("Warning: Destination rectangle exceeds screen dimensions. This is ok if you really do want the clipping...\n"));
1193 }
1194
1195 if(vdriver == VDRIVER_OPENGL)
1196 {
1197 try
1198 {
1199 ogl_blitter = new OpenGL_Blitter(video_settings.scanlines, video_settings.shader, video_settings.shader_params, &rs, &gs, &bs, &as);
1200 ogl_blitter->SetViewport(screen_w, screen_h);
1201 }
1202 catch(std::exception& e)
1203 {
1204 MDFN_Notify(MDFN_NOTICE_WARNING, _("Reverting to soft SDL driver because of error initializing OpenGL blitter: %s"), e.what());
1205 goto TryNonGL;
1206 }
1207 }
1208
1209 //printf("%d %d %d %d\n", rs, gs, bs, as);
1210
1211 real_rs = rs;
1212 real_gs = gs;
1213 real_bs = bs;
1214 real_as = as;
1215
1216 /* HQXX only supports this pixel format, sadly, and other pixel formats
1217 can't be easily supported without rewriting the filters.
1218 We do conversion to the real screen format in the blitting function.
1219 */
1220 if(CurrentScaler) {
1221 #ifdef WANT_FANCY_SCALERS
1222 if(CurrentScaler->id == NTVB_HQ2X || CurrentScaler->id == NTVB_HQ3X || CurrentScaler->id == NTVB_HQ4X)
1223 {
1224 rs = 16;
1225 gs = 8;
1226 bs = 0;
1227 as = 24;
1228 }
1229 else if(CurrentScaler->id == NTVB_2XSAI || CurrentScaler->id == NTVB_SUPER2XSAI || CurrentScaler->id == NTVB_SUPEREAGLE)
1230 {
1231 Init_2xSaI(32, 555); // systemColorDepth, BitFormat
1232 }
1233 #endif
1234 }
1235
1236 {
1237 int xmu = std::max<int>(1, screen_w / 402);
1238 int ymu = std::max<int>(1, screen_h / 288);
1239
1240 SMRect.h = 18 + 2;
1241 SMRect.x = 0;
1242 SMRect.y = 0;
1243 SMRect.w = screen_w;
1244
1245 SMDRect.w = SMRect.w * xmu;
1246 SMDRect.h = SMRect.h * ymu;
1247 SMDRect.x = (screen_w - SMDRect.w) / 2;
1248 SMDRect.y = screen_h - SMDRect.h;
1249
1250 if(SMDRect.x < 0)
1251 {
1252 SMRect.w += SMDRect.x * 2 / xmu;
1253 SMDRect.w = SMRect.w * xmu;
1254 SMDRect.x = 0;
1255 }
1256 SMSurface = new MDFN_Surface(NULL, SMRect.w, SMRect.h, SMRect.w, MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as));
1257 }
1258
1259 pf_normal = MDFN_PixelFormat(MDFN_COLORSPACE_RGB, rs, gs, bs, as);
1260
1261 if(vdriver == VDRIVER_OPENGL)
1262 {
1263 for(int i = 0; i < 2; i++)
1264 {
1265 ogl_blitter->ClearBackBuffer();
1266 SDL_GL_SwapWindow(window);
1267 //ogl_blitter->HardSync();
1268 }
1269 ogl_blitter->ClearBackBuffer();
1270 }
1271 else
1272 {
1273 SDL_FillRect(screen, NULL, 0);
1274 for(int i = 0; i < 2; i++)
1275 SDL_UpdateWindowSurface(window);
1276 }
1277
1278 MarkNeedBBClear();
1279 //
1280 //
1281 //
1282 Video_SetWMInputBehavior(CurWMIB);
1283 }
1284
Video_Init(void)1285 void Video_Init(void)
1286 {
1287 winpos_x = SDL_WINDOWPOS_CENTERED; //MDFN_GetSettingI("video.window.x");
1288 winpos_y = SDL_WINDOWPOS_CENTERED; //MDFN_GetSettingI("video.window.y");
1289 winpos_applied = false;
1290
1291 CurWMIB.Cursor = true;
1292 CurWMIB.MouseAbs = true;
1293 CurWMIB.MouseRel = false;
1294 CurWMIB.Grab = false;
1295 //
1296 IconSurface = SDL_CreateRGBSurfaceFrom((void*)icon_128x128, 128, 128, 32, 128 * 4, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
1297 //
1298 for(unsigned i = 0; i < 2 && !window; i++)
1299 {
1300 static const uint32 try_flags[2] =
1301 {
1302 SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL,
1303 SDL_WINDOW_HIDDEN
1304 };
1305 window = SDL_CreateWindow("Mednafen", winpos_x, winpos_y, 64, 64, try_flags[i]);
1306 }
1307
1308 if(!window)
1309 throw MDFN_Error(0, _("SDL_CreateWindow() failed: %s\n"), SDL_GetError());
1310 //
1311 SDL_SetWindowIcon(window, IconSurface);
1312 }
1313
1314 static uint32 howlong = 0;
1315 static char* CurrentMessage = NULL;
1316 static MDFN_NoticeType CurrentMessageType;
1317
Video_ShowNotice(MDFN_NoticeType t,char * s)1318 void Video_ShowNotice(MDFN_NoticeType t, char* s)
1319 {
1320 howlong = Time::MonoMS() + MDFN_GetSettingUI("osd.message_display_time");
1321
1322 if(CurrentMessage)
1323 {
1324 free(CurrentMessage);
1325 CurrentMessage = NULL;
1326 }
1327
1328 CurrentMessage = s;
1329 CurrentMessageType = t;
1330 }
1331
BlitRaw(MDFN_Surface * src,const MDFN_Rect * src_rect,const MDFN_Rect * dest_rect,int source_alpha)1332 void BlitRaw(MDFN_Surface *src, const MDFN_Rect *src_rect, const MDFN_Rect *dest_rect, int source_alpha)
1333 {
1334 if(ogl_blitter)
1335 ogl_blitter->BlitRaw(src, src_rect, dest_rect, (source_alpha != 0) && osd_alpha_blend);
1336 else
1337 {
1338 SDL_to_MDFN_Surface_Wrapper m_surface(screen);
1339
1340 //MDFN_SrcAlphaBlitSurface(src, src_rect, &m_surface, dest_rect);
1341 MDFN_StretchBlitSurface(src, *src_rect, &m_surface, *dest_rect, (source_alpha > 0) && osd_alpha_blend);
1342 }
1343
1344 bool cond1 = (dest_rect->x < screen_dest_rect.x || (dest_rect->x + dest_rect->w) > (screen_dest_rect.x + screen_dest_rect.w));
1345 bool cond2 = (dest_rect->y < screen_dest_rect.y || (dest_rect->y + dest_rect->h) > (screen_dest_rect.y + screen_dest_rect.h));
1346
1347 if(cond1 || cond2)
1348 MarkNeedBBClear();
1349 }
1350
BlitInternalMessage(void)1351 static bool BlitInternalMessage(void)
1352 {
1353 if(Time::MonoMS() >= howlong)
1354 {
1355 if(CurrentMessage)
1356 {
1357 free(CurrentMessage);
1358 CurrentMessage = NULL;
1359 }
1360 return false;
1361 }
1362
1363 if(CurrentMessage)
1364 {
1365 uint32 shad_color = SMSurface->MakeColor(0x00, 0x00, 0x00, 0xFF);
1366 uint32 text_color;
1367 uint8 fill_alpha = 0xC0;
1368
1369 switch(CurrentMessageType)
1370 {
1371 default:
1372 case MDFN_NOTICE_STATUS:
1373 text_color = SMSurface->MakeColor(0xFF, 0xFF, 0xFF, 0xFF);
1374 break;
1375
1376 case MDFN_NOTICE_WARNING:
1377 text_color = SMSurface->MakeColor(0xFF, 0xFF, 0x00, 0xFF);
1378 break;
1379
1380 case MDFN_NOTICE_ERROR:
1381 text_color = SMSurface->MakeColor(0xFF, 0x00, 0x00, 0xFF);
1382 fill_alpha = 0xFF;
1383 break;
1384 }
1385
1386 SMSurface->Fill(0x00, 0x00, 0x00, fill_alpha);
1387
1388 DrawTextShadow(SMSurface, 0, 1, CurrentMessage, text_color, shad_color, MDFN_FONT_9x18_18x18, SMRect.w);
1389 free(CurrentMessage);
1390 CurrentMessage = NULL;
1391 }
1392
1393 BlitRaw(SMSurface, &SMRect, &SMDRect);
1394
1395 return true;
1396 }
1397
SubBlit(const MDFN_Surface * source_surface,const MDFN_Rect & src_rect,const MDFN_Rect & dest_rect,const int InterlaceField)1398 static void SubBlit(const MDFN_Surface *source_surface, const MDFN_Rect &src_rect, const MDFN_Rect &dest_rect, const int InterlaceField)
1399 {
1400 const MDFN_Surface *eff_source_surface = source_surface;
1401 MDFN_Rect eff_src_rect = src_rect;
1402
1403 if(!(src_rect.w > 0 && src_rect.w <= 32767) || !(src_rect.h > 0 && src_rect.h <= 32767))
1404 {
1405 //fprintf(stderr, "BUG: Source rect out of range; w=%d, h=%d\n", src_rect.w, src_rect.h);
1406 return;
1407 }
1408 //#if 0
1409 // assert(src_rect.w > 0 && src_rect.w <= 32767);
1410 // assert(src_rect.h > 0 && src_rect.h <= 32767);
1411 //#endif
1412
1413 assert(dest_rect.w > 0);
1414 assert(dest_rect.h > 0);
1415
1416 if(CurrentScaler)
1417 {
1418 MDFN_Rect boohoo_rect({0, 0, eff_src_rect.w * CurrentScaler->xscale, eff_src_rect.h * CurrentScaler->yscale});
1419 MDFN_Surface bah_surface(NULL, boohoo_rect.w, boohoo_rect.h, boohoo_rect.w, eff_source_surface->format, false);
1420 uint8* screen_pixies = (uint8 *)bah_surface.pixels;
1421 uint32 screen_pitch = bah_surface.pitch32 << 2;
1422
1423 if(CurrentScaler->id == NTVB_SCALE4X || CurrentScaler->id == NTVB_SCALE3X || CurrentScaler->id == NTVB_SCALE2X)
1424 {
1425 #ifdef WANT_FANCY_SCALERS
1426 //
1427 // scale2x and scale3x apparently can't handle source heights less than 2.
1428 // scale4x, it's less than 4
1429 //
1430 // None can handle source widths less than 2.
1431 //
1432 if(eff_src_rect.w < 2 || eff_src_rect.h < 2 || (CurrentScaler->id == NTVB_SCALE4X && eff_src_rect.h < 4))
1433 {
1434 nnx(CurrentScaler->id - NTVB_SCALE2X + 2, eff_source_surface, eff_src_rect, &bah_surface, boohoo_rect);
1435 }
1436 else
1437 {
1438 uint8 *source_pixies = (uint8 *)eff_source_surface->pixels + eff_src_rect.x * sizeof(uint32) + eff_src_rect.y * eff_source_surface->pitchinpix * sizeof(uint32);
1439 scale((CurrentScaler->id == NTVB_SCALE2X)?2:(CurrentScaler->id == NTVB_SCALE4X)?4:3, screen_pixies, screen_pitch, source_pixies, eff_source_surface->pitchinpix * sizeof(uint32), sizeof(uint32), eff_src_rect.w, eff_src_rect.h);
1440 }
1441 #endif
1442 }
1443 else if(CurrentScaler->id == NTVB_NN2X || CurrentScaler->id == NTVB_NN3X || CurrentScaler->id == NTVB_NN4X)
1444 {
1445 nnx(CurrentScaler->id - NTVB_NN2X + 2, eff_source_surface, eff_src_rect, &bah_surface, boohoo_rect);
1446 }
1447 else if(CurrentScaler->id == NTVB_NNY2X || CurrentScaler->id == NTVB_NNY3X || CurrentScaler->id == NTVB_NNY4X)
1448 {
1449 nnyx(CurrentScaler->id - NTVB_NNY2X + 2, eff_source_surface, eff_src_rect, &bah_surface, boohoo_rect);
1450 }
1451 #ifdef WANT_FANCY_SCALERS
1452 else
1453 {
1454 uint8 *source_pixies = (uint8 *)(eff_source_surface->pixels + eff_src_rect.x + eff_src_rect.y * eff_source_surface->pitchinpix);
1455
1456 if(CurrentScaler->id == NTVB_HQ2X)
1457 hq2x_32(source_pixies, screen_pixies, eff_src_rect.w, eff_src_rect.h, eff_source_surface->pitchinpix * sizeof(uint32), screen_pitch);
1458 else if(CurrentScaler->id == NTVB_HQ3X)
1459 hq3x_32(source_pixies, screen_pixies, eff_src_rect.w, eff_src_rect.h, eff_source_surface->pitchinpix * sizeof(uint32), screen_pitch);
1460 else if(CurrentScaler->id == NTVB_HQ4X)
1461 hq4x_32(source_pixies, screen_pixies, eff_src_rect.w, eff_src_rect.h, eff_source_surface->pitchinpix * sizeof(uint32), screen_pitch);
1462 else if(CurrentScaler->id == NTVB_2XSAI || CurrentScaler->id == NTVB_SUPER2XSAI || CurrentScaler->id == NTVB_SUPEREAGLE)
1463 {
1464 MDFN_Surface saisrc(NULL, eff_src_rect.w + 4, eff_src_rect.h + 4, eff_src_rect.w + 4, eff_source_surface->format);
1465
1466 for(int y = 0; y < 2; y++)
1467 {
1468 memcpy(saisrc.pixels + (y * saisrc.pitchinpix) + 2, (uint32 *)source_pixies, eff_src_rect.w * sizeof(uint32));
1469 memcpy(saisrc.pixels + ((2 + y + eff_src_rect.h) * saisrc.pitchinpix) + 2, (uint32 *)source_pixies + (eff_src_rect.h - 1) * eff_source_surface->pitchinpix, eff_src_rect.w * sizeof(uint32));
1470 }
1471
1472 for(int y = 0; y < eff_src_rect.h; y++)
1473 {
1474 memcpy(saisrc.pixels + ((2 + y) * saisrc.pitchinpix) + 2, (uint32*)source_pixies + y * eff_source_surface->pitchinpix, eff_src_rect.w * sizeof(uint32));
1475 memcpy(saisrc.pixels + ((2 + y) * saisrc.pitchinpix) + (2 + eff_src_rect.w),
1476 saisrc.pixels + ((2 + y) * saisrc.pitchinpix) + (2 + eff_src_rect.w - 1), sizeof(uint32));
1477 }
1478
1479 {
1480 uint8 *saipix = (uint8 *)(saisrc.pixels + 2 * saisrc.pitchinpix + 2);
1481 uint32 saipitch = saisrc.pitchinpix << 2;
1482
1483 if(CurrentScaler->id == NTVB_2XSAI)
1484 _2xSaI32(saipix, saipitch, screen_pixies, screen_pitch, eff_src_rect.w, eff_src_rect.h);
1485 else if(CurrentScaler->id == NTVB_SUPER2XSAI)
1486 Super2xSaI32(saipix, saipitch, screen_pixies, screen_pitch, eff_src_rect.w, eff_src_rect.h);
1487 else if(CurrentScaler->id == NTVB_SUPEREAGLE)
1488 SuperEagle32(saipix, saipitch, screen_pixies, screen_pitch, eff_src_rect.w, eff_src_rect.h);
1489 }
1490 }
1491
1492 if(bah_surface.format.Rshift != real_rs || bah_surface.format.Gshift != real_gs || bah_surface.format.Bshift != real_bs)
1493 {
1494 uint32 *lineptr = bah_surface.pixels;
1495
1496 unsigned int srs = bah_surface.format.Rshift;
1497 unsigned int sgs = bah_surface.format.Gshift;
1498 unsigned int sbs = bah_surface.format.Bshift;
1499 unsigned int drs = real_rs;
1500 unsigned int dgs = real_gs;
1501 unsigned int dbs = real_bs;
1502
1503 for(int y = 0; y < boohoo_rect.h; y++)
1504 {
1505 for(int x = 0; x < boohoo_rect.w; x++)
1506 {
1507 uint32 pixel = lineptr[x];
1508 lineptr[x] = (((pixel >> srs) & 0xFF) << drs) | (((pixel >> sgs) & 0xFF) << dgs) | (((pixel >> sbs) & 0xFF) << dbs);
1509 }
1510 lineptr += bah_surface.pitchinpix;
1511 }
1512 }
1513 }
1514 #endif
1515
1516 if(ogl_blitter)
1517 ogl_blitter->Blit(&bah_surface, &boohoo_rect, &dest_rect, &eff_src_rect, InterlaceField, evideoip, rotated);
1518 else
1519 {
1520 SDL_to_MDFN_Surface_Wrapper m_surface(screen);
1521
1522 MDFN_StretchBlitSurface(&bah_surface, boohoo_rect, &m_surface, dest_rect, false, video_settings.scanlines, &eff_src_rect, rotated, InterlaceField);
1523 }
1524 }
1525 else // No special scaler:
1526 {
1527 if(ogl_blitter)
1528 ogl_blitter->Blit(eff_source_surface, &eff_src_rect, &dest_rect, &eff_src_rect, InterlaceField, evideoip, rotated);
1529 else
1530 {
1531 SDL_to_MDFN_Surface_Wrapper m_surface(screen);
1532
1533 MDFN_StretchBlitSurface(eff_source_surface, eff_src_rect, &m_surface, dest_rect, false, video_settings.scanlines, &eff_src_rect, rotated, InterlaceField);
1534 }
1535 }
1536 }
1537
1538 // FIXME: JoyGunTranslate is kind of sloppy, not entirely thread safe, though the consequences in practice should be negligible...
1539 static struct
1540 {
1541 float mul;
1542 float sub;
1543 } volatile JoyGunTranslate[2] = { { 0, 0 }, { 0, 0 } };
1544
BlitScreen(MDFN_Surface * msurface,const MDFN_Rect * DisplayRect,const int32 * LineWidths,const int new_rotated,const int InterlaceField,const bool take_ssnapshot)1545 void BlitScreen(MDFN_Surface *msurface, const MDFN_Rect *DisplayRect, const int32 *LineWidths, const int new_rotated, const int InterlaceField, const bool take_ssnapshot)
1546 {
1547 //
1548 // Reduce CPU usage when minimized, and prevent OpenGL memory quasi-leaks on Windows(though I have the feeling there's a
1549 // cleaner less-racey way to prevent that memory leak problem).
1550 //
1551 {
1552 int wflags = SDL_GetWindowFlags(window);
1553
1554 // printf("Borp %08x %u\n", wflags, Time::MonoMS());
1555
1556 if(wflags & (SDL_WINDOW_HIDDEN | SDL_WINDOW_MINIMIZED))
1557 {
1558 return;
1559 }
1560 }
1561 //
1562 //
1563 //
1564 MDFN_Rect src_rect;
1565
1566 if(rotated != new_rotated)
1567 {
1568 rotated = new_rotated;
1569 //
1570 if(video_settings.fullscreen)
1571 {
1572 if(MDFN_UNLIKELY(!GenerateFullscreenDestRect()))
1573 Video_Sync(VideoGI);
1574 else
1575 MarkNeedBBClear();
1576 }
1577 else
1578 Video_Sync(VideoGI);
1579 }
1580
1581 if(vdriver != VDRIVER_OPENGL)
1582 {
1583 int ow, oh, op;
1584 int nw, nh, np;
1585 SDL_PixelFormat of, nf;
1586
1587 of = *screen->format;
1588 ow = screen->w;
1589 oh = screen->h;
1590 op = screen->pitch;
1591 if(!(screen = SDL_GetWindowSurface(window)))
1592 throw MDFN_Error(0, _("SDL_GetWindowSurface() failed: %s"), SDL_GetError());
1593
1594 nf = *screen->format;
1595 nw = screen->w;
1596 nh = screen->h;
1597 np = screen->pitch;
1598
1599 if((of.format != nf.format) || (of.BitsPerPixel != nf.BitsPerPixel) || (of.BytesPerPixel != nf.BytesPerPixel) ||
1600 (of.Rmask != nf.Rmask) || (of.Gmask != nf.Gmask) || (of.Bmask != nf.Bmask) || (of.Amask != nf.Amask) ||
1601 (ow != nw) || (oh != nh) || (op != np))
1602 {
1603 Video_Sync(VideoGI);
1604 }
1605 }
1606
1607 #if 1
1608 {
1609 int sub[2] = { 0, 0 };
1610 SDL_DisplayMode mode;
1611
1612 if(!(SDL_GetWindowFlags(window) & SDL_WINDOW_FULLSCREEN))
1613 SDL_GetWindowPosition(window, &sub[0], &sub[1]);
1614
1615 sub[0] += screen_dest_rect.x;
1616 sub[1] += screen_dest_rect.y;
1617
1618 if(SDL_GetCurrentDisplayMode(SDL_GetWindowDisplayIndex(window), &mode) >= 0)
1619 {
1620 JoyGunTranslate[0].mul = (float)mode.w / screen_dest_rect.w / 32768.0;
1621 JoyGunTranslate[0].sub = (float)sub[0] / screen_dest_rect.w;
1622
1623 JoyGunTranslate[1].mul = (float)mode.h / screen_dest_rect.h / 32768.0;
1624 JoyGunTranslate[1].sub = (float)sub[1] / screen_dest_rect.h;
1625 }
1626 }
1627 #endif
1628
1629 if(NeedClear)
1630 {
1631 uint32 ct = Time::MonoMS();
1632
1633 if((ct - LastBBClearTime) >= 30)
1634 {
1635 LastBBClearTime = ct;
1636 NeedClear--;
1637 }
1638
1639 if(vdriver == VDRIVER_OPENGL)
1640 ogl_blitter->ClearBackBuffer();
1641 else
1642 {
1643 SDL_FillRect(screen, NULL, 0);
1644 NeedClear = 0;
1645 }
1646 }
1647
1648 msurface->SetFormat(pf_normal, true);
1649
1650 src_rect.x = DisplayRect->x;
1651 src_rect.w = DisplayRect->w;
1652 src_rect.y = DisplayRect->y;
1653 src_rect.h = DisplayRect->h;
1654
1655 if(LineWidths[0] == ~0) // Skip multi line widths code?
1656 {
1657 SubBlit(msurface, src_rect, screen_dest_rect, InterlaceField);
1658 }
1659 else
1660 {
1661 int y;
1662 int last_y = src_rect.y;
1663 int last_width = LineWidths[src_rect.y];
1664
1665 MDFN_Rect sub_src_rect;
1666 MDFN_Rect sub_dest_rect;
1667
1668 for(y = src_rect.y; y < (src_rect.y + src_rect.h + 1); y++)
1669 {
1670 if(y == (src_rect.y + src_rect.h) || LineWidths[y] != last_width)
1671 {
1672 sub_src_rect.x = src_rect.x;
1673 sub_src_rect.w = last_width;
1674 sub_src_rect.y = last_y;
1675 sub_src_rect.h = y - last_y;
1676
1677 if(rotated == MDFN_ROTATE90)
1678 {
1679 sub_dest_rect.x = screen_dest_rect.x + (last_y - src_rect.y) * screen_dest_rect.w / src_rect.h;
1680 sub_dest_rect.y = screen_dest_rect.y;
1681
1682 sub_dest_rect.w = sub_src_rect.h * screen_dest_rect.w / src_rect.h;
1683 sub_dest_rect.h = screen_dest_rect.h;
1684 //printf("sdr.x=%f, sdr.w=%f\n", (double)screen_dest_rect.x + (double)(last_y - src_rect.y) * screen_dest_rect.w / src_rect.h, (double)sub_src_rect.h * screen_dest_rect.w / src_rect.h);
1685 }
1686 else if(rotated == MDFN_ROTATE270)
1687 {
1688 sub_dest_rect.x = screen_dest_rect.x + (src_rect.h - (y - src_rect.y)) * screen_dest_rect.w / src_rect.h;
1689 sub_dest_rect.y = screen_dest_rect.y;
1690
1691 sub_dest_rect.w = sub_src_rect.h * screen_dest_rect.w / src_rect.h;
1692 sub_dest_rect.h = screen_dest_rect.h;
1693 }
1694 else
1695 {
1696 sub_dest_rect.x = screen_dest_rect.x;
1697 sub_dest_rect.w = screen_dest_rect.w;
1698 sub_dest_rect.y = screen_dest_rect.y + (last_y - src_rect.y) * screen_dest_rect.h / src_rect.h;
1699 sub_dest_rect.h = sub_src_rect.h * screen_dest_rect.h / src_rect.h;
1700 }
1701
1702 if(!sub_dest_rect.h) // May occur with small yscale values in certain cases, so prevent triggering an assert()
1703 sub_dest_rect.h = 1;
1704
1705 // Blit here!
1706 SubBlit(msurface, sub_src_rect, sub_dest_rect, InterlaceField);
1707
1708 last_y = y;
1709
1710 if(y != (src_rect.y + src_rect.h))
1711 {
1712 last_width = LineWidths[y];
1713 }
1714
1715 }
1716 }
1717 }
1718
1719 if(take_ssnapshot)
1720 {
1721 try
1722 {
1723 std::unique_ptr<MDFN_Surface> ib;
1724 MDFN_Rect sr;
1725 MDFN_Rect tr;
1726
1727 sr = screen_dest_rect;
1728 if(sr.x < 0) { sr.w += sr.x; sr.x = 0; }
1729 if(sr.y < 0) { sr.h += sr.y; sr.y = 0; }
1730 if(sr.w < 0) sr.w = 0;
1731 if(sr.h < 0) sr.h = 0;
1732 if(sr.w > screen_w) sr.w = screen_w;
1733 if(sr.h > screen_h) sr.h = screen_h;
1734
1735 ib.reset(new MDFN_Surface(NULL, sr.w, sr.h, sr.w, MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as)));
1736
1737 if(ogl_blitter)
1738 ogl_blitter->ReadPixels(ib.get(), &sr);
1739 else
1740 {
1741 if(SDL_MUSTLOCK(screen))
1742 SDL_LockSurface(screen);
1743
1744 for(int y = 0; y < sr.h; y++)
1745 {
1746 for(int x = 0; x < sr.w; x++)
1747 {
1748 ib->pixels[y * ib->pitchinpix + x] = ((uint32*)((uint8*)screen->pixels + (sr.y + y) * screen->pitch))[sr.x + x];
1749 }
1750 }
1751
1752 if(SDL_MUSTLOCK(screen))
1753 SDL_UnlockSurface(screen);
1754 }
1755
1756
1757 tr.x = tr.y = 0;
1758 tr.w = ib->w;
1759 tr.h = ib->h;
1760 MDFNI_SaveSnapshot(ib.get(), &tr, NULL);
1761 }
1762 catch(std::exception &e)
1763 {
1764 MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
1765 }
1766 }
1767
1768
1769 Debugger_MT_DrawToScreen(MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as), screen_w, screen_h);
1770
1771 #if 0
1772 if(CKGUI_IsActive())
1773 {
1774 if(!CKGUISurface)
1775 {
1776 CKGUIRect.w = screen_w;
1777 CKGUIRect.h = screen_h;
1778
1779 CKGUISurface = SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_SRCALPHA, CKGUIRect.w, CKGUIRect.h, 32, 0xFF << real_rs, 0xFF << real_gs, 0xFF << real_bs, 0xFF << real_as);
1780 SDL_SetColorKey(CKGUISurface, SDL_SRCCOLORKEY, 0);
1781 SDL_SetAlpha(CKGUISurface, SDL_SRCALPHA, 0);
1782 }
1783 MDFN_Rect zederect = CKGUIRect;
1784 CKGUI_Draw(CKGUISurface, &CKGUIRect);
1785 BlitRaw(CKGUISurface, &CKGUIRect, &zederect);
1786 }
1787 else if(CKGUISurface)
1788 {
1789 SDL_FreeSurface(CKGUISurface);
1790 CKGUISurface = NULL;
1791 }
1792 #endif
1793
1794 try
1795 {
1796 DrawSaveStates(screen_w, screen_h, exs, eys, real_rs, real_gs, real_bs, real_as);
1797
1798 CheatIF_MT_Draw(MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as), screen_w, screen_h);
1799 Netplay_MT_Draw(MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as), screen_w, screen_h);
1800
1801 if(Help_IsActive())
1802 {
1803 if(!HelpSurface)
1804 {
1805 HelpRect.x = 0;
1806 HelpRect.y = 0;
1807 HelpRect.w = std::min<int>(512, screen_w);
1808 HelpRect.h = std::min<int>(408, screen_h);
1809
1810 HelpSurface = new MDFN_Surface(NULL, 512, 408, 512, MDFN_PixelFormat(MDFN_COLORSPACE_RGB, real_rs, real_gs, real_bs, real_as));
1811 Help_Draw(HelpSurface, HelpRect);
1812 }
1813
1814 MDFN_Rect zederect;
1815 int32 sfx = screen_w / HelpRect.w;
1816 int32 sfy = screen_h / HelpRect.h;
1817
1818 if(sfy > sfx)
1819 sfy = sfx + 1;
1820
1821 if(sfx > sfy)
1822 sfx = sfy + 1;
1823
1824 zederect.w = HelpRect.w * sfx;
1825 zederect.h = HelpRect.h * sfy;
1826
1827 zederect.x = (screen_w - zederect.w) / 2;
1828 zederect.y = (screen_h - zederect.h) / 2;
1829
1830 BlitRaw(HelpSurface, &HelpRect, &zederect, 0);
1831 }
1832 else if(HelpSurface)
1833 {
1834 delete HelpSurface;
1835 HelpSurface = NULL;
1836 }
1837 }
1838 catch(std::exception& e)
1839 {
1840 MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
1841 }
1842
1843 BlitInternalMessage();
1844
1845 //
1846 {
1847 int32 p[4] = { 0, 0, screen_w, screen_h };
1848 MDFN_Rect cr;
1849
1850 // When using soft-SDL, position the FPS display so we won't incur a potentially large(on older/slower hardware) penalty due
1851 // to a requisite backbuffer clear(we could avoid this with some sort of dirty-rects system so only parts of the backbuffer are cleared,
1852 // but that gets awfully complicated and prone to bugs when dealing with double/triple-buffered video...).
1853 //
1854 // std::max so we don't position it offscreen if the user has selected xscalefs or yscalefs values that are too large.
1855 if(vdriver != VDRIVER_OPENGL)
1856 {
1857 p[0] = std::max<int32>(std::min<int32>(screen_w, screen_dest_rect.x), 0);
1858 p[1] = std::max<int32>(std::min<int32>(screen_h, screen_dest_rect.y), 0);
1859
1860 p[2] = std::max<int32>(std::min<int32>(screen_w, screen_dest_rect.x + screen_dest_rect.w), 0);
1861 p[3] = std::max<int32>(std::min<int32>(screen_w, screen_dest_rect.y + screen_dest_rect.y), 0);
1862 }
1863
1864 cr = { p[0], p[1], p[2] - p[0], p[3] - p[1] };
1865 FPS_DrawToScreen(real_rs, real_gs, real_bs, real_as, cr, std::min(screen_w, screen_h));
1866 }
1867 //
1868
1869 if(vdriver != VDRIVER_OPENGL)
1870 SDL_UpdateWindowSurface(window);
1871 else
1872 {
1873 PumpWrap();
1874 SDL_GL_SwapWindow(window);
1875 // Don't insert any GL calls after SDL_GL_SwapWindow() here that could block until the swap completes.
1876 //ogl_blitter->HardSync();
1877 }
1878 }
1879
Video_Exposed(void)1880 void Video_Exposed(void)
1881 {
1882 MarkNeedBBClear();
1883 }
1884
Video_PtoV(const int in_x,const int in_y,float * out_x,float * out_y)1885 void Video_PtoV(const int in_x, const int in_y, float* out_x, float* out_y)
1886 {
1887 assert(VideoGI);
1888 //
1889 int32 tmp_x = in_x - screen_dest_rect.x;
1890 int32 tmp_y = in_y - screen_dest_rect.y;
1891 int32 div_x = screen_dest_rect.w;
1892 int32 div_y = screen_dest_rect.h;
1893 /*
1894 switch(rotated)
1895 {
1896 case MDFN_ROTATE0:
1897 break;
1898
1899 case MDFN_ROTATE90:
1900 std::swap(tmp_x, tmp_y);
1901 std::swap(div_x, div_y);
1902 tmp_x = screen_dest_rect.h - 1 - tmp_x;
1903 break;
1904
1905 case MDFN_ROTATE270:
1906 std::swap(tmp_x, tmp_y);
1907 std::swap(div_x, div_y);
1908 tmp_y = screen_dest_rect.w - 1 - tmp_y;
1909 break;
1910 }
1911 */
1912 *out_x = (float)tmp_x / div_x;
1913 *out_y = (float)tmp_y / div_y;
1914 }
1915
Video_PtoV_J(const int32 inv,const bool axis,const bool scr_scale)1916 float Video_PtoV_J(const int32 inv, const bool axis, const bool scr_scale)
1917 {
1918 assert(VideoGI);
1919 assert(window);
1920
1921 if(!scr_scale)
1922 return 0.5 + (inv / 32768.0 - 0.5) * std::max<int32>(VideoGI->nominal_width, VideoGI->nominal_height) / (axis ? VideoGI->nominal_height : VideoGI->nominal_width);
1923 else
1924 return inv * JoyGunTranslate[axis].mul - JoyGunTranslate[axis].sub;
1925 }
1926