1 /*
2   Hatari - screen.c
3 
4   This file is distributed under the GNU General Public License, version 2
5   or at your option any later version. Read the file gpl.txt for details.
6 
7   This code converts a 1/2/4 plane ST format screen to either 8, 16 or 32-bit PC
8   format. An awful lot of processing is needed to do this conversion - we
9   cannot simply change palettes on  interrupts as it is possible with DOS.
10   The main code processes the palette/resolution mask tables to find exactly
11   which lines need to updating and the conversion routines themselves only
12   update 16-pixel blocks which differ from the previous frame - this gives a
13   large performance increase.
14   Each conversion routine can convert any part of the source ST screen (which
15   includes the overscan border, usually set to colour zero) so they can be used
16   for both window and full-screen mode.
17   Note that in Hi-Resolution we have no overscan and just two colors so we can
18   optimise things further.
19   In color mode it seems possible to display 47 lines in the bottom border
20   with a second 60/50 Hz switch, but most programs consider there are 45
21   visible lines in the bottom border only, which gives a total of 274 lines
22   for a screen. So not displaying the last two lines fixes garbage that could
23   appear in the last two lines when displaying 47 lines (Digiworld 2 by ICE,
24   Tyranny by DHS).
25 */
26 
27 const char Screen_fileid[] = "Hatari screen.c : " __DATE__ " " __TIME__;
28 
29 #include <SDL.h>
30 #include <SDL_endian.h>
31 #include <assert.h>
32 
33 #include "main.h"
34 #include "configuration.h"
35 #include "avi_record.h"
36 #include "log.h"
37 #include "paths.h"
38 #include "options.h"
39 #include "screen.h"
40 #include "screenConvert.h"
41 #include "control.h"
42 #include "convert/routines.h"
43 #include "resolution.h"
44 #include "spec512.h"
45 #include "statusbar.h"
46 #include "vdi.h"
47 #include "video.h"
48 #include "falcon/videl.h"
49 
50 #define DEBUG 0
51 
52 #if DEBUG
53 # define DEBUGPRINT(x) printf x
54 #else
55 # define DEBUGPRINT(x)
56 #endif
57 
58 /* extern for several purposes */
59 SDL_Surface *sdlscrn = NULL;                /* The SDL screen surface */
60 int nScreenZoomX, nScreenZoomY;             /* Zooming factors, used for scaling mouse motions */
61 int nBorderPixelsLeft, nBorderPixelsRight;  /* Pixels in left and right border */
62 static int nBorderPixelsTop, nBorderPixelsBottom;  /* Lines in top and bottom border */
63 
64 /* extern for shortcuts etc. */
65 bool bGrabMouse = false;      /* Grab the mouse cursor in the window */
66 bool bInFullScreen = false;   /* true if in full screen */
67 
68 /* extern for spec512.c */
69 int STScreenLeftSkipBytes;
70 int STScreenStartHorizLine;   /* Start lines to be converted */
71 Uint32 STRGBPalette[16];      /* Palette buffer used in conversion routines */
72 Uint32 ST2RGB[4096];          /* Table to convert ST 0x777 / STe 0xfff palette to PC format RGB551 (2 pixels each entry) */
73 
74 /* extern for video.c */
75 Uint8 *pSTScreen;
76 FRAMEBUFFER *pFrameBuffer;    /* Pointer into current 'FrameBuffer' */
77 
78 static FRAMEBUFFER FrameBuffers[NUM_FRAMEBUFFERS]; /* Store frame buffer details to tell how to update */
79 static Uint8 *pSTScreenCopy;                       /* Keep track of current and previous ST screen data */
80 static Uint8 *pPCScreenDest;                       /* Destination PC buffer */
81 static int STScreenEndHorizLine;                   /* End lines to be converted */
82 static int PCScreenBytesPerLine;
83 static int STScreenWidthBytes;
84 static int PCScreenOffsetX;                        /* how many pixels to skip from left when drawing */
85 static int PCScreenOffsetY;                        /* how many pixels to skip from top when drawing */
86 static SDL_Rect STScreenRect;                      /* screen size without statusbar */
87 
88 static int STScreenLineOffset[NUM_VISIBLE_LINES];  /* Offsets for ST screen lines eg, 0,160,320... */
89 static Uint16 HBLPalette[16], PrevHBLPalette[16];  /* Current palette for line, also copy of first line */
90 
91 static void (*ScreenDrawFunctionsNormal[3])(void); /* Screen draw functions */
92 
93 static bool bScreenContentsChanged;     /* true if buffer changed and requires blitting */
94 static bool bScrDoubleY;                /* true if double on Y */
95 static int ScrUpdateFlag;               /* Bit mask of how to update screen */
96 static bool bRGBTableInSync;            /* Is RGB table up to date? */
97 
98 /* These are used for the generic screen conversion functions */
99 static int genconv_width_req, genconv_height_req, genconv_bpp;
100 static bool genconv_do_update;          /* HW surface is available -> the SDL need not to update the surface after ->pixel access */
101 
102 
103 static bool Screen_DrawFrame(bool bForceFlip);
104 
105 #if WITH_SDL2
106 
107 SDL_Window *sdlWindow;
108 static SDL_Renderer *sdlRenderer;
109 static SDL_Texture *sdlTexture;
110 static bool bUseSdlRenderer;            /* true when using SDL2 renderer */
111 
SDL_UpdateRects(SDL_Surface * screen,int numrects,SDL_Rect * rects)112 void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects)
113 {
114 	if (bUseSdlRenderer)
115 	{
116 		SDL_UpdateTexture(sdlTexture, NULL, screen->pixels, screen->pitch);
117 		SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, NULL);
118 		SDL_RenderPresent(sdlRenderer);
119 	}
120 	else
121 	{
122 		SDL_UpdateWindowSurfaceRects(sdlWindow, rects, numrects);
123 	}
124 }
125 
SDL_UpdateRect(SDL_Surface * screen,Sint32 x,Sint32 y,Sint32 w,Sint32 h)126 void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
127 {
128 	SDL_Rect rect;
129 
130 	if (w == 0 && h == 0) {
131 		x = y = 0;
132 		w = screen->w;
133 		h = screen->h;
134 	}
135 
136 	rect.x = x; rect.y = y;
137 	rect.w = w; rect.h = h;
138 	SDL_UpdateRects(screen, 1, &rect);
139 }
140 
141 #endif /* WITH_SDL2 */
142 
143 /*-----------------------------------------------------------------------*/
144 /**
145  * Create ST 0x777 / STe 0xfff color format to 16 or 32 bits per pixel
146  * conversion table. Called each time when changed resolution or to/from
147  * fullscreen mode.
148  */
Screen_SetupRGBTable(void)149 static void Screen_SetupRGBTable(void)
150 {
151 	Uint16 STColor;
152 	Uint32 RGBColor;
153 	int r, g, b;
154 	int rr, gg, bb;
155 
156 	/* Do Red, Green and Blue for all 16*16*16 = 4096 STe colors */
157 	for (r = 0; r < 16; r++)
158 	{
159 		for (g = 0; g < 16; g++)
160 		{
161 			for (b = 0; b < 16; b++)
162 			{
163 				/* STe 0xfff format */
164 				STColor = (r<<8) | (g<<4) | (b);
165 				rr = ((r & 0x7) << 1) | ((r & 0x8) >> 3);
166 				rr |= rr << 4;
167 				gg = ((g & 0x7) << 1) | ((g & 0x8) >> 3);
168 				gg |= gg << 4;
169 				bb = ((b & 0x7) << 1) | ((b & 0x8) >> 3);
170 				bb |= bb << 4;
171 				RGBColor = SDL_MapRGB(sdlscrn->format, rr, gg, bb);
172 				if (sdlscrn->format->BitsPerPixel <= 16)
173 				{
174 					/* As longs, for speed (write two pixels at once) */
175 					ST2RGB[STColor] = (RGBColor<<16) | RGBColor;
176 				}
177 				else
178 				{
179 					ST2RGB[STColor] = RGBColor;
180 				}
181 			}
182 		}
183 	}
184 }
185 
186 
187 /**
188  * Convert 640x400 monochrome screen
189  */
Screen_ConvertHighRes(void)190 static void Screen_ConvertHighRes(void)
191 {
192 	int linewidth = 640 / 16;
193 
194 	Screen_GenConvert(pSTScreen, 640, 400, 1, linewidth, 0, 0, 0, 0, 0);
195 	bScreenContentsChanged = true;
196 }
197 
198 /**
199  * Set screen draw functions.
200  */
Screen_SetDrawFunctions(int nBitCount,bool bDoubleLowRes)201 static void Screen_SetDrawFunctions(int nBitCount, bool bDoubleLowRes)
202 {
203 	if (nBitCount <= 16)
204 	{
205 		/* High color */
206 		if (bDoubleLowRes)
207 			ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_640x16Bit;
208 		else
209 			ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_320x16Bit;
210 		ScreenDrawFunctionsNormal[ST_MEDIUM_RES] = ConvertMediumRes_640x16Bit;
211 	}
212 	else /* Assume 32 bit drawing functions */
213 	{
214 		/* True color */
215 		if (bDoubleLowRes)
216 			ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_640x32Bit;
217 		else
218 			ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_320x32Bit;
219 		ScreenDrawFunctionsNormal[ST_MEDIUM_RES] = ConvertMediumRes_640x32Bit;
220 	}
221 }
222 
223 
224 /*-----------------------------------------------------------------------*/
225 /**
226  * Set amount of border pixels
227  */
Screen_SetBorderPixels(int leftX,int leftY)228 static void Screen_SetBorderPixels(int leftX, int leftY)
229 {
230 	/* All screen widths need to be aligned to 16-bits */
231 	nBorderPixelsLeft = Opt_ValueAlignMinMax(leftX/2, 16, 0, 48);
232 	nBorderPixelsRight = nBorderPixelsLeft;
233 
234 	/* assertain assumption of code below */
235 	assert(OVERSCAN_TOP < MAX_OVERSCAN_BOTTOM);
236 
237 	if (leftY > 2*OVERSCAN_TOP)
238 	{
239 		nBorderPixelsTop = OVERSCAN_TOP;
240 		if (leftY >= OVERSCAN_TOP + MAX_OVERSCAN_BOTTOM)
241 			nBorderPixelsBottom = MAX_OVERSCAN_BOTTOM;
242 		else
243 			nBorderPixelsBottom = leftY - OVERSCAN_TOP;
244 	}
245 	else
246 	{
247 		if (leftY > 0)
248 			nBorderPixelsTop = nBorderPixelsBottom = leftY/2;
249 		else
250 			nBorderPixelsTop = nBorderPixelsBottom = 0;
251 	}
252 }
253 
254 /*-----------------------------------------------------------------------*/
255 /**
256  * store Y offset for each horizontal line in our source ST screen for
257  * reference in the convert functions.
258  */
Screen_SetSTScreenOffsets(void)259 static void Screen_SetSTScreenOffsets(void)
260 {
261 	int i;
262 
263 	/* Store offset to each horizontal line, uses
264 	 * nBorderPixels* variables.
265 	 */
266 	for (i = 0; i < NUM_VISIBLE_LINES; i++)
267 	{
268 		STScreenLineOffset[i] = i * SCREENBYTES_LINE;
269 	}
270 }
271 
272 /**
273  * Return true if Falcon/TT/VDI generic screen convert functions
274  * need to be used instead of the ST/STE functions.
275  */
Screen_UseGenConvScreen(void)276 static bool Screen_UseGenConvScreen(void)
277 {
278 	return Config_IsMachineFalcon() || Config_IsMachineTT()
279 		|| bUseHighRes || bUseVDIRes;
280 }
281 
Screen_WantToKeepResolution(void)282 static bool Screen_WantToKeepResolution(void)
283 {
284 #if WITH_SDL2
285 	return ConfigureParams.Screen.bKeepResolution;
286 #else
287 	if (Screen_UseGenConvScreen())
288 		return ConfigureParams.Screen.bKeepResolution;
289 	else
290 		return ConfigureParams.Screen.bKeepResolutionST;
291 #endif
292 }
293 
294 #if WITH_SDL2
Screen_FreeSDL2Resources(void)295 static void Screen_FreeSDL2Resources(void)
296 {
297 	if (sdlTexture)
298 	{
299 		SDL_DestroyTexture(sdlTexture);
300 		sdlTexture = NULL;
301 	}
302 	if (sdlscrn)
303 	{
304 		if (bUseSdlRenderer)
305 			SDL_FreeSurface(sdlscrn);
306 		sdlscrn = NULL;
307 	}
308 	if (sdlRenderer)
309 	{
310 		SDL_DestroyRenderer(sdlRenderer);
311 		sdlRenderer = NULL;
312 	}
313 }
314 #endif
315 
316 /**
317  * Change the SDL video mode.
318  * @return true if mode has been changed, false if change was not necessary
319  */
Screen_SetSDLVideoSize(int width,int height,int bitdepth,bool bForceChange)320 bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChange)
321 {
322 	Uint32 sdlVideoFlags;
323 	char *psSdlVideoDriver;
324 	bool bUseDummyMode;
325 #if WITH_SDL2
326 	static int nPrevRenderScaleQuality = 0;
327 	static bool bPrevUseVsync = false;
328 	static bool bPrevInFullScreen;
329 	int win_width, win_height;
330 
331 	if (bitdepth == 0 || bitdepth == 24)
332 		bitdepth = 32;
333 #endif
334 	/* Check if we really have to change the video mode: */
335 	if (sdlscrn != NULL && sdlscrn->w == width && sdlscrn->h == height
336 	    && sdlscrn->format->BitsPerPixel == bitdepth && !bForceChange)
337 		return false;
338 
339 	psSdlVideoDriver = SDL_getenv("SDL_VIDEODRIVER");
340 	bUseDummyMode = psSdlVideoDriver && !strcmp(psSdlVideoDriver, "dummy");
341 	if (bUseDummyMode)
342 	{
343 		ConfigureParams.Screen.DisableVideo = true;
344 	}
345 
346 #ifdef _MUDFLAP
347 	if (sdlscrn)
348 	{
349 		__mf_unregister(sdlscrn->pixels, sdlscrn->pitch*sdlscrn->h, __MF_TYPE_GUESS);
350 	}
351 #endif
352 	if (bInFullScreen)
353 	{
354 		/* unhide the Hatari WM window for fullscreen */
355 		Control_ReparentWindow(width, height, bInFullScreen);
356 	}
357 
358 #if WITH_SDL2
359 	bUseSdlRenderer = ConfigureParams.Screen.bUseSdlRenderer && !bUseDummyMode;
360 
361 	/* SDL Video attributes: */
362 	win_width = width;
363 	win_height = height;
364 	if (bInFullScreen)
365 	{
366 		sdlVideoFlags = SDL_WINDOW_BORDERLESS | SDL_WINDOW_INPUT_GRABBED;
367 		if (ConfigureParams.Screen.bKeepResolution)
368 			sdlVideoFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
369 		else
370 			sdlVideoFlags |= SDL_WINDOW_FULLSCREEN;
371 	}
372 	else
373 	{
374 		int deskw, deskh;
375 		if (getenv("PARENT_WIN_ID") != NULL)	/* Embedded window? */
376 			sdlVideoFlags = SDL_WINDOW_BORDERLESS;
377 		else if (ConfigureParams.Screen.bResizable && bUseSdlRenderer)
378 			sdlVideoFlags = SDL_WINDOW_RESIZABLE;
379 		else
380 			sdlVideoFlags = 0;
381 		/* Make sure that window is not bigger than current desktop */
382 		Resolution_GetDesktopSize(&deskw, &deskh);
383 		if (win_width > deskw)
384 			win_width = deskw;
385 		if (win_height > deskh)
386 			win_height = deskh;
387 	}
388 
389 	Screen_FreeSDL2Resources();
390 	if (sdlWindow &&
391 	    ((bInFullScreen && !ConfigureParams.Screen.bKeepResolution) ||
392 	     (bPrevInFullScreen != bInFullScreen) ||
393 	     bForceChange
394 	    ))
395 	{
396 		SDL_DestroyWindow(sdlWindow);
397 		sdlWindow = NULL;
398 	}
399 	bPrevInFullScreen = bInFullScreen;
400 
401 	/* Set SDL2 video hints */
402 	if (nPrevRenderScaleQuality != ConfigureParams.Screen.nRenderScaleQuality)
403 	{
404 		char hint[2] = { '0' + ConfigureParams.Screen.nRenderScaleQuality, 0 };
405 		SDL_SetHintWithPriority(SDL_HINT_RENDER_SCALE_QUALITY, hint, SDL_HINT_OVERRIDE);
406 		nPrevRenderScaleQuality = ConfigureParams.Screen.nRenderScaleQuality;
407 	}
408 	if (bPrevUseVsync != ConfigureParams.Screen.bUseVsync)
409 	{
410 		char hint[2] = { '0' + ConfigureParams.Screen.bUseVsync, 0 };
411 		SDL_SetHintWithPriority(SDL_HINT_RENDER_VSYNC, hint, SDL_HINT_OVERRIDE);
412 		bPrevUseVsync = ConfigureParams.Screen.bUseVsync;
413 	}
414 
415 #ifdef SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4		/* requires sdl >= 2.0.4 */
416 	/* Disable closing Hatari with alt+F4 under Windows as alt+F4 can be used by some emulated programs */
417 	SDL_SetHintWithPriority(SDL_HINT_WINDOWS_NO_CLOSE_ON_ALT_F4, "1", SDL_HINT_OVERRIDE);
418 #endif
419 
420 	/* Set new video mode */
421 	DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height,
422 	        bitdepth, bInFullScreen?"fullscreen":"windowed"));
423 
424 	if (sdlWindow)
425 	{
426 		if ((SDL_GetWindowFlags(sdlWindow) & SDL_WINDOW_MAXIMIZED) == 0)
427 			SDL_SetWindowSize(sdlWindow, win_width, win_height);
428 	}
429 	else
430 	{
431 		sdlWindow = SDL_CreateWindow("Hatari", SDL_WINDOWPOS_UNDEFINED,
432 		                             SDL_WINDOWPOS_UNDEFINED,
433 		                             win_width, win_height, sdlVideoFlags);
434 	}
435 	if (!sdlWindow)
436 	{
437 		fprintf(stderr, "ERROR: Failed to create %dx%d window!\n",
438 		        win_width, win_height);
439 		exit(-1);
440 	}
441 	if (bUseSdlRenderer)
442 	{
443 		int rm, bm, gm, pfmt;
444 
445 		sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
446 		if (!sdlRenderer)
447 		{
448 			fprintf(stderr, "ERROR: Failed to create %dx%d renderer!\n",
449 			        win_width, win_height);
450 			exit(1);
451 		}
452 
453 		if (bInFullScreen)
454 			SDL_RenderSetLogicalSize(sdlRenderer, width, height);
455 
456 		if (bitdepth == 16)
457 		{
458 			rm = 0xF800;
459 			gm = 0x07E0;
460 			bm = 0x001F;
461 			pfmt = SDL_PIXELFORMAT_RGB565;
462 		}
463 		else
464 		{
465 			rm = 0x00FF0000;
466 			gm = 0x0000FF00;
467 			bm = 0x000000FF;
468 			pfmt = SDL_PIXELFORMAT_RGB888;
469 		}
470 		sdlscrn = SDL_CreateRGBSurface(0, width, height, bitdepth,
471 		                               rm, gm, bm, 0);
472 		sdlTexture = SDL_CreateTexture(sdlRenderer, pfmt,
473 		                               SDL_TEXTUREACCESS_STREAMING,
474 		                               width, height);
475 		if (!sdlTexture)
476 		{
477 			fprintf(stderr, "ERROR: Failed to create %dx%d@%d texture!\n",
478 			       width, height, bitdepth);
479 			exit(-3);
480 		}
481 	}
482 	else
483 	{
484 		sdlscrn = SDL_GetWindowSurface(sdlWindow);
485 	}
486 
487 #else	/* !WITH_SDL2 */
488 
489 	/* SDL Video attributes: */
490 	if (bInFullScreen)
491 	{
492 		sdlVideoFlags  = SDL_HWSURFACE|SDL_FULLSCREEN/*|SDL_DOUBLEBUF*/;
493 		/* SDL_DOUBLEBUF helps avoiding tearing and can be faster on suitable HW,
494 		 * but it doesn't work with partial screen updates done by the ST screen
495 		 * update code or the Hatari GUI, so double buffering is disabled. */
496 	}
497 	else
498 	{
499 		sdlVideoFlags  = SDL_SWSURFACE;
500 	}
501 
502 	/* Set new video mode */
503 	DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height, bitdepth,
504 	            bInFullScreen?"fullscreen":"windowed"));
505 	sdlscrn = SDL_SetVideoMode(width, height, bitdepth, sdlVideoFlags);
506 
507 	/* By default ConfigureParams.Screen.nForceBpp and therefore
508 	 * BitCount is zero which means "SDL color depth autodetection".
509 	 * In this case the SDL_SetVideoMode() call might return
510 	 * a 24 bpp resolution
511 	 */
512 	if (sdlscrn && sdlscrn->format->BitsPerPixel == 24)
513 	{
514 		Log_Printf(LOG_WARN, "Unsupported color depth 24, trying 32 bpp instead...\n");
515 		sdlscrn = SDL_SetVideoMode(width, height, 32, sdlVideoFlags);
516 	}
517 
518 #endif	/* !WITH_SDL2 */
519 
520 	/* Exit if we can not open a screen */
521 	if (!sdlscrn)
522 	{
523 		fprintf(stderr, "ERROR: Could not set video mode:\n %s\n", SDL_GetError() );
524 		SDL_Quit();
525 		exit(-2);
526 	}
527 
528 	DEBUGPRINT(("SDL screen granted: %d x %d @ %d\n", sdlscrn->w, sdlscrn->h,
529 	            sdlscrn->format->BitsPerPixel));
530 
531 #ifdef _MUDFLAP
532 	__mf_register(sdlscrn->pixels, sdlscrn->pitch*sdlscrn->h, __MF_TYPE_GUESS, "SDL pixels");
533 #endif
534 
535 	if (!bInFullScreen)
536 	{
537 		/* re-embed the new Hatari SDL window */
538 		Control_ReparentWindow(width, height, bInFullScreen);
539 	}
540 
541 	Avi_SetSurface(sdlscrn);
542 
543 	bRGBTableInSync = false;
544 
545 	return true;
546 }
547 
548 
549 /**
550  * Initialize ST/STE screen resolution.
551  */
Screen_SetSTResolution(bool bForceChange)552 static void Screen_SetSTResolution(bool bForceChange)
553 {
554 	int Width, Height, nZoom, SBarHeight, BitCount, maxW, maxH;
555 	bool bDoubleLowRes = false;
556 
557 	/* Bits per pixel */
558 	BitCount = ConfigureParams.Screen.nForceBpp;
559 
560 	nBorderPixelsTop = nBorderPixelsBottom = 0;
561 	nBorderPixelsLeft = nBorderPixelsRight = 0;
562 
563 	nScreenZoomX = 1;
564 	nScreenZoomY = 1;
565 
566 	if (STRes == ST_LOW_RES)
567 	{
568 		Width = 320;
569 		Height = 200;
570 		nZoom = 1;
571 	}
572 	else    /* else use 640x400, also for med-rez */
573 	{
574 		Width = 640;
575 		Height = 400;
576 		nZoom = 2;
577 	}
578 
579 	/* Statusbar height for doubled screen size */
580 	SBarHeight = Statusbar_GetHeightForSize(640, 400);
581 
582 	Resolution_GetLimits(&maxW, &maxH, &BitCount, Screen_WantToKeepResolution());
583 
584 	/* Zoom if necessary, factors used for scaling mouse motions */
585 	if (STRes == ST_LOW_RES &&
586 	    2*Width <= maxW && 2*Height+SBarHeight <= maxH)
587 	{
588 		nZoom = 2;
589 		Width *= 2;
590 		Height *= 2;
591 		nScreenZoomX = 2;
592 		nScreenZoomY = 2;
593 		bDoubleLowRes = true;
594 	}
595 	else if (STRes == ST_MEDIUM_RES)
596 	{
597 		/* med-rez conversion functions want always
598 		 * to double vertically, they don't support
599 		 * skipping that (only leaving doubled lines
600 		 * black for the TV mode).
601 		 */
602 		nScreenZoomX = 1;
603 		nScreenZoomY = 2;
604 	}
605 
606 	/* Adjust width/height for overscan borders, if mono or VDI we have no overscan */
607 	if (ConfigureParams.Screen.bAllowOverscan && !bUseHighRes)
608 	{
609 		int leftX = maxW - Width;
610 		int leftY = maxH - (Height + Statusbar_GetHeightForSize(Width, Height));
611 
612 		Screen_SetBorderPixels(leftX/nZoom, leftY/nZoom);
613 		DEBUGPRINT(("resolution limit:\n\t%d x %d\nlimited resolution:\n\t", maxW, maxH));
614 		DEBUGPRINT(("%d * (%d + %d + %d) x (%d + %d + %d)\n", nZoom,
615 			    nBorderPixelsLeft, Width/nZoom, nBorderPixelsRight,
616 			    nBorderPixelsTop, Height/nZoom, nBorderPixelsBottom));
617 		Width += (nBorderPixelsRight + nBorderPixelsLeft)*nZoom;
618 		Height += (nBorderPixelsTop + nBorderPixelsBottom)*nZoom;
619 		DEBUGPRINT(("\t= %d x %d (+ statusbar)\n", Width, Height));
620 	}
621 
622 	Screen_SetSTScreenOffsets();
623 	Height += Statusbar_SetHeight(Width, Height);
624 
625 	PCScreenOffsetX = PCScreenOffsetY = 0;
626 
627 	/* Video attributes: */
628 #if !WITH_SDL2
629 	if (bInFullScreen && ConfigureParams.Screen.bKeepResolutionST)
630 	{
631 		/* use desktop resolution */
632 		Resolution_GetDesktopSize(&maxW, &maxH);
633 		SBarHeight = Statusbar_GetHeightForSize(maxW, maxH);
634 		/* re-calculate statusbar height for this resolution */
635 		Statusbar_SetHeight(maxW, maxH-SBarHeight);
636 		/* center Atari screen to resolution */
637 		PCScreenOffsetY = (maxH - Height)/2;
638 		PCScreenOffsetX = (maxW - Width)/2;
639 		/* and select desktop resolution */
640 		Height = maxH;
641 		Width = maxW;
642 	}
643 #endif
644 
645 	if (Screen_SetSDLVideoSize(Width, Height, BitCount, bForceChange))
646 	{
647 		Statusbar_Init(sdlscrn);
648 
649 		/* screen area without the statusbar */
650 		STScreenRect.x = 0;
651 		STScreenRect.y = 0;
652 		STScreenRect.w = sdlscrn->w;
653 		STScreenRect.h = sdlscrn->h - Statusbar_GetHeight();
654 	}
655 
656 	if (!bRGBTableInSync)
657 	{
658 		Screen_SetupRGBTable();   /* Create color conversion table */
659 		bRGBTableInSync = true;
660 	}
661 
662 	/* Set drawing functions */
663 	Screen_SetDrawFunctions(sdlscrn->format->BitsPerPixel, bDoubleLowRes);
664 
665 	Screen_SetFullUpdate();           /* Cause full update of screen */
666 }
667 
668 
669 /**
670  * Change resolution, according to the machine and display type
671  * that we're currently emulating.
672  */
Screen_ChangeResolution(bool bForceChange)673 static void Screen_ChangeResolution(bool bForceChange)
674 {
675 	int hbpp = ConfigureParams.Screen.nForceBpp;
676 
677 	if (bUseVDIRes)
678 	{
679 		Screen_SetGenConvSize(VDIWidth, VDIHeight, hbpp, bForceChange);
680 	}
681 	else if (Config_IsMachineFalcon())
682 	{
683 		Videl_ScreenModeChanged(bForceChange);
684 	}
685 	else if (Config_IsMachineTT())
686 	{
687 		int width, height, bpp;
688 		Video_GetTTRes(&width, &height, &bpp);
689 		Screen_SetGenConvSize(width, height, hbpp, bForceChange);
690 	}
691 	else if (bUseHighRes)
692 	{
693 		Screen_SetGenConvSize(640, 400, hbpp, bForceChange);
694 	}
695 	else
696 	{
697 		Screen_SetSTResolution(bForceChange);
698 	}
699 
700 	if (bInFullScreen || bGrabMouse)
701 		SDL_WM_GrabInput(SDL_GRAB_ON);
702 	else
703 		SDL_WM_GrabInput(SDL_GRAB_OFF);
704 }
705 
706 
707 /**
708  * Change the resolution - but only if it was already initialized before
709  */
Screen_ModeChanged(bool bForceChange)710 void Screen_ModeChanged(bool bForceChange)
711 {
712 	if (sdlscrn)	/* Do it only if we're already up and running */
713 	{
714 		Screen_ChangeResolution(bForceChange);
715 	}
716 }
717 
718 
719 /*-----------------------------------------------------------------------*/
720 /**
721  * Init Screen bitmap and buffers/tables needed for ST to PC screen conversion
722  */
Screen_Init(void)723 void Screen_Init(void)
724 {
725 	int i;
726 	SDL_Surface *pIconSurf;
727 	char sIconFileName[FILENAME_MAX];
728 
729 	/* Clear frame buffer structures and set current pointer */
730 	memset(FrameBuffers, 0, NUM_FRAMEBUFFERS * sizeof(FRAMEBUFFER));
731 
732 	/* Allocate previous screen check workspace. We are going to double-buffer a double-buffered screen. Oh. */
733 	for (i = 0; i < NUM_FRAMEBUFFERS; i++)
734 	{
735 		FrameBuffers[i].pSTScreen = malloc(MAX_VDI_BYTES);
736 		FrameBuffers[i].pSTScreenCopy = malloc(MAX_VDI_BYTES);
737 		if (!FrameBuffers[i].pSTScreen || !FrameBuffers[i].pSTScreenCopy)
738 		{
739 			fprintf(stderr, "ERROR: Failed to allocate frame buffer memory.\n");
740 			exit(-1);
741 		}
742 	}
743 	pFrameBuffer = &FrameBuffers[0];
744 
745 	/* Set initial window resolution */
746 	bInFullScreen = ConfigureParams.Screen.bFullScreen;
747 	Screen_ChangeResolution(false);
748 	ScreenDrawFunctionsNormal[ST_HIGH_RES] = Screen_ConvertHighRes;
749 
750 	Video_SetScreenRasters();                       /* Set rasters ready for first screen */
751 
752 	/* Load and set icon */
753 	snprintf(sIconFileName, sizeof(sIconFileName), "%s%chatari-icon.bmp",
754 	         Paths_GetDataDir(), PATHSEP);
755 	pIconSurf = SDL_LoadBMP(sIconFileName);
756 	if (pIconSurf)
757 	{
758 #if WITH_SDL2
759 		SDL_SetColorKey(pIconSurf, SDL_TRUE, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
760 		SDL_SetWindowIcon(sdlWindow, pIconSurf);
761 #else
762 		SDL_SetColorKey(pIconSurf, SDL_SRCCOLORKEY, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
763 		SDL_WM_SetIcon(pIconSurf, NULL);
764 #endif
765 		SDL_FreeSurface(pIconSurf);
766 	}
767 
768 	/* Configure some SDL stuff: */
769 	SDL_ShowCursor(SDL_DISABLE);
770 }
771 
772 
773 /*-----------------------------------------------------------------------*/
774 /**
775  * Free screen bitmap and allocated resources
776  */
Screen_UnInit(void)777 void Screen_UnInit(void)
778 {
779 	int i;
780 
781 	/* Free memory used for copies */
782 	for (i = 0; i < NUM_FRAMEBUFFERS; i++)
783 	{
784 		free(FrameBuffers[i].pSTScreen);
785 		free(FrameBuffers[i].pSTScreenCopy);
786 	}
787 
788 #if WITH_SDL2
789 	Screen_FreeSDL2Resources();
790 	if (sdlWindow)
791 	{
792 		SDL_DestroyWindow(sdlWindow);
793 		sdlWindow = NULL;
794 	}
795 #endif
796 }
797 
798 
799 /*-----------------------------------------------------------------------*/
800 /**
801  * Reset screen
802  */
Screen_Reset(void)803 void Screen_Reset(void)
804 {
805 	/* On re-boot, always correct ST resolution for monitor, eg Colour/Mono */
806 	if (bUseVDIRes)
807 	{
808 		STRes = VDIRes;
809 	}
810 	else
811 	{
812 		if (bUseHighRes)
813 		{
814 			STRes = ST_HIGH_RES;
815 			TTRes = TT_HIGH_RES;
816 		}
817 		else
818 		{
819 			STRes = ST_LOW_RES;
820 			TTRes = TT_MEDIUM_RES;
821 		}
822 	}
823 	/* Cause full update */
824 	Screen_ModeChanged(false);
825 }
826 
827 
828 /*-----------------------------------------------------------------------*/
829 /**
830  * Set flags so screen will be TOTALLY re-drawn (clears whole of full-screen)
831  * next time around
832  */
Screen_SetFullUpdate(void)833 void Screen_SetFullUpdate(void)
834 {
835 	int i;
836 
837 	/* Update frame buffers */
838 	for (i = 0; i < NUM_FRAMEBUFFERS; i++)
839 		FrameBuffers[i].bFullUpdate = true;
840 }
841 
842 
843 /*-----------------------------------------------------------------------*/
844 /**
845  * Clear Window display memory
846  */
Screen_ClearScreen(void)847 static void Screen_ClearScreen(void)
848 {
849 	SDL_FillRect(sdlscrn, &STScreenRect, SDL_MapRGB(sdlscrn->format, 0, 0, 0));
850 }
851 
852 
853 /*-----------------------------------------------------------------------*/
854 /**
855  * Force screen redraw.  Does the right thing regardless of whether
856  * we're in ST/STe, Falcon or TT mode.  Needed when switching modes
857  * while emulation is paused.
858  */
Screen_Refresh(void)859 static void Screen_Refresh(void)
860 {
861 	if (bUseVDIRes)
862 	{
863 		Screen_GenDraw(VideoBase, VDIWidth, VDIHeight, VDIPlanes,
864 		               VDIWidth * VDIPlanes / 16, 0, 0, 0, 0);
865 	}
866 	else if (Config_IsMachineFalcon())
867 	{
868 		VIDEL_renderScreen();
869 	}
870 	else if (Config_IsMachineTT())
871 	{
872 		Video_RenderTTScreen();
873 	}
874 	else
875 	{
876 		Screen_DrawFrame(true);
877 	}
878 }
879 
880 
881 /*-----------------------------------------------------------------------*/
882 /**
883  * Enter Full screen mode
884  */
Screen_EnterFullScreen(void)885 void Screen_EnterFullScreen(void)
886 {
887 	bool bWasRunning;
888 
889 	if (!bInFullScreen)
890 	{
891 		/* Hold things... */
892 		bWasRunning = Main_PauseEmulation(false);
893 		bInFullScreen = true;
894 
895 		if (Screen_UseGenConvScreen())
896 		{
897 			Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
898 			                      genconv_bpp, true);
899 			/* force screen redraw */
900 			Screen_GenConvUpdate(NULL, true);
901 		}
902 		else
903 		{
904 			Screen_SetSTResolution(true);
905 			Screen_ClearScreen();       /* Black out screen bitmap as will be invalid when return */
906 		}
907 
908 		if (!Screen_WantToKeepResolution())
909 		{
910 			/* Give monitor time to change to new resolution */
911 			SDL_Delay(20);
912 		}
913 
914 		if (bWasRunning)
915 		{
916 			/* And off we go... */
917 			Main_UnPauseEmulation();
918 		}
919 		else
920 		{
921 			Screen_Refresh();
922 		}
923 		SDL_WM_GrabInput(SDL_GRAB_ON);  /* Grab mouse pointer in fullscreen */
924 	}
925 }
926 
927 
928 /*-----------------------------------------------------------------------*/
929 /**
930  * Return from Full screen mode back to a window
931  */
Screen_ReturnFromFullScreen(void)932 void Screen_ReturnFromFullScreen(void)
933 {
934 	bool bWasRunning;
935 
936 	if (bInFullScreen)
937 	{
938 		/* Hold things... */
939 		bWasRunning = Main_PauseEmulation(false);
940 		bInFullScreen = false;
941 
942 		if (Screen_UseGenConvScreen())
943 		{
944 			Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
945 			                      genconv_bpp, true);
946 			/* force screen redraw */
947 			Screen_GenConvUpdate(NULL, true);
948 		}
949 		else
950 		{
951 			Screen_SetSTResolution(true);
952 		}
953 
954 		if (!Screen_WantToKeepResolution())
955 		{
956 			/* Give monitor time to switch resolution */
957 			SDL_Delay(20);
958 		}
959 
960 		if (bWasRunning)
961 		{
962 			/* And off we go... */
963 			Main_UnPauseEmulation();
964 		}
965 		else
966 		{
967 			Screen_Refresh();
968 		}
969 
970 		if (!bGrabMouse)
971 		{
972 			/* Un-grab mouse pointer in windowed mode */
973 			SDL_WM_GrabInput(SDL_GRAB_OFF);
974 		}
975 	}
976 }
977 
978 
979 /*-----------------------------------------------------------------------*/
980 /**
981  * Have we changed between low/med/high res?
982  */
Screen_DidResolutionChange(int new_res)983 static void Screen_DidResolutionChange(int new_res)
984 {
985 	if (new_res != STRes)
986 	{
987 		STRes = new_res;
988 		Screen_ModeChanged(false);
989 	}
990 	else
991 	{
992 		/* Did change overscan mode? Causes full update */
993 		if (pFrameBuffer->VerticalOverscanCopy != VerticalOverscan)
994 			pFrameBuffer->bFullUpdate = true;
995 	}
996 }
997 
998 
999 /**
1000  * Compare current resolution on line with previous, and set 'UpdateLine' accordingly
1001  * Return if swap between low/medium resolution
1002  */
Screen_CompareResolution(int y,int * pUpdateLine,int oldres)1003 static bool Screen_CompareResolution(int y, int *pUpdateLine, int oldres)
1004 {
1005 	/* Check if wrote to resolution register */
1006 	if (HBLPaletteMasks[y]&PALETTEMASK_RESOLUTION)  /* See 'Intercept_ShifterMode_WriteByte' */
1007 	{
1008 		int newres = (HBLPaletteMasks[y]>>16)&ST_MEDIUM_RES_BIT;
1009 		/* Did resolution change? */
1010 		if (newres != (int)((pFrameBuffer->HBLPaletteMasks[y]>>16)&ST_MEDIUM_RES_BIT))
1011 			*pUpdateLine |= PALETTEMASK_UPDATERES;
1012 		else
1013 			*pUpdateLine &= ~PALETTEMASK_UPDATERES;
1014 		/* Have used any low/medium res mix? */
1015 		return (newres != (oldres&ST_MEDIUM_RES_BIT));
1016 	}
1017 	return false;
1018 }
1019 
1020 
1021 /*-----------------------------------------------------------------------*/
1022 /**
1023  * Check to see if palette changes cause screen update and keep 'HBLPalette[]' up-to-date
1024  */
Screen_ComparePalette(int y,int * pUpdateLine)1025 static void Screen_ComparePalette(int y, int *pUpdateLine)
1026 {
1027 	bool bPaletteChanged = false;
1028 	int i;
1029 
1030 	/* Did write to palette in this or previous frame? */
1031 	if (((HBLPaletteMasks[y]|pFrameBuffer->HBLPaletteMasks[y])&PALETTEMASK_PALETTE)!=0)
1032 	{
1033 		/* Check and update ones which changed */
1034 		for (i = 0; i < 16; i++)
1035 		{
1036 			if (HBLPaletteMasks[y]&(1<<i))        /* Update changes in ST palette */
1037 				HBLPalette[i] = HBLPalettes[(y*16)+i];
1038 		}
1039 		/* Now check with same palette from previous frame for any differences(may be changing palette back) */
1040 		for (i = 0; (i < 16) && (!bPaletteChanged); i++)
1041 		{
1042 			if (HBLPalette[i]!=pFrameBuffer->HBLPalettes[(y*16)+i])
1043 				bPaletteChanged = true;
1044 		}
1045 		if (bPaletteChanged)
1046 			*pUpdateLine |= PALETTEMASK_UPDATEPAL;
1047 		else
1048 			*pUpdateLine &= ~PALETTEMASK_UPDATEPAL;
1049 	}
1050 }
1051 
1052 
1053 /*-----------------------------------------------------------------------*/
1054 /**
1055  * Check for differences in Palette and Resolution from Mask table and update
1056  * and store off which lines need updating and create full-screen palette.
1057  * (It is very important for these routines to check for colour changes with
1058  * the previous screen so only the very minimum parts are updated).
1059  * Return new STRes value.
1060  */
Screen_ComparePaletteMask(int res)1061 static int Screen_ComparePaletteMask(int res)
1062 {
1063 	bool bLowMedMix = false;
1064 	int LineUpdate = 0;
1065 	int y;
1066 
1067 	/* Set for monochrome? */
1068 	if (bUseHighRes)
1069 	{
1070 		VerticalOverscan = V_OVERSCAN_NONE;
1071 
1072 		/* Just copy mono colors */
1073 		if (HBLPalettes[0] & 0x777)
1074 		{
1075 			HBLPalettes[0] = 0x777;
1076 			HBLPalettes[1] = 0x000;
1077 		}
1078 		else
1079 		{
1080 			HBLPalettes[0] = 0x000;
1081 			HBLPalettes[1] = 0x777;
1082 		}
1083 
1084 		/* Colors changed? */
1085 		if (HBLPalettes[0] != PrevHBLPalette[0])
1086 			pFrameBuffer->bFullUpdate = true;
1087 
1088 		/* Set bit to flag 'full update' */
1089 		if (pFrameBuffer->bFullUpdate)
1090 			ScrUpdateFlag = PALETTEMASK_UPDATEFULL;
1091 		else
1092 			ScrUpdateFlag = 0x00000000;
1093 
1094 		/* Force to standard hi-resolution screen, without overscan */
1095 		res = ST_HIGH_RES;
1096 	}
1097 	else    /* Full colour */
1098 	{
1099 		/* Get resolution */
1100 		//res = (HBLPaletteMasks[0]>>16)&ST_RES_MASK;
1101 		/* [NP] keep only low/med bit (could be hires in case of overscan on the 1st line) */
1102 		res = (HBLPaletteMasks[0]>>16)&ST_MEDIUM_RES_BIT;
1103 
1104 		/* Do all lines - first is tagged as full-update */
1105 		for (y = 0; y < NUM_VISIBLE_LINES; y++)
1106 		{
1107 			/* Find any resolution/palette change and update palette/mask buffer */
1108 			/* ( LineUpdate has top two bits set to say if line needs updating due to palette or resolution change ) */
1109 			bLowMedMix |= Screen_CompareResolution(y, &LineUpdate, res);
1110 			Screen_ComparePalette(y,&LineUpdate);
1111 			HBLPaletteMasks[y] = (HBLPaletteMasks[y]&(~PALETTEMASK_UPDATEMASK)) | LineUpdate;
1112 			/* Copy palette and mask for next frame */
1113 			memcpy(&pFrameBuffer->HBLPalettes[y*16],HBLPalette,sizeof(short int)*16);
1114 			pFrameBuffer->HBLPaletteMasks[y] = HBLPaletteMasks[y];
1115 		}
1116 		/* Did mix/have medium resolution? */
1117 		if (bLowMedMix || (res & ST_MEDIUM_RES_BIT))
1118 			res = ST_MEDIUM_RES;
1119 	}
1120 
1121 	/* Copy old palette for compare */
1122 	memcpy(PrevHBLPalette, HBLPalettes, sizeof(Uint16)*16);
1123 
1124 	return res;
1125 }
1126 
1127 
1128 /*-----------------------------------------------------------------------*/
1129 /**
1130  * Update Palette Mask to show 'full-update' required. This is usually done after a resolution change
1131  * or when going between a Window and full-screen display
1132  */
Screen_SetFullUpdateMask(void)1133 static void Screen_SetFullUpdateMask(void)
1134 {
1135 	int y;
1136 
1137 	for (y = 0; y < NUM_VISIBLE_LINES; y++)
1138 		HBLPaletteMasks[y] |= PALETTEMASK_UPDATEFULL;
1139 }
1140 
1141 
1142 /*-----------------------------------------------------------------------*/
1143 /**
1144  * Set details for ST screen conversion.
1145  */
Screen_SetConvertDetails(void)1146 static void Screen_SetConvertDetails(void)
1147 {
1148 	pSTScreen = pFrameBuffer->pSTScreen;          /* Source in ST memory */
1149 	pSTScreenCopy = pFrameBuffer->pSTScreenCopy;  /* Previous ST screen */
1150 	pPCScreenDest = sdlscrn->pixels;              /* Destination PC screen */
1151 
1152 	PCScreenBytesPerLine = sdlscrn->pitch;        /* Bytes per line */
1153 
1154 	/* Center to available framebuffer */
1155 	pPCScreenDest += PCScreenOffsetY * PCScreenBytesPerLine + PCScreenOffsetX * (sdlscrn->format->BitsPerPixel/8);
1156 
1157 	pHBLPalettes = pFrameBuffer->HBLPalettes;     /* HBL palettes pointer */
1158 	/* Not in TV-Mode? Then double up on Y: */
1159 	bScrDoubleY = !(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_TV);
1160 
1161 	if (ConfigureParams.Screen.bAllowOverscan)  /* Use borders? */
1162 	{
1163 		/* Always draw to WHOLE screen including ALL borders */
1164 		STScreenLeftSkipBytes = 0;              /* Number of bytes to skip on ST screen for left (border) */
1165 
1166 		if (bUseHighRes)
1167 		{
1168 				pFrameBuffer->VerticalOverscanCopy = VerticalOverscan = V_OVERSCAN_NONE;
1169 			STScreenStartHorizLine = 0;
1170 			STScreenEndHorizLine = 400;
1171 		}
1172 		else
1173 		{
1174 			STScreenWidthBytes = SCREENBYTES_LINE;  /* Number of horizontal bytes in our ST screen */
1175 			STScreenStartHorizLine = OVERSCAN_TOP - nBorderPixelsTop;
1176 			STScreenEndHorizLine = OVERSCAN_TOP + 200 + nBorderPixelsBottom;
1177 		}
1178 	}
1179 	else
1180 	{
1181 		/* Only draw main area and centre on Y */
1182 		STScreenLeftSkipBytes = SCREENBYTES_LEFT;
1183 		STScreenWidthBytes = SCREENBYTES_MIDDLE;
1184 		STScreenStartHorizLine = OVERSCAN_TOP;
1185 		STScreenEndHorizLine = OVERSCAN_TOP + (bUseHighRes ? 400 : 200);
1186 	}
1187 }
1188 
1189 
1190 /*-----------------------------------------------------------------------*/
1191 /**
1192  * Lock full-screen for drawing
1193  */
Screen_Lock(void)1194 bool Screen_Lock(void)
1195 {
1196 	if (SDL_MUSTLOCK(sdlscrn))
1197 	{
1198 		if (SDL_LockSurface(sdlscrn))
1199 		{
1200 			Screen_ReturnFromFullScreen();   /* All OK? If not need to jump back to a window */
1201 			return false;
1202 		}
1203 	}
1204 
1205 	return true;
1206 }
1207 
1208 /*-----------------------------------------------------------------------*/
1209 /**
1210  * UnLock full-screen
1211  */
Screen_UnLock(void)1212 void Screen_UnLock(void)
1213 {
1214 	if ( SDL_MUSTLOCK(sdlscrn) )
1215 		SDL_UnlockSurface(sdlscrn);
1216 }
1217 
1218 
1219 /*-----------------------------------------------------------------------*/
1220 /**
1221  * Blit our converted ST screen to window/full-screen
1222  */
Screen_Blit(SDL_Rect * sbar_rect)1223 static void Screen_Blit(SDL_Rect *sbar_rect)
1224 {
1225 	unsigned char *pTmpScreen;
1226 
1227 #if 0	/* double buffering cannot be used with partial screen updates */
1228 # if NUM_FRAMEBUFFERS > 1
1229 	if (bInFullScreen && (sdlscrn->flags & SDL_DOUBLEBUF))
1230 	{
1231 		/* Swap screen */
1232 		if (pFrameBuffer==&FrameBuffers[0])
1233 			pFrameBuffer = &FrameBuffers[1];
1234 		else
1235 			pFrameBuffer = &FrameBuffers[0];
1236 		SDL_Flip(sdlscrn);
1237 	}
1238 	else
1239 # endif
1240 #endif
1241 	{
1242 		int count = 1;
1243 		SDL_Rect rects[2];
1244 		rects[0] = STScreenRect;
1245 		if (sbar_rect)
1246 		{
1247 			rects[1] = *sbar_rect;
1248 			count = 2;
1249 		}
1250 		SDL_UpdateRects(sdlscrn, count, rects);
1251 	}
1252 
1253 	/* Swap copy/raster buffers in screen. */
1254 	pTmpScreen = pFrameBuffer->pSTScreenCopy;
1255 	pFrameBuffer->pSTScreenCopy = pFrameBuffer->pSTScreen;
1256 	pFrameBuffer->pSTScreen = pTmpScreen;
1257 }
1258 
1259 
1260 /*-----------------------------------------------------------------------*/
1261 /**
1262  * Draw ST screen to window/full-screen framebuffer
1263  * @param  bForceFlip  Force screen update, even if contents did not change
1264  * @return  true if screen contents changed
1265  */
Screen_DrawFrame(bool bForceFlip)1266 static bool Screen_DrawFrame(bool bForceFlip)
1267 {
1268 	int new_res;
1269 	void (*pDrawFunction)(void);
1270 	static bool bPrevFrameWasSpec512 = false;
1271 	SDL_Rect *sbar_rect;
1272 
1273 	assert(!bUseVDIRes);
1274 
1275 	/* Scan palette/resolution masks for each line and build up palette/difference tables */
1276 	new_res = Screen_ComparePaletteMask(STRes);
1277 	/* Did we change resolution this frame - allocate new screen if did so */
1278 	Screen_DidResolutionChange(new_res);
1279 	/* Is need full-update, tag as such */
1280 	if (pFrameBuffer->bFullUpdate)
1281 		Screen_SetFullUpdateMask();
1282 
1283 	/* restore area potentially left under overlay led
1284 	 * and saved by Statusbar_OverlayBackup()
1285 	 */
1286 	Statusbar_OverlayRestore(sdlscrn);
1287 
1288 	/* Lock screen for direct screen surface format writes */
1289 	if (ConfigureParams.Screen.DisableVideo || !Screen_Lock())
1290 	{
1291 		return false;
1292 	}
1293 
1294 	bScreenContentsChanged = false;      /* Did change (ie needs blit?) */
1295 
1296 	/* Set details */
1297 	Screen_SetConvertDetails();
1298 
1299 	/* Clear screen on full update to clear out borders and also interleaved lines */
1300 	if (pFrameBuffer->bFullUpdate)
1301 		Screen_ClearScreen();
1302 
1303 	/* Call drawing for full-screen */
1304 	pDrawFunction = ScreenDrawFunctionsNormal[STRes];
1305 	/* Check if is Spec512 image */
1306 	if (Spec512_IsImage())
1307 	{
1308 		bPrevFrameWasSpec512 = true;
1309 		/* What mode were we in? Keep to 320xH or 640xH */
1310 		if (pDrawFunction==ConvertLowRes_320x16Bit)
1311 			pDrawFunction = ConvertLowRes_320x16Bit_Spec;
1312 		else if (pDrawFunction==ConvertLowRes_640x16Bit)
1313 			pDrawFunction = ConvertLowRes_640x16Bit_Spec;
1314 		else if (pDrawFunction==ConvertLowRes_320x32Bit)
1315 			pDrawFunction = ConvertLowRes_320x32Bit_Spec;
1316 		else if (pDrawFunction==ConvertLowRes_640x32Bit)
1317 			pDrawFunction = ConvertLowRes_640x32Bit_Spec;
1318 		else if (pDrawFunction==ConvertMediumRes_640x32Bit)
1319 			pDrawFunction = ConvertMediumRes_640x32Bit_Spec;
1320 		else if (pDrawFunction==ConvertMediumRes_640x16Bit)
1321 			pDrawFunction = ConvertMediumRes_640x16Bit_Spec;
1322 	}
1323 	else if (bPrevFrameWasSpec512)
1324 	{
1325 		/* If we switch back from Spec512 mode to normal
1326 		 * screen rendering, we have to make sure to do
1327 		 * a full update of the screen. */
1328 		Screen_SetFullUpdateMask();
1329 		bPrevFrameWasSpec512 = false;
1330 	}
1331 
1332 	if (pDrawFunction)
1333 		CALL_VAR(pDrawFunction);
1334 
1335 	/* Unlock screen */
1336 	Screen_UnLock();
1337 
1338 	/* draw overlay led(s) or statusbar after unlock */
1339 	Statusbar_OverlayBackup(sdlscrn);
1340 	sbar_rect = Statusbar_Update(sdlscrn, false);
1341 
1342 	/* Clear flags, remember type of overscan as if change need screen full update */
1343 	pFrameBuffer->bFullUpdate = false;
1344 	pFrameBuffer->VerticalOverscanCopy = VerticalOverscan;
1345 
1346 	/* And show to user */
1347 	if (bScreenContentsChanged || bForceFlip || sbar_rect)
1348 	{
1349 		Screen_Blit(sbar_rect);
1350 	}
1351 
1352 	return bScreenContentsChanged;
1353 }
1354 
1355 
1356 /*-----------------------------------------------------------------------*/
1357 /**
1358  * Draw ST screen to window/full-screen
1359  */
Screen_Draw(void)1360 bool Screen_Draw(void)
1361 {
1362 	if (bQuitProgram)
1363 	{
1364 		return false;
1365 	}
1366 
1367 	/* And draw (if screen contents changed) */
1368 	return Screen_DrawFrame(false);
1369 }
1370 
1371 /**
1372  * This is used to set the size of the SDL screen
1373  * when we're using the generic conversion functions.
1374  */
Screen_SetGenConvSize(int width,int height,int bpp,bool bForceChange)1375 void Screen_SetGenConvSize(int width, int height, int bpp, bool bForceChange)
1376 {
1377 	const bool keep = ConfigureParams.Screen.bKeepResolution;
1378 	int screenwidth, screenheight, maxw, maxh;
1379 	int scalex, scaley, sbarheight;
1380 
1381 	if (bpp == 24)
1382 		bpp = 32;
1383 
1384 	/* constrain size request to user's desktop size */
1385 	Resolution_GetDesktopSize(&maxw, &maxh);
1386 #if !WITH_SDL2
1387 	scalex = scaley = 1;
1388 	while (width > maxw*scalex) {
1389 		scalex *= 2;
1390 	}
1391 	while (height > maxh*scaley) {
1392 		scaley *= 2;
1393 	}
1394 	if (scalex * scaley > 1) {
1395 		Log_Printf(LOG_WARN, "Too large screen size %dx%d -> divided by %dx%d!\n",
1396 			width, height, scalex, scaley);
1397 		width /= scalex;
1398 		height /= scaley;
1399 	}
1400 #endif
1401 
1402 	Resolution_GetLimits(&maxw, &maxh, &bpp, keep);
1403 	nScreenZoomX = nScreenZoomY = 1;
1404 
1405 	if (ConfigureParams.Screen.bAspectCorrect) {
1406 		/* Falcon (and TT) pixel scaling factors seem to 2^x
1407 		 * (quarter/half pixel, interlace/double line), so
1408 		 * do aspect correction as 2's exponent.
1409 		 */
1410 		while (nScreenZoomX*width < height &&
1411 		       2*nScreenZoomX*width < maxw) {
1412 			nScreenZoomX *= 2;
1413 		}
1414 		while (2*nScreenZoomY*height < width &&
1415 		       2*nScreenZoomY*height < maxh) {
1416 			nScreenZoomY *= 2;
1417 		}
1418 		if (nScreenZoomX*nScreenZoomY > 2) {
1419 			Log_Printf(LOG_WARN, "Strange screen size %dx%d -> aspect corrected by %dx%d!\n",
1420 				width, height, nScreenZoomX, nScreenZoomY);
1421 		}
1422 	}
1423 
1424 	/* then select scale as close to target size as possible
1425 	 * without having larger size than it
1426 	 */
1427 	scalex = maxw/(nScreenZoomX*width);
1428 	scaley = maxh/(nScreenZoomY*height);
1429 	if (scalex > 1 && scaley > 1) {
1430 		/* keep aspect ratio */
1431 		if (scalex < scaley) {
1432 			nScreenZoomX *= scalex;
1433 			nScreenZoomY *= scalex;
1434 		} else {
1435 			nScreenZoomX *= scaley;
1436 			nScreenZoomY *= scaley;
1437 		}
1438 	}
1439 
1440 	genconv_width_req = width;
1441 	genconv_height_req = height;
1442 	width *= nScreenZoomX;
1443 	height *= nScreenZoomY;
1444 
1445 	/* get statusbar size for this screen size */
1446 	sbarheight = Statusbar_GetHeightForSize(width, height);
1447 	screenheight = height + sbarheight;
1448 	screenwidth = width;
1449 
1450 #if !WITH_SDL2
1451 	/* get resolution corresponding to these */
1452 	Resolution_Search(&screenwidth, &screenheight, &bpp, keep);
1453 #endif
1454 	/* re-calculate statusbar height for this resolution */
1455 	sbarheight = Statusbar_SetHeight(screenwidth, screenheight-sbarheight);
1456 
1457 	genconv_bpp = bpp;
1458 	/* screen area without the statusbar */
1459 	STScreenRect.x = STScreenRect.y = 0;
1460 	STScreenRect.w = screenwidth;
1461 	STScreenRect.h = screenheight - sbarheight;
1462 
1463 	if (!Screen_SetSDLVideoSize(screenwidth, screenheight, bpp, bForceChange))
1464 	{
1465 		/* same host screen size despite Atari resolution change,
1466 		 * -> no time consuming host video mode change needed
1467 		 */
1468 		if (screenwidth > width || screenheight > height+sbarheight) {
1469 			/* Atari screen smaller than host -> clear screen */
1470 			Screen_ClearScreen();
1471 			/* re-calculate variables in case height + statusbar height
1472 			 * don't anymore match SDL surface size (there's an assert
1473 			 * for that)
1474 			 */
1475 			Statusbar_Init(sdlscrn);
1476 		}
1477 #if WITH_SDL2
1478 		genconv_do_update = true;
1479 #else
1480 		genconv_do_update = ( sdlscrn->flags & SDL_HWSURFACE ) == 0;
1481 #endif
1482 		return;
1483 	}
1484 
1485 	// In case surface format changed, remap the native palette
1486 	Screen_RemapPalette();
1487 
1488 	// redraw statusbar
1489 	Statusbar_Init(sdlscrn);
1490 
1491 	DEBUGPRINT(("Surface Pitch = %d, width = %d, height = %d\n", sdlscrn->pitch, sdlscrn->w, sdlscrn->h));
1492 	DEBUGPRINT(("Must Lock? %s\n", SDL_MUSTLOCK(sdlscrn) ? "YES" : "NO"));
1493 
1494 #if WITH_SDL2
1495 	genconv_do_update = true;
1496 #else
1497 	// is the SDL_update needed?
1498 	genconv_do_update = ( sdlscrn->flags & SDL_HWSURFACE ) == 0;
1499 #endif
1500 
1501 	DEBUGPRINT(("Pixel format:bitspp=%d, tmasks r=%04x g=%04x b=%04x"
1502 			", tshifts r=%d g=%d b=%d"
1503 			", tlosses r=%d g=%d b=%d\n",
1504 			sdlscrn->format->BitsPerPixel,
1505 			sdlscrn->format->Rmask, sdlscrn->format->Gmask, sdlscrn->format->Bmask,
1506 			sdlscrn->format->Rshift, sdlscrn->format->Gshift, sdlscrn->format->Bshift,
1507 			sdlscrn->format->Rloss, sdlscrn->format->Gloss, sdlscrn->format->Bloss));
1508 
1509 	Main_WarpMouse(sdlscrn->w/2,sdlscrn->h/2, false);
1510 }
1511 
Screen_GenConvUpdate(SDL_Rect * extra,bool forced)1512 void Screen_GenConvUpdate(SDL_Rect *extra, bool forced)
1513 {
1514 	SDL_Rect rects[2];
1515 	int count = 1;
1516 
1517 	/* Don't update anything on screen if video output is disabled */
1518 	if ( ConfigureParams.Screen.DisableVideo )
1519 		return;
1520 
1521 	if (!forced && !genconv_do_update) // the HW surface is available
1522 		return;
1523 
1524 	rects[0] = STScreenRect;
1525 	if (extra) {
1526 		rects[1] = *extra;
1527 		count = 2;
1528 	}
1529 	SDL_UpdateRects(sdlscrn, count, rects);
1530 }
1531 
Screen_GetGenConvWidth(void)1532 Uint32 Screen_GetGenConvWidth(void)
1533 {
1534 	return STScreenRect.w;
1535 }
1536 
Screen_GetGenConvHeight(void)1537 Uint32 Screen_GetGenConvHeight(void)
1538 {
1539 	return STScreenRect.h;
1540 }
1541 
1542 
1543 /* -------------- screen conversion routines --------------------------------
1544   Screen conversion routines. We have a number of routines to convert ST screen
1545   to PC format. We split these into Low, Medium and High each with 8/16-bit
1546   versions. To gain extra speed, as almost half of the processing time can be
1547   spent in these routines, we check for any changes from the previously
1548   displayed frame. AdjustLinePaletteRemap() sets a flag to tell the routines
1549   if we need to totally update a line (ie full update, or palette/res change)
1550   or if we just can do a difference check.
1551   We convert each screen 16 pixels at a time by use of a couple of look-up
1552   tables. These tables convert from 2-plane format to bbp and then we can add
1553   two of these together to get 4-planes. This keeps the tables small and thus
1554   improves speed. We then look these bbp values up as an RGB/Index value to
1555   copy to the screen.
1556 */
1557 
1558 
1559 /*-----------------------------------------------------------------------*/
1560 /**
1561  * Update the STRGBPalette[] array with current colours for this raster line.
1562  *
1563  * Return 'ScrUpdateFlag', 0x80000000=Full update, 0x40000000=Update
1564  * as palette changed
1565  */
AdjustLinePaletteRemap(int y)1566 static int AdjustLinePaletteRemap(int y)
1567 {
1568 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1569 	static const int endiantable[16] = {0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15};
1570 #endif
1571 	Uint16 *actHBLPal;
1572 	int i;
1573 
1574 	/* Copy palette and convert to RGB in display format */
1575 	actHBLPal = pHBLPalettes + (y<<4);    /* offset in palette */
1576 	for (i=0; i<16; i++)
1577 	{
1578 #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1579 		STRGBPalette[endiantable[i]] = ST2RGB[*actHBLPal++];
1580 #else
1581 		STRGBPalette[i] = ST2RGB[*actHBLPal++];
1582 #endif
1583 	}
1584 	ScrUpdateFlag = HBLPaletteMasks[y];
1585 	return ScrUpdateFlag;
1586 }
1587 
1588 
1589 /*-----------------------------------------------------------------------*/
1590 /**
1591  * Run updates to palette(STRGBPalette[]) until get to screen line
1592  * we are to convert from
1593  */
Convert_StartFrame(void)1594 static void Convert_StartFrame(void)
1595 {
1596 	int y = 0;
1597 	/* Get #lines before conversion starts */
1598 	int lines = STScreenStartHorizLine;
1599 	while (lines--)
1600 		AdjustLinePaletteRemap(y++);     /* Update palette */
1601 }
1602 
1603 /* lookup tables and conversion macros */
1604 #include "convert/macros.h"
1605 
1606 /* Conversion routines */
1607 
1608 #include "convert/low320x16.c"		/* LowRes To 320xH x 16-bit color */
1609 #include "convert/low640x16.c"		/* LowRes To 640xH x 16-bit color */
1610 #include "convert/med640x16.c"		/* MediumRes To 640xH x 16-bit color */
1611 #include "convert/low320x16_spec.c"	/* LowRes Spectrum 512 To 320xH x 16-bit color */
1612 #include "convert/low640x16_spec.c"	/* LowRes Spectrum 512 To 640xH x 16-bit color */
1613 #include "convert/med640x16_spec.c"	/* MediumRes Spectrum 512 To 640xH x 16-bit color */
1614 
1615 #include "convert/low320x32.c"		/* LowRes To 320xH x 32-bit color */
1616 #include "convert/low640x32.c"		/* LowRes To 640xH x 32-bit color */
1617 #include "convert/med640x32.c"		/* MediumRes To 640xH x 32-bit color */
1618 #include "convert/low320x32_spec.c"	/* LowRes Spectrum 512 To 320xH x 32-bit color */
1619 #include "convert/low640x32_spec.c"	/* LowRes Spectrum 512 To 640xH x 32-bit color */
1620 #include "convert/med640x32_spec.c"	/* MediumRes Spectrum 512 To 640xH x 32-bit color */
1621