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