LCOV - code coverage report
Current view: top level - SDL - MyOpenGLView.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 13 0.0 %
Date: 2025-05-28 07:50:54 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : MyOpenGLView.m
       4             : 
       5             : Oolite
       6             : Copyright (C) 2004-2013 Giles C Williams and contributors
       7             : 
       8             : This program is free software; you can redistribute it and/or
       9             : modify it under the terms of the GNU General Public License
      10             : as published by the Free Software Foundation; either version 2
      11             : of the License, or (at your option) any later version.
      12             : 
      13             : This program is distributed in the hope that it will be useful,
      14             : but WITHOUT ANY WARRANTY; without even the implied warranty of
      15             : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16             : GNU General Public License for more details.
      17             : 
      18             : You should have received a copy of the GNU General Public License
      19             : along with this program; if not, write to the Free Software
      20             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
      21             : MA 02110-1301, USA.
      22             : 
      23             : */
      24             : 
      25             : #import "png.h"
      26             : #import "MyOpenGLView.h"
      27             : 
      28             : #import "GameController.h"
      29             : #import "Universe.h"
      30             : #import "OOSDLJoystickManager.h"
      31             : #import "SDL_syswm.h"
      32             : #import "OOSound.h"
      33             : #import "NSFileManagerOOExtensions.h" // to find savedir
      34             : #import "PlayerEntity.h"
      35             : #import "GuiDisplayGen.h"
      36             : #import "PlanetEntity.h"
      37             : #import "OOGraphicsResetManager.h"
      38             : #import "OOCollectionExtractors.h" // for splash screen settings
      39             : #import "OOFullScreenController.h"
      40             : #import "ResourceManager.h"
      41             : 
      42           0 : #define STB_IMAGE_WRITE_IMPLEMENTATION
      43             : #import "stb_image_write.h"
      44             : 
      45           0 : #define kOOLogUnconvertedNSLog @"unclassified.MyOpenGLView"
      46             : 
      47           0 : extern int SaveEXRSnapshot(const char* outfilename, int width, int height, const float* rgb);
      48             : 
      49           0 : static NSString * kOOLogKeyUp                           = @"input.keyMapping.keyPress.keyUp";
      50           0 : static NSString * kOOLogKeyDown                         = @"input.keyMapping.keyPress.keyDown";
      51             : 
      52             : #include <ctype.h>
      53             : 
      54             : #if OOLITE_WINDOWS
      55             : #ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
      56             : #define DWMWA_USE_IMMERSIVE_DARK_MODE   20
      57             : #endif
      58             : HRESULT WINAPI DwmSetWindowAttribute (HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute);
      59             : 
      60             : #define USE_UNDOCUMENTED_DARKMODE_API   1
      61             : 
      62             : #if USE_UNDOCUMENTED_DARKMODE_API
      63             : #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
      64             : #define LOAD_LIBRARY_SEARCH_SYSTEM32    0x00000800
      65             : #endif
      66             : typedef DWORD(WINAPI* pfnSetPreferredAppMode)(DWORD appMode);
      67             : enum PreferredAppMode
      68             : {
      69             :     Default,
      70             :     AllowDark,
      71             :     ForceDark,
      72             :     ForceLight,
      73             :     Max
      74             : };
      75             : #endif
      76             : #endif //OOLITE_WINDOWS
      77             : 
      78             : @interface MyOpenGLView (OOPrivate)
      79             : 
      80           0 : - (void) resetSDLKeyModifiers;
      81           0 : - (void) setWindowBorderless:(BOOL)borderless;
      82           0 : - (void) handleStringInput: (SDL_KeyboardEvent *) kbd_event keyID:(Uint16)key_id; // DJS
      83             : @end
      84             : 
      85             : @implementation MyOpenGLView
      86             : 
      87             : + (NSMutableDictionary *) getNativeSize
      88             : {
      89             :         NSMutableDictionary *mode=[[NSMutableDictionary alloc] init];
      90             :         int nativeDisplayWidth = 1024;
      91             :         int nativeDisplayHeight = 768;
      92             : 
      93             : #if OOLITE_LINUX
      94             :         SDL_SysWMinfo  dpyInfo;
      95             :         SDL_VERSION(&dpyInfo.version);
      96             :         if(SDL_GetWMInfo(&dpyInfo))
      97             :         {
      98             :                 nativeDisplayWidth = DisplayWidth(dpyInfo.info.x11.display, 0);
      99             :                 nativeDisplayHeight = DisplayHeight(dpyInfo.info.x11.display, 0);
     100             :                 OOLog(@"display.mode.list.native", @"X11 native resolution detected: %d x %d", nativeDisplayWidth, nativeDisplayHeight);
     101             :         }
     102             :         else
     103             :         {
     104             :                 OOLog(@"display.mode.list.native.failed", @"%@", @"SDL_GetWMInfo failed, defaulting to 1024x768 for native size");
     105             :         }
     106             : #elif OOLITE_WINDOWS
     107             :         nativeDisplayWidth = GetSystemMetrics(SM_CXSCREEN);
     108             :         nativeDisplayHeight = GetSystemMetrics(SM_CYSCREEN);
     109             :         OOLog(@"display.mode.list.native", @"Windows native resolution detected: %d x %d", nativeDisplayWidth, nativeDisplayHeight);
     110             : #else
     111             :         OOLog(@"display.mode.list.native.unknown", @"Unknown architecture, defaulting to 1024x768");
     112             : #endif
     113             :         [mode setValue: [NSNumber numberWithInt: nativeDisplayWidth] forKey:kOODisplayWidth];
     114             :         [mode setValue: [NSNumber numberWithInt: nativeDisplayHeight] forKey: kOODisplayHeight];
     115             :         [mode setValue: [NSNumber numberWithInt: 0] forKey: kOODisplayRefreshRate];
     116             : 
     117             :         return [mode autorelease];
     118             : }
     119             : 
     120             : 
     121           0 : - (void) createSurface
     122             : {
     123             :         // Changing these flags can trigger texture bugs.
     124             :         const int videoModeFlags = SDL_HWSURFACE | SDL_OPENGL | SDL_RESIZABLE;
     125             : 
     126             :         if (showSplashScreen)
     127             :         {
     128             : #if OOLITE_WINDOWS
     129             :                 // Pre setVideoMode adjustments.
     130             :                 NSSize tmp = currentWindowSize;
     131             :                 ShowWindow(SDL_Window,SW_SHOWMINIMIZED);
     132             :                 updateContext = NO;     //don't update the (splash screen) window yet!
     133             : 
     134             :                 // Initialise the SDL surface. (need custom SDL.dll)
     135             :                 surface = SDL_SetVideoMode(firstScreen.width, firstScreen.height, 32, videoModeFlags);
     136             : 
     137             :                 // Post setVideoMode adjustments.
     138             :                 currentWindowSize=tmp;
     139             : #else
     140             :                 // Changing the flags can trigger texture bugs.
     141             :                 surface = SDL_SetVideoMode(8, 8, 32, videoModeFlags);
     142             : #endif
     143             :                 if (!surface) {
     144             :                         return;
     145             :                 }
     146             :         }
     147             :         else
     148             :         {
     149             : #if OOLITE_WINDOWS
     150             :                 updateContext = YES;
     151             : #endif
     152             :                 surface = SDL_SetVideoMode(firstScreen.width, firstScreen.height, 32, videoModeFlags);
     153             :                 if (!surface) {
     154             :                         return;
     155             :                 }
     156             :                 // blank the surface / go to fullscreen
     157             :                 [self initialiseGLWithSize: firstScreen];
     158             :         }
     159             : 
     160             :         _gamma = 1.0f;
     161             :         if (SDL_SetGamma(_gamma, _gamma, _gamma) < 0 )
     162             :         {
     163             :                 char * errStr = SDL_GetError();
     164             :                 OOLogWARN(@"gamma.set.failed", @"Could not set gamma: %s", errStr);
     165             :                 // CIM: this doesn't seem to necessarily be fatal. Gamma settings
     166             :                 // mostly work on mine despite this function failing.
     167             :                 //      exit(1);
     168             :         }
     169             :         SDL_EnableUNICODE(1);
     170             : }
     171             : 
     172             : 
     173             : - (id) init
     174             : {
     175             :         self = [super init];
     176             : 
     177             :         Uint32          colorkey;
     178             :         SDL_Surface     *icon=NULL;
     179             :         NSString                *imagesDir;
     180             :         NSString                *cmdLineArgsStr = @"Startup command: ";
     181             : 
     182             :         // SDL splash screen  settings
     183             : 
     184             :         NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
     185             :         showSplashScreen = [prefs oo_boolForKey:@"splash-screen" defaultValue:YES];
     186             :         BOOL    vSyncPreference = [prefs oo_boolForKey:@"v-sync" defaultValue:YES];
     187             :         int     bitsPerColorComponent = [prefs oo_boolForKey:@"hdr" defaultValue:NO] ? 16 : 8;
     188             :         int             vSyncValue;
     189             : 
     190             :         NSArray                         *arguments = nil;
     191             :         NSEnumerator            *argEnum = nil;
     192             :         NSString                        *arg = nil;
     193             :         BOOL                            noSplashArgFound = NO;
     194             : 
     195             :         [self initKeyMappingData];
     196             : 
     197             :         // preload the printscreen key into our translation array because SDLK_PRINTSCREEN isn't available
     198             :         scancode2Unicode[55] = gvPrintScreenKey;
     199             : 
     200             :         arguments = [[NSProcessInfo processInfo] arguments];
     201             : 
     202             :         // scan for splash screen overrides: -nosplash || --nosplash , -splash || --splash
     203             :         // scan for V-sync disabling overrides: -novsync || --novsync
     204             :         for (argEnum = [arguments objectEnumerator]; (arg = [argEnum nextObject]); )
     205             :         {
     206             :                 if ([arg isEqual:@"-nosplash"] || [arg isEqual:@"--nosplash"])
     207             :                 {
     208             :                         showSplashScreen = NO;
     209             :                         noSplashArgFound = YES; // -nosplash always trumps -splash
     210             :                 }
     211             :                 else if (([arg isEqual:@"-splash"] || [arg isEqual:@"--splash"]) && !noSplashArgFound)
     212             :                 {
     213             :                         showSplashScreen = YES;
     214             :                 }
     215             :                 
     216             :                 // if V-sync is disabled at the command line, override the defaults file
     217             :                 if ([arg isEqual:@"-novsync"] || [arg isEqual:@"--novsync"])  vSyncPreference = NO;
     218             :                 
     219             :                 if ([arg isEqual: @"-hdr"])  bitsPerColorComponent = 16;
     220             : 
     221             :                 // build the startup command string so that we can log it
     222             :                 cmdLineArgsStr = [cmdLineArgsStr stringByAppendingFormat:@"%@ ", arg];
     223             :         }
     224             : 
     225             :         OOLog(@"process.args", @"%@", cmdLineArgsStr);
     226             :         
     227             :         matrixManager = [[OOOpenGLMatrixManager alloc] init];
     228             : 
     229             :         // TODO: This code up to and including stickHandler really ought
     230             :         // not to be in this class.
     231             :         OOLog(@"sdl.init", @"%@", @"initialising SDL");
     232             :         if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0)
     233             :         {
     234             :                 OOLog(@"sdl.init.failed", @"Unable to init SDL: %s\n", SDL_GetError());
     235             :                 [self dealloc];
     236             :                 return nil;
     237             :         }
     238             : 
     239             :         SDL_putenv ("SDL_VIDEO_WINDOW_POS=center");
     240             : 
     241             :         [OOJoystickManager setStickHandlerClass:[OOSDLJoystickManager class]];
     242             :         // end TODO
     243             : 
     244             :         [OOSound setUp];
     245             :         if (![OOSound isSoundOK])  OOLog(@"sound.init", @"%@", @"Sound system disabled.");
     246             : 
     247             :         // Generate the window caption, containing the version number and the date the executable was compiled.
     248             :         static char windowCaption[128];
     249             :         NSString *versionString = [NSString stringWithFormat:@"Oolite v%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]];
     250             : 
     251             :         strcpy (windowCaption, [versionString UTF8String]);
     252             :         strcat (windowCaption, " - "__DATE__);
     253             :         SDL_WM_SetCaption (windowCaption, "Oolite");  // Set window title.
     254             : 
     255             : #if OOLITE_WINDOWS
     256             :         // needed for enabling system window manager events, which is needed for handling window movement messages
     257             :         SDL_EventState (SDL_SYSWMEVENT, SDL_ENABLE);
     258             :         
     259             :         //capture the window handle for later
     260             :         static SDL_SysWMinfo wInfo;
     261             :         SDL_VERSION(&wInfo.version);
     262             :         SDL_GetWMInfo(&wInfo);
     263             :         SDL_Window   = wInfo.window;
     264             :         
     265             :         // This must be inited after SDL_Window has been set - we need the main window handle in order to get monitor info
     266             :         if (![self getCurrentMonitorInfo:&monitorInfo])
     267             :         {
     268             :                 OOLogWARN(@"display.initGL.monitorInfoWarning", @"Could not get current monitor information.");
     269             :         }
     270             : 
     271             :         atDesktopResolution = YES;
     272             :         
     273             : #if USE_UNDOCUMENTED_DARKMODE_API
     274             :         // dark mode stuff - this is mainly for the winodw titlebar's context menu
     275             :         HMODULE hUxTheme = LoadLibraryExW(L"uxtheme.dll", NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
     276             :         if (hUxTheme)
     277             :         {
     278             :                 // hack alert! ordinal 135 is undocumented and could change in a future version of Windows
     279             :                 pfnSetPreferredAppMode SetPreferredAppMode = (pfnSetPreferredAppMode)GetProcAddress(hUxTheme, MAKEINTRESOURCEA(135));
     280             :                 if (SetPreferredAppMode)  SetPreferredAppMode(AllowDark);
     281             :                 FreeLibrary(hUxTheme);
     282             :         }
     283             : #endif
     284             : #endif //OOLITE_WINDOWS
     285             : 
     286             :         grabMouseStatus = NO;
     287             : 
     288             :         imagesDir = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Images"];
     289             :         icon = SDL_LoadBMP([[imagesDir stringByAppendingPathComponent:@"WMicon.bmp"] UTF8String]);
     290             : 
     291             :         if (icon != NULL)
     292             :         {
     293             :                 colorkey = SDL_MapRGB(icon->format, 128, 0, 128);
     294             :                 SDL_SetColorKey(icon, SDL_SRCCOLORKEY, colorkey);
     295             :                 SDL_WM_SetIcon(icon, NULL);
     296             :         }
     297             :         SDL_FreeSurface(icon);
     298             : 
     299             :         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, bitsPerColorComponent);
     300             :         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, bitsPerColorComponent);
     301             :         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, bitsPerColorComponent);
     302             :         SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, bitsPerColorComponent);
     303             :         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
     304             :         SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
     305             :         
     306             :         _colorSaturation = 1.0f;
     307             :         
     308             :         _hdrOutput = NO;
     309             : #if OOLITE_WINDOWS
     310             :         _hdrMaxBrightness = [prefs oo_floatForKey:@"hdr-max-brightness" defaultValue:1000.0f];
     311             :         _hdrPaperWhiteBrightness = [prefs oo_floatForKey:@"hdr-paperwhite-brightness" defaultValue:200.0f];
     312             :         if (bitsPerColorComponent == 16)
     313             :         {
     314             :                 // SDL.dll built specifically for Oolite required
     315             :                 SDL_GL_SetAttribute(SDL_GL_PIXEL_TYPE_FLOAT, 1);
     316             :                 _hdrOutput = YES;
     317             :         }
     318             : #endif
     319             :         
     320             :         // V-sync settings - we set here, but can only verify after SDL_SetVideoMode has been called.
     321             :         SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, vSyncPreference);      // V-sync on by default.
     322             :         OOLog(@"display.initGL", @"V-Sync %@requested.", vSyncPreference ? @"" : @"not ");
     323             :         
     324             :         /* Multisampling significantly improves graphics quality with
     325             :          * basically no extra programming effort on our part, especially
     326             :          * for curved surfaces like the planet, but is also expensive - in
     327             :          * the worst case the entire scene must be rendered four
     328             :          * times. For now it can be a hidden setting. If early testing
     329             :          * doesn't give any problems (other than speed on low-end graphics
     330             :          * cards) a game options entry might be useful. - CIM, 24 Aug 2013*/
     331             :         if ([prefs oo_boolForKey:@"anti-aliasing" defaultValue:NO])
     332             :         {
     333             :                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
     334             :                 SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4);
     335             :         }
     336             : 
     337             :         OOLog(@"display.mode.list", @"%@", @"CREATING MODE LIST");
     338             :         [self populateFullScreenModelist];
     339             :         currentSize = 0;
     340             : 
     341             :         // Find what the full screen and windowed settings are.
     342             :         fullScreen = NO;
     343             :         [self loadFullscreenSettings];
     344             :         [self loadWindowSize];
     345             : 
     346             :         // Set up the drawing surface's dimensions.
     347             :         firstScreen= (fullScreen) ? [self modeAsSize: currentSize] : currentWindowSize;
     348             :         viewSize = firstScreen; // viewSize must be set prior to splash screen initialization
     349             : 
     350             :         OOLog(@"display.initGL", @"Trying %d-bpcc, 24-bit depth buffer", bitsPerColorComponent);
     351             :         [self createSurface];
     352             :         
     353             :         if (surface == NULL)
     354             :         {
     355             :                 // Retry with hardcoded 8 bits per color component
     356             :                 OOLog(@"display.initGL", @"%@", @"Trying 8-bpcc, 32-bit depth buffer");
     357             :                 SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
     358             :                 SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
     359             :                 SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
     360             :                 SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
     361             :                 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32);
     362             :                 [self createSurface];
     363             :                 
     364             :                 if (surface == NULL)
     365             :                 {
     366             :                         // Still not working? One last go...
     367             :                         // Retry, allowing 16-bit contexts.
     368             :                         OOLog(@"display.initGL", @"%@", @"Trying 5-bpcc, 16-bit depth buffer");
     369             :                         SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
     370             :                         SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
     371             :                         SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
     372             :                         SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
     373             :                         // and if it's this bad, forget even trying to multisample!
     374             :                         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0);
     375             :                         SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0);
     376             : 
     377             :                         [self createSurface];
     378             : 
     379             :                         if (surface == NULL)
     380             :                         {
     381             :                                 char * errStr = SDL_GetError();
     382             :                                 OOLogERR(@"display.mode.error", @"Could not create display surface: %s", errStr);
     383             : #if OOLITE_WINDOWS
     384             :                                 if (showSplashScreen)
     385             :                                 {
     386             :                                         [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"splash-screen"];
     387             :                                         [[NSUserDefaults standardUserDefaults] synchronize];
     388             :                                         OOLogWARN(@"display.mode.conflict",@"Possible incompatibility between the splash screen and video drivers detected.");
     389             :                                         OOLogWARN(@"display.mode.conflict",@"Oolite will start without showing the splash screen from now on. Override with 'oolite.exe -splash'");
     390             :                                 }
     391             : #endif
     392             :                                 exit(1);
     393             :                         }
     394             :                 }
     395             :         }
     396             :         
     397             :         int testAttrib = -1;
     398             :         OOLog(@"display.initGL", @"%@", @"Achieved color / depth buffer sizes (bits):");
     399             :         SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &testAttrib);
     400             :         OOLog(@"display.initGL", @"Red: %d", testAttrib);
     401             :         SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &testAttrib);
     402             :         OOLog(@"display.initGL", @"Green: %d", testAttrib);
     403             :         SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &testAttrib);
     404             :         OOLog(@"display.initGL", @"Blue: %d", testAttrib);
     405             :         SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &testAttrib);
     406             :         OOLog(@"display.initGL", @"Alpha: %d", testAttrib);
     407             :         SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &testAttrib);
     408             :         OOLog(@"display.initGL", @"Depth Buffer: %d", testAttrib);
     409             : #if OOLITE_WINDOWS
     410             :         SDL_GL_GetAttribute(SDL_GL_PIXEL_TYPE_FLOAT, &testAttrib);
     411             :         OOLog(@"display.initGL", @"Pixel type is float : %d", testAttrib);
     412             : 
     413             :         OOLog(@"display.initGL", @"Pixel format index: %d", GetPixelFormat(GetDC(SDL_Window)));
     414             : #endif
     415             :         
     416             :         // Verify V-sync successfully set - report it if not
     417             :         if (vSyncPreference && SDL_GL_GetAttribute(SDL_GL_SWAP_CONTROL, &vSyncValue) == -1)
     418             :         {
     419             :                 OOLogWARN(@"display.initGL", @"Could not enable V-Sync. Please check that your graphics driver supports the %@_swap_control extension.",
     420             :                                         OOLITE_WINDOWS ? @"WGL_EXT" : @"[GLX_SGI/GLX_MESA]");
     421             :         }
     422             : 
     423             :         bounds.size.width = surface->w;
     424             :         bounds.size.height = surface->h;
     425             : 
     426             :         [self autoShowMouse];
     427             : 
     428             :         virtualJoystickPosition = NSMakePoint(0.0,0.0);
     429             :         mouseWarped = NO;
     430             :         
     431             :         _mouseVirtualStickSensitivityFactor = OOClamp_0_1_f([prefs oo_floatForKey:@"mouse-flight-sensitivity" defaultValue:0.95f]);
     432             :         // ensure no chance of a divide by zero later on
     433             :         if (_mouseVirtualStickSensitivityFactor < 0.005f)  _mouseVirtualStickSensitivityFactor = 0.005f;
     434             : 
     435             :         typedString = [[NSMutableString alloc] initWithString:@""];
     436             :         allowingStringInput = gvStringInputNo;
     437             :         isAlphabetKeyDown = NO;
     438             : 
     439             :         timeIntervalAtLastClick = timeSinceLastMouseWheel = [NSDate timeIntervalSinceReferenceDate];
     440             :         
     441             :         _mouseWheelDelta = 0.0f;
     442             : 
     443             :         m_glContextInitialized = NO;
     444             : 
     445             :         return self;
     446             : }
     447             : 
     448             : - (void) endSplashScreen
     449             : {
     450             : #if OOLITE_WINDOWS
     451             :         if ([self hdrOutput] && ![self isOutputDisplayHDREnabled])
     452             :         {
     453             :                 if (MessageBox(NULL,    "No primary display in HDR mode was detected.\n\n"
     454             :                                                         "If you continue, graphics will not be rendered as intended.\n"
     455             :                                                         "Click OK to launch anyway, or Cancel to exit.", "oolite.exe - HDR requested",
     456             :                                                         MB_OKCANCEL | MB_ICONWARNING) == IDCANCEL)
     457             :                 {
     458             :                         exit (1);
     459             :                 }
     460             :         }
     461             : #endif // OOLITE_WINDOWS
     462             :         
     463             :         if (!showSplashScreen) return;
     464             : 
     465             : #if OOLITE_WINDOWS
     466             : 
     467             :         wasFullScreen = !fullScreen;
     468             :         updateContext = YES;
     469             :         ShowWindow(SDL_Window,SW_RESTORE);
     470             :         [self initialiseGLWithSize: firstScreen];
     471             : 
     472             : #else
     473             : 
     474             :         int videoModeFlags = SDL_HWSURFACE | SDL_OPENGL;
     475             : 
     476             :         videoModeFlags |= (fullScreen) ? SDL_FULLSCREEN : SDL_RESIZABLE;
     477             :         surface = SDL_SetVideoMode(firstScreen.width, firstScreen.height, 32, videoModeFlags);
     478             : 
     479             :         if (!surface && fullScreen == YES)
     480             :         {
     481             :                 [self setFullScreenMode: NO];
     482             :                 videoModeFlags &= ~SDL_FULLSCREEN;
     483             :                 videoModeFlags |= SDL_RESIZABLE;
     484             :                 surface = SDL_SetVideoMode(currentWindowSize.width, currentWindowSize.height, 32, videoModeFlags);
     485             :         }
     486             : 
     487             :         SDL_putenv ("SDL_VIDEO_WINDOW_POS=none"); //stop linux from auto centering on resize
     488             : 
     489             :         /* MKW 2011.11.11
     490             :          * Eat all SDL events to gobble up any resize events while the
     491             :          * splash-screen was visible.  They affected the main window after 1.74.
     492             :          * TODO: should really process SDL events while showing the splash-screen
     493             : 
     494             :         int numEvents = 0;
     495             :         */
     496             :         SDL_Event dummyEvent;
     497             :         while (SDL_PollEvent(&dummyEvent))
     498             :         {
     499             :                 /* Do nothing; the below is for development info
     500             :                 numEvents++;
     501             :                 OOLog(@"display.splash", @"Suppressed splash-screen event %d: %d ", numEvents, dummyEvent.type);
     502             :                 */
     503             :         }
     504             : 
     505             : 
     506             : #endif
     507             : 
     508             :         [self updateScreen];
     509             :         [self autoShowMouse];
     510             : }
     511             : 
     512             : 
     513             : - (void) initKeyMappingData
     514             : {
     515             :         NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
     516             :         // load in our keyboard scancode mappings
     517             : #if OOLITE_WINDOWS      
     518             :         NSDictionary *kmap = [NSDictionary dictionaryWithDictionary:[ResourceManager dictionaryFromFilesNamed:@"keymappings_windows.plist" inFolder:@"Config" mergeMode:MERGE_BASIC cache:NO]];
     519             : #else
     520             :         NSDictionary *kmap = [NSDictionary dictionaryWithDictionary:[ResourceManager dictionaryFromFilesNamed:@"keymappings_linux.plist" inFolder:@"Config" mergeMode:MERGE_BASIC cache:NO]];
     521             : #endif
     522             :         // get the stored keyboard code from preferences
     523             :         NSString *kbd = [prefs oo_stringForKey:@"keyboard-code" defaultValue:@"default"];
     524             :         NSDictionary *subset = [kmap objectForKey:kbd];
     525             : 
     526             :         [keyMappings_normal release];
     527             :         keyMappings_normal = [[subset objectForKey:@"mapping_normal"] copy];
     528             :         [keyMappings_shifted release];
     529             :         keyMappings_shifted = [[subset objectForKey:@"mapping_shifted"] copy];
     530             : }
     531             : 
     532             : 
     533             : - (void) dealloc
     534             : {
     535             :         if (typedString)
     536             :                 [typedString release];
     537             : 
     538             :         if (screenSizes)
     539             :                 [screenSizes release];
     540             : 
     541             :         if (surface != 0)
     542             :         {
     543             :                 SDL_FreeSurface(surface);
     544             :                 surface = 0;
     545             :         }
     546             : 
     547             :         if (keyMappings_normal)
     548             :                 [keyMappings_normal release];
     549             :         
     550             :         if (keyMappings_shifted)
     551             :                 [keyMappings_shifted release];
     552             : 
     553             :         SDL_Quit();
     554             : 
     555             :         if (matrixManager)
     556             :         {
     557             :                 [matrixManager release];
     558             :         }
     559             : 
     560             :         [super dealloc];
     561             : }
     562             : 
     563             : - (void) autoShowMouse
     564             : {
     565             :         //don't touch the 'please wait...' cursor.
     566             :         if (fullScreen)
     567             :         {
     568             :                 if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE)
     569             :                         SDL_ShowCursor(SDL_DISABLE);
     570             :         }
     571             :         else
     572             :         {
     573             :                 if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE)
     574             :                         SDL_ShowCursor(SDL_ENABLE);
     575             :         }
     576             : }
     577             : 
     578             : - (void) setStringInput: (enum StringInput) value
     579             : {
     580             :         allowingStringInput = value;
     581             : }
     582             : 
     583             : 
     584             : - (void) allowStringInput: (BOOL) value
     585             : {
     586             :         if (value)
     587             :                 allowingStringInput = gvStringInputAlpha;
     588             :         else
     589             :                 allowingStringInput = gvStringInputNo;
     590             : }
     591             : 
     592             : -(enum StringInput) allowingStringInput
     593             : {
     594             :         return allowingStringInput;
     595             : }
     596             : 
     597             : 
     598             : - (NSString *) typedString
     599             : {
     600             :         return typedString;
     601             : }
     602             : 
     603             : 
     604             : - (void) resetTypedString
     605             : {
     606             :         [typedString setString:@""];
     607             : }
     608             : 
     609             : 
     610             : - (void) setTypedString:(NSString*) value
     611             : {
     612             :         [typedString setString:value];
     613             : }
     614             : 
     615             : 
     616             : - (NSRect) bounds
     617             : {
     618             :         return bounds;
     619             : }
     620             : 
     621             : 
     622             : - (NSSize) viewSize
     623             : {
     624             :         return viewSize;
     625             : }
     626             : 
     627             : 
     628             : - (NSSize) backingViewSize
     629             : {
     630             :         return viewSize;
     631             : }
     632             : 
     633             : 
     634             : - (GLfloat) display_z
     635             : {
     636             :         return display_z;
     637             : }
     638             : 
     639             : 
     640             : - (GLfloat) x_offset
     641             : {
     642             :         return x_offset;
     643             : }
     644             : 
     645             : 
     646             : - (GLfloat) y_offset
     647             : {
     648             :         return y_offset;
     649             : }
     650             : 
     651             : 
     652             : - (GameController *) gameController
     653             : {
     654             :         return gameController;
     655             : }
     656             : 
     657             : 
     658             : - (void) setGameController:(GameController *) controller
     659             : {
     660             :         gameController = controller;
     661             : }
     662             : 
     663             : 
     664             : - (void) noteMouseInteractionModeChangedFrom:(OOMouseInteractionMode)oldMode to:(OOMouseInteractionMode)newMode
     665             : {
     666             :         [self autoShowMouse];
     667             :         [self setMouseInDeltaMode:OOMouseInteractionModeIsFlightMode(newMode)];
     668             : }
     669             : 
     670             : 
     671             : - (BOOL) inFullScreenMode
     672             : {
     673             :         return fullScreen;
     674             : }
     675             : 
     676             : #ifdef GNUSTEP
     677             : - (void) setFullScreenMode:(BOOL)fsm
     678             : {
     679             :         fullScreen = fsm;
     680             : 
     681             :         // Save the settings for later.
     682             :         [[NSUserDefaults standardUserDefaults]
     683             :                 setBool: fullScreen forKey:@"fullscreen"];
     684             :         [[NSUserDefaults standardUserDefaults] synchronize];
     685             : }
     686             : 
     687             : 
     688             : - (void) toggleScreenMode
     689             : {
     690             :         [self setFullScreenMode: !fullScreen];
     691             : #if OOLITE_WINDOWS
     692             :         [self getCurrentMonitorInfo:&monitorInfo];
     693             : #endif
     694             :         if(fullScreen)
     695             :         {
     696             : #if OOLITE_WINDOWS
     697             :                 if(![self isRunningOnPrimaryDisplayDevice])
     698             :                 {
     699             :                         [self initialiseGLWithSize:NSMakeSize(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left,
     700             :                                                                                                 monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top)];
     701             :                 }
     702             :                 else  [self initialiseGLWithSize:[self modeAsSize: currentSize]];
     703             : #else
     704             :                 [self initialiseGLWithSize:[self modeAsSize: currentSize]];
     705             : #endif
     706             :         }
     707             :         else
     708             :                 [self initialiseGLWithSize: currentWindowSize];
     709             : 
     710             : 
     711             :         // do screen resizing updates
     712             :         if ([PlayerEntity sharedPlayer])
     713             :         {
     714             :                 [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates];
     715             :         }
     716             : }
     717             : 
     718             : 
     719             : - (void) setDisplayMode:(int)mode  fullScreen:(BOOL)fsm
     720             : {
     721             :         [self setFullScreenMode: fsm];
     722             :         currentSize=mode;
     723             :         if(fullScreen)
     724             :                 [self initialiseGLWithSize: [self modeAsSize: mode]];
     725             : }
     726             : 
     727             : 
     728             : - (int) indexOfCurrentSize
     729             : {
     730             :         return currentSize;
     731             : }
     732             : 
     733             : 
     734             : - (void) setScreenSize: (int)sizeIndex
     735             : {
     736             :         currentSize=sizeIndex;
     737             :         if(fullScreen)
     738             :                 [self initialiseGLWithSize: [self modeAsSize: currentSize]];
     739             : }
     740             : 
     741             : 
     742             : - (NSMutableArray *)getScreenSizeArray
     743             : {
     744             :         return screenSizes;
     745             : }
     746             : 
     747             : 
     748             : - (NSSize) modeAsSize:(int)sizeIndex
     749             : {
     750             :         NSDictionary *mode=[screenSizes objectAtIndex: sizeIndex];
     751             :         return NSMakeSize([[mode objectForKey: kOODisplayWidth] intValue],
     752             :                         [[mode objectForKey: kOODisplayHeight] intValue]);
     753             : }
     754             : 
     755             : #endif
     756             : 
     757             : - (void) display
     758             : {
     759             :         [self updateScreen];
     760             : }
     761             : 
     762             : - (void) updateScreen
     763             : {
     764             :         [self drawRect: NSMakeRect(0, 0, viewSize.width, viewSize.height)];
     765             : }
     766             : 
     767             : - (void) drawRect:(NSRect)rect
     768             : {
     769             :         [self updateScreenWithVideoMode:YES];
     770             : }
     771             : 
     772             : - (void) updateScreenWithVideoMode:(BOOL) v_mode
     773             : {
     774             :         if ((viewSize.width != surface->w)||(viewSize.height != surface->h)) // resized
     775             :         {
     776             : #if OOLITE_LINUX
     777             :                 m_glContextInitialized = NO; //probably not needed
     778             : #endif
     779             :                 viewSize.width = surface->w;
     780             :                 viewSize.height = surface->h;
     781             :         }
     782             : 
     783             :     if (m_glContextInitialized == NO)
     784             :         {
     785             :                 [self initialiseGLWithSize:viewSize useVideoMode:v_mode];
     786             :         }
     787             : 
     788             :         if (surface == 0)
     789             :                 return;
     790             : 
     791             :         // do all the drawing!
     792             :         //
     793             :         if (UNIVERSE)  [UNIVERSE drawUniverse];
     794             :         else
     795             :         {
     796             :                 // not set up yet, draw a black screen
     797             :                 glClearColor( 0.0, 0.0, 0.0, 0.0);
     798             :                 glClear( GL_COLOR_BUFFER_BIT);
     799             :         }
     800             : 
     801             :         SDL_GL_SwapBuffers();
     802             : }
     803             : 
     804             : - (void) initSplashScreen
     805             : {
     806             :         if (!showSplashScreen) return;
     807             : 
     808             :         //too early for OOTexture!
     809             :         SDL_Surface             *image=NULL;
     810             :         SDL_Rect                        dest;
     811             : 
     812             :         NSString                *imagesDir = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Images"];
     813             : 
     814             :         image = SDL_LoadBMP([[imagesDir stringByAppendingPathComponent:@"splash.bmp"] UTF8String]);
     815             : 
     816             :         if (image == NULL)
     817             :         {
     818             :                 SDL_FreeSurface(image);
     819             :                 OOLogWARN(@"sdl.gameStart", @"%@", @"image 'splash.bmp' not found!");
     820             :                 [self endSplashScreen];
     821             :                 return;
     822             :         }
     823             : 
     824             :         dest.x = 0;
     825             :         dest.y = 0;
     826             :         dest.w = image->w;
     827             :         dest.h = image->h;
     828             : 
     829             :   #if OOLITE_WINDOWS
     830             : 
     831             :         dest.x = (GetSystemMetrics(SM_CXSCREEN)- dest.w)/2;
     832             :         dest.y = (GetSystemMetrics(SM_CYSCREEN)-dest.h)/2;
     833             :         SetWindowLong(SDL_Window,GWL_STYLE,GetWindowLong(SDL_Window,GWL_STYLE) & ~WS_CAPTION & ~WS_THICKFRAME);
     834             :         ShowWindow(SDL_Window,SW_RESTORE);
     835             :         MoveWindow(SDL_Window,dest.x,dest.y,dest.w,dest.h,TRUE);
     836             : 
     837             :   #else
     838             : 
     839             :         /* MKW 2011.11.11
     840             :          * According to Marc using the NOFRAME flag causes trouble under Ubuntu 8.04.
     841             :          *
     842             :          * The current Ubuntu LTS is 10.04, which doesn't seem to have that problem.
     843             :          * 12.04 LTS is going to be released soon, also without apparent problems.
     844             :          * Changed to SDL_NOFRAME, throwing caution to the wind - Kaks 2012.03.23
     845             :          * Took SDL_NOFRAME out, since it still causes strange problems here - cim 2012.04.09
     846             :          */
     847             :          surface = SDL_SetVideoMode(dest.w, dest.h, 32, SDL_HWSURFACE | SDL_OPENGL);
     848             : 
     849             :   #endif
     850             : 
     851             :         OOSetOpenGLState(OPENGL_STATE_OVERLAY);
     852             : 
     853             :         glViewport( 0, 0, dest.w, dest.h);
     854             : 
     855             :         glEnable( GL_TEXTURE_2D );
     856             :         glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
     857             :         glClear( GL_COLOR_BUFFER_BIT );
     858             : 
     859             :         [matrixManager resetProjection];
     860             :         [matrixManager orthoLeft: 0.0f right: dest.w bottom: dest.h top: 0.0 near: -1.0 far: 1.0];
     861             :         [matrixManager syncProjection];
     862             : 
     863             :         [matrixManager resetModelView];
     864             :         [matrixManager syncModelView];
     865             : 
     866             :         GLuint texture;
     867             :         GLenum texture_format;
     868             :         GLint  nOfColors;
     869             : 
     870             :         // get the number of channels in the SDL image
     871             :         nOfColors = image->format->BytesPerPixel;
     872             :         if (nOfColors == 4)     // contains an alpha channel
     873             :         {
     874             :                 if (image->format->Rmask == 0x000000ff)
     875             :                         texture_format = GL_RGBA;
     876             :                 else
     877             :                         texture_format = GL_BGRA;
     878             :         }
     879             :         else if (nOfColors == 3)     // no alpha channel
     880             :         {
     881             :                 if (image->format->Rmask == 0x000000ff)
     882             :                         texture_format = GL_RGB;
     883             :                 else
     884             :                         texture_format = GL_BGR;
     885             :         } else {
     886             :                 SDL_FreeSurface(image);
     887             :                 OOLog(@"Sdl.GameStart", @"%@", @"----- Encoding error within image 'splash.bmp'");
     888             :                 [self endSplashScreen];
     889             :                 return;
     890             :         }
     891             : 
     892             :         glGenTextures( 1, &texture );
     893             :         glBindTexture( GL_TEXTURE_2D, texture );
     894             : 
     895             :         // Set the texture's stretching properties
     896             :         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
     897             :         glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
     898             : 
     899             :         // Set the texture image data with the information  from SDL_Surface
     900             :         glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, image->w, image->h, 0,
     901             :                       texture_format, GL_UNSIGNED_BYTE, image->pixels );
     902             : 
     903             :         glBindTexture( GL_TEXTURE_2D, texture );
     904             :         glBegin( GL_QUADS );
     905             : 
     906             :         glTexCoord2i( 0, 0 );
     907             :         glVertex2i( 0, 0 );
     908             :         glTexCoord2i( 1, 0 );
     909             :         glVertex2i( dest.w, 0 );
     910             :         glTexCoord2i( 1, 1 );
     911             :         glVertex2i( dest.w, dest.h );
     912             :         glTexCoord2i( 0, 1 );
     913             :         glVertex2i( 0, dest.h );
     914             : 
     915             :         glEnd();
     916             : 
     917             :         SDL_GL_SwapBuffers();
     918             :         [matrixManager resetModelView];
     919             :         [matrixManager syncModelView];
     920             : 
     921             :         if ( image ) {
     922             :                 SDL_FreeSurface( image );
     923             :         }
     924             :         glDeleteTextures(1, &texture);
     925             : 
     926             :         glDisable( GL_TEXTURE_2D );
     927             :         OOVerifyOpenGLState();
     928             : }
     929             : 
     930             : 
     931             : #if OOLITE_WINDOWS
     932             : - (MONITORINFOEX) currentMonitorInfo
     933             : {
     934             :         return monitorInfo;
     935             : }
     936             : 
     937             : 
     938             : - (BOOL) getCurrentMonitorInfo:(MONITORINFOEX *)mInfo
     939             : {
     940             :         HMONITOR hMon = MonitorFromWindow(SDL_Window, MONITOR_DEFAULTTOPRIMARY);
     941             :         ZeroMemory(mInfo, sizeof(MONITORINFOEX));
     942             :         mInfo->cbSize = sizeof(MONITORINFOEX);
     943             :         if (GetMonitorInfo (hMon, (LPMONITORINFO)mInfo))
     944             :         {
     945             :                 return YES;
     946             :         }
     947             :         return NO;
     948             : }
     949             : 
     950             : 
     951             : - (BOOL) isRunningOnPrimaryDisplayDevice
     952             : {
     953             :         BOOL result = YES;
     954             :         [self getCurrentMonitorInfo:&monitorInfo];
     955             :         if (!(monitorInfo.dwFlags & MONITORINFOF_PRIMARY))
     956             :         {
     957             :                 result = NO;
     958             :         }
     959             :         return result;
     960             : }
     961             : 
     962             : 
     963             : - (void) grabMouseInsideGameWindow:(BOOL) value
     964             : {
     965             :         if(value)
     966             :         {
     967             :                 RECT gameWindowRect;
     968             :                 GetWindowRect(SDL_Window, &gameWindowRect);
     969             :                 ClipCursor(&gameWindowRect);
     970             :         }
     971             :         else
     972             :         {
     973             :                 ClipCursor(NULL);
     974             :         }
     975             :         grabMouseStatus = !!value;
     976             : }
     977             : 
     978             : 
     979             : - (void) stringToClipboard:(NSString *)stringToCopy
     980             : {
     981             :         if (stringToCopy)
     982             :         {
     983             :                 const char *clipboardText = [stringToCopy cStringUsingEncoding:NSUTF8StringEncoding];
     984             :                 const size_t clipboardTextLength = strlen(clipboardText) + 1;
     985             :                 HGLOBAL clipboardMem = GlobalAlloc(GMEM_MOVEABLE, clipboardTextLength);
     986             :                 if (clipboardMem)
     987             :                 {
     988             :                         memcpy(GlobalLock(clipboardMem), clipboardText, clipboardTextLength);
     989             :                         GlobalUnlock(clipboardMem);
     990             :                         OpenClipboard(0);
     991             :                         EmptyClipboard();
     992             :                         if (!SetClipboardData(CF_TEXT, clipboardMem))
     993             :                         {
     994             :                                 OOLog(@"stringToClipboard.failed", @"Failed to copy string %@ to clipboard", stringToCopy);
     995             :                                 // free global allocated memory if clipboard copy failed
     996             :                                 // note: no need to free it if copy succeeded; the OS becomes
     997             :                                 // the owner of the copied memory once SetClipboardData has
     998             :                                 // been executed successfully
     999             :                                 GlobalFree(clipboardMem);
    1000             :                         }
    1001             :                         CloseClipboard();
    1002             :                 }
    1003             :         }
    1004             : }
    1005             : 
    1006             : 
    1007             : - (void) resetSDLKeyModifiers
    1008             : {
    1009             :         // this is used when we regain focus to ensure that all
    1010             :         // modifier keys are reset to their correct status
    1011             :         SDLMod modState = SDL_GetModState();
    1012             :         Uint8 *keyState = SDL_GetKeyState(NULL);
    1013             :         BYTE keyboardStatus[256];
    1014             :         #define OO_RESET_SDLKEY_MODIFIER(vkCode, kModCode, sdlkCode)    do {\
    1015             :         if (keyboardStatus[vkCode] & 0x0080) \
    1016             :         { \
    1017             :                 modState |= kModCode; \
    1018             :                 keyState[sdlkCode] = SDL_PRESSED; \
    1019             :         } \
    1020             :         else \
    1021             :         { \
    1022             :                 modState &= ~kModCode; \
    1023             :                 keyState[sdlkCode] = SDL_RELEASED; \
    1024             :         } \
    1025             :         } while(0)
    1026             :         if (GetKeyboardState(keyboardStatus))
    1027             :         {
    1028             :                 // A bug noted here https://github.com/libsdl-org/SDL-1.2/issues/449
    1029             :                 // was patched in SDL here https://github.com/libsdl-org/SDL-1.2/commit/09980c67290f11c3d088a6a039c550be83536c81
    1030             :                 // This was replicated in our SDL binary (Windows-deps rev. 36fd5e6),
    1031             :                 // so we no longer need to check the state of Alt when returning to the app.
    1032             :                 // SDL change researched and implemented by Nikos 20220622.
    1033             :                 // Alt key
    1034             :                 //OO_RESET_SDLKEY_MODIFIER(VK_LMENU, KMOD_LALT, SDLK_LALT);
    1035             :                 //OO_RESET_SDLKEY_MODIFIER(VK_RMENU, KMOD_RALT, SDLK_RALT);
    1036             :                 //opt =  (modState & KMOD_LALT || modState & KMOD_RALT);
    1037             :                 
    1038             :                 //Ctrl key
    1039             :                 OO_RESET_SDLKEY_MODIFIER(VK_LCONTROL, KMOD_LCTRL, SDLK_LCTRL);
    1040             :                 OO_RESET_SDLKEY_MODIFIER(VK_RCONTROL, KMOD_RCTRL, SDLK_RCTRL);
    1041             :                 ctrl =  (modState & KMOD_LCTRL || modState & KMOD_RCTRL);
    1042             :                 
    1043             :                 // Shift key
    1044             :                 OO_RESET_SDLKEY_MODIFIER(VK_LSHIFT, KMOD_LSHIFT, SDLK_LSHIFT);
    1045             :                 OO_RESET_SDLKEY_MODIFIER(VK_RSHIFT, KMOD_RSHIFT, SDLK_RSHIFT);
    1046             :                 shift =  (modState & KMOD_LSHIFT || modState & KMOD_RSHIFT);
    1047             :                 
    1048             :                 // Caps Lock key state
    1049             :                 if (GetKeyState(VK_CAPITAL) & 0x0001)
    1050             :                 {
    1051             :                         modState |= KMOD_CAPS;
    1052             :                         keyState[SDLK_CAPSLOCK] = SDL_PRESSED;
    1053             :                 }
    1054             :                 else
    1055             :                 {
    1056             :                         modState &= ~KMOD_CAPS;
    1057             :                         keyState[SDLK_CAPSLOCK] = SDL_RELEASED;
    1058             :                 }
    1059             :         }
    1060             :         
    1061             :         SDL_SetModState(modState);
    1062             : }
    1063             : 
    1064             : 
    1065             : - (void) setWindowBorderless:(BOOL)borderless
    1066             : {
    1067             :         LONG currentWindowStyle = GetWindowLong(SDL_Window, GWL_STYLE);
    1068             :         
    1069             :         // window already has the desired style?
    1070             :         if ((!borderless && (currentWindowStyle & WS_CAPTION)) ||
    1071             :                 (borderless && !(currentWindowStyle & WS_CAPTION)))  return;
    1072             :                 
    1073             :         if (borderless)
    1074             :         {
    1075             :                 SetWindowLong(SDL_Window, GWL_STYLE, currentWindowStyle & ~WS_CAPTION & ~WS_THICKFRAME);
    1076             :         }
    1077             :         else
    1078             :         {
    1079             :                 SetWindowLong(SDL_Window, GWL_STYLE, currentWindowStyle |
    1080             :                                                 WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX );
    1081             :                 [self refreshDarKOrLightMode];
    1082             :         }
    1083             :         SetWindowPos(SDL_Window, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
    1084             : }
    1085             : 
    1086             : 
    1087             : - (void) refreshDarKOrLightMode
    1088             : {
    1089             :         int shouldSetDarkMode = [self isDarkModeOn];
    1090             :         DwmSetWindowAttribute (SDL_Window, DWMWA_USE_IMMERSIVE_DARK_MODE, &shouldSetDarkMode, sizeof(shouldSetDarkMode));
    1091             : }
    1092             : 
    1093             : 
    1094             : - (BOOL) isDarkModeOn
    1095             : {
    1096             :         char buffer[4];
    1097             :         DWORD bufferSize = sizeof(buffer);
    1098             :         
    1099             :         // reading a REG_DWORD value from the Registry
    1100             :         HRESULT resultRegGetValue = RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize",
    1101             :                                                                         L"AppsUseLightTheme", RRF_RT_REG_DWORD, NULL, buffer, &bufferSize);
    1102             :         if (resultRegGetValue != ERROR_SUCCESS)
    1103             :         {
    1104             :                 return NO;
    1105             :         }
    1106             :         
    1107             :         // get our 4 obtained bytes into integer little endian format
    1108             :         int i = (int)(buffer[3] << 24 | buffer[2] << 16 | buffer[1] << 8 | buffer[0]);
    1109             :         
    1110             :         // dark mode is 0, light mode is 1
    1111             :         return i == 0;
    1112             : }
    1113             : 
    1114             : 
    1115             : - (BOOL) atDesktopResolution
    1116             : {
    1117             :         return atDesktopResolution;
    1118             : }
    1119             : 
    1120             : 
    1121             : - (BOOL) hdrOutput
    1122             : {
    1123             :         return _hdrOutput;
    1124             : }
    1125             : 
    1126             : 
    1127             : - (BOOL) isOutputDisplayHDREnabled
    1128             : {
    1129             :         UINT32 pathCount, modeCount;
    1130             :         DISPLAYCONFIG_PATH_INFO *pPathInfoArray;
    1131             :         DISPLAYCONFIG_MODE_INFO *pModeInfoArray;
    1132             :         UINT32 flags = QDC_ONLY_ACTIVE_PATHS | QDC_VIRTUAL_MODE_AWARE;
    1133             :         LONG tempResult = ERROR_SUCCESS;
    1134             :         BOOL isAdvColorInfo2DetectionSuccess = NO;
    1135             :         BOOL result = NO;
    1136             :         
    1137             :         do
    1138             :         {
    1139             :                 // determine how many path and mode structures to allocate
    1140             :                 tempResult = GetDisplayConfigBufferSizes(flags, &pathCount, &modeCount);
    1141             :                 
    1142             :                 if (tempResult != ERROR_SUCCESS)
    1143             :                 {
    1144             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: %d", HRESULT_FROM_WIN32(tempResult));
    1145             :                         return NO;
    1146             :                 }
    1147             :                 
    1148             :                 // allocate the path and mode arrays
    1149             :                 pPathInfoArray = (DISPLAYCONFIG_PATH_INFO *)malloc(pathCount * sizeof(DISPLAYCONFIG_PATH_INFO));
    1150             :                 if (!pPathInfoArray)
    1151             :                 {
    1152             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: -1");
    1153             :                         return NO;
    1154             :                 }
    1155             :                 
    1156             :                 pModeInfoArray = (DISPLAYCONFIG_MODE_INFO *)malloc(modeCount * sizeof(DISPLAYCONFIG_MODE_INFO));
    1157             :                 if (!pModeInfoArray)
    1158             :                 {
    1159             :                         if (pPathInfoArray)
    1160             :                                 free(pPathInfoArray);
    1161             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: -1");
    1162             :                         return NO;
    1163             :                 }
    1164             :                 
    1165             :                 // get all active paths and their modes
    1166             :                 tempResult = QueryDisplayConfig(flags, &pathCount, pPathInfoArray, &modeCount, pModeInfoArray, NULL);
    1167             :                 
    1168             :                 if (tempResult != ERROR_SUCCESS)
    1169             :                 {
    1170             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: %d", HRESULT_FROM_WIN32(tempResult));
    1171             :                         return NO;
    1172             :                 }
    1173             :         
    1174             :                 // the function may have returned fewer paths/modes than estimated
    1175             :                 pPathInfoArray = realloc(pPathInfoArray, pathCount * sizeof(DISPLAYCONFIG_PATH_INFO));
    1176             :                 if (!pPathInfoArray)
    1177             :                 {
    1178             :                         OOLogERR(@"gameView.isOutputDisplayHDREnabled", @"Failed ro reallocate pPathInfoArray");
    1179             :                         exit (1);
    1180             :                 }
    1181             :                 pModeInfoArray = realloc(pModeInfoArray, modeCount * sizeof(DISPLAYCONFIG_MODE_INFO));
    1182             :                 if (!pModeInfoArray)
    1183             :                 {
    1184             :                         OOLogERR(@"gameView.isOutputDisplayHDREnabled", @"Failed to reallocate pModeInfoArray");
    1185             :                         exit (1);
    1186             :                 }
    1187             :         
    1188             :                 // it's possible that between the call to GetDisplayConfigBufferSizes and QueryDisplayConfig
    1189             :                 // that the display state changed, so loop on the case of ERROR_INSUFFICIENT_BUFFER.
    1190             :         } while (tempResult == ERROR_INSUFFICIENT_BUFFER);
    1191             :         
    1192             :         if (tempResult != ERROR_SUCCESS)
    1193             :         {
    1194             :                 OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: %d", HRESULT_FROM_WIN32(tempResult));
    1195             :                 return NO;
    1196             :         }
    1197             : 
    1198             :     // for each active path
    1199             :         int i;
    1200             :         for (i = 0; i < pathCount; i++)
    1201             :         {
    1202             :                 DISPLAYCONFIG_PATH_INFO *path = &pPathInfoArray[i];
    1203             :                 // find the target (monitor) friendly name
    1204             :                 DISPLAYCONFIG_TARGET_DEVICE_NAME targetName = {};
    1205             :                 targetName.header.adapterId = path->targetInfo.adapterId;
    1206             :                 targetName.header.id = path->targetInfo.id;
    1207             :                 targetName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
    1208             :                 targetName.header.size = sizeof(targetName);
    1209             :                 tempResult = DisplayConfigGetDeviceInfo(&targetName.header);
    1210             :                 
    1211             :                 if (tempResult != ERROR_SUCCESS)
    1212             :                 {
    1213             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: %d", HRESULT_FROM_WIN32(tempResult));
    1214             :                         return NO;
    1215             :                 }
    1216             :                 
    1217             :                 // find the advanced color information using the more reliable advanced color info 2 api
    1218             :                 DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO_2 advColorInfo2 = {};
    1219             :                 advColorInfo2.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO_2;
    1220             :                 advColorInfo2.header.adapterId = path->targetInfo.adapterId;
    1221             :                 advColorInfo2.header.id = path->targetInfo.id;
    1222             :                 advColorInfo2.header.size = sizeof(advColorInfo2);
    1223             :                 
    1224             :                 tempResult = DisplayConfigGetDeviceInfo(&advColorInfo2.header);
    1225             :                 
    1226             :                 if (tempResult == ERROR_SUCCESS)  isAdvColorInfo2DetectionSuccess = YES;
    1227             :                 else
    1228             :                 {
    1229             :                         OOLogWARN(@"gameView.isOutputDisplayHDREnabled", @"Received 0x%08X while attempting to detect HDR mode using Advanced Color Info 2 API. Retrying detection using legacy API.", HRESULT_FROM_WIN32(tempResult));
    1230             :                         // no return, just fall through and try again using standard advanced color info api
    1231             :                 }
    1232             :                 
    1233             :                 // find the advanced color information
    1234             :                 DISPLAYCONFIG_GET_ADVANCED_COLOR_INFO advColorInfo = {};
    1235             :                 advColorInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_ADVANCED_COLOR_INFO;
    1236             :                 advColorInfo.header.adapterId = path->targetInfo.adapterId;
    1237             :                 advColorInfo.header.id = path->targetInfo.id;
    1238             :                 advColorInfo.header.size = sizeof(advColorInfo);
    1239             :                 
    1240             :                 tempResult = DisplayConfigGetDeviceInfo(&advColorInfo.header);
    1241             :                 
    1242             :                 if (tempResult != ERROR_SUCCESS)
    1243             :                 {
    1244             :                         OOLog(@"gameView.isOutputDisplayHDREnabled", @"Error! Code: %d", HRESULT_FROM_WIN32(tempResult));
    1245             :                         return NO;
    1246             :                 }
    1247             :                 
    1248             :                 char saveDeviceName[64];
    1249             :                 wchar_t wcsDeviceID[256];
    1250             :                 DISPLAY_DEVICE dd;
    1251             :                 ZeroMemory(&dd, sizeof(dd));
    1252             :                 dd.cb = sizeof(dd);
    1253             :                 EnumDisplayDevices(NULL, i, &dd, 0);
    1254             :                 BOOL isPrimaryDisplayDevice = dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE;
    1255             :                 // second call to EnumDisplayDevices gets us the monitor device ID
    1256             :                 strncpy(saveDeviceName, dd.DeviceName, 33);
    1257             :                 EnumDisplayDevices(saveDeviceName, 0, &dd, 0x00000001);
    1258             :                 mbstowcs(wcsDeviceID, dd.DeviceID, 129);
    1259             :                 
    1260             :                 // we are starting om the primary device, so check that one for advanced color support
    1261             :                 // we also ensure that wide color gamut SDR displays do not get incorrectly detected as supporting HDR 
    1262             :                 // just to be safe, ensure that the monitor device from QDC being checked is the same as the one from EnumDisplayDevices
    1263             :                 if (isPrimaryDisplayDevice && !wcscmp(targetName.monitorDevicePath, wcsDeviceID) && 
    1264             :                         ((isAdvColorInfo2DetectionSuccess && advColorInfo2.highDynamicRangeSupported && advColorInfo2.activeColorMode == DISPLAYCONFIG_ADVANCED_COLOR_MODE_HDR) ||
    1265             :                         (!isAdvColorInfo2DetectionSuccess && advColorInfo.advancedColorSupported && advColorInfo.advancedColorEnabled && !advColorInfo.wideColorEnforced)))
    1266             :                 {
    1267             :                         result = YES;
    1268             :                         break;
    1269             :                 }
    1270             :         }
    1271             :         
    1272             :         OOLog(@"gameView.isOutputDisplayHDREnabled", @"HDR display output requested - checking availability: %@", result ? @"YES" : @"NO");
    1273             :         
    1274             :         free (pModeInfoArray);
    1275             :         free (pPathInfoArray);
    1276             : 
    1277             :         return result;
    1278             : }
    1279             : 
    1280             : 
    1281             : - (float) hdrMaxBrightness
    1282             : {
    1283             :         return _hdrMaxBrightness;
    1284             : }
    1285             : 
    1286             : 
    1287             : - (void) setHDRMaxBrightness: (float)newMaxBrightness
    1288             : {
    1289             :         if (newMaxBrightness < MIN_HDR_MAXBRIGHTNESS)  newMaxBrightness = MIN_HDR_MAXBRIGHTNESS;
    1290             :         if (newMaxBrightness > MAX_HDR_MAXBRIGHTNESS)  newMaxBrightness = MAX_HDR_MAXBRIGHTNESS;
    1291             :         _hdrMaxBrightness = newMaxBrightness;
    1292             :         
    1293             :         [[NSUserDefaults standardUserDefaults] setFloat:_hdrMaxBrightness forKey:@"hdr-max-brightness"];
    1294             : }
    1295             : 
    1296             : 
    1297             : - (float) hdrPaperWhiteBrightness
    1298             : {
    1299             :         return _hdrPaperWhiteBrightness;
    1300             : }
    1301             : 
    1302             : 
    1303             : - (void) setHDRPaperWhiteBrightness: (float)newPaperWhiteBrightness
    1304             : {
    1305             :         if (newPaperWhiteBrightness < MIN_HDR_PAPERWHITE)  newPaperWhiteBrightness = MIN_HDR_PAPERWHITE;
    1306             :         if (newPaperWhiteBrightness > MAX_HDR_PAPERWHITE)  newPaperWhiteBrightness = MAX_HDR_PAPERWHITE;
    1307             :         _hdrPaperWhiteBrightness = newPaperWhiteBrightness;
    1308             :         
    1309             :         [[NSUserDefaults standardUserDefaults] setFloat:_hdrPaperWhiteBrightness forKey:@"hdr-paperwhite-brightness"];
    1310             : }
    1311             : 
    1312             : 
    1313             : #else   // Linus stub methods
    1314             : 
    1315             : // for Linux we assume we are always on the primary monitor for now
    1316             : - (BOOL) isRunningOnPrimaryDisplayDevice
    1317             : {
    1318             :         return YES;
    1319             : }
    1320             : 
    1321             : 
    1322             : - (void) grabMouseInsideGameWindow:(BOOL) value
    1323             : {
    1324             :         // do nothing
    1325             : }
    1326             : 
    1327             : 
    1328             : - (void) stringToClipboard:(NSString *)stringToCopy
    1329             : {
    1330             :         // TODO: implement string clipboard copy for Linux
    1331             : }
    1332             : 
    1333             : 
    1334           0 : - (void) resetSDLKeyModifiers
    1335             : {
    1336             :         // probably not needed for Linux
    1337             : }
    1338             : 
    1339             : 
    1340           0 : - (void) setWindowBorderless:(BOOL)borderless
    1341             : {
    1342             :         // do nothing on Linux
    1343             : }
    1344             : 
    1345             : 
    1346             : - (BOOL) hdrOutput
    1347             : {
    1348             :         return NO;
    1349             : }
    1350             : 
    1351             : 
    1352             : - (BOOL) isOutputDisplayHDREnabled
    1353             : {
    1354             :         return NO;
    1355             : }
    1356             : 
    1357             : #endif //OOLITE_WINDOWS
    1358             : 
    1359             : 
    1360             : - (void) initialiseGLWithSize:(NSSize) v_size
    1361             : {
    1362             :         [self initialiseGLWithSize:v_size useVideoMode:YES];
    1363             : }
    1364             : 
    1365             : 
    1366             : - (void) initialiseGLWithSize:(NSSize) v_size useVideoMode:(BOOL) v_mode
    1367             : {
    1368             : #if OOLITE_LINUX
    1369             :         NSSize oldViewSize = viewSize;
    1370             : #endif
    1371             :         viewSize = v_size;
    1372             :         OOLog(@"display.initGL", @"Requested a new surface of %d x %d, %@.", (int)viewSize.width, (int)viewSize.height,(fullScreen ? @"fullscreen" : @"windowed"));
    1373             :         SDL_GL_SwapBuffers();   // clear the buffer before resize
    1374             :         
    1375             : #if OOLITE_WINDOWS
    1376             :         if (!updateContext) return;
    1377             : 
    1378             :         DEVMODE settings;
    1379             :         settings.dmSize        = sizeof(DEVMODE);
    1380             :         settings.dmDriverExtra = 0;
    1381             :         EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &settings);
    1382             :         
    1383             :         WINDOWPLACEMENT windowPlacement;
    1384             :         windowPlacement.length = sizeof(WINDOWPLACEMENT);
    1385             :         GetWindowPlacement(SDL_Window, &windowPlacement);
    1386             :         
    1387             :         static BOOL lastWindowPlacementMaximized = NO;
    1388             :         if (fullScreen && (windowPlacement.showCmd == SW_SHOWMAXIMIZED))
    1389             :         {
    1390             :                 if (!wasFullScreen)
    1391             :                 {
    1392             :                         lastWindowPlacementMaximized = YES;
    1393             :                 }
    1394             :         }
    1395             :         
    1396             :         if (lastWindowPlacementMaximized)
    1397             :         {
    1398             :                 windowPlacement.showCmd = SW_SHOWMAXIMIZED;
    1399             :         }
    1400             :         
    1401             :         // are we attempting to go to a different screen resolution? Note: this also takes care of secondary monitor situations because 
    1402             :         // by design the only resolution available for fullscreen on a secondary display device is its native one - Nikos 20150605
    1403             :         BOOL changingResolution =       [self isRunningOnPrimaryDisplayDevice] &&
    1404             :                                                                 ((fullScreen && (settings.dmPelsWidth != viewSize.width || settings.dmPelsHeight != viewSize.height)) ||
    1405             :                                                                 (wasFullScreen && (settings.dmPelsWidth != [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayWidth] intValue]
    1406             :                                                                 || settings.dmPelsHeight != [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayHeight] intValue])));
    1407             :                         
    1408             :         RECT wDC;
    1409             : 
    1410             :         if (fullScreen)
    1411             :         {
    1412             :                 /*NOTE: If we ever decide to change the default behaviour of launching
    1413             :                 always on primary monitor to launching on the monitor the program was 
    1414             :                 started on, all that needs to be done is comment out the line below, as
    1415             :                 well as the identical one in the else branch further down.
    1416             :                 Nikos 20141222
    1417             :            */
    1418             :            [self getCurrentMonitorInfo: &monitorInfo];
    1419             :                 
    1420             :                 settings.dmPelsWidth = viewSize.width;
    1421             :                 settings.dmPelsHeight = viewSize.height;
    1422             :                 settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT;
    1423             :                                 
    1424             :                 // just before going fullscreen, save the location of the current window. It
    1425             :                 // may be needed in case of potential attempts to move our fullscreen window
    1426             :                 // in a maximized state (yes, in Windows this is entirely possible).
    1427             :                 if(lastWindowPlacementMaximized)
    1428             :                 {
    1429             :                         CopyRect(&lastGoodRect, &windowPlacement.rcNormalPosition);
    1430             :                         // if maximized, switch to normal placement before going full screen
    1431             :                         windowPlacement.showCmd = SW_SHOWNORMAL;
    1432             :                         SetWindowPlacement(SDL_Window, &windowPlacement);
    1433             :                 }
    1434             :                 else  GetWindowRect(SDL_Window, &lastGoodRect);
    1435             :                 
    1436             :                 // ok, can go fullscreen now
    1437             :                 SetForegroundWindow(SDL_Window);
    1438             :                 if (changingResolution)
    1439             :                 {
    1440             :                         if (ChangeDisplaySettingsEx(monitorInfo.szDevice, &settings, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL)
    1441             :                         {
    1442             :                                 m_glContextInitialized = YES;
    1443             :                                 OOLogERR(@"displayMode.change.error", @"Could not switch to requested display mode.");
    1444             :                                 return;
    1445             :                         }
    1446             :                         atDesktopResolution = settings.dmPelsWidth == [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayWidth] intValue]
    1447             :                                                                 && settings.dmPelsHeight == [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayHeight] intValue];
    1448             :                 }
    1449             :                 
    1450             :                 MoveWindow(SDL_Window, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, (int)viewSize.width, (int)viewSize.height, TRUE);
    1451             :                 if(!wasFullScreen)
    1452             :                 {
    1453             :                         [self setWindowBorderless:YES];
    1454             :                 }
    1455             :         }
    1456             :         
    1457             :         else if ( wasFullScreen )
    1458             :         {
    1459             :                 if (changingResolution)
    1460             :                 {
    1461             :                         // restore original desktop resolution
    1462             :                         if (ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL) == DISP_CHANGE_SUCCESSFUL)
    1463             :                         {
    1464             :                                 atDesktopResolution = YES;
    1465             :                         }
    1466             :                 }
    1467             :                 
    1468             :                 /*NOTE: If we ever decide to change the default behaviour of launching
    1469             :                 always on primary monitor to launching on the monitor the program was 
    1470             :                 started on, we need to comment out the line below.
    1471             :                 For now, this line is needed for correct positioning of our window in case
    1472             :                 we return from a non-native resolution fullscreen and has to come after the
    1473             :                 display settings have been reverted.
    1474             :                 Nikos 20141222
    1475             :                 */
    1476             :                 [self getCurrentMonitorInfo: &monitorInfo];
    1477             :                 
    1478             :                 if (lastWindowPlacementMaximized)  CopyRect(&windowPlacement.rcNormalPosition, &lastGoodRect);
    1479             :                 SetWindowPlacement(SDL_Window, &windowPlacement);
    1480             :                 if (!lastWindowPlacementMaximized)
    1481             :                 {
    1482             :                         MoveWindow(SDL_Window,  (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left - (int)viewSize.width)/2 +
    1483             :                                                                 monitorInfo.rcMonitor.left,
    1484             :                                                                 (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top - (int)viewSize.height)/2 +
    1485             :                                                                 monitorInfo.rcMonitor.top,
    1486             :                                                                 (int)viewSize.width, (int)viewSize.height, TRUE);
    1487             :                 }
    1488             :                 
    1489             :                 [self setWindowBorderless:NO];
    1490             :                                                                 
    1491             :                 lastWindowPlacementMaximized = NO;
    1492             :                 ShowWindow(SDL_Window,SW_SHOW);
    1493             :         }
    1494             :         
    1495             :         // stop saveWindowSize from reacting to caption & frame if necessary
    1496             :         saveSize = !wasFullScreen;
    1497             : 
    1498             :         GetClientRect(SDL_Window, &wDC);
    1499             : 
    1500             :         if (!fullScreen && (bounds.size.width != wDC.right - wDC.left
    1501             :                                         || bounds.size.height != wDC.bottom - wDC.top))
    1502             :         {
    1503             :                 // Resize the game window if needed. When we ask for a W x H
    1504             :                 // window, we intend that the client area be W x H. The actual
    1505             :                 // window itself must become big enough to accomodate an area
    1506             :                 // of such size. 
    1507             :                 if (wasFullScreen)      // this is true when switching from full screen or when starting in windowed mode
    1508             :                                                         //after the splash screen has ended
    1509             :                 {
    1510             :                         RECT desiredClientRect;
    1511             :                         GetWindowRect(SDL_Window, &desiredClientRect);
    1512             :                         AdjustWindowRect(&desiredClientRect, WS_CAPTION | WS_THICKFRAME, FALSE);
    1513             :                         SetWindowPos(SDL_Window, NULL,  desiredClientRect.left, desiredClientRect.top,
    1514             :                                                                                         desiredClientRect.right - desiredClientRect.left,
    1515             :                                                                                         desiredClientRect.bottom - desiredClientRect.top, 0);
    1516             :                 }
    1517             :                 GetClientRect(SDL_Window, &wDC);
    1518             :                 viewSize.width = wDC.right - wDC.left;
    1519             :                 viewSize.height = wDC.bottom - wDC.top;
    1520             :         }
    1521             : 
    1522             :         // Reset bounds and viewSize to current values
    1523             :         bounds.size.width = viewSize.width = wDC.right - wDC.left;
    1524             :         bounds.size.height = viewSize.height = wDC.bottom - wDC.top;
    1525             :         
    1526             :         if (fullScreen) // bounds on fullscreen coincide with client area, since we are borderless
    1527             :         {
    1528             :                 bounds.origin.x = monitorInfo.rcMonitor.left;
    1529             :                 bounds.origin.y = monitorInfo.rcMonitor.top;
    1530             :         }
    1531             :         wasFullScreen=fullScreen;
    1532             : 
    1533             : #else //OOLITE_LINUX
    1534             : 
    1535             :         int videoModeFlags = SDL_HWSURFACE | SDL_OPENGL;
    1536             : 
    1537             :         if (v_mode == NO)
    1538             :                 videoModeFlags |= SDL_NOFRAME;
    1539             :         if (fullScreen == YES)
    1540             :         {
    1541             :                 videoModeFlags |= SDL_FULLSCREEN;
    1542             :         }
    1543             :         else
    1544             :         {
    1545             :                 videoModeFlags |= SDL_RESIZABLE;
    1546             :         }
    1547             :         surface = SDL_SetVideoMode((int)viewSize.width, (int)viewSize.height, 32, videoModeFlags);
    1548             : 
    1549             :         if (!surface && fullScreen == YES)
    1550             :         {
    1551             :                 [self setFullScreenMode: NO];
    1552             :                 viewSize = oldViewSize;
    1553             :                 videoModeFlags &= ~SDL_FULLSCREEN;
    1554             :                 videoModeFlags |= SDL_RESIZABLE;
    1555             :                 surface = SDL_SetVideoMode((int)viewSize.width, (int)viewSize.height, 32, videoModeFlags);
    1556             :         }
    1557             : 
    1558             :         if (!surface)
    1559             :         {
    1560             :           // we should always have a valid surface, but in case we don't
    1561             :                 OOLogERR(@"display.mode.error",@"Unable to change display mode: %s",SDL_GetError());
    1562             :                 exit(1);
    1563             :         }
    1564             : 
    1565             :         bounds.size.width = surface->w;
    1566             :         bounds.size.height = surface->h;
    1567             : 
    1568             : #endif
    1569             :         OOLog(@"display.initGL", @"Created a new surface of %d x %d, %@.", (int)viewSize.width, (int)viewSize.height,(fullScreen ? @"fullscreen" : @"windowed"));
    1570             : 
    1571             :         if (viewSize.width/viewSize.height > 4.0/3.0) {
    1572             :                 display_z = 480.0 * bounds.size.width/bounds.size.height;
    1573             :                 x_offset = 240.0 * bounds.size.width/bounds.size.height;
    1574             :                 y_offset = 240.0;
    1575             :         } else {
    1576             :                 display_z = 640.0;
    1577             :                 x_offset = 320.0;
    1578             :                 y_offset = 320.0 * bounds.size.height/bounds.size.width;
    1579             :         }
    1580             : 
    1581             :         if (surface != 0)  SDL_FreeSurface(surface);
    1582             : 
    1583             :         [self autoShowMouse];
    1584             : 
    1585             :         [[self gameController] setUpBasicOpenGLStateWithSize:viewSize];
    1586             :         SDL_GL_SwapBuffers();
    1587             :         squareX = 0.0f;
    1588             : 
    1589             :         m_glContextInitialized = YES;
    1590             : }
    1591             : 
    1592             : 
    1593             : - (float) colorSaturation
    1594             : {
    1595             :         return _colorSaturation;
    1596             : }
    1597             : 
    1598             : 
    1599             : - (void) adjustColorSaturation:(float)colorSaturationAdjustment;
    1600             : {
    1601             :         _colorSaturation += colorSaturationAdjustment;
    1602             :         _colorSaturation = OOClamp_0_max_f(_colorSaturation, MAX_COLOR_SATURATION);
    1603             : }
    1604             : 
    1605             : 
    1606             : - (BOOL) snapShot:(NSString *)filename
    1607             : {
    1608             :         BOOL snapShotOK = YES;
    1609             :         SDL_Surface* tmpSurface;
    1610             : 
    1611             :         // backup the previous directory
    1612             :         NSString* originalDirectory = [[NSFileManager defaultManager] currentDirectoryPath];
    1613             :         // use the snapshots directory
    1614             :         [[NSFileManager defaultManager] chdirToSnapshotPath];
    1615             : 
    1616             :         BOOL                            withFilename = (filename != nil);
    1617             :         static unsigned         imageNo = 0;
    1618             :         unsigned                        tmpImageNo = 0;
    1619             :         NSString                        *pathToPic = nil;
    1620             :         NSString                        *baseName = @"oolite";
    1621             : 
    1622             : #if SNAPSHOTS_PNG_FORMAT
    1623             :         NSString                        *extension = @".png";
    1624             : #else
    1625             :         NSString                        *extension = @".bmp";
    1626             : #endif
    1627             : 
    1628             :         if (withFilename)
    1629             :         {
    1630             :                 baseName = filename;
    1631             :                 pathToPic = [filename stringByAppendingString:extension];
    1632             :         }
    1633             :         else
    1634             :         {
    1635             :                 tmpImageNo = imageNo;
    1636             :         }
    1637             : 
    1638             :         if (withFilename && [[NSFileManager defaultManager] fileExistsAtPath:pathToPic])
    1639             :         {
    1640             :                 OOLog(@"screenshot.filenameExists", @"Snapshot \"%@%@\" already exists - adding numerical sequence.", pathToPic, extension);
    1641             :                 pathToPic = nil;
    1642             :         }
    1643             : 
    1644             :         if (pathToPic == nil)
    1645             :         {
    1646             :                 do
    1647             :                 {
    1648             :                         tmpImageNo++;
    1649             :                         pathToPic = [NSString stringWithFormat:@"%@-%03d%@", baseName, tmpImageNo, extension];
    1650             :                 } while ([[NSFileManager defaultManager] fileExistsAtPath:pathToPic]);
    1651             :         }
    1652             : 
    1653             :         if (!withFilename)
    1654             :         {
    1655             :                 imageNo = tmpImageNo;
    1656             :         }
    1657             : 
    1658             :         OOLog(@"screenshot", @"Saving screen shot \"%@\" (%u x %u pixels).", pathToPic, surface->w, surface->h);
    1659             : 
    1660             :         int pitch = surface->w * 3;
    1661             :         unsigned char *pixls = malloc(pitch * surface->h);
    1662             :         int y;
    1663             :         int off;
    1664             : 
    1665             :         if (surface->w % 4) glPixelStorei(GL_PACK_ALIGNMENT,1);
    1666             :         else                glPixelStorei(GL_PACK_ALIGNMENT,4);
    1667             :         for (y=surface->h-1, off=0; y>=0; y--, off+=pitch)
    1668             :         {
    1669             :                 glReadPixels(0, y, surface->w, 1, GL_RGB, GL_UNSIGNED_BYTE, pixls + off);
    1670             :         }
    1671             :         
    1672             :         tmpSurface=SDL_CreateRGBSurfaceFrom(pixls,surface->w,surface->h,24,surface->w*3,0xFF,0xFF00,0xFF0000,0x0);
    1673             : #if SNAPSHOTS_PNG_FORMAT
    1674             :         if(![self pngSaveSurface:pathToPic withSurface:tmpSurface])
    1675             :         {
    1676             :                 OOLog(@"screenshotPNG", @"Failed to save %@", pathToPic);
    1677             :                 snapShotOK = NO;
    1678             :         }
    1679             : #else
    1680             :         if (SDL_SaveBMP(tmpSurface, [pathToPic UTF8String]) == -1)
    1681             :         {
    1682             :                 OOLog(@"screenshotBMP", @"Failed to save %@", pathToPic);
    1683             :                 snapShotOK = NO;
    1684             :         }
    1685             : #endif
    1686             :         SDL_FreeSurface(tmpSurface);
    1687             :         free(pixls);
    1688             :         
    1689             :         // if outputting HDR signal, save also either an .exr or a Radiance .hdr snapshot
    1690             :         if ([self hdrOutput])
    1691             :         {
    1692             :                 NSString *fileExtension = [[NSUserDefaults standardUserDefaults] oo_stringForKey:@"hdr-snapshot-format" defaultValue:SNAPSHOTHDR_EXTENSION_DEFAULT];
    1693             :                 
    1694             :                 // we accept file extension with or without a leading dot; if it is without, insert it at the beginning now
    1695             :                 if (![[fileExtension substringToIndex:1] isEqual:@"."])  fileExtension = [@"." stringByAppendingString:fileExtension];
    1696             :                 
    1697             :                 if (![fileExtension isEqual:SNAPSHOTHDR_EXTENSION_EXR] && ![fileExtension isEqual:SNAPSHOTHDR_EXTENSION_HDR])
    1698             :                 {
    1699             :                         OOLog(@"screenshotHDR", @"Unrecognized HDR file format requested, defaulting to "SNAPSHOTHDR_EXTENSION_DEFAULT);
    1700             :                         fileExtension = SNAPSHOTHDR_EXTENSION_DEFAULT;
    1701             :                 }
    1702             :                 
    1703             :                 NSString *pathToPicHDR = [pathToPic stringByReplacingString:@".png" withString:fileExtension];
    1704             :                 OOLog(@"screenshot", @"Saving screen shot \"%@\" (%u x %u pixels).", pathToPicHDR, surface->w, surface->h);
    1705             :                 GLfloat *pixlsf = (GLfloat *)malloc(pitch * surface->h * sizeof(GLfloat));
    1706             :                 for (y=surface->h-1, off=0; y>=0; y--, off+=pitch)
    1707             :                 {
    1708             :                         glReadPixels(0, y, surface->w, 1, GL_RGB, GL_FLOAT, pixlsf + off);
    1709             :                 }
    1710             :                 
    1711             :                 if (([fileExtension isEqual:SNAPSHOTHDR_EXTENSION_EXR] && SaveEXRSnapshot([pathToPicHDR cStringUsingEncoding:NSUTF8StringEncoding], surface->w, surface->h, pixlsf) != 0) //TINYEXR_SUCCESS
    1712             :                         || ([fileExtension isEqual:SNAPSHOTHDR_EXTENSION_HDR] && !stbi_write_hdr([pathToPicHDR cStringUsingEncoding:NSUTF8StringEncoding], surface->w, surface->h, 3, pixlsf)))
    1713             :                 {
    1714             :                         OOLog(@"screenshotHDR", @"Failed to save %@", pathToPicHDR);
    1715             :                         snapShotOK = NO;
    1716             :                 }
    1717             :                 
    1718             :                 free(pixlsf);
    1719             :         }
    1720             :         
    1721             :         // return to the previous directory
    1722             :         [[NSFileManager defaultManager] changeCurrentDirectoryPath:originalDirectory];
    1723             :         return snapShotOK;
    1724             : }
    1725             : 
    1726             : 
    1727             : #if SNAPSHOTS_PNG_FORMAT
    1728             : // This method is heavily based on 'Mars, Land of No Mercy' SDL examples, by Angelo "Encelo" Theodorou, see http://encelo.netsons.org/programming/sdl
    1729             : - (BOOL) pngSaveSurface:(NSString *)fileName withSurface:(SDL_Surface *)surf
    1730             : {
    1731             :         FILE *fp;
    1732             :         png_structp pngPtr;
    1733             :         png_infop infoPtr;
    1734             :         int i, colorType;
    1735             :         png_bytep *rowPointers;
    1736             : 
    1737             :         fp = fopen([fileName UTF8String], "wb");
    1738             :         if (fp == NULL)
    1739             :         {
    1740             :                 OOLog(@"pngSaveSurface.fileCreate.failed", @"Failed to create output screenshot file %@", fileName);
    1741             :                 return NO;
    1742             :         }
    1743             : 
    1744             :         // initialize png structures (no callbacks)
    1745             :         pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    1746             :         if (pngPtr == NULL)
    1747             :         {
    1748             :                 return NO;
    1749             :         }
    1750             : 
    1751             :         infoPtr = png_create_info_struct(pngPtr);
    1752             :         if (infoPtr == NULL) {
    1753             :                 png_destroy_write_struct(&pngPtr, (png_infopp)NULL);
    1754             :                 OOLog(@"pngSaveSurface.info_struct.failed", @"%@", @"png_create_info_struct error");
    1755             :                 exit(-1);
    1756             :         }
    1757             : 
    1758             :         if (setjmp(png_jmpbuf(pngPtr)))
    1759             :         {
    1760             :                 png_destroy_write_struct(&pngPtr, &infoPtr);
    1761             :                 fclose(fp);
    1762             :                 exit(-1);
    1763             :         }
    1764             : 
    1765             :         png_init_io(pngPtr, fp);
    1766             : 
    1767             :         colorType = PNG_COLOR_MASK_COLOR; /* grayscale not supported */
    1768             :         if (surf->format->palette)
    1769             :         {
    1770             :                 colorType |= PNG_COLOR_MASK_PALETTE;
    1771             :         }
    1772             :         else if (surf->format->Amask)
    1773             :         {
    1774             :                 colorType |= PNG_COLOR_MASK_ALPHA;
    1775             :         }
    1776             : 
    1777             :         png_set_IHDR(pngPtr, infoPtr, surf->w, surf->h, 8, colorType,     PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    1778             :         
    1779             :         // if we are outputting HDR, our backbuffer is linear, so gamma is 1.0. Make sure our png has this info
    1780             :         // note: some image viewers seem to ignore the gAMA chunk; still, this is better than not having it at all
    1781             :         if ([self hdrOutput])  png_set_gAMA(pngPtr, infoPtr, 1.0f);
    1782             : 
    1783             :         // write the image
    1784             :         png_write_info(pngPtr, infoPtr);
    1785             :         png_set_packing(pngPtr);
    1786             : 
    1787             :         rowPointers = (png_bytep*) malloc(sizeof(png_bytep)*surf->h);
    1788             :         for (i = 0; i < surf->h; i++)
    1789             :         {
    1790             :                 rowPointers[i] = (png_bytep)(Uint8 *)surf->pixels + i*surf->pitch;
    1791             :         }
    1792             :         png_write_image(pngPtr, rowPointers);
    1793             :         png_write_end(pngPtr, infoPtr);
    1794             : 
    1795             :         free(rowPointers);
    1796             :         png_destroy_write_struct(&pngPtr, &infoPtr);
    1797             :         fclose(fp);
    1798             : 
    1799             :         return YES;
    1800             : }
    1801             : #endif  // SNAPSHOTS_PNG_FORMAT
    1802             : 
    1803             : 
    1804             : /*     Turn the Cocoa ArrowKeys into our arrow key constants. */
    1805             : - (int) translateKeyCode: (int) input
    1806             : {
    1807             :         int key = input;
    1808             :         switch ( input )
    1809             :         {
    1810             :                 case NSUpArrowFunctionKey:
    1811             :                         key = gvArrowKeyUp;
    1812             :                         break;
    1813             : 
    1814             :                 case NSDownArrowFunctionKey:
    1815             :                         key = gvArrowKeyDown;
    1816             :                         break;
    1817             : 
    1818             :                 case NSLeftArrowFunctionKey:
    1819             :                         key = gvArrowKeyLeft;
    1820             :                         break;
    1821             : 
    1822             :                 case NSRightArrowFunctionKey:
    1823             :                         key = gvArrowKeyRight;
    1824             :                         break;
    1825             : 
    1826             :                 case NSF1FunctionKey:
    1827             :                         key = gvFunctionKey1;
    1828             :                         break;
    1829             : 
    1830             :                 case NSF2FunctionKey:
    1831             :                         key = gvFunctionKey2;
    1832             :                         break;
    1833             : 
    1834             :                 case NSF3FunctionKey:
    1835             :                         key = gvFunctionKey3;
    1836             :                         break;
    1837             : 
    1838             :                 case NSF4FunctionKey:
    1839             :                         key = gvFunctionKey4;
    1840             :                         break;
    1841             : 
    1842             :                 case NSF5FunctionKey:
    1843             :                         key = gvFunctionKey5;
    1844             :                         break;
    1845             : 
    1846             :                 case NSF6FunctionKey:
    1847             :                         key = gvFunctionKey6;
    1848             :                         break;
    1849             : 
    1850             :                 case NSF7FunctionKey:
    1851             :                         key = gvFunctionKey7;
    1852             :                         break;
    1853             : 
    1854             :                 case NSF8FunctionKey:
    1855             :                         key = gvFunctionKey8;
    1856             :                         break;
    1857             : 
    1858             :                 case NSF9FunctionKey:
    1859             :                         key = gvFunctionKey9;
    1860             :                         break;
    1861             : 
    1862             :                 case NSF10FunctionKey:
    1863             :                         key = gvFunctionKey10;
    1864             :                         break;
    1865             : 
    1866             :                 case NSF11FunctionKey:
    1867             :                         key = gvFunctionKey11;
    1868             :                         break;
    1869             : 
    1870             :                 case NSHomeFunctionKey:
    1871             :                         key = gvHomeKey;
    1872             :                         break;
    1873             : 
    1874             :                 default:
    1875             :                         break;
    1876             :         }
    1877             :         return key;
    1878             : }
    1879             : 
    1880             : 
    1881             : - (void) setVirtualJoystick:(double) vmx :(double) vmy
    1882             : {
    1883             :         virtualJoystickPosition.x = vmx;
    1884             :         virtualJoystickPosition.y = vmy;
    1885             : }
    1886             : 
    1887             : 
    1888             : - (NSPoint) virtualJoystickPosition
    1889             : {
    1890             :         return virtualJoystickPosition;
    1891             : }
    1892             : 
    1893             : 
    1894             : /////////////////////////////////////////////////////////////
    1895             : 
    1896             : - (void) clearKeys
    1897             : {
    1898             :         int i;
    1899             :         lastKeyShifted = NO;
    1900             :         for (i = 0; i < [self numKeys]; i++)
    1901             :                 keys[i] = NO;
    1902             : }
    1903             : 
    1904             : 
    1905             : - (void) clearMouse
    1906             : {
    1907             :         keys[gvMouseDoubleClick] = NO;
    1908             :         keys[gvMouseLeftButton] = NO;
    1909             :         doubleClick = NO;
    1910             : }
    1911             : 
    1912             : 
    1913             : - (void) clearKey: (int)theKey
    1914             : {
    1915             :         if (theKey >= 0 && theKey < [self numKeys])
    1916             :         {
    1917             :                 keys[theKey] = NO;
    1918             :         }
    1919             : }
    1920             : 
    1921             : 
    1922             : - (void) resetMouse
    1923             : {
    1924             :         [self setVirtualJoystick:0.0 :0.0];
    1925             :         if ([[PlayerEntity sharedPlayer] isMouseControlOn])
    1926             :         {
    1927             :                 SDL_WarpMouse([self viewSize].width / 2, [self viewSize].height / 2);
    1928             :                 mouseWarped = YES;
    1929             :         }
    1930             : }
    1931             : 
    1932             : 
    1933             : - (BOOL) isAlphabetKeyDown
    1934             : {
    1935             :         return isAlphabetKeyDown = NO;;
    1936             : }
    1937             : 
    1938             : // DJS: When entering submenus in the gui, it is not helpful if the
    1939             : // key down that brought you into the submenu is still registered
    1940             : // as down when we're in. This makes isDown return NO until a key up
    1941             : // event has been received from SDL.
    1942             : - (void) suppressKeysUntilKeyUp
    1943             : {
    1944             :         if (keys[gvMouseDoubleClick] == NO)
    1945             :         {
    1946             :                 suppressKeys = YES;
    1947             :                 [self clearKeys];
    1948             :         }
    1949             :         else
    1950             :         {
    1951             :                 [self clearMouse];
    1952             :         }
    1953             : 
    1954             : }
    1955             : 
    1956             : 
    1957             : - (BOOL) isDown: (int) key
    1958             : {
    1959             :         if ( suppressKeys )
    1960             :                 return NO;
    1961             :         if ( key < 0 )
    1962             :                 return NO;
    1963             :         if ( key >= [self numKeys] )
    1964             :                 return NO;
    1965             :         return keys[key];
    1966             : }
    1967             : 
    1968             : 
    1969             : - (BOOL) isOptDown
    1970             : {
    1971             :         return opt;
    1972             : }
    1973             : 
    1974             : 
    1975             : - (BOOL) isCtrlDown
    1976             : {
    1977             :         return ctrl;
    1978             : }
    1979             : 
    1980             : 
    1981             : - (BOOL) isCommandDown
    1982             : {
    1983             :         return command;
    1984             : }
    1985             : 
    1986             : 
    1987             : - (BOOL) isShiftDown
    1988             : {
    1989             :         return shift;
    1990             : }
    1991             : 
    1992             : 
    1993             : - (BOOL) isCapsLockOn
    1994             : {
    1995             :         /* Caps Lock state check - This effectively gives us
    1996             :            an alternate keyboard state to play with and, in
    1997             :            the future, we could assign different behaviours
    1998             :            to existing controls, depending on the state of
    1999             :            Caps Lock. - Nikos 20160304
    2000             :         */
    2001             :         return (SDL_GetModState() & KMOD_CAPS) == KMOD_CAPS;
    2002             : }
    2003             : 
    2004             : 
    2005             : - (BOOL) lastKeyWasShifted
    2006             : {
    2007             :         return lastKeyShifted;
    2008             : }
    2009             : 
    2010             : - (int) numKeys
    2011             : {
    2012             :         return NUM_KEYS;
    2013             : }
    2014             : 
    2015             : 
    2016             : - (int) mouseWheelState
    2017             : {
    2018             :         if (_mouseWheelDelta > 0.0f)
    2019             :                 return gvMouseWheelUp;
    2020             :         else if (_mouseWheelDelta < 0.0f)
    2021             :                 return gvMouseWheelDown;
    2022             :         else
    2023             :                 return gvMouseWheelNeutral;
    2024             : }
    2025             : 
    2026             : 
    2027             : - (float) mouseWheelDelta
    2028             : {
    2029             :         return _mouseWheelDelta / OOMOUSEWHEEL_DELTA;
    2030             : }
    2031             : 
    2032             : 
    2033             : - (void) setMouseWheelDelta: (float) newWheelDelta
    2034             : {
    2035             :         _mouseWheelDelta = newWheelDelta * OOMOUSEWHEEL_DELTA;
    2036             : }
    2037             : 
    2038             : 
    2039             : - (BOOL) isCommandQDown
    2040             : {
    2041             :         return NO;
    2042             : }
    2043             : 
    2044             : 
    2045             : - (BOOL) isCommandFDown
    2046             : {
    2047             :         return NO;
    2048             : }
    2049             : 
    2050             : 
    2051             : - (void) clearCommandF
    2052             : {
    2053             :         // SDL stub for the mac function.
    2054             : }
    2055             : 
    2056             : 
    2057             : - (void)pollControls
    2058             : {
    2059             :         SDL_Event                               event;
    2060             :         SDL_KeyboardEvent               *kbd_event;
    2061             :         SDL_MouseButtonEvent    *mbtn_event;
    2062             :         SDL_MouseMotionEvent    *mmove_event;
    2063             :         int                                             mxdelta, mydelta;
    2064             :         float                                   mouseVirtualStickSensitivityX = viewSize.width * _mouseVirtualStickSensitivityFactor;
    2065             :         float                                   mouseVirtualStickSensitivityY = viewSize.height * _mouseVirtualStickSensitivityFactor;
    2066             :         NSTimeInterval                  timeNow = [NSDate timeIntervalSinceReferenceDate];
    2067             :         Uint16                                  key_id;
    2068             :         int                                             scan_code;
    2069             : 
    2070             :         while (SDL_PollEvent(&event))
    2071             :         {
    2072             :                 switch (event.type) {
    2073             :                         case SDL_JOYAXISMOTION:
    2074             :                         case SDL_JOYBUTTONUP:
    2075             :                         case SDL_JOYBUTTONDOWN:
    2076             :                         case SDL_JOYHATMOTION:
    2077             :                                 [(OOSDLJoystickManager*)[OOJoystickManager sharedStickHandler] handleSDLEvent: &event];
    2078             :                                 break;
    2079             : 
    2080             :                         case SDL_MOUSEBUTTONDOWN:
    2081             :                                 mbtn_event = (SDL_MouseButtonEvent*)&event;
    2082             : #if OOLITE_LINUX
    2083             :                                 short inDelta = 0;
    2084             : #else
    2085             :                                 // specially built SDL.dll is required for this
    2086             :                                 short inDelta = mbtn_event->wheelDelta;
    2087             : #endif
    2088             :                                 switch(mbtn_event->button)
    2089             :                                 {
    2090             :                                         case SDL_BUTTON_LEFT:
    2091             :                                                 keys[gvMouseLeftButton] = YES;
    2092             :                                                 break;
    2093             :                                         case SDL_BUTTON_RIGHT:
    2094             :                                                 // Cocoa version does this in the GameController
    2095             :                                                 /*
    2096             :                                                  The mouseWarped variable is quite important as far as mouse control is concerned. When we
    2097             :                                                  reset the virtual joystick (mouse) coordinates, we need to send a WarpMouse call because we
    2098             :                                                  must recenter the pointer physically on screen. This goes together with a mouse motion event,
    2099             :                                                  so we use mouseWarped to simply ignore handling of motion events in this case. - Nikos 20110721
    2100             :                                                 */
    2101             :                                                 [self resetMouse]; // Will set mouseWarped to YES
    2102             :                                                 break;
    2103             :                                         // mousewheel stuff
    2104             : #if OOLITE_LINUX
    2105             :                                         case SDL_BUTTON_WHEELUP:
    2106             :                                                 inDelta = OOMOUSEWHEEL_DELTA;
    2107             :                                                 // allow fallthrough
    2108             :                                         case SDL_BUTTON_WHEELDOWN:
    2109             :                                                 if (inDelta == 0)  inDelta = -OOMOUSEWHEEL_DELTA;
    2110             : #else
    2111             :                                         case SDL_BUTTON_WHEELUP:
    2112             :                                         case SDL_BUTTON_WHEELDOWN:
    2113             : #endif
    2114             :                                                 if (inDelta > 0)
    2115             :                                                 {
    2116             :                                                         if (_mouseWheelDelta >= 0.0f)
    2117             :                                                                 _mouseWheelDelta += inDelta;
    2118             :                                                         else
    2119             :                                                                 _mouseWheelDelta = 0.0f;
    2120             :                                                 }
    2121             :                                                 else if (inDelta < 0)
    2122             :                                                 {
    2123             :                                                         if (_mouseWheelDelta <= 0.0f)
    2124             :                                                                 _mouseWheelDelta += inDelta;
    2125             :                                                         else
    2126             :                                                                 _mouseWheelDelta = 0.0f;
    2127             :                                                 }
    2128             :                                                 break;
    2129             :                                 }
    2130             :                                 break;
    2131             : 
    2132             :                         case SDL_MOUSEBUTTONUP:
    2133             :                                 mbtn_event = (SDL_MouseButtonEvent*)&event;
    2134             :                                 NSTimeInterval timeBetweenClicks = timeNow - timeIntervalAtLastClick;
    2135             :                                 timeIntervalAtLastClick += timeBetweenClicks;
    2136             :                                 if (mbtn_event->button == SDL_BUTTON_LEFT)
    2137             :                                 {
    2138             :                                         if (!doubleClick)
    2139             :                                         {
    2140             :                                                 doubleClick = (timeBetweenClicks < MOUSE_DOUBLE_CLICK_INTERVAL);     // One fifth of a second
    2141             :                                                 keys[gvMouseDoubleClick] = doubleClick;
    2142             :                                         }
    2143             :                                         keys[gvMouseLeftButton] = NO;
    2144             :                                 }
    2145             :                                 /* 
    2146             :                                    Mousewheel handling - just note time since last use here and mark as inactive,
    2147             :                                    if needed, at the end of this method. Note that the mousewheel button up event is 
    2148             :                                    kind of special, as in, it is sent at the same time as its corresponding mousewheel
    2149             :                                    button down one - Nikos 20140809
    2150             :                                 */
    2151             :                                 if (mbtn_event->button == SDL_BUTTON_WHEELUP || mbtn_event->button == SDL_BUTTON_WHEELDOWN)
    2152             :                                 {
    2153             :                                         NSTimeInterval timeBetweenMouseWheels = timeNow - timeSinceLastMouseWheel;
    2154             :                                         timeSinceLastMouseWheel += timeBetweenMouseWheels;
    2155             :                                 }
    2156             :                                 break;
    2157             : 
    2158             :                         case SDL_MOUSEMOTION:
    2159             :                         {
    2160             :                                 // Delta mode is set when the game is in 'flight' mode.
    2161             :                                 // In this mode, the mouse movement delta is used rather
    2162             :                                 // than absolute position. This is because if the user
    2163             :                                 // clicks the right button to recentre the virtual joystick,
    2164             :                                 // if we are using absolute joystick positioning, as soon
    2165             :                                 // as the player touches the mouse again, the virtual joystick
    2166             :                                 // will snap back to the absolute position (which can be
    2167             :                                 // annoyingly fatal in battle).
    2168             :                                 if(mouseInDeltaMode)
    2169             :                                 {
    2170             :                                         // note: virtual stick sensitivity is configurable
    2171             :                                         SDL_GetRelativeMouseState(&mxdelta, &mydelta);
    2172             :                                         double mxd=(double)mxdelta / mouseVirtualStickSensitivityX;
    2173             :                                         double myd=(double)mydelta / mouseVirtualStickSensitivityY;
    2174             : 
    2175             :                                         if (!mouseWarped) // Standard event, update coordinates
    2176             :                                         {
    2177             :                                                 virtualJoystickPosition.x += mxd;
    2178             :                                                 virtualJoystickPosition.y += myd;
    2179             : 
    2180             :                                                 // if we excceed the limits, revert changes
    2181             :                                                 if(fabs(virtualJoystickPosition.x) > MOUSEX_MAXIMUM)
    2182             :                                                 {
    2183             :                                                         virtualJoystickPosition.x -= mxd;
    2184             :                                                 }
    2185             :                                                 if(fabs(virtualJoystickPosition.y) > MOUSEY_MAXIMUM)
    2186             :                                                 {
    2187             :                                                         virtualJoystickPosition.y -= myd;
    2188             :                                                 }
    2189             :                                         }
    2190             :                                         else
    2191             :                                         {
    2192             :                                                 // Motion event generated by WarpMouse is ignored and
    2193             :                                                 // we reset mouseWarped for the next time.
    2194             :                                                 mouseWarped = NO;
    2195             :                                         }
    2196             :                                 }
    2197             :                                 else
    2198             :                                 {
    2199             :                                         // Windowed mode. Use the absolute position so the
    2200             :                                         // Oolite mouse pointer appears under the X Window System
    2201             :                                         // mouse pointer.
    2202             :                                         mmove_event = (SDL_MouseMotionEvent*)&event;
    2203             : 
    2204             :                                         int w=bounds.size.width;
    2205             :                                         int h=bounds.size.height;
    2206             : 
    2207             :                                         if (!mouseWarped) // standard event, handle it
    2208             :                                         {
    2209             :                                                 double mx = mmove_event->x - w/2.0;
    2210             :                                                 double my = mmove_event->y - h/2.0;
    2211             :                                                 if (display_z > 640.0)
    2212             :                                                 {
    2213             :                                                         mx /= w * MAIN_GUI_PIXEL_WIDTH / display_z;
    2214             :                                                         my /= h;
    2215             :                                                 }
    2216             :                                                 else
    2217             :                                                 {
    2218             :                                                         mx /= MAIN_GUI_PIXEL_WIDTH * w / 640.0;
    2219             :                                                         my /= MAIN_GUI_PIXEL_HEIGHT * w / 640.0;
    2220             :                                                 }
    2221             : 
    2222             :                                                 [self setVirtualJoystick:mx :my];
    2223             :                                         }
    2224             :                                         else
    2225             :                                         {
    2226             :                                                 // event coming from WarpMouse ignored, get ready for the next
    2227             :                                                 mouseWarped = NO;
    2228             :                                         }
    2229             :                                 }
    2230             :                                 break;
    2231             :                         }
    2232             :                         case SDL_KEYDOWN:
    2233             :                                 kbd_event = (SDL_KeyboardEvent*)&event;
    2234             :                                 key_id = (Uint16)kbd_event->keysym.unicode;
    2235             :                                 scan_code = kbd_event->keysym.scancode;
    2236             : 
    2237             :                                 //char *keychar = SDL_GetKeyName(kbd_event->keysym.sym);
    2238             :                                 // deal with modifiers first
    2239             :                                 BOOL modifier_pressed = NO;
    2240             :                                 BOOL special_key = NO;
    2241             : 
    2242             :                                 // translate scancode to unicode equiv
    2243             :                                 switch (kbd_event->keysym.sym) 
    2244             :                                 {
    2245             :                                         case SDLK_LSHIFT:
    2246             :                                         case SDLK_RSHIFT:
    2247             :                                                 shift = YES;
    2248             :                                                 modifier_pressed = YES;
    2249             :                                                 break;
    2250             : 
    2251             :                                         case SDLK_LCTRL:
    2252             :                                         case SDLK_RCTRL:
    2253             :                                                 ctrl = YES;
    2254             :                                                 modifier_pressed = YES;
    2255             :                                                 break;
    2256             :                                                 
    2257             :                                         case SDLK_LALT:
    2258             :                                         case SDLK_RALT:
    2259             :                                                 opt = YES;
    2260             :                                                 modifier_pressed = YES;
    2261             :                                                 break;
    2262             : 
    2263             :                                         case SDLK_KP0: key_id = (!allowingStringInput ? gvNumberPadKey0 : gvNumberKey0); special_key = YES; break;
    2264             :                                         case SDLK_KP1: key_id = (!allowingStringInput ? gvNumberPadKey1 : gvNumberKey1); special_key = YES; break;
    2265             :                                         case SDLK_KP2: key_id = (!allowingStringInput ? gvNumberPadKey2 : gvNumberKey2); special_key = YES; break;
    2266             :                                         case SDLK_KP3: key_id = (!allowingStringInput ? gvNumberPadKey3 : gvNumberKey3); special_key = YES; break;
    2267             :                                         case SDLK_KP4: key_id = (!allowingStringInput ? gvNumberPadKey4 : gvNumberKey4); special_key = YES; break;
    2268             :                                         case SDLK_KP5: key_id = (!allowingStringInput ? gvNumberPadKey5 : gvNumberKey5); special_key = YES; break;
    2269             :                                         case SDLK_KP6: key_id = (!allowingStringInput ? gvNumberPadKey6 : gvNumberKey6); special_key = YES; break;
    2270             :                                         case SDLK_KP7: key_id = (!allowingStringInput ? gvNumberPadKey7 : gvNumberKey7); special_key = YES; break;
    2271             :                                         case SDLK_KP8: key_id = (!allowingStringInput ? gvNumberPadKey8 : gvNumberKey8); special_key = YES; break;
    2272             :                                         case SDLK_KP9: key_id = (!allowingStringInput ? gvNumberPadKey9 : gvNumberKey9); special_key = YES; break;
    2273             :                                         case SDLK_KP_PERIOD: key_id = (!allowingStringInput ? gvNumberPadKeyPeriod : 46); special_key = YES; break;
    2274             :                                         case SDLK_KP_DIVIDE: key_id = (!allowingStringInput ? gvNumberPadKeyDivide : 47); special_key = YES; break;
    2275             :                                         case SDLK_KP_MULTIPLY: key_id = (!allowingStringInput ? gvNumberPadKeyMultiply : 42); special_key = YES; break;
    2276             :                                         case SDLK_KP_MINUS: key_id = (!allowingStringInput ? gvNumberPadKeyMinus : 45); special_key = YES; break;
    2277             :                                         case SDLK_KP_PLUS: key_id = (!allowingStringInput ? gvNumberPadKeyPlus : 43); special_key = YES; break;
    2278             :                                         case SDLK_KP_EQUALS: key_id = (!allowingStringInput ? gvNumberPadKeyEquals : 61); special_key = YES; break;
    2279             :                                         case SDLK_KP_ENTER: key_id = gvNumberPadKeyEnter; special_key = YES; break;
    2280             :                                         case SDLK_HOME: key_id = gvHomeKey; special_key = YES; break;
    2281             :                                         case SDLK_END: key_id = gvEndKey; special_key = YES; break;
    2282             :                                         case SDLK_INSERT: key_id = gvInsertKey; special_key = YES; break;
    2283             :                                         case SDLK_PAGEUP: key_id = gvPageUpKey; special_key = YES; break;
    2284             :                                         case SDLK_PAGEDOWN: key_id = gvPageDownKey; special_key = YES; break;
    2285             :                                         case SDLK_SPACE: key_id = 32; special_key = YES; break;
    2286             :                                         case SDLK_RETURN: key_id = 13; special_key = YES; break;
    2287             :                                         case SDLK_TAB: key_id = 9; special_key = YES; break;
    2288             :                                         case SDLK_UP: key_id = gvArrowKeyUp; special_key = YES; break;
    2289             :                                         case SDLK_DOWN: key_id = gvArrowKeyDown; special_key = YES; break;
    2290             :                                         case SDLK_LEFT: key_id = gvArrowKeyLeft; special_key = YES; break;
    2291             :                                         case SDLK_RIGHT: key_id = gvArrowKeyRight; special_key = YES; break;
    2292             :                                         case SDLK_PAUSE: key_id = gvPauseKey; special_key = YES; break;
    2293             :                                         case SDLK_BACKSPACE: key_id = gvBackspaceKey; special_key = YES; break;
    2294             :                                         case SDLK_DELETE: key_id = gvDeleteKey; special_key = YES; break;
    2295             :                                         case SDLK_F1: key_id = gvFunctionKey1; special_key = YES; break;
    2296             :                                         case SDLK_F2: key_id = gvFunctionKey2; special_key = YES; break;
    2297             :                                         case SDLK_F3: key_id = gvFunctionKey3; special_key = YES; break;
    2298             :                                         case SDLK_F4: key_id = gvFunctionKey4; special_key = YES; break;
    2299             :                                         case SDLK_F5: key_id = gvFunctionKey5; special_key = YES; break;
    2300             :                                         case SDLK_F6: key_id = gvFunctionKey6; special_key = YES; break;
    2301             :                                         case SDLK_F7: key_id = gvFunctionKey7; special_key = YES; break;
    2302             :                                         case SDLK_F8: key_id = gvFunctionKey8; special_key = YES; break;
    2303             :                                         case SDLK_F9: key_id = gvFunctionKey9; special_key = YES; break;
    2304             :                                         case SDLK_F10: key_id = gvFunctionKey10; special_key = YES; break;
    2305             :                                         case SDLK_F11: key_id = gvFunctionKey11; special_key = YES; break;
    2306             :                                         case SDLK_F12:
    2307             :                                                 key_id = 327;
    2308             :                                                 [self toggleScreenMode];
    2309             :                                                 special_key = YES; 
    2310             :                                                 break;
    2311             : 
    2312             :                                         case SDLK_ESCAPE:
    2313             :                                                 if (shift)
    2314             :                                                 {
    2315             :                                                         SDL_FreeSurface(surface);
    2316             :                                                         [gameController exitAppWithContext:@"Shift-escape pressed"];
    2317             :                                                 }
    2318             :                                                 else
    2319             :                                                         key_id = 27;
    2320             :                                                         special_key = YES; 
    2321             :                                                 break;
    2322             :                                         default:
    2323             :                                                 //OOLog(@"keys.test", @"Unhandled Keydown scancode with unicode = 0: %d", scan_code);
    2324             :                                                 ;
    2325             :                                 }
    2326             : 
    2327             :                                 // the keyup event doesn't give us the unicode value, so store it here so it can be retrieved on keyup
    2328             :                                 // the ctrl key tends to mix up the unicode values, so deal with some special cases
    2329             :                                 // we also need (in most cases) to get the character without the impact of caps lock. 
    2330             :                                 if (((!special_key && (ctrl || key_id == 0)) || ([self isCapsLockOn] && (!special_key && !allowingStringInput))) && !modifier_pressed) //  
    2331             :                                 {
    2332             :                                         // ctrl changes alpha characters to control codes (1-26)
    2333             :                                         if (ctrl && key_id >=1 && key_id <= 26) 
    2334             :                                         {
    2335             :                                                 if (shift) 
    2336             :                                                         key_id += 64; // A-Z is from 65, offset by -1 for the scancode start point
    2337             :                                                 else
    2338             :                                                         key_id += 96; // a-z is from 97, offset by -1 for the scancode start point
    2339             :                                         } 
    2340             :                                         else 
    2341             :                                         {
    2342             :                                                 key_id = 0; // reset the value here to force a lookup from the keymappings data
    2343             :                                         }
    2344             :                                 }
    2345             : 
    2346             :                                 // if we get here and we still don't have a key id, grab the unicode value from our keymappings dict
    2347             :                                 if (key_id == 0) 
    2348             :                                 {
    2349             :                                         // get unicode value for keycode from keymappings files
    2350             :                                         // this handles all the non-functional keys. the function keys are handled in the switch above
    2351             :                                         if (!shift)
    2352             :                                         {
    2353             :                                                 NSString *keyNormal = [keyMappings_normal objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
    2354             :                                                 if (keyNormal) key_id = [keyNormal integerValue];
    2355             :                                         }
    2356             :                                         else
    2357             :                                         {
    2358             :                                                 NSString *keyShifted = [keyMappings_shifted objectForKey:[NSString stringWithFormat:@"%d", scan_code]];
    2359             :                                                 if (keyShifted) key_id = [keyShifted integerValue];
    2360             :                                         }
    2361             :                                 }
    2362             : 
    2363             :                                 // if we've got the unicode value, we can store it in our array now
    2364             :                                 if (key_id > 0) scancode2Unicode[scan_code] = key_id;
    2365             : 
    2366             :                                 if(allowingStringInput)
    2367             :                                 {
    2368             :                                         [self handleStringInput:kbd_event keyID:key_id];
    2369             :                                 }
    2370             : 
    2371             :                                 OOLog(kOOLogKeyDown, @"Keydown scancode = %d, unicode = %i, sym = %i, character = %c, shift = %d, ctrl = %d, alt = %d", scan_code, key_id, kbd_event->keysym.sym, key_id, shift, ctrl, opt);
    2372             :                                 //OOLog(kOOLogKeyDown, @"Keydown scancode = %d, unicode = %i", kbd_event->keysym.scancode, key_id);
    2373             : 
    2374             :                                 if (key_id > 0 && key_id <= [self numKeys]) 
    2375             :                                 {
    2376             :                                         keys[key_id] = YES;
    2377             :                                 }
    2378             :                                 else 
    2379             :                                 {
    2380             :                                         //OOLog(@"keys.test", @"Unhandled Keydown scancode/unicode: %d %i", scan_code, key_id);
    2381             :                                 }
    2382             :                                 break;
    2383             : 
    2384             :                         case SDL_KEYUP:
    2385             :                                 suppressKeys = NO;    // DJS
    2386             :                                 kbd_event = (SDL_KeyboardEvent*)&event;
    2387             :                                 scan_code = kbd_event->keysym.scancode;
    2388             : 
    2389             :                                 // all the work should have been down on the keydown event, so all we need to do is get the unicode value from the array
    2390             :                                 key_id = scancode2Unicode[scan_code];
    2391             : 
    2392             :                                 // deal with modifiers first
    2393             :                                 switch (kbd_event->keysym.sym)
    2394             :                                 {
    2395             :                                         case SDLK_LSHIFT:
    2396             :                                         case SDLK_RSHIFT:
    2397             :                                                 shift = NO;
    2398             :                                                 break;
    2399             : 
    2400             :                                         case SDLK_LCTRL:
    2401             :                                         case SDLK_RCTRL:
    2402             :                                                 ctrl = NO;
    2403             :                                                 break;
    2404             :                                                 
    2405             :                                         case SDLK_LALT:
    2406             :                                         case SDLK_RALT:
    2407             :                                                 opt = NO;
    2408             :                                                 break;
    2409             :                                         default:
    2410             :                                                 ;
    2411             :                                 }
    2412             :                                 OOLog(kOOLogKeyUp, @"Keyup scancode = %d, unicode = %i, sym = %i, character = %c, shift = %d, ctrl = %d, alt = %d", scan_code, key_id, kbd_event->keysym.sym, key_id, shift, ctrl, opt);
    2413             :                                 //OOLog(kOOLogKeyUp, @"Keyup scancode = %d, shift = %d, ctrl = %d, alt = %d", scan_code, shift, ctrl, opt);
    2414             :                                 
    2415             :                                 // translate scancode to unicode equiv
    2416             :                                 switch (kbd_event->keysym.sym) 
    2417             :                                 {
    2418             :                                         case SDLK_KP0: key_id = (!allowingStringInput ? gvNumberPadKey0 : gvNumberKey0); break;
    2419             :                                         case SDLK_KP1: key_id = (!allowingStringInput ? gvNumberPadKey1 : gvNumberKey1); break;
    2420             :                                         case SDLK_KP2: key_id = (!allowingStringInput ? gvNumberPadKey2 : gvNumberKey2); break;
    2421             :                                         case SDLK_KP3: key_id = (!allowingStringInput ? gvNumberPadKey3 : gvNumberKey3); break;
    2422             :                                         case SDLK_KP4: key_id = (!allowingStringInput ? gvNumberPadKey4 : gvNumberKey4); break;
    2423             :                                         case SDLK_KP5: key_id = (!allowingStringInput ? gvNumberPadKey5 : gvNumberKey5); break;
    2424             :                                         case SDLK_KP6: key_id = (!allowingStringInput ? gvNumberPadKey6 : gvNumberKey6); break;
    2425             :                                         case SDLK_KP7: key_id = (!allowingStringInput ? gvNumberPadKey7 : gvNumberKey7); break;
    2426             :                                         case SDLK_KP8: key_id = (!allowingStringInput ? gvNumberPadKey8 : gvNumberKey8); break;
    2427             :                                         case SDLK_KP9: key_id = (!allowingStringInput ? gvNumberPadKey9 : gvNumberKey9); break;
    2428             :                                         case SDLK_KP_PERIOD: key_id = (!allowingStringInput ? gvNumberPadKeyPeriod : 46); break;
    2429             :                                         case SDLK_KP_DIVIDE: key_id = (!allowingStringInput ? gvNumberPadKeyDivide : 47); break;
    2430             :                                         case SDLK_KP_MULTIPLY: key_id = (!allowingStringInput ? gvNumberPadKeyMultiply : 42); break;
    2431             :                                         case SDLK_KP_MINUS: key_id = (!allowingStringInput ? gvNumberPadKeyMinus : 45); break;
    2432             :                                         case SDLK_KP_PLUS: key_id = (!allowingStringInput ? gvNumberPadKeyPlus : 43); break;
    2433             :                                         case SDLK_KP_EQUALS: key_id = (!allowingStringInput ? gvNumberPadKeyEquals : 61); break;
    2434             :                                         case SDLK_KP_ENTER: key_id = gvNumberPadKeyEnter; break;
    2435             :                                         case SDLK_HOME: key_id = gvHomeKey; break;
    2436             :                                         case SDLK_END: key_id = gvEndKey; break;
    2437             :                                         case SDLK_INSERT: key_id = gvInsertKey; break;
    2438             :                                         case SDLK_PAGEUP: key_id = gvPageUpKey; break;
    2439             :                                         case SDLK_PAGEDOWN: key_id = gvPageDownKey; break;
    2440             :                                         case SDLK_SPACE: key_id = 32; break;
    2441             :                                         case SDLK_RETURN: key_id = 13; break;
    2442             :                                         case SDLK_TAB: key_id = 9; break;
    2443             :                                         case SDLK_ESCAPE: key_id = 27; break;
    2444             :                                         case SDLK_UP: key_id = gvArrowKeyUp; break;
    2445             :                                         case SDLK_DOWN: key_id = gvArrowKeyDown; break;
    2446             :                                         case SDLK_LEFT: key_id = gvArrowKeyLeft; break;
    2447             :                                         case SDLK_RIGHT: key_id = gvArrowKeyRight; break;
    2448             :                                         case SDLK_PAUSE: key_id = gvPauseKey; break;
    2449             :                                         case SDLK_F1: key_id = gvFunctionKey1; break;
    2450             :                                         case SDLK_F2: key_id = gvFunctionKey2; break;
    2451             :                                         case SDLK_F3: key_id = gvFunctionKey3; break;
    2452             :                                         case SDLK_F4: key_id = gvFunctionKey4; break;
    2453             :                                         case SDLK_F5: key_id = gvFunctionKey5; break;
    2454             :                                         case SDLK_F6: key_id = gvFunctionKey6; break;
    2455             :                                         case SDLK_F7: key_id = gvFunctionKey7; break;
    2456             :                                         case SDLK_F8: key_id = gvFunctionKey8; break;
    2457             :                                         case SDLK_F9: key_id = gvFunctionKey9; break;
    2458             :                                         case SDLK_F10: key_id = gvFunctionKey10; break;
    2459             :                                         case SDLK_F11: key_id = gvFunctionKey11; break;
    2460             :                                         case SDLK_F12: key_id = 327; break;
    2461             :                                         case SDLK_BACKSPACE: key_id = gvBackspaceKey; break;
    2462             :                                         case SDLK_DELETE: key_id = gvDeleteKey; break;
    2463             : 
    2464             :                                         default:
    2465             :                                                 //OOLog(@"keys.test", @"Unhandled Keyup scancode with unicode = 0: %d", kbd_event->keysym.scancode);
    2466             :                                                 ;
    2467             :                                 }
    2468             : 
    2469             :                                 if (key_id > 0 && key_id <= [self numKeys]) 
    2470             :                                 {
    2471             :                                         keys[key_id] = NO;
    2472             :                                 }
    2473             :                                 else 
    2474             :                                 {
    2475             :                                         //OOLog(@"keys.test", @"Unhandled Keyup scancode: %d", kbd_event->keysym.scancode);
    2476             :                                 }
    2477             :                                 break;
    2478             : 
    2479             :                         case SDL_VIDEORESIZE:
    2480             :                         {
    2481             :                                 SDL_ResizeEvent *rsevt=(SDL_ResizeEvent *)&event;
    2482             :                                 NSSize newSize=NSMakeSize(rsevt->w, rsevt->h);
    2483             : #if OOLITE_WINDOWS
    2484             :                                 if (!fullScreen && updateContext)
    2485             :                                 {
    2486             :                                         if (saveSize == NO)
    2487             :                                         {
    2488             :                                                 // event triggered by caption & frame
    2489             :                                                 // next event will be a real resize.
    2490             :                                                 saveSize = YES;
    2491             :                                         }
    2492             :                                         else
    2493             :                                         {
    2494             :                                                 [self initialiseGLWithSize: newSize];
    2495             :                                                 [self saveWindowSize: newSize];
    2496             :                                         }
    2497             :                                 }
    2498             : #else
    2499             :                                 [self initialiseGLWithSize: newSize];
    2500             :                                 [self saveWindowSize: newSize];
    2501             : #endif
    2502             :                                 // certain gui screens will require an immediate redraw after
    2503             :                                 // a resize event - Nikos 20140129
    2504             :                                 if ([PlayerEntity sharedPlayer])
    2505             :                                 {
    2506             :                                         [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates];
    2507             :                                 }
    2508             :                                 break;
    2509             :                         }
    2510             :                         
    2511             : #if OOLITE_WINDOWS
    2512             :                         // if we minimize the window while in fullscreen (e.g. via
    2513             :                         // Win+M or Win+DownArrow), restore the non-borderless window
    2514             :                         // style before minimuzing and reset it when we return, otherwise
    2515             :                         // there might be issues with the game window remaining stuck on
    2516             :                         // top in some cases (seen with some Intel gfx chips).
    2517             :                         // N.B. active event gain of zero means app is iconified
    2518             :                         case SDL_ACTIVEEVENT:
    2519             :                         {                       
    2520             :                                 if ((event.active.state & SDL_APPACTIVE) && fullScreen)
    2521             :                                 {
    2522             :                                         [self setWindowBorderless:event.active.gain];
    2523             :                                 }
    2524             :                                 break;
    2525             :                         }
    2526             :                         
    2527             :                         // need to track this because the user may move the game window
    2528             :                         // to a secondary monitor, in which case we must potentially
    2529             :                         // refresh the information displayed (e.g. Game Options screen)
    2530             :                         // Nikos - 20140920
    2531             :                         case SDL_SYSWMEVENT:
    2532             :                         {
    2533             :                                 DWORD dwLastError = 0;
    2534             :                                 switch (event.syswm.msg->msg)
    2535             :                                 {
    2536             :                                         case WM_WINDOWPOSCHANGING:
    2537             :                                                 /* if we are in fullscreen mode we normally don't worry about having the window moved.
    2538             :                                                    However, when using multiple monitors, one can use hotkey combinations to make the
    2539             :                                                    window "jump" from one monitor to the next. We don't want this to happen, so if we
    2540             :                                                    detect that our (fullscreen) window has moved, we immediately bring it back to its
    2541             :                                                    original position. Nikos - 20140922
    2542             :                                                 */
    2543             :                                                 if (fullScreen)
    2544             :                                                 {
    2545             :                                                         RECT rDC;
    2546             :                                                         
    2547             :                                                         /* attempting to move our fullscreen window while in maximized state can freak
    2548             :                                                            Windows out and the window may not return to its original position properly.
    2549             :                                                            Solution: if such a move takes place, first change the window placement to
    2550             :                                                            normal, move it normally, then restore its placement to maximized again. 
    2551             :                                                            Additionally, the last good known window position seems to be lost in such
    2552             :                                                            a case. While at it, update also the coordinates of the non-maximized window
    2553             :                                                            so that it can return to its original position - this is why we need lastGoodRect.
    2554             :                                                          */
    2555             :                                                         WINDOWPLACEMENT wp;
    2556             :                                                         wp.length = sizeof(WINDOWPLACEMENT);
    2557             :                                                         GetWindowPlacement(SDL_Window, &wp);
    2558             :                                                         
    2559             :                                                         GetWindowRect(SDL_Window, &rDC);
    2560             :                                                         if (rDC.left != monitorInfo.rcMonitor.left || rDC.top != monitorInfo.rcMonitor.top)
    2561             :                                                         {
    2562             :                                                                 BOOL fullScreenMaximized = NO;
    2563             :                                                                 if (wp.showCmd == SW_SHOWMAXIMIZED && !fullScreenMaximized)
    2564             :                                                                 {
    2565             :                                                                         fullScreenMaximized = YES;
    2566             :                                                                         wp.showCmd = SW_SHOWNORMAL;
    2567             :                                                                         SetWindowPlacement(SDL_Window, &wp);
    2568             :                                                                 }
    2569             :                         
    2570             :                                                                 if (wp.showCmd != SW_SHOWMINIMIZED && wp.showCmd != SW_MINIMIZE)
    2571             :                                                                 {
    2572             :                                                                         MoveWindow(SDL_Window, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top,
    2573             :                                                                                                         (int)viewSize.width, (int)viewSize.height, TRUE);
    2574             :                                                                 }
    2575             :                                                                 
    2576             :                                                                 if (fullScreenMaximized)
    2577             :                                                                 {
    2578             :                                                                         GetWindowPlacement(SDL_Window, &wp);
    2579             :                                                                         wp.showCmd = SW_SHOWMAXIMIZED;
    2580             :                                                                         CopyRect(&wp.rcNormalPosition, &lastGoodRect);
    2581             :                                                                         SetWindowPlacement(SDL_Window, &wp);
    2582             :                                                                 }
    2583             :                                                         }
    2584             :                                                         else if (wp.showCmd == SW_SHOWMAXIMIZED)
    2585             :                                                         {
    2586             :                                                                         CopyRect(&wp.rcNormalPosition, &lastGoodRect);
    2587             :                                                                         SetWindowPlacement(SDL_Window, &wp);
    2588             :                                                         }
    2589             :                                                 }
    2590             :                                                 // it is important that this gets done after we've dealt with possible fullscreen movements,
    2591             :                                                 // because -doGuiScreenResizeUpdates does itself an update on current monitor
    2592             :                                                 if ([PlayerEntity sharedPlayer])
    2593             :                                                 {
    2594             :                                                         [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates];
    2595             :                                                 }
    2596             :                                                 /*
    2597             :                                                  deliberately no break statement here - moving or resizing the window changes its bounds
    2598             :                                                  rectangle. Therefore we must check whether to clip the mouse or not inside the newly
    2599             :                                                  updated rectangle, so just let it fall through
    2600             :                                                 */
    2601             :                                                 
    2602             :                                         case WM_ACTIVATEAPP:
    2603             :                                                 if(grabMouseStatus)  [self grabMouseInsideGameWindow:YES];
    2604             :                                                 break;
    2605             :                                                 
    2606             :                                         case WM_SETTINGCHANGE:
    2607             :                                                 // TODO: we really should be checking the status of event.syswm.msg->lParam here and run our
    2608             :                                                 // dark / light mode refresh check only if the lParam LPCTSTR matches "ImmersiveColorSet".
    2609             :                                                 // However, for some reason I cannot get an actual string on lParam. This means that the
    2610             :                                                 // mode refresh check runs every time something changes the Windows Registry while the game
    2611             :                                                 // is running. Still, should be OK because our refreshDarKOrLightMode will be transparent in
    2612             :                                                 // such cases, plus we would not practically expect too many events doing things to the Registry
    2613             :                                                 // while we are running. If in the future we need to respond to a different event which changes 
    2614             :                                                 // system settings in real time, then yes, we will have to find a way to decode lParam properly.
    2615             :                                                 // Nikos, 20230805
    2616             :                                                 [self refreshDarKOrLightMode];
    2617             :                                                 break;
    2618             :                                                 
    2619             :                                         case WM_SETFOCUS:
    2620             :                                                 /*
    2621             :         `                                       make sure that all modifier keys like Shift, Alt, Ctrl and Caps Lock
    2622             :         `                                       are set correctly to what they should be when we get focus. We have
    2623             :         `                                       to do it ourselves because SDL on Windows has problems with this
    2624             :         `                                       when focus change events occur, like e.g. Alt-Tab in/out of the
    2625             :                                                 application
    2626             :         `                                       */
    2627             :                                                 [self resetSDLKeyModifiers];
    2628             :                                                 if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
    2629             :                                                 {
    2630             :                                                         dwLastError = GetLastError();
    2631             :                                                         OOLog(@"wm_setfocus.message", @"Setting thread priority to time critical failed! (error code: %d)", dwLastError);
    2632             :                                                 }
    2633             :                                                 break;
    2634             :                                                 
    2635             :                                         case WM_KILLFOCUS:
    2636             :                                                 if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL))
    2637             :                                                 {
    2638             :                                                         dwLastError = GetLastError();
    2639             :                                                         OOLog(@"wm_killfocus.message", @"Setting thread priority to normal failed! (error code: %d)", dwLastError);
    2640             :                                                 }
    2641             :                                                 break;
    2642             :                                                 
    2643             :                                         default:
    2644             :                                                 ;
    2645             :                                 }
    2646             :                                 break;
    2647             :                         }
    2648             : #endif
    2649             : 
    2650             :                         // caused by INTR or someone hitting close
    2651             :                         case SDL_QUIT:
    2652             :                         {
    2653             :                                 SDL_FreeSurface(surface);
    2654             :                                 [gameController exitAppWithContext:@"SDL_QUIT event received"];
    2655             :                         }
    2656             :                 }
    2657             :         }
    2658             :         // check if enough time has passed since last use of the mousewheel and act
    2659             :         // if needed
    2660             :         if (timeNow >= timeSinceLastMouseWheel + OOMOUSEWHEEL_EVENTS_DELAY_INTERVAL)
    2661             :         {
    2662             :                 _mouseWheelDelta = 0.0f;
    2663             :         }
    2664             : }
    2665             : 
    2666             : 
    2667             : // DJS: String input handler. Since for SDL versions we're also handling
    2668             : // freeform typing this has necessarily got more complex than the non-SDL
    2669             : // versions.
    2670           0 : - (void) handleStringInput: (SDL_KeyboardEvent *) kbd_event keyID:(Uint16)key_id;
    2671             : {
    2672             :         SDLKey key=kbd_event->keysym.sym;
    2673             : 
    2674             :         // Del, Backspace
    2675             :         if((key == SDLK_BACKSPACE || key == SDLK_DELETE) && [typedString length] > 0)
    2676             :         {
    2677             :                 // delete
    2678             :                 [typedString deleteCharactersInRange:NSMakeRange([typedString length]-1, 1)];
    2679             :         }
    2680             : 
    2681             :         isAlphabetKeyDown=NO;
    2682             : 
    2683             :         // TODO: a more flexible mechanism  for max. string length ?
    2684             :         if([typedString length] < 40)
    2685             :         {
    2686             :                 lastKeyShifted = shift;
    2687             :                 if (allowingStringInput == gvStringInputAlpha)
    2688             :                 {
    2689             :                         // inputAlpha - limited input for planet find screen
    2690             :                         if(key >= SDLK_a && key <= SDLK_z)
    2691             :                         {
    2692             :                                 isAlphabetKeyDown=YES;
    2693             :                                 [typedString appendFormat:@"%c", key];
    2694             :                                 // if in inputAlpha, keep in lower case.
    2695             :                         }
    2696             :                 }
    2697             :                 else
    2698             :                 {
    2699             :                         //Uint16 unicode = kbd_event->keysym.unicode;
    2700             :                         // printable range
    2701             :                         if (key_id >= 32 && key_id <= 255) // 126
    2702             :                         {
    2703             :                                 if ((char)key_id != '/' || allowingStringInput == gvStringInputAll)
    2704             :                                 {
    2705             :                                         isAlphabetKeyDown=YES;
    2706             :                                         [typedString appendFormat:@"%c", key_id];
    2707             :                                 }
    2708             :                         }
    2709             :                 }
    2710             :         }
    2711             : }
    2712             : 
    2713             : 
    2714             : // Full screen mode enumerator.
    2715             : - (void) populateFullScreenModelist
    2716             : {
    2717             :         int i;
    2718             :         SDL_Rect **modes;
    2719             :         NSMutableDictionary *mode;
    2720             : 
    2721             :         screenSizes=[[NSMutableArray alloc] init];
    2722             : 
    2723             :         // The default resolution (slot 0) is the resolution we are
    2724             :         // already in since this is guaranteed to work.
    2725             :         mode=[MyOpenGLView getNativeSize];
    2726             :         [screenSizes addObject: mode];
    2727             : 
    2728             :         modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE);
    2729             :         if(modes == (SDL_Rect **)NULL)
    2730             :         {
    2731             :                 OOLog(@"display.mode.list.none", @"%@", @"SDL didn't return any screen modes");
    2732             :                 return;
    2733             :         }
    2734             : 
    2735             :         if(modes == (SDL_Rect **)-1)
    2736             :         {
    2737             :                 OOLog(@"display.mode.list.none", @"%@", @"SDL claims 'all resolutions available' which is unhelpful in the extreme");
    2738             :                 return;
    2739             :         }
    2740             : 
    2741             :         int lastw=[[mode objectForKey: kOODisplayWidth] intValue];
    2742             :         int lasth=[[mode objectForKey: kOODisplayHeight] intValue];
    2743             :         for(i=0; modes[i]; i++)
    2744             :         {
    2745             :                 // SDL_ListModes often lists a mode several times,
    2746             :                 // presumably because each mode has several refresh rates.
    2747             :                 // But the modes pointer is an SDL_Rect which can't represent
    2748             :                 // refresh rates. WHY!?
    2749             :                 if(modes[i]->w != lastw || modes[i]->h != lasth)
    2750             :                 {
    2751             :                         // new resolution, save it
    2752             :                         mode=[NSMutableDictionary dictionary];
    2753             :                         [mode setValue: [NSNumber numberWithInt: (int)modes[i]->w]
    2754             :                                         forKey: kOODisplayWidth];
    2755             :                         [mode setValue: [NSNumber numberWithInt: (int)modes[i]->h]
    2756             :                                         forKey: kOODisplayHeight];
    2757             :                         [mode setValue: [NSNumber numberWithInt: 0]
    2758             :                                         forKey: kOODisplayRefreshRate];
    2759             :                         if (![screenSizes containsObject:mode])
    2760             :                         {
    2761             :                                 [screenSizes addObject: mode];
    2762             :                                 OOLog(@"display.mode.list", @"Added res %d x %d", modes[i]->w, modes[i]->h);
    2763             :                                 lastw=modes[i]->w;
    2764             :                                 lasth=modes[i]->h;
    2765             :                         }
    2766             :                 }
    2767             :         }
    2768             : }
    2769             : 
    2770             : 
    2771             : // Save and restore window sizes to/from defaults.
    2772             : - (void) saveWindowSize: (NSSize) windowSize
    2773             : {
    2774             :         NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
    2775             :         [defaults setInteger: (int)windowSize.width forKey: @"window_width"];
    2776             :         [defaults setInteger: (int)windowSize.height forKey: @"window_height"];
    2777             :         currentWindowSize=windowSize;
    2778             : }
    2779             : 
    2780             : 
    2781             : - (NSSize) loadWindowSize
    2782             : {
    2783             :         NSSize windowSize;
    2784             :         NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults];
    2785             :         if([defaults objectForKey:@"window_width"] && [defaults objectForKey:@"window_height"])
    2786             :         {
    2787             :                 windowSize=NSMakeSize([defaults integerForKey: @"window_width"],
    2788             :                                         [defaults integerForKey: @"window_height"]);
    2789             :         }
    2790             :         else
    2791             :         {
    2792             :                 windowSize=NSMakeSize(WINDOW_SIZE_DEFAULT_WIDTH, WINDOW_SIZE_DEFAULT_HEIGHT);
    2793             :         }
    2794             :         currentWindowSize=windowSize;
    2795             :         return windowSize;
    2796             : }
    2797             : 
    2798             : 
    2799             : - (int) loadFullscreenSettings
    2800             : {
    2801             :         currentSize=0;
    2802             :         int width=0, height=0, refresh=0;
    2803             :         unsigned i;
    2804             : 
    2805             :         NSArray* cmdline_arguments = [[NSProcessInfo processInfo] arguments];
    2806             : 
    2807             :         NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    2808             :         if ([userDefaults objectForKey:@"display_width"])
    2809             :                 width = [userDefaults integerForKey:@"display_width"];
    2810             :         if ([userDefaults objectForKey:@"display_height"])
    2811             :                 height = [userDefaults integerForKey:@"display_height"];
    2812             :         if ([userDefaults objectForKey:@"display_refresh"])
    2813             :                 refresh = [userDefaults integerForKey:@"display_refresh"];
    2814             :         if([userDefaults objectForKey:@"fullscreen"])
    2815             :                 fullScreen=[userDefaults boolForKey:@"fullscreen"];
    2816             : 
    2817             :         // Check if -fullscreen or -windowed has been passed on the command line. If yes,
    2818             :         // set it regardless of what is set by .GNUstepDefaults. If both are found in the
    2819             :         // arguments list, the one that comes last wins.
    2820             :         for (i = 0; i < [cmdline_arguments count]; i++)
    2821             :         {
    2822             :                 if ([[cmdline_arguments objectAtIndex:i] isEqual:@"-fullscreen"]) fullScreen = YES;
    2823             :                 if ([[cmdline_arguments objectAtIndex:i] isEqual:@"-windowed"]) fullScreen = NO;
    2824             :         }
    2825             :         
    2826             :         if(width && height)
    2827             :         {
    2828             :                 currentSize=[self findDisplayModeForWidth: width Height: height Refresh: refresh];
    2829             :                 return currentSize;
    2830             :         }
    2831             :         return currentSize;
    2832             : }
    2833             : 
    2834             : 
    2835             : - (int) findDisplayModeForWidth:(unsigned int) d_width Height:(unsigned int) d_height Refresh:(unsigned int) d_refresh
    2836             : {
    2837             :         int i, modeCount;
    2838             :         NSDictionary *mode;
    2839             :         unsigned int modeWidth, modeHeight, modeRefresh;
    2840             : 
    2841             :         modeCount = [screenSizes count];
    2842             : 
    2843             :         for (i = 0; i < modeCount; i++)
    2844             :         {
    2845             :                 mode = [screenSizes objectAtIndex: i];
    2846             :                 modeWidth = [[mode objectForKey: kOODisplayWidth] intValue];
    2847             :                 modeHeight = [[mode objectForKey: kOODisplayHeight] intValue];
    2848             :                 modeRefresh = [[mode objectForKey: kOODisplayRefreshRate] intValue];
    2849             :                 if ((modeWidth == d_width)&&(modeHeight == d_height)&&(modeRefresh == d_refresh))
    2850             :                 {
    2851             :                         OOLog(@"display.mode.found", @"Found mode %@", mode);
    2852             :                         return i;
    2853             :                 }
    2854             :         }
    2855             : 
    2856             :         OOLog(@"display.mode.found.failed", @"Failed to find mode: width=%d height=%d refresh=%d", d_width, d_height, d_refresh);
    2857             :         OOLog(@"display.mode.found.failed.list", @"Contents of list: %@", screenSizes);
    2858             :         return 0;
    2859             : }
    2860             : 
    2861             : 
    2862             : - (NSSize) currentScreenSize
    2863             : {
    2864             :         NSDictionary *mode=[screenSizes objectAtIndex: currentSize];
    2865             : 
    2866             :         if(mode)
    2867             :         {
    2868             :                 return NSMakeSize([[mode objectForKey: kOODisplayWidth] intValue],
    2869             :                                 [[mode objectForKey: kOODisplayHeight] intValue]);
    2870             :         }
    2871             :         OOLog(@"display.mode.unknown", @"%@", @"Screen size unknown!");
    2872             :         return NSMakeSize(WINDOW_SIZE_DEFAULT_WIDTH, WINDOW_SIZE_DEFAULT_HEIGHT);
    2873             : }
    2874             : 
    2875             : 
    2876             : - (void) setMouseInDeltaMode: (BOOL) inDelta
    2877             : {
    2878             :         mouseInDeltaMode=inDelta;
    2879             : }
    2880             : 
    2881             : 
    2882             : - (void) setGammaValue: (float) value
    2883             : {
    2884             :         if (value < 0.2f)  value = 0.2f;
    2885             :         if (value > 4.0f)  value = 4.0f;
    2886             : 
    2887             :         _gamma = value;
    2888             :         SDL_SetGamma(_gamma, _gamma, _gamma);
    2889             :         
    2890             :         [[NSUserDefaults standardUserDefaults] setFloat:_gamma forKey:@"gamma-value"];
    2891             : }
    2892             : 
    2893             : 
    2894             : - (float) gammaValue
    2895             : {
    2896             :         return _gamma;
    2897             : }
    2898             : 
    2899             : 
    2900             : - (void) setFov:(float)value fromFraction:(BOOL)fromFraction
    2901             : {
    2902             :         _fov = fromFraction ? value : tan((value / 2) * M_PI / 180);
    2903             : }
    2904             : 
    2905             : 
    2906             : - (float) fov:(BOOL)inFraction
    2907             : {
    2908             :         return inFraction ? _fov : 2 * atan(_fov) * 180 / M_PI;
    2909             : }
    2910             : 
    2911             : 
    2912             : - (BOOL) msaa
    2913             : {
    2914             :         return _msaa;
    2915             : }
    2916             : 
    2917             : 
    2918             : - (void) setMsaa:(BOOL)newMsaa
    2919             : {
    2920             :         _msaa = !!newMsaa;
    2921             : }
    2922             : 
    2923             : 
    2924             : - (OOOpenGLMatrixManager *) getOpenGLMatrixManager
    2925             : {
    2926             :         return matrixManager;
    2927             : }
    2928             : 
    2929             : 
    2930             : + (BOOL)pollShiftKey
    2931             : {
    2932             :         return 0 != (SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT));
    2933             : }
    2934             : 
    2935             : 
    2936             : #ifndef NDEBUG
    2937             : - (void) dumpRGBAToFileNamed:(NSString *)name
    2938             :                                            bytes:(uint8_t *)bytes
    2939             :                                            width:(NSUInteger)width
    2940             :                                           height:(NSUInteger)height
    2941             :                                         rowBytes:(NSUInteger)rowBytes
    2942             : {
    2943             :         if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4)  return;
    2944             : 
    2945             :         // use the snapshots directory
    2946             :         NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR];
    2947             :         dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]];
    2948             : 
    2949             :         // convert transparency to black before saving to bmp
    2950             :         SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 32, rowBytes, 0xFF, 0xFF00, 0xFF0000, 0xFF000000);
    2951             :         SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]);
    2952             :         SDL_FreeSurface(tmpSurface);
    2953             : }
    2954             : 
    2955             : 
    2956             : - (void) dumpRGBToFileNamed:(NSString *)name
    2957             :                                            bytes:(uint8_t *)bytes
    2958             :                                            width:(NSUInteger)width
    2959             :                                           height:(NSUInteger)height
    2960             :                                         rowBytes:(NSUInteger)rowBytes
    2961             : {
    2962             :         if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 3)  return;
    2963             : 
    2964             :         // use the snapshots directory
    2965             :         NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR];
    2966             :         dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]];
    2967             : 
    2968             :         SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 24, rowBytes, 0xFF, 0xFF00, 0xFF0000, 0x0);
    2969             :         SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]);
    2970             :         SDL_FreeSurface(tmpSurface);
    2971             : }
    2972             : 
    2973             : 
    2974             : - (void) dumpGrayToFileNamed:(NSString *)name
    2975             :                                            bytes:(uint8_t *)bytes
    2976             :                                            width:(NSUInteger)width
    2977             :                                           height:(NSUInteger)height
    2978             :                                         rowBytes:(NSUInteger)rowBytes
    2979             : {
    2980             :         if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width)  return;
    2981             : 
    2982             :         // use the snapshots directory
    2983             :         NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR];
    2984             :         dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]];
    2985             : 
    2986             :         SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 8, rowBytes, 0xFF, 0xFF, 0xFF, 0x0);
    2987             :         SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]);
    2988             :         SDL_FreeSurface(tmpSurface);
    2989             : }
    2990             : 
    2991             : 
    2992             : - (void) dumpGrayAlphaToFileNamed:(NSString *)name
    2993             :                                            bytes:(uint8_t *)bytes
    2994             :                                            width:(NSUInteger)width
    2995             :                                           height:(NSUInteger)height
    2996             :                                         rowBytes:(NSUInteger)rowBytes
    2997             : {
    2998             :         if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 2)  return;
    2999             : 
    3000             :         // use the snapshots directory
    3001             :         NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR];
    3002             :         dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]];
    3003             : 
    3004             :         SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 16, rowBytes, 0xFF, 0xFF, 0xFF, 0xFF);
    3005             :         SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]);
    3006             :         SDL_FreeSurface(tmpSurface);
    3007             : }
    3008             : 
    3009             : 
    3010             : - (void) dumpRGBAToRGBFileNamed:(NSString *)rgbName
    3011             :                            andGrayFileNamed:(NSString *)grayName
    3012             :                                                   bytes:(uint8_t *)bytes
    3013             :                                                   width:(NSUInteger)width
    3014             :                                                  height:(NSUInteger)height
    3015             :                                            rowBytes:(NSUInteger)rowBytes
    3016             : {
    3017             :         if ((rgbName == nil && grayName == nil) || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4)  return;
    3018             : 
    3019             :         uint8_t                         *rgbBytes, *rgbPx, *grayBytes, *grayPx, *srcPx;
    3020             :         NSUInteger                      x, y;
    3021             :         BOOL                            trivalAlpha = YES;
    3022             : 
    3023             :         rgbPx = rgbBytes = malloc(width * height * 3);
    3024             :         if (rgbBytes == NULL)  return;
    3025             : 
    3026             :         grayPx = grayBytes = malloc(width * height);
    3027             :         if (grayBytes == NULL)
    3028             :         {
    3029             :                 free(rgbBytes);
    3030             :                 return;
    3031             :         }
    3032             : 
    3033             :         for (y = 0; y < height; y++)
    3034             :         {
    3035             :                 srcPx = bytes + rowBytes * y;
    3036             : 
    3037             :                 for (x = 0; x < width; x++)
    3038             :                 {
    3039             :                         *rgbPx++ = *srcPx++;
    3040             :                         *rgbPx++ = *srcPx++;
    3041             :                         *rgbPx++ = *srcPx++;
    3042             :                         trivalAlpha = trivalAlpha && ((*srcPx == 0xFF) || (*srcPx == 0x00));    // Look for any "interesting" pixels in alpha.
    3043             :                         *grayPx++ = *srcPx++;
    3044             :                 }
    3045             :         }
    3046             : 
    3047             :         [self dumpRGBToFileNamed:rgbName
    3048             :                                            bytes:rgbBytes
    3049             :                                            width:width
    3050             :                                           height:height
    3051             :                                         rowBytes:width * 3];
    3052             :         free(rgbBytes);
    3053             : 
    3054             :         if (!trivalAlpha)
    3055             :         {
    3056             :                 [self dumpGrayToFileNamed:grayName
    3057             :                                                         bytes:grayBytes
    3058             :                                                         width:width
    3059             :                                                    height:height
    3060             :                                                  rowBytes:width];
    3061             :         }
    3062             :         free(grayBytes);
    3063             : }
    3064             : #endif
    3065             : 
    3066             : @end

Generated by: LCOV version 1.14