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