|
|
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.