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