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

Generated by: LCOV version 1.14