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
|