|
|
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 emualtor ! 8: */ ! 9: ! 10: #include <SDL.h> ! 11: #include <SDL_endian.h> ! 12: #include <SDL_blendmode.h> ! 13: ! 14: #if 0 ! 15: void blitDimension(SDL_Texture* tex) {} ! 16: #include "screen.c" ! 17: #else ! 18: ! 19: const char Screen_fileid[] = "Previous fast_screen.c : " __DATE__ " " __TIME__; ! 20: ! 21: #include "main.h" ! 22: #include "configuration.h" ! 23: #include "log.h" ! 24: #include "m68000.h" ! 25: #include "dimension.h" ! 26: #include "paths.h" ! 27: #include "screen.h" ! 28: #include "control.h" ! 29: #include "convert/routines.h" ! 30: #include "resolution.h" ! 31: #include "statusbar.h" ! 32: #include "video.h" ! 33: ! 34: SDL_Window* sdlWindow; ! 35: SDL_Surface* sdlscrn = NULL; /* The SDL screen surface */ ! 36: int nScreenZoomX, nScreenZoomY;/* Zooming factors, used for scaling mouse motions */ ! 37: ! 38: /* extern for shortcuts and falcon/hostscreen.c */ ! 39: volatile bool bGrabMouse = false; /* Grab the mouse cursor in the window */ ! 40: volatile bool bInFullScreen = false; /* true if in full screen */ ! 41: ! 42: static SDL_Thread* repaintThread; ! 43: static SDL_sem* initLatch; ! 44: static SDL_atomic_t blitUI; /* When value = 1, the repaint thread will blit the sldscrn surface to the screen on the next redraw */ ! 45: static SDL_atomic_t blitStatusBar; /* When value = 1, the repaint thread will blit the sldscrn status bar on the next redraw */ ! 46: static bool doUIblit; ! 47: static SDL_Rect saveWindowBounds; /* Window bounds before going fullscreen. Used to restore window size & position. */ ! 48: static void* uiBuffer; /* uiBuffer used for ui texture */ ! 49: static SDL_SpinLock uiBufferLock; /* Lock for concurrent access to UI buffer between main thread and repainter */ ! 50: static Uint32 mask; /* green screen mask for transparent UI areas */ ! 51: static volatile bool doRepaint = true; /* Repaint thread runs while true */ ! 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 = ConfigureParams.System.bTurbo ? 280 : 288; ! 80: SDL_LockTexture(tex, NULL, &pixels, &d); ! 81: Uint32* dst = (Uint32*)pixels; ! 82: for(int y = 0; y < 832; y++) { ! 83: int src = y * pitch; ! 84: for(int x = 0; x < 280; 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 = ConfigureParams.System.bTurbo ? 1120 : 1152; ! 102: SDL_LockTexture(tex, NULL, &pixels, &d); ! 103: Uint32* dst = (Uint32*)pixels; ! 104: for(int y = 0; y < 832; y++) { ! 105: Uint16* src = (Uint16*)NEXTColorVideo + (y*pitch); ! 106: for(int x = 0; x < 1120; x++) { ! 107: *dst++ = COL2RGB[*src++]; ! 108: } ! 109: } ! 110: SDL_UnlockTexture(tex); ! 111: } ! 112: ! 113: extern ! 114: ! 115: /* ! 116: Dimension format is 8bit per pixel, big-endian: RRGGGBBBA ! 117: */ ! 118: void blitDimension(SDL_Texture* tex) { ! 119: #if ENABLE_DIMENSION ! 120: Uint32* src = (Uint32*)&ND_vram[ND_vram_off]; ! 121: void* pixels; ! 122: int d; ! 123: Uint32 format; ! 124: SDL_QueryTexture(tex, &format, &d, &d, &d); ! 125: SDL_LockTexture(tex, NULL, &pixels, &d); ! 126: Uint32* dst = (Uint32*)pixels; ! 127: if(SDL_BYTEORDER == SDL_BIG_ENDIAN) { ! 128: /* Add big-endian accelerated blit loops as needed */ ! 129: switch (format) { ! 130: default: { ! 131: /* fallback to SDL_MapRGB */ ! 132: SDL_PixelFormat* pformat = SDL_AllocFormat(format); ! 133: for(int y = 832; --y >= 0;) { ! 134: for(int x = 1120; --x >= 0;) { ! 135: Uint32 v = *src++; ! 136: *dst++ = SDL_MapRGB(pformat, (v >> 24) & 0xFF, (v>>16) & 0xFF, (v>>8) & 0xFF); ! 137: } ! 138: src += 1152 - 1120; ! 139: } ! 140: SDL_FreeFormat(pformat); ! 141: break; ! 142: } ! 143: } ! 144: } else { ! 145: /* Add little-endian accelerated blit loops as needed */ ! 146: switch (format) { ! 147: case SDL_PIXELFORMAT_ARGB8888: ! 148: for(int y = 832; --y >= 0;) { ! 149: for(int x = 1120; --x >= 0;) { ! 150: // Uint32 LE: AABBGGRR ! 151: // Target: AARRGGBB ! 152: Uint32 v = *src++; ! 153: *dst++ = (v & 0xFF000000) | ((v<<16) &0x00FF0000) | (v &0x0000FF00) | ((v>>16) &0x000000FF); ! 154: } ! 155: src += 1152 - 1120; ! 156: } ! 157: break; ! 158: default: { ! 159: /* fallback to SDL_MapRGB */ ! 160: SDL_PixelFormat* pformat = SDL_AllocFormat(format); ! 161: for(int y = 832; --y >= 0;) { ! 162: for(int x = 1120; --x >= 0;) { ! 163: Uint32 v = SDL_Swap32(*src++); ! 164: *dst++ = SDL_MapRGB(pformat, (v >> 24) & 0xFF, (v>>16) & 0xFF, (v>>8) & 0xFF); ! 165: } ! 166: src += 1152 - 1120; ! 167: } ! 168: SDL_FreeFormat(pformat); ! 169: break; ! 170: } ! 171: } ! 172: } ! 173: SDL_UnlockTexture(tex); ! 174: #endif ! 175: } ! 176: ! 177: /* ! 178: Blit NeXT framebuffer to texture. ! 179: */ ! 180: static void blitScreen(SDL_Texture* tex) { ! 181: #if ENABLE_DIMENSION ! 182: if (ConfigureParams.Screen.nMonitorType==MONITOR_TYPE_DIMENSION) { ! 183: blitDimension(tex); ! 184: return; ! 185: } ! 186: #endif ! 187: if(ConfigureParams.System.bColor) { ! 188: blitColor(tex); ! 189: } else { ! 190: blitBW(tex); ! 191: } ! 192: } ! 193: ! 194: /* ! 195: Initializes sdl graphics and the enter 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_GetWindowSize(sdlWindow, &width, &height); ! 204: ! 205: SDL_Renderer* sdlRenderer; ! 206: SDL_Texture* uiTexture; ! 207: SDL_Texture* fbTexture; ! 208: SDL_Rect statusBar = {0,832,width,height-832}; ! 209: ! 210: Uint32 r, g, b, a; ! 211: ! 212: sdlRenderer = SDL_CreateRenderer(sdlWindow, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); ! 213: SDL_RenderSetLogicalSize(sdlRenderer, width, height); ! 214: ! 215: uiTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height); ! 216: SDL_SetTextureBlendMode(uiTexture, SDL_BLENDMODE_BLEND); ! 217: ! 218: fbTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_UNKNOWN, SDL_TEXTUREACCESS_STREAMING, width, height); ! 219: SDL_SetTextureBlendMode(fbTexture, SDL_BLENDMODE_NONE); ! 220: ! 221: Uint32 format; ! 222: int d; ! 223: SDL_QueryTexture(uiTexture, &format, &d, &d, &d); ! 224: SDL_PixelFormatEnumToMasks(format, &d, &r, &g, &b, &a); ! 225: mask = g | a; ! 226: sdlscrn = SDL_CreateRGBSurface(SDL_SWSURFACE, width, height, 32, r, g, b, a); ! 227: uiBuffer = malloc(sdlscrn->h * sdlscrn->pitch); ! 228: // clear UI with mask ! 229: SDL_FillRect(sdlscrn, NULL, mask); ! 230: ! 231: /* Exit if we can not open a screen */ ! 232: if (!sdlscrn) { ! 233: fprintf(stderr, "Could not set video mode:\n %s\n", SDL_GetError() ); ! 234: SDL_Quit(); ! 235: exit(-2); ! 236: } ! 237: ! 238: if (!bInFullScreen) { ! 239: /* re-embed the new SDL window */ ! 240: Control_ReparentWindow(width, height, bInFullScreen); ! 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: /* Enter repaint loop */ ! 270: while(doRepaint) { ! 271: SDL_RenderClear(sdlRenderer); ! 272: ! 273: // Blit the NeXT framebuffer to textrue ! 274: blitScreen(fbTexture); ! 275: // Render NeXT framebuffer texture ! 276: SDL_RenderCopy(sdlRenderer, fbTexture, NULL, NULL); ! 277: ! 278: // Copy UI surface to texture ! 279: if(SDL_AtomicSet(&blitUI, 0)) { ! 280: // update UI texture ! 281: SDL_AtomicLock(&uiBufferLock); ! 282: SDL_UpdateTexture(uiTexture, NULL, uiBuffer, sdlscrn->pitch); ! 283: SDL_AtomicUnlock(&uiBufferLock); ! 284: } else if(SDL_AtomicSet(&blitStatusBar, 0)) { ! 285: SDL_LockSurface(sdlscrn); ! 286: SDL_UpdateTexture(uiTexture, &statusBar, &((Uint8*)sdlscrn->pixels)[statusBar.y*sdlscrn->pitch], sdlscrn->pitch); ! 287: SDL_UnlockSurface(sdlscrn); ! 288: } ! 289: // Render UI texture ! 290: SDL_RenderCopy(sdlRenderer, uiTexture, NULL, NULL); ! 291: ! 292: // SDL_RenderPresent sleeps until next VSYNC because of SDL_RENDERER_PRESENTVSYNC in ScreenInit ! 293: SDL_RenderPresent(sdlRenderer); ! 294: } ! 295: return 0; ! 296: } ! 297: ! 298: /*-----------------------------------------------------------------------*/ ! 299: /** ! 300: * Init Screen, creates window and starts repatin thread ! 301: */ ! 302: void Screen_Init(void) { ! 303: /* Set initial window resolution */ ! 304: bInFullScreen = ConfigureParams.Screen.bFullScreen; ! 305: nScreenZoomX = 1; ! 306: nScreenZoomY = 1; ! 307: ! 308: int width = 1120; ! 309: int height = 832; ! 310: int sBarHeight, bitCount, maxW, maxH; ! 311: ! 312: /* Statusbar height for doubled screen size */ ! 313: sBarHeight = Statusbar_GetHeightForSize(1120, 832); ! 314: Resolution_GetLimits(&maxW, &maxH, &bitCount); ! 315: height += Statusbar_SetHeight(width, height); ! 316: ! 317: if (bInFullScreen) { ! 318: /* unhide the WM window for fullscreen */ ! 319: Control_ReparentWindow(width, height, bInFullScreen); ! 320: } ! 321: ! 322: /* Set new video mode */ ! 323: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); ! 324: ! 325: fprintf(stderr, "SDL screen request: %d x %d @ %d (%s)\n", width, height, bitCount, bInFullScreen ? "fullscreen" : "windowed"); ! 326: ! 327: int x = SDL_WINDOWPOS_UNDEFINED; ! 328: if(ConfigureParams.Screen.nMonitorType == MONITOR_TYPE_DUAL) { ! 329: for(int i = 0; i < SDL_GetNumVideoDisplays(); i++) { ! 330: SDL_Rect r; ! 331: SDL_GetDisplayBounds(i, &r); ! 332: if(r.w >= width * 2) { ! 333: x = r.x + width + ((r.w - width * 2) / 2); ! 334: break; ! 335: } ! 336: if(r.x >= 0 && SDL_GetNumVideoDisplays() == 1) x = r.x + 8; ! 337: } ! 338: } ! 339: sdlWindow = SDL_CreateWindow(PROG_NAME, x, SDL_WINDOWPOS_UNDEFINED, width, height, 0); ! 340: if (!sdlWindow) { ! 341: fprintf(stderr,"Failed to create window: %s!\n", SDL_GetError()); ! 342: exit(-1); ! 343: } ! 344: ! 345: initLatch = SDL_CreateSemaphore(0); ! 346: repaintThread = SDL_CreateThread(repainter, "[Previous] screen repaint", NULL); ! 347: SDL_SemWait(initLatch); ! 348: } ! 349: ! 350: extern void nd_sdl_destroy(); ! 351: ! 352: /*-----------------------------------------------------------------------*/ ! 353: /** ! 354: * Free screen bitmap and allocated resources ! 355: */ ! 356: void Screen_UnInit(void) { ! 357: doRepaint = false; // stop repaint thread ! 358: int s; ! 359: SDL_WaitThread(repaintThread, &s); ! 360: nd_sdl_destroy(); ! 361: } ! 362: ! 363: /*-----------------------------------------------------------------------*/ ! 364: /** ! 365: * Enter Full screen mode ! 366: */ ! 367: void Screen_EnterFullScreen(void) { ! 368: bool bWasRunning; ! 369: ! 370: if (!bInFullScreen) { ! 371: /* Hold things... */ ! 372: bWasRunning = Main_PauseEmulation(false); ! 373: bInFullScreen = true; ! 374: ! 375: SDL_GetWindowPosition(sdlWindow, &saveWindowBounds.x, &saveWindowBounds.y); ! 376: SDL_GetWindowSize(sdlWindow, &saveWindowBounds.w, &saveWindowBounds.h); ! 377: SDL_SetWindowFullscreen(sdlWindow, SDL_WINDOW_FULLSCREEN_DESKTOP); ! 378: SDL_Delay(20); /* To give monitor time to change to new resolution */ ! 379: ! 380: if (bWasRunning) { ! 381: /* And off we go... */ ! 382: Main_UnPauseEmulation(); ! 383: } ! 384: SDL_SetRelativeMouseMode(SDL_TRUE); ! 385: SDL_SetWindowGrab(sdlWindow, SDL_TRUE); ! 386: } ! 387: } ! 388: ! 389: /*-----------------------------------------------------------------------*/ ! 390: /** ! 391: * Return from Full screen mode back to a window ! 392: */ ! 393: void Screen_ReturnFromFullScreen(void) { ! 394: bool bWasRunning; ! 395: ! 396: if (bInFullScreen) { ! 397: /* Hold things... */ ! 398: bWasRunning = Main_PauseEmulation(false); ! 399: bInFullScreen = false; ! 400: ! 401: SDL_SetWindowFullscreen(sdlWindow, 0); ! 402: SDL_Delay(20); /* To give monitor time to switch resolution */ ! 403: SDL_SetWindowPosition(sdlWindow, saveWindowBounds.x, saveWindowBounds.y); ! 404: SDL_SetWindowSize(sdlWindow, saveWindowBounds.w, saveWindowBounds.h); ! 405: ! 406: if (bWasRunning) { ! 407: /* And off we go... */ ! 408: Main_UnPauseEmulation(); ! 409: } ! 410: ! 411: if (!bGrabMouse) { ! 412: /* Un-grab mouse pointer in windowed mode */ ! 413: SDL_SetRelativeMouseMode(SDL_FALSE); ! 414: SDL_SetWindowGrab(sdlWindow, SDL_FALSE); ! 415: } ! 416: } ! 417: } ! 418: ! 419: /*-----------------------------------------------------------------------*/ ! 420: /** ! 421: * Force things associated with changing between low/medium/high res. ! 422: */ ! 423: void Screen_ModeChanged(void) { ! 424: if (!sdlscrn) { ! 425: /* screen not yet initialized */ ! 426: return; ! 427: } ! 428: if (bInFullScreen || bGrabMouse) { ! 429: SDL_SetRelativeMouseMode(SDL_TRUE); ! 430: SDL_SetWindowGrab(sdlWindow, SDL_TRUE); ! 431: } else { ! 432: SDL_SetRelativeMouseMode(SDL_FALSE); ! 433: SDL_SetWindowGrab(sdlWindow, SDL_FALSE); ! 434: } ! 435: } ! 436: ! 437: /*-----------------------------------------------------------------------*/ ! 438: /** ! 439: * Draw screen to window/full-screen - (SC) empty. Screen re-draw is done in repaint thread. ! 440: */ ! 441: bool Screen_Draw(void) { ! 442: ! 443: Statusbar_OverlayBackup(sdlscrn); ! 444: Statusbar_Update(sdlscrn); ! 445: ! 446: return !bQuitProgram; ! 447: } ! 448: ! 449: static void uiUpdate() { ! 450: SDL_LockSurface(sdlscrn); ! 451: int count = sdlscrn->w * sdlscrn->h; ! 452: Uint32* dst = (Uint32*)uiBuffer; ! 453: Uint32* src = (Uint32*)sdlscrn->pixels; ! 454: SDL_AtomicLock(&uiBufferLock); ! 455: // poor man's green-screen - would be nice if SDL had more blending modes... ! 456: for(int i = count; --i >= 0; src++) ! 457: *dst++ = *src == mask ? 0 : *src; ! 458: SDL_AtomicUnlock(&uiBufferLock); ! 459: SDL_UnlockSurface(sdlscrn); ! 460: SDL_AtomicSet(&blitUI, 1); ! 461: } ! 462: ! 463: void SDL_UpdateRects(SDL_Surface *screen, int numrects, SDL_Rect *rects) { ! 464: while(numrects--) { ! 465: if(rects->y < 832) { ! 466: uiUpdate(); ! 467: doUIblit = true; ! 468: } else { ! 469: if(doUIblit) { ! 470: uiUpdate(); ! 471: doUIblit = false; ! 472: } else { ! 473: SDL_AtomicSet(&blitStatusBar, 1); ! 474: } ! 475: } ! 476: } ! 477: } ! 478: ! 479: void SDL_UpdateRect(SDL_Surface *screen, Sint32 x, Sint32 y, Sint32 w, Sint32 h) { ! 480: SDL_Rect rect = { x, y, w, h }; ! 481: SDL_UpdateRects(screen, 1, &rect); ! 482: } ! 483: ! 484: /*-----------------------------------------------------------------------*/ ! 485: /** ! 486: * Reset screen - (SC) unused ! 487: */ ! 488: void Screen_Reset(void) {} ! 489: ! 490: /*-----------------------------------------------------------------------*/ ! 491: /** ! 492: * Set flags so screen will be TOTALLY re-drawn (clears whole of full-screen) ! 493: * next time around - (SC) unused ! 494: */ ! 495: void Screen_SetFullUpdate(void) {} ! 496: ! 497: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.