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