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