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