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