Annotation of previous/src/fast_screen.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.