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