|
|
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"
20: #include "dimension.h"
21: #include "paths.h"
22: #include "screen.h"
23: #include "control.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: */
116: void blitDimension(SDL_Texture* tex) {
1.1.1.2 ! root 117: #if ND_STEP
! 118: Uint32* src = (Uint32*)&ND_vram[0];
! 119: #else
! 120: Uint32* src = (Uint32*)&ND_vram[16];
! 121: #endif
1.1 root 122: void* pixels;
123: int d;
124: Uint32 format;
125: SDL_QueryTexture(tex, &format, &d, &d, &d);
126: SDL_LockTexture(tex, NULL, &pixels, &d);
127: Uint32* dst = (Uint32*)pixels;
128: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
1.1.1.2 ! root 129: /* Add big-endian accelerated blit loops as needed here */
1.1 root 130: switch (format) {
131: default: {
132: /* fallback to SDL_MapRGB */
133: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
1.1.1.2 ! root 134: for(int y = NeXT_SCRN_HEIGHT; --y >= 0;) {
! 135: for(int x = NeXT_SCRN_WIDTH; --x >= 0;) {
1.1 root 136: Uint32 v = *src++;
137: *dst++ = SDL_MapRGB(pformat, (v >> 24) & 0xFF, (v>>16) & 0xFF, (v>>8) & 0xFF);
138: }
1.1.1.2 ! root 139: src += 32;
1.1 root 140: }
141: SDL_FreeFormat(pformat);
142: break;
143: }
144: }
145: } else {
1.1.1.2 ! root 146: /* Add little-endian accelerated blit loops as needed here */
1.1 root 147: switch (format) {
148: case SDL_PIXELFORMAT_ARGB8888:
1.1.1.2 ! root 149: for(int y = NeXT_SCRN_HEIGHT; --y >= 0;) {
! 150: for(int x = NeXT_SCRN_WIDTH; --x >= 0;) {
1.1 root 151: // Uint32 LE: AABBGGRR
152: // Target: AARRGGBB
153: Uint32 v = *src++;
154: *dst++ = (v & 0xFF000000) | ((v<<16) &0x00FF0000) | (v &0x0000FF00) | ((v>>16) &0x000000FF);
155: }
1.1.1.2 ! root 156: src += 32;
1.1 root 157: }
158: break;
159: default: {
160: /* fallback to SDL_MapRGB */
161: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
1.1.1.2 ! root 162: for(int y = NeXT_SCRN_HEIGHT; --y >= 0;) {
! 163: for(int x = NeXT_SCRN_WIDTH; --x >= 0;) {
1.1 root 164: Uint32 v = SDL_Swap32(*src++);
165: *dst++ = SDL_MapRGB(pformat, (v >> 24) & 0xFF, (v>>16) & 0xFF, (v>>8) & 0xFF);
166: }
1.1.1.2 ! root 167: src += 32;
1.1 root 168: }
169: SDL_FreeFormat(pformat);
170: break;
171: }
172: }
173: }
174: SDL_UnlockTexture(tex);
175: }
176:
177: /*
178: Blit NeXT framebuffer to texture.
179: */
180: static void blitScreen(SDL_Texture* tex) {
181: if (ConfigureParams.Screen.nMonitorType==MONITOR_TYPE_DIMENSION) {
182: blitDimension(tex);
183: return;
184: }
185: if(ConfigureParams.System.bColor) {
186: blitColor(tex);
187: } else {
188: blitBW(tex);
189: }
190: }
191:
192: /*
1.1.1.2 ! root 193: Initializes SDL graphics and then enters repaint loop.
! 194: Loop: Blits the NeXT framebuffer to the fbTexture, blends with the GUI surface and
1.1 root 195: shows it.
196: */
197: static int repainter(void* unused) {
198: int width;
199: int height;
200:
1.1.1.2 ! root 201: SDL_SetThreadPriority(SDL_THREAD_PRIORITY_NORMAL);
1.1 root 202: SDL_GetWindowSize(sdlWindow, &width, &height);
203:
1.1.1.2 ! root 204: statusBar.x = 0;
! 205: statusBar.y = NeXT_SCRN_HEIGHT;
! 206: statusBar.w = width;
! 207: statusBar.h = height - NeXT_SCRN_HEIGHT;
! 208:
1.1 root 209: SDL_Texture* uiTexture;
210: SDL_Texture* fbTexture;
211:
212: Uint32 r, g, b, a;
213:
214: sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
215: SDL_RenderSetLogicalSize(sdlRenderer, width, height);
1.1.1.2 ! root 216:
1.1 root 217: uiTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height);
218: SDL_SetTextureBlendMode(uiTexture, SDL_BLENDMODE_BLEND);
219:
220: fbTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height);
221: SDL_SetTextureBlendMode(fbTexture, SDL_BLENDMODE_NONE);
222:
223: Uint32 format;
224: int d;
225: SDL_QueryTexture(uiTexture, &format, &d, &d, &d);
226: SDL_PixelFormatEnumToMasks(format, &d, &r, &g, &b, &a);
227: mask = g | a;
1.1.1.2 ! root 228: sdlscrn = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, r, g, b, a);
! 229: uiBuffer = malloc(sdlscrn->h * sdlscrn->pitch);
! 230: uiBufferTmp = malloc(sdlscrn->h * sdlscrn->pitch);
1.1 root 231: // clear UI with mask
232: SDL_FillRect(sdlscrn, NULL, mask);
233:
234: /* Exit if we can not open a screen */
235: if (!sdlscrn) {
236: fprintf(stderr, "Could not set video mode:\n %s\n", SDL_GetError() );
237: SDL_Quit();
238: exit(-2);
239: }
240:
241: if (!bInFullScreen) {
242: /* re-embed the new SDL window */
243: Control_ReparentWindow(width, height, bInFullScreen);
244: }
245:
246: Statusbar_Init(sdlscrn);
247:
248: if (bGrabMouse) {
249: SDL_SetRelativeMouseMode(SDL_TRUE);
250: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
251: }
252:
253: /* Configure some SDL stuff: */
254: SDL_ShowCursor(SDL_DISABLE);
255:
256: /* Setup lookup tables */
257: SDL_PixelFormat* pformat = SDL_AllocFormat(format);
258: /* initialize BW lookup table */
259: for(int i = 0; i < 0x100; i++) {
260: BW2RGB[i*4+0] = bw2rgb(pformat, i>>6);
261: BW2RGB[i*4+1] = bw2rgb(pformat, i>>4);
262: BW2RGB[i*4+2] = bw2rgb(pformat, i>>2);
263: BW2RGB[i*4+3] = bw2rgb(pformat, i>>0);
264: }
265: /* initialize color lookup table */
266: for(int i = 0; i < 0x10000; i++)
267: COL2RGB[SDL_BYTEORDER == SDL_BIG_ENDIAN ? i : SDL_Swap16(i)] = col2rgb(pformat, i);
268:
269: /* Initialization done -> signal */
270: SDL_SemPost(initLatch);
271:
1.1.1.2 ! root 272: /* Start with framebuffer blit enabled */
! 273: SDL_AtomicSet(&blitFB, 1);
! 274:
1.1 root 275: /* Enter repaint loop */
276: while(doRepaint) {
1.1.1.2 ! root 277: bool updateFB = false;
! 278: bool updateUI = false;
1.1 root 279:
1.1.1.2 ! root 280: if (SDL_AtomicGet(&blitFB)) {
! 281: // Blit the NeXT framebuffer to textrue
! 282: blitScreen(fbTexture);
! 283: updateFB = true;
! 284: }
1.1 root 285:
286: // Copy UI surface to texture
1.1.1.2 ! root 287: SDL_AtomicLock(&uiBufferLock);
1.1 root 288: if(SDL_AtomicSet(&blitUI, 0)) {
1.1.1.2 ! root 289: // update full UI texture
! 290: memcpy(uiBufferTmp, uiBuffer, sdlscrn->h * sdlscrn->pitch);
! 291: updateUI = true;
! 292: }
! 293: SDL_AtomicUnlock(&uiBufferLock);
! 294:
! 295: if(updateUI) {
! 296: SDL_UpdateTexture(uiTexture, NULL, uiBufferTmp, sdlscrn->pitch);
1.1 root 297: }
298:
1.1.1.2 ! root 299: // Update and render UI texture
! 300: if (updateFB || updateUI) {
! 301: SDL_RenderClear(sdlRenderer);
! 302: // Render NeXT framebuffer texture
! 303: SDL_RenderCopy(sdlRenderer, fbTexture, NULL, NULL);
! 304: SDL_RenderCopy(sdlRenderer, uiTexture, NULL, NULL);
! 305: // SDL_RenderPresent sleeps until next VSYNC because of SDL_RENDERER_PRESENTVSYNC in ScreenInit
! 306: SDL_RenderPresent(sdlRenderer);
! 307: } else {
! 308: host_sleep_ms(10);
! 309: }
1.1 root 310: }
311: return 0;
312: }
313:
314: /*-----------------------------------------------------------------------*/
315: /**
1.1.1.2 ! root 316: * Pause Screen, pauses or resumes drawing of NeXT framebuffer
! 317: */
! 318: void Screen_Pause(bool pause) {
! 319: if (pause) {
! 320: SDL_AtomicSet(&blitFB, 0);
! 321: } else {
! 322: SDL_AtomicSet(&blitFB, 1);
! 323: }
! 324: }
! 325:
! 326: /*-----------------------------------------------------------------------*/
! 327: /**
! 328: * Init Screen, creates window and starts repaint thread
1.1 root 329: */
330: void Screen_Init(void) {
331: /* Set initial window resolution */
332: bInFullScreen = ConfigureParams.Screen.bFullScreen;
333: nScreenZoomX = 1;
334: nScreenZoomY = 1;
335:
1.1.1.2 ! root 336: int width = NeXT_SCRN_WIDTH;
! 337: int height = NeXT_SCRN_HEIGHT;
! 338:
! 339: /* Statusbar height */
1.1 root 340: height += Statusbar_SetHeight(width, height);
341:
342: if (bInFullScreen) {
343: /* unhide the WM window for fullscreen */
344: Control_ReparentWindow(width, height, bInFullScreen);
345: }
346:
347: /* Set new video mode */
348: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
349:
1.1.1.2 ! root 350: fprintf(stderr, "SDL screen request: %d x %d (%s)\n", width, height, bInFullScreen ? "fullscreen" : "windowed");
1.1 root 351:
352: int x = SDL_WINDOWPOS_UNDEFINED;
353: if(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_DUAL) {
354: for(int i = 0; i < SDL_GetNumVideoDisplays(); i++) {
355: SDL_Rect r;
356: SDL_GetDisplayBounds(i, &r);
357: if(r.w >= width * 2) {
358: x = r.x + width + ((r.w - width * 2) / 2);
359: break;
360: }
361: if(r.x >= 0 && SDL_GetNumVideoDisplays() == 1) x = r.x + 8;
362: }
363: }
364: sdlWindow = SDL_CreateWindow(PROG_NAME, x, SDL_WINDOWPOS_UNDEFINED, width, height, 0);
365: if (!sdlWindow) {
366: fprintf(stderr,"Failed to create window: %s!\n", SDL_GetError());
367: exit(-1);
368: }
369:
370: initLatch = SDL_CreateSemaphore(0);
371: repaintThread = SDL_CreateThread(repainter, "[Previous] screen repaint", NULL);
372: SDL_SemWait(initLatch);
373: }
374:
1.1.1.2 ! root 375: void nd_sdl_destroy(void);
1.1 root 376:
377: /*-----------------------------------------------------------------------*/
378: /**
379: * Free screen bitmap and allocated resources
380: */
381: void Screen_UnInit(void) {
382: doRepaint = false; // stop repaint thread
383: int s;
384: SDL_WaitThread(repaintThread, &s);
385: nd_sdl_destroy();
386: }
387:
388: /*-----------------------------------------------------------------------*/
389: /**
390: * Enter Full screen mode
391: */
392: void Screen_EnterFullScreen(void) {
393: bool bWasRunning;
394:
395: if (!bInFullScreen) {
396: /* Hold things... */
397: bWasRunning = Main_PauseEmulation(false);
398: bInFullScreen = true;
399:
400: SDL_GetWindowPosition(sdlWindow, &saveWindowBounds.x, &saveWindowBounds.y);
401: SDL_GetWindowSize(sdlWindow, &saveWindowBounds.w, &saveWindowBounds.h);
402: SDL_SetWindowFullscreen(sdlWindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
403: SDL_Delay(20); /* To give monitor time to change to new resolution */
404:
405: if (bWasRunning) {
406: /* And off we go... */
407: Main_UnPauseEmulation();
408: }
409: SDL_SetRelativeMouseMode(SDL_TRUE);
410: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
411: }
412: }
413:
414: /*-----------------------------------------------------------------------*/
415: /**
416: * Return from Full screen mode back to a window
417: */
418: void Screen_ReturnFromFullScreen(void) {
419: bool bWasRunning;
420:
421: if (bInFullScreen) {
422: /* Hold things... */
423: bWasRunning = Main_PauseEmulation(false);
424: bInFullScreen = false;
425:
426: SDL_SetWindowFullscreen(sdlWindow, 0);
427: SDL_Delay(20); /* To give monitor time to switch resolution */
428: SDL_SetWindowPosition(sdlWindow, saveWindowBounds.x, saveWindowBounds.y);
429: SDL_SetWindowSize(sdlWindow, saveWindowBounds.w, saveWindowBounds.h);
430:
431: if (bWasRunning) {
432: /* And off we go... */
433: Main_UnPauseEmulation();
434: }
435:
436: if (!bGrabMouse) {
437: /* Un-grab mouse pointer in windowed mode */
438: SDL_SetRelativeMouseMode(SDL_FALSE);
439: SDL_SetWindowGrab(sdlWindow, SDL_FALSE);
440: }
441: }
442: }
443:
444: /*-----------------------------------------------------------------------*/
445: /**
1.1.1.2 ! root 446: * Force things associated with changing between fullscreen/windowed
1.1 root 447: */
448: void Screen_ModeChanged(void) {
449: if (!sdlscrn) {
450: /* screen not yet initialized */
451: return;
452: }
453: if (bInFullScreen || bGrabMouse) {
454: SDL_SetRelativeMouseMode(SDL_TRUE);
455: SDL_SetWindowGrab(sdlWindow, SDL_TRUE);
456: } else {
457: SDL_SetRelativeMouseMode(SDL_FALSE);
458: SDL_SetWindowGrab(sdlWindow, SDL_FALSE);
459: }
460: }
461:
1.1.1.2 ! root 462:
1.1 root 463: /*-----------------------------------------------------------------------*/
464: /**
1.1.1.2 ! root 465: * Draw screen to window/full-screen - (SC) Just status bar updates. Screen redraw is done in repaint thread.
1.1 root 466: */
1.1.1.2 ! root 467:
! 468: static bool shieldStatusBarUpdate;
! 469:
! 470: static void statusBarUpdate(void) {
! 471: if(shieldStatusBarUpdate) return;
! 472: SDL_LockSurface(sdlscrn);
! 473: SDL_AtomicLock(&uiBufferLock);
! 474: memcpy(&((Uint8*)uiBuffer)[statusBar.y*sdlscrn->pitch], &((Uint8*)sdlscrn->pixels)[statusBar.y*sdlscrn->pitch], statusBar.h * sdlscrn->pitch);
! 475: SDL_AtomicSet(&blitUI, 1);
! 476: SDL_AtomicUnlock(&uiBufferLock);
! 477: SDL_UnlockSurface(sdlscrn);
! 478: }
! 479:
! 480: bool Update_StatusBar(void) {
! 481: shieldStatusBarUpdate = true;
1.1 root 482: Statusbar_OverlayBackup(sdlscrn);
483: Statusbar_Update(sdlscrn);
1.1.1.2 ! root 484: shieldStatusBarUpdate = false;
1.1 root 485:
1.1.1.2 ! root 486: statusBarUpdate();
! 487:
1.1 root 488: return !bQuitProgram;
489: }
490:
1.1.1.2 ! root 491: /*
! 492: Copy UI SDL surface to uiBuffer and replace mask pixels with transparent pixels for
! 493: UI blending with framebuffer texture.
! 494: */
! 495: static void uiUpdate(void) {
1.1 root 496: SDL_LockSurface(sdlscrn);
497: int count = sdlscrn->w * sdlscrn->h;
498: Uint32* dst = (Uint32*)uiBuffer;
499: Uint32* src = (Uint32*)sdlscrn->pixels;
500: SDL_AtomicLock(&uiBufferLock);
501: // poor man's green-screen - would be nice if SDL had more blending modes...
502: for(int i = count; --i >= 0; src++)
503: *dst++ = *src == mask ? 0 : *src;
1.1.1.2 ! root 504: SDL_AtomicSet(&blitUI, 1);
1.1 root 505: SDL_AtomicUnlock(&uiBufferLock);
506: SDL_UnlockSurface(sdlscrn);
507: }
508:
509: void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) {
510: while(numrects--) {
1.1.1.2 ! root 511: if(rects->y < NeXT_SCRN_HEIGHT) {
1.1 root 512: uiUpdate();
513: doUIblit = true;
514: } else {
515: if(doUIblit) {
516: uiUpdate();
517: doUIblit = false;
518: } else {
1.1.1.2 ! root 519: statusBarUpdate();
1.1 root 520: }
521: }
522: }
523: }
524:
525: void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h) {
526: SDL_Rect rect = { x, y, w, h };
527: SDL_UpdateRects(screen, 1, &rect);
528: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.