|
|
1.1 root 1: /*
1.1.1.6 root 2: Hatari - screen.c
1.1 root 3:
1.1.1.21 root 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.
1.1.1.6 root 6:
1.1.1.17 root 7: This code converts a 1/2/4 plane ST format screen to either 8, 16 or 32-bit PC
1.1.1.11 root 8: format. An awful lot of processing is needed to do this conversion - we
1.1.1.6 root 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
1.1.1.17 root 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).
1.1 root 25: */
1.1.1.15 root 26:
1.1.1.17 root 27: const char Screen_fileid[] = "Hatari screen.c : " __DATE__ " " __TIME__;
1.1 root 28:
29: #include <SDL.h>
1.1.1.11 root 30: #include <SDL_endian.h>
1.1.1.24 root 31: #include <assert.h>
1.1 root 32:
33: #include "main.h"
1.1.1.7 root 34: #include "configuration.h"
1.1.1.23 root 35: #include "avi_record.h"
1.1.1.13 root 36: #include "log.h"
37: #include "paths.h"
1.1.1.23 root 38: #include "options.h"
1.1 root 39: #include "screen.h"
1.1.1.24 root 40: #include "screenConvert.h"
1.1.1.15 root 41: #include "control.h"
1.1.1.11 root 42: #include "convert/routines.h"
1.1.1.18 root 43: #include "resolution.h"
1.1 root 44: #include "spec512.h"
1.1.1.15 root 45: #include "statusbar.h"
1.1 root 46: #include "vdi.h"
47: #include "video.h"
1.1.1.13 root 48: #include "falcon/videl.h"
1.1 root 49:
1.1.1.22 root 50: #define DEBUG 0
51:
52: #if DEBUG
53: # define DEBUGPRINT(x) printf x
54: #else
55: # define DEBUGPRINT(x)
56: #endif
1.1 root 57:
1.1.1.13 root 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 */
1.1.1.23 root 62: static int nBorderPixelsTop, nBorderPixelsBottom; /* Lines in top and bottom border */
1.1.1.13 root 63:
1.1.1.24 root 64: /* extern for shortcuts etc. */
1.1.1.17 root 65: bool bGrabMouse = false; /* Grab the mouse cursor in the window */
66: bool bInFullScreen = false; /* true if in full screen */
1.1.1.13 root 67:
68: /* extern for spec512.c */
1.1.1.11 root 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) */
1.1.1.13 root 73:
74: /* extern for video.c */
1.1.1.11 root 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;
1.1.1.20 root 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 */
1.1.1.15 root 86: static SDL_Rect STScreenRect; /* screen size without statusbar */
1.1 root 87:
1.1.1.11 root 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 */
1.1 root 90:
1.1.1.13 root 91: static void (*ScreenDrawFunctionsNormal[3])(void); /* Screen draw functions */
1.1.1.10 root 92:
1.1.1.17 root 93: static bool bScreenContentsChanged; /* true if buffer changed and requires blitting */
94: static bool bScrDoubleY; /* true if double on Y */
1.1.1.11 root 95: static int ScrUpdateFlag; /* Bit mask of how to update screen */
1.1.1.25 root 96: static bool bRGBTableInSync; /* Is RGB table up to date? */
1.1.1.11 root 97:
1.1.1.24 root 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:
1.1.1.10 root 102:
1.1.1.18 root 103: static bool Screen_DrawFrame(bool bForceFlip);
104:
1.1.1.23 root 105: #if WITH_SDL2
106:
107: SDL_Window *sdlWindow;
108: static SDL_Renderer *sdlRenderer;
109: static SDL_Texture *sdlTexture;
1.1.1.26! root 110: static bool bUseSdlRenderer; /* true when using SDL2 renderer */
1.1.1.23 root 111:
112: void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects)
113: {
1.1.1.26! root 114: if (bUseSdlRenderer)
1.1.1.25 root 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: }
1.1.1.23 root 124: }
125:
126: void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h)
127: {
1.1.1.25 root 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;
1.1.1.23 root 138: SDL_UpdateRects(screen, 1, &rect);
139: }
140:
1.1.1.26! root 141: #endif /* WITH_SDL2 */
1.1.1.18 root 142:
1.1.1.10 root 143: /*-----------------------------------------------------------------------*/
1.1.1.13 root 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: */
1.1.1.10 root 149: static void Screen_SetupRGBTable(void)
150: {
1.1.1.13 root 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);
1.1.1.20 root 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;
1.1.1.13 root 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: }
1.1.1.10 root 184: }
185:
1.1.1.13 root 186:
187: /**
1.1.1.24 root 188: * Convert 640x400 monochrome screen
1.1.1.13 root 189: */
1.1.1.24 root 190: static void Screen_ConvertHighRes(void)
1.1.1.10 root 191: {
1.1.1.24 root 192: int linewidth = 640 / 16;
1.1.1.10 root 193:
1.1.1.24 root 194: Screen_GenConvert(pSTScreen, 640, 400, 1, linewidth, 0, 0, 0, 0, 0);
195: bScreenContentsChanged = true;
1.1.1.10 root 196: }
197:
1.1.1.13 root 198: /**
199: * Set screen draw functions.
200: */
1.1.1.18 root 201: static void Screen_SetDrawFunctions(int nBitCount, bool bDoubleLowRes)
1.1.1.10 root 202: {
1.1.1.24 root 203: if (nBitCount <= 16)
1.1.1.13 root 204: {
205: /* High color */
1.1.1.18 root 206: if (bDoubleLowRes)
1.1.1.13 root 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 */
1.1.1.18 root 215: if (bDoubleLowRes)
1.1.1.13 root 216: ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_640x32Bit;
217: else
218: ScreenDrawFunctionsNormal[ST_LOW_RES] = ConvertLowRes_320x32Bit;
219: ScreenDrawFunctionsNormal[ST_MEDIUM_RES] = ConvertMediumRes_640x32Bit;
220: }
1.1.1.10 root 221: }
222:
223:
224: /*-----------------------------------------------------------------------*/
1.1.1.13 root 225: /**
1.1.1.18 root 226: * Set amount of border pixels
1.1.1.13 root 227: */
1.1.1.18 root 228: static void Screen_SetBorderPixels(int leftX, int leftY)
1.1.1.7 root 229: {
1.1.1.23 root 230: /* All screen widths need to be aligned to 16-bits */
231: nBorderPixelsLeft = Opt_ValueAlignMinMax(leftX/2, 16, 0, 48);
1.1.1.18 root 232: nBorderPixelsRight = nBorderPixelsLeft;
1.1.1.7 root 233:
1.1.1.18 root 234: /* assertain assumption of code below */
235: assert(OVERSCAN_TOP < MAX_OVERSCAN_BOTTOM);
236:
237: if (leftY > 2*OVERSCAN_TOP)
1.1.1.13 root 238: {
1.1.1.18 root 239: nBorderPixelsTop = OVERSCAN_TOP;
240: if (leftY >= OVERSCAN_TOP + MAX_OVERSCAN_BOTTOM)
241: nBorderPixelsBottom = MAX_OVERSCAN_BOTTOM;
242: else
243: nBorderPixelsBottom = leftY - OVERSCAN_TOP;
1.1.1.13 root 244: }
245: else
246: {
1.1.1.18 root 247: if (leftY > 0)
248: nBorderPixelsTop = nBorderPixelsBottom = leftY/2;
249: else
250: nBorderPixelsTop = nBorderPixelsBottom = 0;
1.1.1.13 root 251: }
1.1.1.18 root 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: */
259: static void Screen_SetSTScreenOffsets(void)
260: {
261: int i;
1.1.1.13 root 262:
1.1.1.18 root 263: /* Store offset to each horizontal line, uses
264: * nBorderPixels* variables.
265: */
1.1.1.13 root 266: for (i = 0; i < NUM_VISIBLE_LINES; i++)
267: {
268: STScreenLineOffset[i] = i * SCREENBYTES_LINE;
269: }
1.1.1.7 root 270: }
271:
1.1.1.24 root 272: /**
273: * Return true if Falcon/TT/VDI generic screen convert functions
274: * need to be used instead of the ST/STE functions.
275: */
276: static bool Screen_UseGenConvScreen(void)
277: {
278: return Config_IsMachineFalcon() || Config_IsMachineTT()
279: || bUseHighRes || bUseVDIRes;
280: }
281:
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:
1.1.1.23 root 294: #if WITH_SDL2
295: static void Screen_FreeSDL2Resources(void)
296: {
297: if (sdlTexture)
298: {
299: SDL_DestroyTexture(sdlTexture);
300: sdlTexture = NULL;
301: }
302: if (sdlscrn)
303: {
1.1.1.26! root 304: if (bUseSdlRenderer)
1.1.1.25 root 305: SDL_FreeSurface(sdlscrn);
1.1.1.23 root 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: */
320: bool Screen_SetSDLVideoSize(int width, int height, int bitdepth, bool bForceChange)
321: {
322: Uint32 sdlVideoFlags;
1.1.1.26! root 323: char *psSdlVideoDriver;
! 324: bool bUseDummyMode;
1.1.1.23 root 325: #if WITH_SDL2
326: static int nPrevRenderScaleQuality = 0;
327: static bool bPrevUseVsync = false;
1.1.1.24 root 328: static bool bPrevInFullScreen;
329: int win_width, win_height;
1.1.1.23 root 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:
1.1.1.26! root 339: psSdlVideoDriver = SDL_getenv("SDL_VIDEODRIVER");
! 340: bUseDummyMode = psSdlVideoDriver && !strcmp(psSdlVideoDriver, "dummy");
! 341: if (bUseDummyMode)
! 342: {
! 343: ConfigureParams.Screen.DisableVideo = true;
! 344: }
! 345:
1.1.1.23 root 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
1.1.1.26! root 359: bUseSdlRenderer = ConfigureParams.Screen.bUseSdlRenderer && !bUseDummyMode;
1.1.1.23 root 360:
361: /* SDL Video attributes: */
1.1.1.24 root 362: win_width = width;
363: win_height = height;
1.1.1.23 root 364: if (bInFullScreen)
365: {
1.1.1.24 root 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;
1.1.1.23 root 371: }
372: else
373: {
1.1.1.24 root 374: int deskw, deskh;
1.1.1.25 root 375: if (getenv("PARENT_WIN_ID") != NULL) /* Embedded window? */
376: sdlVideoFlags = SDL_WINDOW_BORDERLESS;
1.1.1.26! root 377: else if (ConfigureParams.Screen.bResizable && bUseSdlRenderer)
1.1.1.25 root 378: sdlVideoFlags = SDL_WINDOW_RESIZABLE;
379: else
380: sdlVideoFlags = 0;
1.1.1.24 root 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;
1.1.1.23 root 387: }
388:
389: Screen_FreeSDL2Resources();
1.1.1.25 root 390: if (sdlWindow &&
391: ((bInFullScreen && !ConfigureParams.Screen.bKeepResolution) ||
392: (bPrevInFullScreen != bInFullScreen) ||
393: bForceChange
394: ))
1.1.1.24 root 395: {
396: SDL_DestroyWindow(sdlWindow);
397: sdlWindow = NULL;
398: }
399: bPrevInFullScreen = bInFullScreen;
1.1.1.23 root 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:
1.1.1.25 root 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:
1.1.1.23 root 420: /* Set new video mode */
421: DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height,
422: bitdepth, bInFullScreen?"fullscreen":"windowed"));
423:
1.1.1.24 root 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: }
1.1.1.25 root 435: if (!sdlWindow)
1.1.1.23 root 436: {
1.1.1.26! root 437: fprintf(stderr, "ERROR: Failed to create %dx%d window!\n",
1.1.1.25 root 438: win_width, win_height);
1.1.1.23 root 439: exit(-1);
440: }
1.1.1.26! root 441: if (bUseSdlRenderer)
1.1.1.23 root 442: {
443: int rm, bm, gm, pfmt;
1.1.1.24 root 444:
1.1.1.25 root 445: sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, 0);
446: if (!sdlRenderer)
447: {
1.1.1.26! root 448: fprintf(stderr, "ERROR: Failed to create %dx%d renderer!\n",
1.1.1.25 root 449: win_width, win_height);
450: exit(1);
451: }
452:
1.1.1.24 root 453: if (bInFullScreen)
454: SDL_RenderSetLogicalSize(sdlRenderer, width, height);
455:
1.1.1.23 root 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: {
1.1.1.26! root 477: fprintf(stderr, "ERROR: Failed to create %dx%d@%d texture!\n",
1.1.1.25 root 478: width, height, bitdepth);
1.1.1.23 root 479: exit(-3);
480: }
481: }
1.1.1.25 root 482: else
483: {
484: sdlscrn = SDL_GetWindowSurface(sdlWindow);
485: }
1.1.1.23 root 486:
1.1.1.26! root 487: #else /* !WITH_SDL2 */
1.1.1.23 root 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 */
1.1.1.24 root 503: DEBUGPRINT(("SDL screen request: %d x %d @ %d (%s)\n", width, height, bitdepth,
504: bInFullScreen?"fullscreen":"windowed"));
1.1.1.23 root 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: {
1.1.1.26! root 514: Log_Printf(LOG_WARN, "Unsupported color depth 24, trying 32 bpp instead...\n");
1.1.1.23 root 515: sdlscrn = SDL_SetVideoMode(width, height, 32, sdlVideoFlags);
516: }
517:
1.1.1.26! root 518: #endif /* !WITH_SDL2 */
1.1.1.23 root 519:
520: /* Exit if we can not open a screen */
521: if (!sdlscrn)
522: {
1.1.1.26! root 523: fprintf(stderr, "ERROR: Could not set video mode:\n %s\n", SDL_GetError() );
1.1.1.23 root 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:
1.1.1.25 root 541: Avi_SetSurface(sdlscrn);
542:
543: bRGBTableInSync = false;
544:
1.1.1.23 root 545: return true;
546: }
1.1.1.8 root 547:
1.1.1.24 root 548:
1.1.1.13 root 549: /**
1.1.1.24 root 550: * Initialize ST/STE screen resolution.
1.1.1.13 root 551: */
1.1.1.24 root 552: static void Screen_SetSTResolution(bool bForceChange)
1.1.1.11 root 553: {
1.1.1.18 root 554: int Width, Height, nZoom, SBarHeight, BitCount, maxW, maxH;
555: bool bDoubleLowRes = false;
1.1.1.13 root 556:
557: /* Bits per pixel */
1.1.1.24 root 558: BitCount = ConfigureParams.Screen.nForceBpp;
1.1.1.13 root 559:
1.1.1.18 root 560: nBorderPixelsTop = nBorderPixelsBottom = 0;
561: nBorderPixelsLeft = nBorderPixelsRight = 0;
562:
563: nScreenZoomX = 1;
564: nScreenZoomY = 1;
565:
1.1.1.24 root 566: if (STRes == ST_LOW_RES)
1.1.1.13 root 567: {
1.1.1.24 root 568: Width = 320;
569: Height = 200;
570: nZoom = 1;
1.1.1.13 root 571: }
1.1.1.24 root 572: else /* else use 640x400, also for med-rez */
1.1.1.13 root 573: {
1.1.1.24 root 574: Width = 640;
575: Height = 400;
576: nZoom = 2;
577: }
1.1.1.18 root 578:
1.1.1.24 root 579: /* Statusbar height for doubled screen size */
580: SBarHeight = Statusbar_GetHeightForSize(640, 400);
1.1.1.18 root 581:
1.1.1.24 root 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));
1.1.1.13 root 620: }
1.1.1.23 root 621:
1.1.1.18 root 622: Screen_SetSTScreenOffsets();
623: Height += Statusbar_SetHeight(Width, Height);
1.1.1.13 root 624:
1.1.1.20 root 625: PCScreenOffsetX = PCScreenOffsetY = 0;
626:
1.1.1.23 root 627: /* Video attributes: */
1.1.1.24 root 628: #if !WITH_SDL2
1.1.1.23 root 629: if (bInFullScreen && ConfigureParams.Screen.bKeepResolutionST)
1.1.1.13 root 630: {
1.1.1.23 root 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;
1.1.1.13 root 642: }
1.1.1.24 root 643: #endif
1.1.1.13 root 644:
1.1.1.23 root 645: if (Screen_SetSDLVideoSize(Width, Height, BitCount, bForceChange))
1.1.1.13 root 646: {
1.1.1.15 root 647: Statusbar_Init(sdlscrn);
1.1.1.23 root 648:
1.1.1.15 root 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();
1.1.1.13 root 654: }
655:
1.1.1.25 root 656: if (!bRGBTableInSync)
657: {
658: Screen_SetupRGBTable(); /* Create color conversion table */
659: bRGBTableInSync = true;
660: }
661:
1.1.1.13 root 662: /* Set drawing functions */
1.1.1.18 root 663: Screen_SetDrawFunctions(sdlscrn->format->BitsPerPixel, bDoubleLowRes);
1.1.1.13 root 664:
665: Screen_SetFullUpdate(); /* Cause full update of screen */
1.1.1.11 root 666: }
667:
668:
1.1.1.24 root 669: /**
670: * Change resolution, according to the machine and display type
671: * that we're currently emulating.
672: */
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: {
1.1.1.25 root 683: Videl_ScreenModeChanged(bForceChange);
1.1.1.24 root 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: */
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:
1.1.1.11 root 719: /*-----------------------------------------------------------------------*/
1.1.1.13 root 720: /**
721: * Init Screen bitmap and buffers/tables needed for ST to PC screen conversion
722: */
1.1 root 723: void Screen_Init(void)
724: {
1.1.1.13 root 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: {
1.1.1.21 root 735: FrameBuffers[i].pSTScreen = malloc(MAX_VDI_BYTES);
736: FrameBuffers[i].pSTScreenCopy = malloc(MAX_VDI_BYTES);
1.1.1.13 root 737: if (!FrameBuffers[i].pSTScreen || !FrameBuffers[i].pSTScreenCopy)
738: {
1.1.1.26! root 739: fprintf(stderr, "ERROR: Failed to allocate frame buffer memory.\n");
1.1.1.13 root 740: exit(-1);
741: }
742: }
743: pFrameBuffer = &FrameBuffers[0];
744:
1.1.1.25 root 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:
1.1.1.13 root 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: {
1.1.1.23 root 758: #if WITH_SDL2
759: SDL_SetColorKey(pIconSurf, SDL_TRUE, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
760: SDL_SetWindowIcon(sdlWindow, pIconSurf);
761: #else
1.1.1.13 root 762: SDL_SetColorKey(pIconSurf, SDL_SRCCOLORKEY, SDL_MapRGB(pIconSurf->format, 255, 255, 255));
763: SDL_WM_SetIcon(pIconSurf, NULL);
1.1.1.23 root 764: #endif
1.1.1.13 root 765: SDL_FreeSurface(pIconSurf);
766: }
767:
768: /* Configure some SDL stuff: */
769: SDL_ShowCursor(SDL_DISABLE);
1.1 root 770: }
771:
1.1.1.2 root 772:
1.1 root 773: /*-----------------------------------------------------------------------*/
1.1.1.13 root 774: /**
775: * Free screen bitmap and allocated resources
776: */
1.1 root 777: void Screen_UnInit(void)
778: {
1.1.1.13 root 779: int i;
1.1 root 780:
1.1.1.13 root 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: }
1.1.1.23 root 787:
788: #if WITH_SDL2
789: Screen_FreeSDL2Resources();
1.1.1.24 root 790: if (sdlWindow)
791: {
792: SDL_DestroyWindow(sdlWindow);
793: sdlWindow = NULL;
794: }
1.1.1.23 root 795: #endif
1.1 root 796: }
797:
1.1.1.2 root 798:
1.1 root 799: /*-----------------------------------------------------------------------*/
1.1.1.13 root 800: /**
801: * Reset screen
802: */
1.1 root 803: void Screen_Reset(void)
804: {
1.1.1.13 root 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 */
1.1.1.23 root 824: Screen_ModeChanged(false);
1.1 root 825: }
826:
827:
828: /*-----------------------------------------------------------------------*/
1.1.1.13 root 829: /**
830: * Set flags so screen will be TOTALLY re-drawn (clears whole of full-screen)
831: * next time around
832: */
1.1 root 833: void Screen_SetFullUpdate(void)
834: {
1.1.1.13 root 835: int i;
1.1 root 836:
1.1.1.13 root 837: /* Update frame buffers */
838: for (i = 0; i < NUM_FRAMEBUFFERS; i++)
1.1.1.17 root 839: FrameBuffers[i].bFullUpdate = true;
1.1 root 840: }
841:
1.1.1.5 root 842:
1.1 root 843: /*-----------------------------------------------------------------------*/
1.1.1.13 root 844: /**
845: * Clear Window display memory
846: */
1.1.1.11 root 847: static void Screen_ClearScreen(void)
848: {
1.1.1.15 root 849: SDL_FillRect(sdlscrn, &STScreenRect, SDL_MapRGB(sdlscrn->format, 0, 0, 0));
1.1.1.11 root 850: }
851:
1.1.1.18 root 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: */
859: static void Screen_Refresh(void)
860: {
1.1.1.24 root 861: if (bUseVDIRes)
1.1.1.18 root 862: {
1.1.1.24 root 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);
1.1.1.18 root 877: }
878: }
879:
880:
1.1.1.11 root 881: /*-----------------------------------------------------------------------*/
1.1.1.13 root 882: /**
883: * Enter Full screen mode
884: */
1.1 root 885: void Screen_EnterFullScreen(void)
886: {
1.1.1.18 root 887: bool bWasRunning;
888:
1.1.1.13 root 889: if (!bInFullScreen)
890: {
1.1.1.18 root 891: /* Hold things... */
892: bWasRunning = Main_PauseEmulation(false);
1.1.1.17 root 893: bInFullScreen = true;
1.1.1.2 root 894:
1.1.1.24 root 895: if (Screen_UseGenConvScreen())
1.1.1.13 root 896: {
1.1.1.24 root 897: Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
898: genconv_bpp, true);
899: /* force screen redraw */
900: Screen_GenConvUpdate(NULL, true);
1.1.1.13 root 901: }
902: else
903: {
1.1.1.24 root 904: Screen_SetSTResolution(true);
1.1.1.13 root 905: Screen_ClearScreen(); /* Black out screen bitmap as will be invalid when return */
906: }
1.1 root 907:
1.1.1.24 root 908: if (!Screen_WantToKeepResolution())
909: {
910: /* Give monitor time to change to new resolution */
911: SDL_Delay(20);
912: }
913:
1.1.1.18 root 914: if (bWasRunning)
915: {
916: /* And off we go... */
917: Main_UnPauseEmulation();
918: }
919: else
920: {
921: Screen_Refresh();
922: }
1.1.1.13 root 923: SDL_WM_GrabInput(SDL_GRAB_ON); /* Grab mouse pointer in fullscreen */
924: }
1.1 root 925: }
926:
1.1.1.2 root 927:
1.1 root 928: /*-----------------------------------------------------------------------*/
1.1.1.13 root 929: /**
930: * Return from Full screen mode back to a window
931: */
1.1 root 932: void Screen_ReturnFromFullScreen(void)
933: {
1.1.1.18 root 934: bool bWasRunning;
935:
1.1.1.13 root 936: if (bInFullScreen)
937: {
1.1.1.18 root 938: /* Hold things... */
939: bWasRunning = Main_PauseEmulation(false);
1.1.1.17 root 940: bInFullScreen = false;
1.1.1.13 root 941:
1.1.1.24 root 942: if (Screen_UseGenConvScreen())
1.1.1.13 root 943: {
1.1.1.24 root 944: Screen_SetGenConvSize(genconv_width_req, genconv_height_req,
945: genconv_bpp, true);
946: /* force screen redraw */
947: Screen_GenConvUpdate(NULL, true);
1.1.1.13 root 948: }
949: else
950: {
1.1.1.24 root 951: Screen_SetSTResolution(true);
952: }
953:
954: if (!Screen_WantToKeepResolution())
955: {
956: /* Give monitor time to switch resolution */
957: SDL_Delay(20);
1.1.1.13 root 958: }
1.1.1.18 root 959:
960: if (bWasRunning)
961: {
962: /* And off we go... */
963: Main_UnPauseEmulation();
964: }
965: else
966: {
967: Screen_Refresh();
968: }
1.1.1.17 root 969:
970: if (!bGrabMouse)
1.1.1.18 root 971: {
1.1.1.17 root 972: /* Un-grab mouse pointer in windowed mode */
973: SDL_WM_GrabInput(SDL_GRAB_OFF);
1.1.1.18 root 974: }
1.1.1.13 root 975: }
976: }
1.1.1.2 root 977:
978:
1.1.1.13 root 979: /*-----------------------------------------------------------------------*/
980: /**
981: * Have we changed between low/med/high res?
982: */
983: static void Screen_DidResolutionChange(int new_res)
984: {
985: if (new_res != STRes)
986: {
987: STRes = new_res;
1.1.1.23 root 988: Screen_ModeChanged(false);
1.1.1.13 root 989: }
990: else
991: {
992: /* Did change overscan mode? Causes full update */
1.1.1.24 root 993: if (pFrameBuffer->VerticalOverscanCopy != VerticalOverscan)
1.1.1.17 root 994: pFrameBuffer->bFullUpdate = true;
1.1.1.13 root 995: }
1.1 root 996: }
997:
1.1.1.5 root 998:
1.1.1.13 root 999: /**
1000: * Compare current resolution on line with previous, and set 'UpdateLine' accordingly
1001: * Return if swap between low/medium resolution
1002: */
1.1.1.15 root 1003: static bool Screen_CompareResolution(int y, int *pUpdateLine, int oldres)
1.1 root 1004: {
1.1.1.13 root 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: }
1.1.1.17 root 1017: return false;
1.1 root 1018: }
1019:
1.1.1.7 root 1020:
1.1.1.2 root 1021: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1022: /**
1023: * Check to see if palette changes cause screen update and keep 'HBLPalette[]' up-to-date
1024: */
1.1.1.8 root 1025: static void Screen_ComparePalette(int y, int *pUpdateLine)
1.1 root 1026: {
1.1.1.17 root 1027: bool bPaletteChanged = false;
1.1.1.13 root 1028: int i;
1.1 root 1029:
1.1.1.13 root 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])
1.1.1.17 root 1043: bPaletteChanged = true;
1.1.1.13 root 1044: }
1045: if (bPaletteChanged)
1046: *pUpdateLine |= PALETTEMASK_UPDATEPAL;
1047: else
1048: *pUpdateLine &= ~PALETTEMASK_UPDATEPAL;
1049: }
1.1 root 1050: }
1051:
1.1.1.7 root 1052:
1.1.1.2 root 1053: /*-----------------------------------------------------------------------*/
1.1.1.13 root 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: */
1061: static int Screen_ComparePaletteMask(int res)
1.1 root 1062: {
1.1.1.17 root 1063: bool bLowMedMix = false;
1.1.1.13 root 1064: int LineUpdate = 0;
1065: int y;
1066:
1067: /* Set for monochrome? */
1068: if (bUseHighRes)
1069: {
1.1.1.24 root 1070: VerticalOverscan = V_OVERSCAN_NONE;
1.1.1.13 root 1071:
1.1.1.24 root 1072: /* Just copy mono colors */
1.1.1.13 root 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])
1.1.1.17 root 1086: pFrameBuffer->bFullUpdate = true;
1.1.1.13 root 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:
1.1.1.24 root 1121: /* Copy old palette for compare */
1122: memcpy(PrevHBLPalette, HBLPalettes, sizeof(Uint16)*16);
1123:
1.1.1.13 root 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: */
1.1.1.8 root 1133: static void Screen_SetFullUpdateMask(void)
1.1 root 1134: {
1.1.1.13 root 1135: int y;
1.1 root 1136:
1.1.1.13 root 1137: for (y = 0; y < NUM_VISIBLE_LINES; y++)
1138: HBLPaletteMasks[y] |= PALETTEMASK_UPDATEFULL;
1.1 root 1139: }
1140:
1.1.1.7 root 1141:
1.1.1.2 root 1142: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1143: /**
1144: * Set details for ST screen conversion.
1145: */
1.1.1.10 root 1146: static void Screen_SetConvertDetails(void)
1.1 root 1147: {
1.1.1.13 root 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 */
1.1.1.20 root 1153:
1154: /* Center to available framebuffer */
1155: pPCScreenDest += PCScreenOffsetY * PCScreenBytesPerLine + PCScreenOffsetX * (sdlscrn->format->BitsPerPixel/8);
1156:
1.1.1.13 root 1157: pHBLPalettes = pFrameBuffer->HBLPalettes; /* HBL palettes pointer */
1158: /* Not in TV-Mode? Then double up on Y: */
1.1.1.15 root 1159: bScrDoubleY = !(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_TV);
1.1.1.13 root 1160:
1.1.1.24 root 1161: if (ConfigureParams.Screen.bAllowOverscan) /* Use borders? */
1.1.1.13 root 1162: {
1.1.1.24 root 1163: /* Always draw to WHOLE screen including ALL borders */
1164: STScreenLeftSkipBytes = 0; /* Number of bytes to skip on ST screen for left (border) */
1.1.1.13 root 1165:
1.1.1.24 root 1166: if (bUseHighRes)
1167: {
1168: pFrameBuffer->VerticalOverscanCopy = VerticalOverscan = V_OVERSCAN_NONE;
1169: STScreenStartHorizLine = 0;
1170: STScreenEndHorizLine = 400;
1.1.1.13 root 1171: }
1172: else
1173: {
1.1.1.24 root 1174: STScreenWidthBytes = SCREENBYTES_LINE; /* Number of horizontal bytes in our ST screen */
1175: STScreenStartHorizLine = OVERSCAN_TOP - nBorderPixelsTop;
1176: STScreenEndHorizLine = OVERSCAN_TOP + 200 + nBorderPixelsBottom;
1.1.1.13 root 1177: }
1178: }
1.1.1.24 root 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: }
1.1 root 1187: }
1188:
1.1.1.2 root 1189:
1190: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1191: /**
1192: * Lock full-screen for drawing
1193: */
1.1.1.24 root 1194: bool Screen_Lock(void)
1.1 root 1195: {
1.1.1.13 root 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 */
1.1.1.17 root 1201: return false;
1.1.1.13 root 1202: }
1203: }
1.1.1.2 root 1204:
1.1.1.17 root 1205: return true;
1.1 root 1206: }
1207:
1.1.1.2 root 1208: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1209: /**
1210: * UnLock full-screen
1211: */
1.1.1.24 root 1212: void Screen_UnLock(void)
1.1 root 1213: {
1.1.1.13 root 1214: if ( SDL_MUSTLOCK(sdlscrn) )
1215: SDL_UnlockSurface(sdlscrn);
1.1 root 1216: }
1217:
1.1.1.2 root 1218:
1219: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1220: /**
1221: * Blit our converted ST screen to window/full-screen
1222: */
1.1.1.22 root 1223: static void Screen_Blit(SDL_Rect *sbar_rect)
1.1 root 1224: {
1.1.1.13 root 1225: unsigned char *pTmpScreen;
1226:
1.1.1.18 root 1227: #if 0 /* double buffering cannot be used with partial screen updates */
1228: # if NUM_FRAMEBUFFERS > 1
1229: if (bInFullScreen && (sdlscrn->flags & SDL_DOUBLEBUF))
1.1.1.13 root 1230: {
1231: /* Swap screen */
1.1.1.18 root 1232: if (pFrameBuffer==&FrameBuffers[0])
1233: pFrameBuffer = &FrameBuffers[1];
1234: else
1235: pFrameBuffer = &FrameBuffers[0];
1.1.1.15 root 1236: SDL_Flip(sdlscrn);
1.1.1.13 root 1237: }
1238: else
1.1.1.18 root 1239: # endif
1240: #endif
1.1.1.13 root 1241: {
1.1.1.22 root 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);
1.1.1.13 root 1251: }
1252:
1253: /* Swap copy/raster buffers in screen. */
1254: pTmpScreen = pFrameBuffer->pSTScreenCopy;
1255: pFrameBuffer->pSTScreenCopy = pFrameBuffer->pSTScreen;
1256: pFrameBuffer->pSTScreen = pTmpScreen;
1.1 root 1257: }
1258:
1.1.1.3 root 1259:
1.1.1.2 root 1260: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1261: /**
1262: * Draw ST screen to window/full-screen framebuffer
1.1.1.15 root 1263: * @param bForceFlip Force screen update, even if contents did not change
1.1.1.17 root 1264: * @return true if screen contents changed
1.1.1.13 root 1265: */
1.1.1.15 root 1266: static bool Screen_DrawFrame(bool bForceFlip)
1.1 root 1267: {
1.1.1.13 root 1268: int new_res;
1269: void (*pDrawFunction)(void);
1.1.1.17 root 1270: static bool bPrevFrameWasSpec512 = false;
1.1.1.22 root 1271: SDL_Rect *sbar_rect;
1.1.1.13 root 1272:
1.1.1.24 root 1273: assert(!bUseVDIRes);
1274:
1.1.1.13 root 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:
1.1.1.15 root 1283: /* restore area potentially left under overlay led
1284: * and saved by Statusbar_OverlayBackup()
1285: */
1286: Statusbar_OverlayRestore(sdlscrn);
1.1.1.22 root 1287:
1288: /* Lock screen for direct screen surface format writes */
1.1.1.26! root 1289: if (ConfigureParams.Screen.DisableVideo || !Screen_Lock())
1.1.1.13 root 1290: {
1.1.1.26! root 1291: return false;
! 1292: }
1.1.1.15 root 1293:
1.1.1.26! root 1294: bScreenContentsChanged = false; /* Did change (ie needs blit?) */
1.1.1.24 root 1295:
1.1.1.26! root 1296: /* Set details */
! 1297: Screen_SetConvertDetails();
1.1.1.13 root 1298:
1.1.1.26! root 1299: /* Clear screen on full update to clear out borders and also interleaved lines */
! 1300: if (pFrameBuffer->bFullUpdate)
! 1301: Screen_ClearScreen();
1.1.1.13 root 1302:
1.1.1.26! root 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;
1.1.1.13 root 1330: }
1.1.1.15 root 1331:
1.1.1.26! root 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;
1.1 root 1353: }
1354:
1.1.1.9 root 1355:
1.1.1.2 root 1356: /*-----------------------------------------------------------------------*/
1.1.1.13 root 1357: /**
1358: * Draw ST screen to window/full-screen
1359: */
1.1.1.15 root 1360: bool Screen_Draw(void)
1.1 root 1361: {
1.1.1.25 root 1362: if (bQuitProgram)
1.1.1.13 root 1363: {
1.1.1.24 root 1364: return false;
1.1.1.13 root 1365: }
1.1.1.15 root 1366:
1.1.1.24 root 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: */
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) {
1.1.1.26! root 1395: Log_Printf(LOG_WARN, "Too large screen size %dx%d -> divided by %dx%d!\n",
1.1.1.24 root 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) {
1.1.1.26! root 1419: Log_Printf(LOG_WARN, "Strange screen size %dx%d -> aspect corrected by %dx%d!\n",
1.1.1.24 root 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:
1512: void Screen_GenConvUpdate(SDL_Rect *extra, bool forced)
1513: {
1514: SDL_Rect rects[2];
1515: int count = 1;
1516:
1.1.1.25 root 1517: /* Don't update anything on screen if video output is disabled */
1518: if ( ConfigureParams.Screen.DisableVideo )
1519: return;
1520:
1.1.1.24 root 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:
1532: Uint32 Screen_GetGenConvWidth(void)
1533: {
1534: return STScreenRect.w;
1535: }
1536:
1537: Uint32 Screen_GetGenConvHeight(void)
1538: {
1539: return STScreenRect.h;
1.1 root 1540: }
1.1.1.11 root 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:
1.1.1.13 root 1559: /*-----------------------------------------------------------------------*/
1560: /**
1.1.1.11 root 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: */
1566: static int AdjustLinePaletteRemap(int y)
1567: {
1568: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.13 root 1569: static const int endiantable[16] = {0,2,1,3,8,10,9,11,4,6,5,7,12,14,13,15};
1.1.1.11 root 1570: #endif
1.1.1.13 root 1571: Uint16 *actHBLPal;
1572: int i;
1.1.1.11 root 1573:
1.1.1.13 root 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: {
1.1.1.11 root 1578: #if SDL_BYTEORDER == SDL_BIG_ENDIAN
1.1.1.13 root 1579: STRGBPalette[endiantable[i]] = ST2RGB[*actHBLPal++];
1.1.1.11 root 1580: #else
1.1.1.13 root 1581: STRGBPalette[i] = ST2RGB[*actHBLPal++];
1.1.1.11 root 1582: #endif
1.1.1.13 root 1583: }
1584: ScrUpdateFlag = HBLPaletteMasks[y];
1585: return ScrUpdateFlag;
1.1.1.11 root 1586: }
1587:
1588:
1.1.1.13 root 1589: /*-----------------------------------------------------------------------*/
1590: /**
1.1.1.11 root 1591: * Run updates to palette(STRGBPalette[]) until get to screen line
1592: * we are to convert from
1593: */
1594: static void Convert_StartFrame(void)
1595: {
1.1.1.13 root 1596: int y = 0;
1597: /* Get #lines before conversion starts */
1598: int lines = STScreenStartHorizLine;
1599: while (lines--)
1600: AdjustLinePaletteRemap(y++); /* Update palette */
1.1.1.11 root 1601: }
1602:
1603: /* lookup tables and conversion macros */
1604: #include "convert/macros.h"
1605:
1606: /* Conversion routines */
1.1.1.13 root 1607:
1.1.1.19 root 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 */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.