|
|
1.1 root 1: /*
2: Hatari - screen.c
3:
4: This file is distributed under the GNU Public License, version 2 or at your
5: option any later version. Read the file gpl.txt for details.
6:
1.1.1.2 root 7: (SC) Simon Schubiger - most of it rewritten for Previous NeXT emulator
1.1 root 8: */
9:
10: #include <SDL.h>
11: #include <SDL_endian.h>
12: #include <SDL_blendmode.h>
13:
14: const char Screen_fileid[] = "Previous fast_screen.c : " __DATE__ " " __TIME__;
15:
16: #include "main.h"
17: #include "configuration.h"
18: #include "log.h"
19: #include "m68000.h"
1.1.1.3 ! root 20: #include "dimension.hpp"
! 21: #include "nd_mem.hpp"
1.1 root 22: #include "paths.h"
23: #include "screen.h"
24: #include "statusbar.h"
25: #include "video.h"
26:
1.1.1.2 root 27: SDL_Window* sdlWindow;
28: SDL_Surface* sdlscrn = NULL; /* The SDL screen surface */
29: int nScreenZoomX, nScreenZoomY; /* Zooming factors, used for scaling mouse motions */
30:
31: /* extern for shortcuts */
32: volatile bool bGrabMouse = false; /* Grab the mouse cursor in the window */
33: volatile bool bInFullScreen = false; /* true if in full screen */
34:
35: static const int NeXT_SCRN_WIDTH = 1120;
36: static const int NeXT_SCRN_HEIGHT = 832;
1.1 root 37:
38: static SDL_Thread* repaintThread;
1.1.1.2 root 39: static SDL_Renderer* sdlRenderer;
1.1 root 40: static SDL_sem* initLatch;
1.1.1.2 root 41: static SDL_atomic_t blitFB;
42: static SDL_atomic_t blitUI; /* When value == 1, the repaint thread will blit the sldscrn surface to the screen on the next redraw */
1.1 root 43: static bool doUIblit;
44: static SDL_Rect saveWindowBounds; /* Window bounds before going fullscreen. Used to restore window size & position. */
45: static void* uiBuffer; /* uiBuffer used for ui texture */
1.1.1.2 root 46: static void* uiBufferTmp; /* Temporary uiBuffer used by repainter */
47: static SDL_SpinLock uiBufferLock; /* Lock for concurrent access to UI buffer between m68k thread and repainter */
1.1 root 48: static Uint32 mask; /* green screen mask for transparent UI areas */
49: static volatile bool doRepaint = true; /* Repaint thread runs while true */
1.1.1.2 root 50: static SDL_Rect statusBar;
51:
1.1 root 52:
53: static Uint32 BW2RGB[0x400];
54: static Uint32 COL2RGB[0x10000];
55:
56: static Uint32 bw2rgb(SDL_PixelFormat* format, int bw) {
57: switch(bw & 3) {
58: case 3: return SDL_MapRGB(format, 0, 0, 0);
59: case 2: return SDL_MapRGB(format, 85, 85, 85);
60: case 1: return SDL_MapRGB(format, 170, 170, 170);
61: case 0: return SDL_MapRGB(format, 255, 255, 255);
62: default: return 0;
63: }
64: }
65:
66: static Uint32 col2rgb(SDL_PixelFormat* format, int col) {
67: int r = col & 0xF000; r >>= 12; r |= r << 4;
68: int g = col & 0x0F00; g >>= 8; g |= g << 4;
69: int b = col & 0x00F0; b >>= 4; b |= b << 4;
70: return SDL_MapRGB(format, r, g, b);
71: }
72:
73: /*
74: BW format is 2bit per pixel
75: */
76: static void blitBW(SDL_Texture* tex) {
77: void* pixels;
78: int d;
1.1.1.2 root 79: int pitch = (NeXT_SCRN_WIDTH + (ConfigureParams.System.bTurbo ? 0 : 32)) / 4;
1.1 root 80: SDL_LockTexture(tex, NULL, &pixels, &d);
81: Uint32* dst = (Uint32*)pixels;
1.1.1.2 root 82: for(int y = 0; y < NeXT_SCRN_HEIGHT; y++) {
1.1 root 83: int src = y * pitch;
1.1.1.2 root 84: for(int x = 0; x < NeXT_SCRN_WIDTH/4; x++, src++) {
1.1 root 85: int idx = NEXTVideo[src] * 4;
86: *dst++ = BW2RGB[idx+0];
87: *dst++ = BW2RGB[idx+1];
88: *dst++ = BW2RGB[idx+2];
89: *dst++ = BW2RGB[idx+3];
90: }
91: }
92: SDL_UnlockTexture(tex);
93: }
94:
95: /*
96: Color format is 4bit per pixel, big-endian: RGBx
97: */
98: static void blitColor(SDL_Texture* tex) {
99: void* pixels;
100: int d;
1.1.1.2 root 101: int pitch = NeXT_SCRN_WIDTH + (ConfigureParams.System.bTurbo ? 0 : 32);
1.1 root 102: SDL_LockTexture(tex, NULL, &pixels, &d);
103: Uint32* dst = (Uint32*)pixels;
1.1.1.2 root 104: for(int y = 0; y < NeXT_SCRN_HEIGHT; y++) {
1.1 root 105: Uint16* src = (Uint16*)NEXTColorVideo + (y*pitch);
1.1.1.2 root 106: for(int x = 0; x < NeXT_SCRN_WIDTH; x++) {
1.1 root 107: *dst++ = COL2RGB[*src++];
108: }
109: }
110: SDL_UnlockTexture(tex);
111: }
112:
113: /*
1.1.1.2 root 114: Dimension format is 8bit per pixel, big-endian: RRGGBBAA
1.1 root 115: */
1.1.1.3 ! root 116: void blitDimension(Uint32* vram, SDL_Texture* tex) {
1.1.1.2 root 117: #if ND_STEP
1.1.1.3 ! root 118: Uint32* src = &vram[0];
1.1.1.2 root 119: #else
1.1.1.3 ! root 120: Uint32* src = &vram[16];
1.1.1.2 root 121: #endif
1.1 root 122: int d;
123: Uint32 format;
124: SDL_QueryTexture(tex, &format, &d, &d, &d);
125: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
1.1.1.2 root 126: /* Add big-endian accelerated blit loops as needed here */
1.1 root 127: switch (format) {
128: default: {
1.1.1.3 ! root 129: void* pixels;
! 130: SDL_LockTexture(tex, NULL, &pixels, &d);
! 131: Uint32* dst = (Uint32*)pixels;
! 132:
1.1 root 133: /* fallback to SDL_MapRGB */
134: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
1.1.1.2 root 135: for(int y = NeXT_SCRN_HEIGHT; --y >= 0;) {
136: for(int x = NeXT_SCRN_WIDTH; --x >= 0;) {
1.1 root 137: Uint32 v = *src++;
1.1.1.3 ! root 138: *dst++ = SDL_MapRGB(pformat, (v >> 8) & 0xFF, (v>>16) & 0xFF, (v>>24) & 0xFF);
1.1 root 139: }
1.1.1.2 root 140: src += 32;
1.1 root 141: }
142: SDL_FreeFormat(pformat);
1.1.1.3 ! root 143: SDL_UnlockTexture(tex);
1.1 root 144: break;
145: }
146: }
147: } else {
1.1.1.2 root 148: /* Add little-endian accelerated blit loops as needed here */
1.1 root 149: switch (format) {
1.1.1.3 ! root 150: case SDL_PIXELFORMAT_ARGB8888: {
! 151: SDL_UpdateTexture(tex, NULL, src, (NeXT_SCRN_WIDTH+32)*4);
1.1 root 152: break;
1.1.1.3 ! root 153: }
1.1 root 154: default: {
1.1.1.3 ! root 155: void* pixels;
! 156: SDL_LockTexture(tex, NULL, &pixels, &d);
! 157: Uint32* dst = (Uint32*)pixels;
! 158:
1.1 root 159: /* fallback to SDL_MapRGB */
160: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
1.1.1.2 root 161: for(int y = NeXT_SCRN_HEIGHT; --y >= 0;) {
162: for(int x = NeXT_SCRN_WIDTH; --x >= 0;) {
1.1.1.3 ! root 163: Uint32 v = *src++;
! 164: *dst++ = SDL_MapRGB(pformat, (v >> 16) & 0xFF, (v>>8) & 0xFF, (v>>0) & 0xFF);
1.1 root 165: }
1.1.1.2 root 166: src += 32;
1.1 root 167: }
168: SDL_FreeFormat(pformat);
1.1.1.3 ! root 169: SDL_UnlockTexture(tex);
1.1 root 170: break;
171: }
172: }
173: }
174: }
175:
176: /*
177: Blit NeXT framebuffer to texture.
178: */
179: static void blitScreen(SDL_Texture* tex) {
180: if (ConfigureParams.Screen.nMonitorType==MONITOR_TYPE_DIMENSION) {
1.1.1.3 ! root 181: Uint32* vram = nd_vram_for_slot(ND_SLOT(ConfigureParams.Screen.nMonitorNum));
! 182: if(vram)
! 183: blitDimension(vram, tex);
1.1 root 184: return;
185: }
186: if(ConfigureParams.System.bColor) {
187: blitColor(tex);
188: } else {
189: blitBW(tex);
190: }
191: }
192:
193: /*
1.1.1.2 root 194: Initializes SDL graphics and then enters repaint loop.
195: Loop: Blits the NeXT framebuffer to the fbTexture, blends with the GUI surface and
1.1 root 196: shows it.
197: */
198: static int repainter(void* unused) {
199: int width;
200: int height;
201:
1.1.1.2 root 202: SDL_SetThreadPriority(SDL_THREAD_PRIORITY_NORMAL);
1.1 root 203: SDL_GetWindowSize(sdlWindow, &width, &height);
204:
1.1.1.2 root 205: statusBar.x = 0;
206: statusBar.y = NeXT_SCRN_HEIGHT;
207: statusBar.w = width;
208: statusBar.h = height - NeXT_SCRN_HEIGHT;
209:
1.1 root 210: SDL_Texture* uiTexture;
211: SDL_Texture* fbTexture;
212:
213: Uint32 r, g, b, a;
214:
215: sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
216: SDL_RenderSetLogicalSize(sdlRenderer, width, height);
1.1.1.2 root 217:
1.1 root 218: uiTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height);
219: SDL_SetTextureBlendMode(uiTexture, SDL_BLENDMODE_BLEND);
220:
221: fbTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height);
222: SDL_SetTextureBlendMode(fbTexture, SDL_BLENDMODE_NONE);
223:
224: Uint32 format;
225: int d;
226: SDL_QueryTexture(uiTexture, &format, &d, &d, &d);
227: SDL_PixelFormatEnumToMasks(format, &d, &r, &g, &b, &a);
228: mask = g | a;
1.1.1.2 root 229: sdlscrn = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, r, g, b, a);
230: uiBuffer = malloc(sdlscrn->h * sdlscrn->pitch);
231: uiBufferTmp = malloc(sdlscrn->h * sdlscrn->pitch);
1.1 root 232: // clear UI with mask
233: SDL_FillRect(sdlscrn, NULL, mask);
234:
235: /* Exit if we can not open a screen */
236: if (!sdlscrn) {
237: fprintf(stderr, "Could not set video mode:\n %s\n", SDL_GetError() );
238: SDL_Quit();
239: exit(-2);
240: }
241:
242: Statusbar_Init(sdlscrn);
243:
244: if (bGrabMouse) {
245: SDL_SetRelativeMouseMode(SDL_TRUE);
246: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
247: }
248:
249: /* Configure some SDL stuff: */
250: SDL_ShowCursor(SDL_DISABLE);
251:
252: /* Setup lookup tables */
253: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
254: /* initialize BW lookup table */
255: for(int i = 0; i < 0x100; i++) {
256: BW2RGB[i*4+0] = bw2rgb(pformat, i>>6);
257: BW2RGB[i*4+1] = bw2rgb(pformat, i>>4);
258: BW2RGB[i*4+2] = bw2rgb(pformat, i>>2);
259: BW2RGB[i*4+3] = bw2rgb(pformat, i>>0);
260: }
261: /* initialize color lookup table */
262: for(int i = 0; i < 0x10000; i++)
263: COL2RGB[SDL_BYTEORDER == SDL_BIG_ENDIAN ? i : SDL_Swap16(i)] = col2rgb(pformat, i);
264:
265: /* Initialization done -> signal */
266: SDL_SemPost(initLatch);
267:
1.1.1.2 root 268: /* Start with framebuffer blit enabled */
269: SDL_AtomicSet(&blitFB, 1);
270:
1.1 root 271: /* Enter repaint loop */
272: while(doRepaint) {
1.1.1.2 root 273: bool updateFB = false;
274: bool updateUI = false;
1.1 root 275:
1.1.1.2 root 276: if (SDL_AtomicGet(&blitFB)) {
277: // Blit the NeXT framebuffer to textrue
278: blitScreen(fbTexture);
279: updateFB = true;
280: }
1.1 root 281:
282: // Copy UI surface to texture
1.1.1.2 root 283: SDL_AtomicLock(&uiBufferLock);
1.1 root 284: if(SDL_AtomicSet(&blitUI, 0)) {
1.1.1.2 root 285: // update full UI texture
286: memcpy(uiBufferTmp, uiBuffer, sdlscrn->h * sdlscrn->pitch);
287: updateUI = true;
288: }
289: SDL_AtomicUnlock(&uiBufferLock);
290:
291: if(updateUI) {
292: SDL_UpdateTexture(uiTexture, NULL, uiBufferTmp, sdlscrn->pitch);
1.1 root 293: }
294:
1.1.1.2 root 295: // Update and render UI texture
296: if (updateFB || updateUI) {
297: SDL_RenderClear(sdlRenderer);
298: // Render NeXT framebuffer texture
299: SDL_RenderCopy(sdlRenderer, fbTexture, NULL, NULL);
300: SDL_RenderCopy(sdlRenderer, uiTexture, NULL, NULL);
301: // SDL_RenderPresent sleeps until next VSYNC because of SDL_RENDERER_PRESENTVSYNC in ScreenInit
302: SDL_RenderPresent(sdlRenderer);
303: } else {
304: host_sleep_ms(10);
305: }
1.1 root 306: }
307: return 0;
308: }
309:
310: /*-----------------------------------------------------------------------*/
311: /**
1.1.1.2 root 312: * Pause Screen, pauses or resumes drawing of NeXT framebuffer
313: */
314: void Screen_Pause(bool pause) {
315: if (pause) {
316: SDL_AtomicSet(&blitFB, 0);
317: } else {
318: SDL_AtomicSet(&blitFB, 1);
319: }
320: }
321:
322: /*-----------------------------------------------------------------------*/
323: /**
324: * Init Screen, creates window and starts repaint thread
1.1 root 325: */
326: void Screen_Init(void) {
327: /* Set initial window resolution */
328: bInFullScreen = ConfigureParams.Screen.bFullScreen;
329: nScreenZoomX = 1;
330: nScreenZoomY = 1;
331:
1.1.1.2 root 332: int width = NeXT_SCRN_WIDTH;
333: int height = NeXT_SCRN_HEIGHT;
334:
335: /* Statusbar height */
1.1 root 336: height += Statusbar_SetHeight(width, height);
337:
338: /* Set new video mode */
339: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
340:
1.1.1.2 root 341: fprintf(stderr, "SDL screen request: %d x %d (%s)\n", width, height, bInFullScreen ? "fullscreen" : "windowed");
1.1 root 342:
343: int x = SDL_WINDOWPOS_UNDEFINED;
344: if(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_DUAL) {
345: for(int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
346: SDL_Rect r;
347: SDL_GetDisplayBounds(i, &r);
348: if(r.w >= width * 2) {
349: x = r.x + width + ((r.w - width * 2) / 2);
350: break;
351: }
352: if(r.x >= 0 && SDL_GetNumVideoDisplays() == 1) x = r.x + 8;
353: }
354: }
355: sdlWindow = SDL_CreateWindow(PROG_NAME, x, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
356: if (!sdlWindow) {
357: fprintf(stderr,"Failed to create window: %s!\n", SDL_GetError());
358: exit(-1);
359: }
360:
361: initLatch = SDL_CreateSemaphore(0);
362: repaintThread = SDL_CreateThread(repainter, "[Previous] screen repaint", NULL);
363: SDL_SemWait(initLatch);
364: }
365:
1.1.1.2 root 366: void nd_sdl_destroy(void);
1.1 root 367:
368: /*-----------------------------------------------------------------------*/
369: /**
370: * Free screen bitmap and allocated resources
371: */
372: void Screen_UnInit(void) {
373: doRepaint = false; // stop repaint thread
374: int s;
375: SDL_WaitThread(repaintThread, &s);
376: nd_sdl_destroy();
377: }
378:
379: /*-----------------------------------------------------------------------*/
380: /**
381: * Enter Full screen mode
382: */
383: void Screen_EnterFullScreen(void) {
384: bool bWasRunning;
385:
386: if (!bInFullScreen) {
387: /* Hold things... */
388: bWasRunning = Main_PauseEmulation(false);
389: bInFullScreen = true;
390:
391: SDL_GetWindowPosition(sdlWindow, &saveWindowBounds.x, &saveWindowBounds.y);
392: SDL_GetWindowSize(sdlWindow, &saveWindowBounds.w, &saveWindowBounds.h);
393: SDL_SetWindowFullscreen(sdlWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
394: SDL_Delay(20); /* To give monitor time to change to new resolution */
395:
396: if (bWasRunning) {
397: /* And off we go... */
398: Main_UnPauseEmulation();
399: }
400: SDL_SetRelativeMouseMode(SDL_TRUE);
401: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
402: }
403: }
404:
405: /*-----------------------------------------------------------------------*/
406: /**
407: * Return from Full screen mode back to a window
408: */
409: void Screen_ReturnFromFullScreen(void) {
410: bool bWasRunning;
411:
412: if (bInFullScreen) {
413: /* Hold things... */
414: bWasRunning = Main_PauseEmulation(false);
415: bInFullScreen = false;
416:
417: SDL_SetWindowFullscreen(sdlWindow, 0);
418: SDL_Delay(20); /* To give monitor time to switch resolution */
419: SDL_SetWindowPosition(sdlWindow, saveWindowBounds.x, saveWindowBounds.y);
420: SDL_SetWindowSize(sdlWindow, saveWindowBounds.w, saveWindowBounds.h);
421:
422: if (bWasRunning) {
423: /* And off we go... */
424: Main_UnPauseEmulation();
425: }
426:
427: if (!bGrabMouse) {
428: /* Un-grab mouse pointer in windowed mode */
429: SDL_SetRelativeMouseMode(SDL_FALSE);
430: SDL_SetWindowGrab(sdlWindow, SDL_FALSE);
431: }
432: }
433: }
434:
435: /*-----------------------------------------------------------------------*/
436: /**
1.1.1.2 root 437: * Force things associated with changing between fullscreen/windowed
1.1 root 438: */
439: void Screen_ModeChanged(void) {
440: if (!sdlscrn) {
441: /* screen not yet initialized */
442: return;
443: }
444: if (bInFullScreen || bGrabMouse) {
445: SDL_SetRelativeMouseMode(SDL_TRUE);
446: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
447: } else {
448: SDL_SetRelativeMouseMode(SDL_FALSE);
449: SDL_SetWindowGrab(sdlWindow, SDL_FALSE);
450: }
451: }
452:
1.1.1.2 root 453:
1.1 root 454: /*-----------------------------------------------------------------------*/
455: /**
1.1.1.2 root 456: * Draw screen to window/full-screen - (SC) Just status bar updates. Screen redraw is done in repaint thread.
1.1 root 457: */
1.1.1.2 root 458:
459: static bool shieldStatusBarUpdate;
460:
461: static void statusBarUpdate(void) {
462: if(shieldStatusBarUpdate) return;
463: SDL_LockSurface(sdlscrn);
464: SDL_AtomicLock(&uiBufferLock);
465: memcpy(&((Uint8*)uiBuffer)[statusBar.y*sdlscrn->pitch], &((Uint8*)sdlscrn->pixels)[statusBar.y*sdlscrn->pitch], statusBar.h * sdlscrn->pitch);
466: SDL_AtomicSet(&blitUI, 1);
467: SDL_AtomicUnlock(&uiBufferLock);
468: SDL_UnlockSurface(sdlscrn);
469: }
470:
471: bool Update_StatusBar(void) {
472: shieldStatusBarUpdate = true;
1.1 root 473: Statusbar_OverlayBackup(sdlscrn);
474: Statusbar_Update(sdlscrn);
1.1.1.2 root 475: shieldStatusBarUpdate = false;
1.1 root 476:
1.1.1.2 root 477: statusBarUpdate();
478:
1.1 root 479: return !bQuitProgram;
480: }
481:
1.1.1.2 root 482: /*
483: Copy UI SDL surface to uiBuffer and replace mask pixels with transparent pixels for
484: UI blending with framebuffer texture.
485: */
486: static void uiUpdate(void) {
1.1 root 487: SDL_LockSurface(sdlscrn);
488: int count = sdlscrn->w * sdlscrn->h;
489: Uint32* dst = (Uint32*)uiBuffer;
490: Uint32* src = (Uint32*)sdlscrn->pixels;
491: SDL_AtomicLock(&uiBufferLock);
492: // poor man's green-screen - would be nice if SDL had more blending modes...
493: for(int i = count; --i >= 0; src++)
494: *dst++ = *src == mask ? 0 : *src;
1.1.1.2 root 495: SDL_AtomicSet(&blitUI, 1);
1.1 root 496: SDL_AtomicUnlock(&uiBufferLock);
497: SDL_UnlockSurface(sdlscrn);
498: }
499:
500: void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) {
501: while(numrects--) {
1.1.1.2 root 502: if(rects->y < NeXT_SCRN_HEIGHT) {
1.1 root 503: uiUpdate();
504: doUIblit = true;
505: } else {
506: if(doUIblit) {
507: uiUpdate();
508: doUIblit = false;
509: } else {
1.1.1.2 root 510: statusBarUpdate();
1.1 root 511: }
512: }
513: }
514: }
515:
516: void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h) {
517: SDL_Rect rect = { x, y, w, h };
518: SDL_UpdateRects(screen, 1, &rect);
519: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.