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

Generated by: LCOV version 1.14