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