diff --git a/GNUmakefile b/GNUmakefile index 95b4b28e3..70969c206 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -1,9 +1,21 @@ include $(GNUSTEP_MAKEFILES)/common.make include config.make -vpath %.m src/SDL:src/Core:src/Core/Entities:src/Core/Materials:src/Core/Scripting:src/Core/OXPVerifier:src/Core/Debug -vpath %.h src/SDL:src/Core:src/Core/Entities:src/Core/Materials:src/Core/Scripting:src/Core/OXPVerifier:src/Core/Debug:src/Core/MiniZip -vpath %.c src/SDL:src/Core:src/BSDCompat:src/Core/Debug:src/Core/MiniZip +ifeq ($(OO_SDL2_ENABLED),yes) + OO_SDL_DIR = src/SDL2 + SDL_CFLAGS = `sdl2-config --cflags` -DOO_ENABLE_SDL2 + SDL_OBJCFLAGS = `sdl2-config --cflags` -DOO_ENABLE_SDL2 + SDL_OBJC_LIBS = -lSDL2main -lSDL2 +else + OO_SDL_DIR = src/SDL + SDL_CFLAGS = `sdl-config --cflags` + SDL_OBJCFLAGS = `sdl-config --cflags` + SDL_OBJC_LIBS = -lSDLmain -lSDL +endif + +vpath %.m $(OO_SDL_DIR):src/Core:src/Core/Entities:src/Core/Materials:src/Core/Scripting:src/Core/OXPVerifier:src/Core/Debug +vpath %.h $(OO_SDL_DIR):src/Core:src/Core/Entities:src/Core/Materials:src/Core/Scripting:src/Core/OXPVerifier:src/Core/Debug:src/Core/MiniZip +vpath %.c $(OO_SDL_DIR):src/Core:src/BSDCompat:src/Core/Debug:src/Core/MiniZip GNUSTEP_INSTALLATION_DIR = $(GNUSTEP_USER_ROOT) ifeq ($(GNUSTEP_HOST_OS),mingw32) GNUSTEP_OBJ_DIR_NAME := $(GNUSTEP_OBJ_DIR_NAME).win @@ -23,11 +35,11 @@ ifeq ($(GNUSTEP_HOST_OS),mingw32) else JS_IMPORT_LIBRARY = js32ECMAv5 endif - ADDITIONAL_INCLUDE_DIRS = -I$(WIN_DEPS_DIR)/include -I$(JS_INC_DIR) -Isrc/SDL -Isrc/Core -Isrc/BSDCompat -Isrc/Core/Scripting -Isrc/Core/Materials -Isrc/Core/Entities -Isrc/Core/OXPVerifier -Isrc/Core/Debug -Isrc/Core/Tables -Isrc/Core/MiniZip - ADDITIONAL_OBJC_LIBS = -lglu32 -lopengl32 -lopenal32.dll -lpng14.dll -lmingw32 -lSDLmain -lSDL -lvorbisfile.dll -lvorbis.dll -lz -lgnustep-base -l$(JS_IMPORT_LIBRARY) -lwinmm -mwindows - ADDITIONAL_CFLAGS = -DWIN32 -DNEED_STRLCPY `sdl-config --cflags` -mtune=generic + ADDITIONAL_INCLUDE_DIRS = -I$(WIN_DEPS_DIR)/include -I$(JS_INC_DIR) -I$(OO_SDL_DIR) -Isrc/Core -Isrc/BSDCompat -Isrc/Core/Scripting -Isrc/Core/Materials -Isrc/Core/Entities -Isrc/Core/OXPVerifier -Isrc/Core/Debug -Isrc/Core/Tables -Isrc/Core/MiniZip + ADDITIONAL_OBJC_LIBS = -lglu32 -lopengl32 -lopenal32.dll -lpng14.dll -lmingw32 $(SDL_OBJC_LIBS) -lvorbisfile.dll -lvorbis.dll -lz -lgnustep-base -l$(JS_IMPORT_LIBRARY) -lwinmm -mwindows + ADDITIONAL_CFLAGS = -DWIN32 -DNEED_STRLCPY $(SDL_CFLAGS) -mtune=generic # note the vpath stuff above isn't working for me, so adding src/SDL and src/Core explicitly - ADDITIONAL_OBJCFLAGS = -DLOADSAVEGUI -DWIN32 -DXP_WIN -Wno-import -std=gnu99 `sdl-config --cflags` -mtune=generic + ADDITIONAL_OBJCFLAGS = -DLOADSAVEGUI -DWIN32 -DXP_WIN -Wno-import -std=gnu99 $(SDL_OBJCFLAGS) -mtune=generic ifneq ($(GNUSTEP_HOST_CPU),x86_64) ADDITIONAL_LDFLAGS += -Wl,--large-address-aware else @@ -49,10 +61,10 @@ else LIBJS_LIB_DIR = $(LIBJS_ROOT)/dist/lib LIBJS = js_static - ADDITIONAL_INCLUDE_DIRS = -I$(LIBJS_INC_DIR) -Isrc/SDL -Isrc/Core -Isrc/BSDCompat -Isrc/Core/Scripting -Isrc/Core/Materials -Isrc/Core/Entities -Isrc/Core/OXPVerifier -Isrc/Core/Debug -Isrc/Core/Tables -Isrc/Core/MiniZip - ADDITIONAL_OBJC_LIBS = -lGLU -lGL -lX11 -lSDL -lgnustep-base -l$(LIBJS) `nspr-config --libs` -lstdc++ -lopenal -lz -lvorbisfile - ADDITIONAL_CFLAGS = -Wall -DLINUX -DNEED_STRLCPY `sdl-config --cflags` `nspr-config --cflags` - ADDITIONAL_OBJCFLAGS = -Wall -std=gnu99 -DLOADSAVEGUI -DLINUX -DXP_UNIX -Wno-import `sdl-config --cflags` `nspr-config --cflags` + ADDITIONAL_INCLUDE_DIRS = -I$(LIBJS_INC_DIR) -I$(OO_SDL_DIR) -Isrc/Core -Isrc/BSDCompat -Isrc/Core/Scripting -Isrc/Core/Materials -Isrc/Core/Entities -Isrc/Core/OXPVerifier -Isrc/Core/Debug -Isrc/Core/Tables -Isrc/Core/MiniZip + ADDITIONAL_OBJC_LIBS = -lGLU -lGL -lX11 $(SDL_OBJC_LIBS) -lgnustep-base -l$(LIBJS) `nspr-config --libs` -lstdc++ -lopenal -lz -lvorbisfile + ADDITIONAL_CFLAGS = -Wall -DLINUX -DNEED_STRLCPY $(SDL_CFLAGS) `nspr-config --cflags` + ADDITIONAL_OBJCFLAGS = -Wall -std=gnu99 -DLOADSAVEGUI -DLINUX -DXP_UNIX -Wno-import $(SDL_OBJCFLAGS) `nspr-config --cflags` oolite_LIB_DIRS += -L$(LIBJS_LIB_DIR) -L/usr/X11R6/lib/ ifeq ($(use_deps),yes) diff --git a/config.make b/config.make index 297e7c3df..9d17c73c8 100644 --- a/config.make +++ b/config.make @@ -22,3 +22,4 @@ OO_LOCALIZATION_TOOLS = yes DEBUG_GRAPHVIZ = yes OO_JAVASCRIPT_TRACE = yes OO_FOV_INFLIGHT_CONTROL_ENABLED = no +OO_SDL2_ENABLED = no diff --git a/src/Core/OOJoystickManager.h b/src/Core/OOJoystickManager.h index 530783ed4..71cf285c5 100644 --- a/src/Core/OOJoystickManager.h +++ b/src/Core/OOJoystickManager.h @@ -151,7 +151,6 @@ enum { //SDL Abstracted constants #if OOLITE_SDL - #import enum diff --git a/src/Core/OOOpenGLOnly.h b/src/Core/OOOpenGLOnly.h index 367b4fbc7..f7f77fc50 100644 --- a/src/Core/OOOpenGLOnly.h +++ b/src/Core/OOOpenGLOnly.h @@ -57,6 +57,10 @@ MA 02110-1301, USA. // the standard SDL_opengl.h #include +#if OO_ENABLE_SDL2 +#include +#endif + // include an up-to-date version of glext.h #include diff --git a/src/SDL2/Comparison.h b/src/SDL2/Comparison.h new file mode 100644 index 000000000..55e55aacc --- /dev/null +++ b/src/SDL2/Comparison.h @@ -0,0 +1,121 @@ +// +// $Id: Comparison.h,v 1.3 2004/12/12 20:17:24 will_mason Exp $ +// +// vi: set ft=objc: + +/* + * ObjectiveLib - a library of containers and algorithms for Objective-C + * + * Copyright (c) 2004 + * Will Mason + * + * Portions: + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Copyright (c) 1996,1997 + * Silicon Graphics Computer Systems, Inc. + * + * Copyright (c) 1997 + * Moscow Center for SPARC Technology + * + * Copyright (c) 1999 + * Boris Fomitchev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * You may contact the author at will_mason@users.sourceforge.net. + */ + +#if defined(GNUSTEP) + +#if !defined(__COMPARISON_OL_GUARD) +#define __COMPARISON_OL_GUARD + +#include + +/** + * @category NSObject(OLComparisonMethods) Comparison.h Objectivelib/Comparison.h + * + * Comparison methods used in @ref Functors "function objects". These comparison + * methods are only required to be included when GNUstep is the platform, as + * Cocoa already defines them. Under Cocoa they are declared in the + * intuitively-named file @c NSScriptWhoseTests.h. All of these methods send + * the message @c compare: to the receiving object. + * + * @pre The receiving object must implement the method @c compare:. + */ +@interface NSObject (OLComparisonMethods) + +/** + * Return whether another object is equal to this one. This message returns YES if + * and only if the message @c compare: returns @c NSOrderedSame. + * + * @param object the object to which to compare this one + * @return YES if @a object is equal to this one, NO otherwise + */ +- (BOOL) isEqualTo: (id)object; + +/** + * Return whether this object is greater than another one. This message returns + * YES if and only if @c compare: returns @c NSOrderedDescending. + * + * @param object the object to which to compare this one + * @return YES if this object is greater than @a object, NO otherwise + */ +- (BOOL) isGreaterThan: (id)object; + +/** + * Return whether this object is greater than or equal to another one. This message returns + * YES if and only if @c compare: does not return @c NSOrderedAscending. + * + * @param object the object to which to compare this one + * @return YES if this object is greater than or equal to @a object, NO otherwise + */ +- (BOOL) isGreaterThanOrEqualTo: (id)object; + +/** + * Return whether this object is less than another one. This message returns + * YES if and only if @c compare: returns @c NSOrderedAscending. + * + * @param object the object to which to compare this one + * @return YES if this object is less than @a object, NO otherwise + */ +- (BOOL) isLessThan: (id)object; + +/** + * Return whether this object is less than or equal to another one. This message returns + * YES if and only if @c compare: does not return @c NSOrderedDescending. + * + * @param object the object to which to compare this one + * @return YES if this object is less than or equal to @a object, NO otherwise + */ +- (BOOL) isLessThanOrEqualTo: (id)object; + +/** + * Return whether another object is not equal to this one. This message returns YES if + * and only if the message @c compare: does not return @c NSOrderedSame. + * + * @param object the object to which to compare this one + * @return YES if @a object is not equal to this one, NO otherwise + */ +- (BOOL) isNotEqualTo: (id)object; + +@end + +#endif + +#endif diff --git a/src/SDL2/Comparison.m b/src/SDL2/Comparison.m new file mode 100644 index 000000000..8bcca0c82 --- /dev/null +++ b/src/SDL2/Comparison.m @@ -0,0 +1,88 @@ +// +// $Id: Comparison.m,v 1.3 2004/11/29 23:35:50 will_mason Exp $ +// +// vi: set ft=objc: + +/* + * ObjectiveLib - a library of containers and algorithms for Objective-C + * + * Copyright (c) 2004 + * Will Mason + * + * Portions: + * + * Copyright (c) 1994 + * Hewlett-Packard Company + * + * Copyright (c) 1996,1997 + * Silicon Graphics Computer Systems, Inc. + * + * Copyright (c) 1997 + * Moscow Center for SPARC Technology + * + * Copyright (c) 1999 + * Boris Fomitchev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * You may contact the author at will_mason@users.sourceforge.net. + */ + +#if defined(GNUSTEP) + +#include "Comparison.h" +#include + +@implementation NSObject (OLComparison) + +- (BOOL) isEqualTo: (id)object +{ + return (object != nil && [self compare: object] == NSOrderedSame) ? + YES : NO; +} + +- (BOOL) isGreaterThan: (id)object +{ + return (object != nil && [self compare: object] == NSOrderedDescending) ? + YES : NO; +} + +- (BOOL) isGreaterThanOrEqualTo: (id)object +{ + return (object != nil && [self compare: object] != NSOrderedAscending) ? + YES : NO; +} + +- (BOOL) isLessThan: (id)object +{ + return (object != nil && [self compare: object] == NSOrderedAscending) ? + YES : NO; +} + +- (BOOL) isLessThanOrEqualTo: (id)object +{ + return (object != nil && [self compare: object] != NSOrderedDescending) ? + YES : NO; +} + +- (BOOL) isNotEqualTo: (id)object +{ + return (object != nil && [self compare: object] != NSOrderedSame) ? + YES : NO; +} + +@end + +#endif diff --git a/src/SDL2/GameController+SDLFullScreen.m b/src/SDL2/GameController+SDLFullScreen.m new file mode 100644 index 000000000..175e0b458 --- /dev/null +++ b/src/SDL2/GameController+SDLFullScreen.m @@ -0,0 +1,165 @@ +/* + +GameController+SDLFullScreen.m + +Full-screen rendering support for SDL targets. + + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + + +#import "GameController.h" + +#if OOLITE_SDL + +#import "MyOpenGLView.h" +#import "Universe.h" +#import "OOFullScreenController.h" + + +@implementation GameController (FullScreen) + +- (void) setUpDisplayModes +{ + NSArray *modes = [gameView getScreenSizeArray]; + NSDictionary *mode = nil; + unsigned int modeIndex, modeCount; + unsigned int modeWidth, modeHeight; + + displayModes = [[NSMutableArray alloc] init]; + modeCount = [modes count]; + for (modeIndex = 0; modeIndex < modeCount; modeIndex++) + { + mode = [modes objectAtIndex: modeIndex]; + modeWidth = [[mode objectForKey: kOODisplayWidth] intValue]; + modeHeight = [[mode objectForKey: kOODisplayHeight] intValue]; + + if (modeWidth < DISPLAY_MIN_WIDTH || + modeWidth > DISPLAY_MAX_WIDTH || + modeHeight < DISPLAY_MIN_HEIGHT || + modeHeight > DISPLAY_MAX_HEIGHT) + continue; + [displayModes addObject: mode]; + } + + NSSize fsmSize = [gameView currentScreenSize]; + width = fsmSize.width; + height = fsmSize.height; +} + + +- (void) setFullScreenMode:(BOOL)fsm +{ + fullscreen = fsm; +} + + +- (void) exitFullScreenMode +{ + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"fullscreen"]; + stayInFullScreenMode = NO; +} + + +- (BOOL) inFullScreenMode +{ + return [gameView inFullScreenMode]; +} + + +- (BOOL) setDisplayWidth:(unsigned int) d_width Height:(unsigned int) d_height Refresh:(unsigned int) d_refresh +{ + NSDictionary *d_mode = [self findDisplayModeForWidth: d_width Height: d_height Refresh: d_refresh]; + if (d_mode) + { + width = d_width; + height = d_height; + refresh = d_refresh; + fullscreenDisplayMode = d_mode; + + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + + [userDefaults setInteger:width forKey:@"display_width"]; + [userDefaults setInteger:height forKey:@"display_height"]; + [userDefaults setInteger:refresh forKey:@"display_refresh"]; + + // Manual synchronization is required for SDL And doesn't hurt much for OS X. + [userDefaults synchronize]; + + return YES; + } + return NO; +} + + +- (NSDictionary *) findDisplayModeForWidth:(unsigned int) d_width Height:(unsigned int) d_height Refresh:(unsigned int) d_refresh +{ + int i, modeCount; + NSDictionary *mode; + unsigned int modeWidth, modeHeight, modeRefresh; + + modeCount = [displayModes count]; + + for (i = 0; i < modeCount; i++) + { + mode = [displayModes objectAtIndex: i]; + modeWidth = [[mode objectForKey:kOODisplayWidth] intValue]; + modeHeight = [[mode objectForKey:kOODisplayHeight] intValue]; + modeRefresh = [[mode objectForKey:kOODisplayRefreshRate] intValue]; + if ((modeWidth == d_width)&&(modeHeight == d_height)&&(modeRefresh == d_refresh)) + { + return mode; + } + } + return nil; +} + + +- (NSArray *) displayModes +{ + return [NSArray arrayWithArray:displayModes]; +} + + +- (NSUInteger) indexOfCurrentDisplayMode +{ + NSDictionary *mode; + + mode = [self findDisplayModeForWidth: width Height: height Refresh: refresh]; + if (mode == nil) + return NSNotFound; + else + return [displayModes indexOfObject:mode]; + + return NSNotFound; +} + + +- (void) pauseFullScreenModeToPerform:(SEL) selector onTarget:(id) target +{ + pauseSelector = selector; + pauseTarget = target; + stayInFullScreenMode = NO; +} + +@end + +#endif diff --git a/src/SDL2/MyOpenGLView.h b/src/SDL2/MyOpenGLView.h new file mode 100644 index 000000000..19f1918ad --- /dev/null +++ b/src/SDL2/MyOpenGLView.h @@ -0,0 +1,332 @@ +/* + +MyOpenGLView.h + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + +#import "OOCocoa.h" +#import "OOOpenGL.h" +#import "OOMouseInteractionMode.h" +#import "OOOpenGLMatrixManager.h" + + +#include + +#define MIN_FOV_DEG 30.0f +#define MAX_FOV_DEG 80.0f +#define MIN_FOV (tan((MIN_FOV_DEG / 2) * M_PI / 180.0f)) +#define MAX_FOV (tan((MAX_FOV_DEG / 2) * M_PI / 180.0f)) + +#define MOUSEVIRTUALSTICKSENSITIVITYFACTOR 0.95f +#define MOUSEX_MAXIMUM 0.6 +#define MOUSEY_MAXIMUM 0.6 + +#define MAX_CLEAR_DEPTH 10000000000.0 +// 10 000 000 km. +#define INTERMEDIATE_CLEAR_DEPTH 100.0 +// 100 m. + + +#define NUM_KEYS 320 +#define MOUSE_DOUBLE_CLICK_INTERVAL 0.40 +#define OOMOUSEWHEEL_EVENTS_DELAY_INTERVAL 0.05 + +#define SNAPSHOTS_PNG_FORMAT 1 + +@class Entity, GameController, OpenGLSprite; + +enum GameViewKeys +{ + gvFunctionKey1 = 241, + gvFunctionKey2, + gvFunctionKey3, + gvFunctionKey4, + gvFunctionKey5, + gvFunctionKey6, + gvFunctionKey7, + gvFunctionKey8, + gvFunctionKey9, + gvFunctionKey10, + gvFunctionKey11, + gvArrowKeyRight, + gvArrowKeyLeft, + gvArrowKeyDown, + gvArrowKeyUp, // 255 + gvMouseLeftButton = 301, + gvMouseDoubleClick, + gvHomeKey, + gvEndKey, + gvInsertKey, + gvDeleteKey, + gvPageUpKey, + gvPageDownKey, // 308 + gvNumberKey0 = 48, + gvNumberKey1, + gvNumberKey2, + gvNumberKey3, + gvNumberKey4, + gvNumberKey5, + gvNumberKey6, + gvNumberKey7, + gvNumberKey8, + gvNumberKey9, //57 + gvNumberPadKey0 = 310, + gvNumberPadKey1, + gvNumberPadKey2, + gvNumberPadKey3, + gvNumberPadKey4, + gvNumberPadKey5, + gvNumberPadKey6, + gvNumberPadKey7, + gvNumberPadKey8, + gvNumberPadKey9 //319 +}; + +enum MouseWheelStatus +{ + gvMouseWheelDown = -1, + gvMouseWheelNeutral, + gvMouseWheelUp +}; + +enum StringInput +{ + gvStringInputNo = 0, + gvStringInputAlpha = 1, + gvStringInputLoadSave = 2, + gvStringInputAll = 3 +}; + +enum KeyboardType +{ + gvKeyboardAuto, + gvKeyboardUS, + gvKeyboardUK +}; + +extern int debug; + +@interface MyOpenGLView : NSObject +{ + GameController *gameController; + BOOL keys[NUM_KEYS]; + BOOL supressKeys; // DJS + + BOOL opt, ctrl, command, shift; + BOOL allowingStringInput; + BOOL isAlphabetKeyDown; + + int keycodetrans[255]; + + BOOL m_glContextInitialized; + NSPoint mouseDragStartPoint; + + BOOL mouseWarped; + + NSTimeInterval timeIntervalAtLastClick; + NSTimeInterval timeSinceLastMouseWheel; + BOOL doubleClick; + + NSMutableString *typedString; + + NSPoint virtualJoystickPosition; + + NSSize viewSize; + GLfloat display_z; + GLfloat x_offset, y_offset; + + double squareX,squareY; + NSRect bounds; + + float _gamma; + float _fov; + + // Full screen sizes + NSMutableArray *screenSizes; + int currentSize; //we need an int! + BOOL fullScreen; + + // Windowed mode + NSSize currentWindowSize; + SDL_Window *mainWindow; + SDL_GLContext *glContext; + + BOOL showSplashScreen; + +#if OOLITE_WINDOWS + + BOOL wasFullScreen; + BOOL updateContext; + BOOL saveSize; + unsigned keyboardMap; + HWND Main_Window; + MONITORINFOEX monitorInfo; + RECT lastGoodRect; + +#endif + + BOOL grabMouseStatus; + + NSSize firstScreen; + + OOOpenGLMatrixManager *matrixManager; + + // Mouse mode indicator (for mouse movement model) + BOOL mouseInDeltaMode; + + int _mouseWheelState; +} + +- (void) initSplashScreen; +- (void) endSplashScreen; +- (void) autoShowMouse; + +- (void) setStringInput: (enum StringInput) value; +- (void) allowStringInput: (BOOL) value; +- (enum StringInput) allowingStringInput; +- (NSString *) typedString; +- (void) resetTypedString; +- (void) setTypedString:(NSString*) value; + +- (NSSize) viewSize; +- (NSSize) backingViewSize; +- (GLfloat) display_z; +- (GLfloat) x_offset; +- (GLfloat) y_offset; + +- (GameController *) gameController; +- (void) setGameController:(GameController *) controller; + +- (void) noteMouseInteractionModeChangedFrom:(OOMouseInteractionMode)oldMode to:(OOMouseInteractionMode)newMode; + +- (void) initialiseGLWithSize:(NSSize) v_size; +- (void) initialiseGLWithSize:(NSSize) v_size useVideoMode:(BOOL) v_mode; +- (BOOL) isRunningOnPrimaryDisplayDevice; +- (BOOL) enableDPIAwareness; +#if OOLITE_WINDOWS +- (BOOL) getCurrentMonitorInfo:(MONITORINFOEX *)mInfo; +- (MONITORINFOEX) currentMonitorInfo; +#endif + +- (void) grabMouseInsideGameWindow:(BOOL) value; + +- (void) stringToClipboard:(NSString *)stringToCopy; + +- (void) drawRect:(NSRect)rect; +- (void) updateScreen; +- (void) updateScreenWithVideoMode:(BOOL) v_mode; +- (void) display; + +- (BOOL) snapShot:(NSString *)filename; +#if SNAPSHOTS_PNG_FORMAT +- (BOOL) pngSaveSurface:(NSString *)fileName withSurface:(SDL_Surface *)surf; +#endif + +- (NSRect) bounds; ++ (NSMutableDictionary *) getNativeSize; + +- (void) setFullScreenMode:(BOOL)fsm; +- (BOOL) inFullScreenMode; +- (void) toggleScreenMode; +- (void) setDisplayMode:(int)mode fullScreen:(BOOL)fsm; + +- (int) indexOfCurrentSize; +- (void) setScreenSize: (int)sizeIndex; +- (NSMutableArray *)getScreenSizeArray; +- (void) populateFullScreenModelist; +- (NSSize) modeAsSize: (int)sizeIndex; +- (void) saveWindowSize: (NSSize) windowSize; +- (NSSize) loadWindowSize; +- (int) loadFullscreenSettings; +- (int) findDisplayModeForWidth: (unsigned int) d_width Height:(unsigned int) d_height + Refresh: (unsigned int)d_refresh; +- (NSSize) currentScreenSize; + +- (void) pollControls; + +- (void) setVirtualJoystick:(double) vmx :(double) vmy; +- (NSPoint) virtualJoystickPosition; + +- (void) clearKeys; +- (void) clearMouse; +- (void) clearKey: (int)theKey; +- (void) resetMouse; +- (BOOL) isAlphabetKeyDown; +- (void) supressKeysUntilKeyUp; // DJS +- (BOOL) isDown: (int) key; +- (BOOL) isOptDown; // opt == alt key +- (BOOL) isCtrlDown; +- (BOOL) isCommandDown; +- (BOOL) isShiftDown; +- (BOOL) isCapsLockOn; +- (int) numKeys; +- (int) mouseWheelState; + +// Command-key combinations need special handling. SDL stubs for these mac functions. +- (BOOL) isCommandQDown; +- (BOOL) isCommandFDown; +- (void) clearCommandF; + +- (void) setKeyboardTo: (NSString *) value; +- (void) setMouseInDeltaMode: (BOOL) inDelta; + +- (void) setGammaValue: (float) value; +- (float) gammaValue; + +- (void) setFov:(float)value fromFraction:(BOOL)fromFraction; +- (float) fov:(BOOL)inFraction; + +// Check current state of shift key rather than relying on last event. ++ (BOOL)pollShiftKey; + +- (OOOpenGLMatrixManager *) getOpenGLMatrixManager; + +#ifndef NDEBUG +// General image-dumping method. +- (void) dumpRGBAToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes; +- (void) dumpRGBToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes; +- (void) dumpGrayToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes; +- (void) dumpGrayAlphaToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes; +- (void) dumpRGBAToRGBFileNamed:(NSString *)rgbName + andGrayFileNamed:(NSString *)grayName + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes; +#endif + +@end diff --git a/src/SDL2/MyOpenGLView.m b/src/SDL2/MyOpenGLView.m new file mode 100644 index 000000000..903cb4d5a --- /dev/null +++ b/src/SDL2/MyOpenGLView.m @@ -0,0 +1,2781 @@ +/* + +MyOpenGLView.m + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + +#import "png.h" +#import "MyOpenGLView.h" + +#import "GameController.h" +#import "Universe.h" +#import "OOSDLJoystickManager.h" +#import "SDL_syswm.h" +#import "OOSound.h" +#import "NSFileManagerOOExtensions.h" // to find savedir +#import "PlayerEntity.h" +#import "GuiDisplayGen.h" +#import "PlanetEntity.h" +#import "OOGraphicsResetManager.h" +#import "OOCollectionExtractors.h" // for splash screen settings +#import "OOFullScreenController.h" + +#define kOOLogUnconvertedNSLog @"unclassified.MyOpenGLView" + +#include + +@interface MyOpenGLView (OOPrivate) + +- (void) resetSDLKeyModifiers; +- (void) setWindowBorderless:(BOOL)borderless; +- (void) handleStringInput: (SDL_KeyboardEvent *) kbd_event; // DJS +@end + +@implementation MyOpenGLView + ++ (NSMutableDictionary *) getNativeSize +{ + NSMutableDictionary *mode=[[NSMutableDictionary alloc] init]; + int nativeDisplayWidth = 1024; + int nativeDisplayHeight = 768; + +#if OOLITE_LINUX + SDL_DisplayMode dpyMode; + // This gets the native resolution of the primary display, there may be SDL_GetNumVideoDisplays() + // (TODO?) Support multiple outputs + if(SDL_GetDesktopDisplayMode(0, &dpyMode) == 0) + { + nativeDisplayWidth = dpyMode.w; + nativeDisplayHeight = dpyMode.h; + OOLog(@"display.mode.list.native", @"Native resolution detected: %d x %d", nativeDisplayWidth, nativeDisplayHeight); + } + else + { + OOLog(@"display.mode.list.native.failed", @"%@", @"SDL_GetDesktopDisplayMode failed, defaulting to 1024x768 for native size"); + } +#elif OOLITE_WINDOWS + nativeDisplayWidth = GetSystemMetrics(SM_CXSCREEN); + nativeDisplayHeight = GetSystemMetrics(SM_CYSCREEN); + OOLog(@"display.mode.list.native", @"Windows native resolution detected: %d x %d", nativeDisplayWidth, nativeDisplayHeight); +#else + OOLog(@"display.mode.list.native.unknown", @"Unknown architecture, defaulting to 1024x768"); +#endif + [mode setValue: [NSNumber numberWithInt: nativeDisplayWidth] forKey:kOODisplayWidth]; + [mode setValue: [NSNumber numberWithInt: nativeDisplayHeight] forKey: kOODisplayHeight]; + [mode setValue: [NSNumber numberWithInt: 0] forKey: kOODisplayRefreshRate]; + + return [mode autorelease]; +} + + +- (void) createSurface +{ + if (glContext == NULL) + glContext = SDL_GL_CreateContext(mainWindow); + if (glContext == NULL) + { + // we should have a valid GL context, but in case we don't + OOLogERR(@"display.mode.error",@"Unable to create GL context: %s",SDL_GetError()); + return; + } + + if (showSplashScreen) + { +#if OOLITE_WINDOWS + // Pre setVideoMode adjustments. + NSSize tmp = currentWindowSize; + ShowWindow(Main_Window,SW_SHOWMINIMIZED); + updateContext = NO; //don't update the (splash screen) window yet! + + // Resize the SDL surface? + //SDL_SetWindowSize(mainWindow, (int)firstScreen.width, (int)firstScreen.height); + + // Post setVideoMode adjustments. + currentWindowSize=tmp; +#endif + } + else + { +#if OOLITE_WINDOWS + updateContext = YES; +#endif + // blank the output / go to fullscreen + [self initialiseGLWithSize: firstScreen]; + } + + Uint16* gamma_ramp = (Uint16 *)SDL_malloc(256 * sizeof(Uint16)); + _gamma = 1.0f; + SDL_CalculateGammaRamp(_gamma, gamma_ramp); + + if (SDL_SetWindowGammaRamp(mainWindow, gamma_ramp, gamma_ramp, gamma_ramp) < 0 ) + { + const char * errStr = SDL_GetError(); + OOLogWARN(@"gamma.set.failed", @"Could not set gamma: %s", errStr); + // CIM: this doesn't seem to necessarily be fatal. Gamma settings + // mostly work on mine despite this function failing. + // exit(1); + } +} + + +- (id) init +{ + self = [super init]; + + Uint32 colorkey; + SDL_Surface *icon=NULL; + NSString *imagesDir; + + // SDL splash screen settings + + NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults]; + showSplashScreen = [prefs oo_boolForKey:@"splash-screen" defaultValue:YES]; + BOOL vSyncPreference = [prefs oo_boolForKey:@"v-sync" defaultValue:YES]; + + NSArray *arguments = nil; + NSEnumerator *argEnum = nil; + NSString *arg = nil; + BOOL noSplashArgFound = NO; + + SDL_Rect drawable; + + arguments = [[NSProcessInfo processInfo] arguments]; + + // scan for splash screen overrides: -nosplash || --nosplash , -splash || --splash + // scan for V-sync disabling overrides: -novsync || --novsync + for (argEnum = [arguments objectEnumerator]; (arg = [argEnum nextObject]); ) + { + if ([arg isEqual:@"-nosplash"] || [arg isEqual:@"--nosplash"]) + { + showSplashScreen = NO; + noSplashArgFound = YES; // -nosplash always trumps -splash + } + else if (([arg isEqual:@"-splash"] || [arg isEqual:@"--splash"]) && !noSplashArgFound) + { + showSplashScreen = YES; + } + + // if V-sync is disabled at the command line, override the defaults file + if ([arg isEqual:@"-novsync"] || [arg isEqual:@"--novsync"]) vSyncPreference = NO; + } + + // high-DPI awareness; must be done before any SDL initialization + if (![self enableDPIAwareness]) + { + OOLogWARN(@"display.initGL.dpiAwareness", @"Could not declare application as DPI-aware."); + } + + matrixManager = [[OOOpenGLMatrixManager alloc] init]; + + // TODO: This code up to and including stickHandler really ought + // not to be in this class. + OOLog(@"sdl.init", @"%@", @"initialising SDL"); + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) + { + OOLog(@"sdl.init.failed", @"Unable to init SDL: %s\n", SDL_GetError()); + [self dealloc]; + return nil; + } + + // Generate the window caption, containing the version number and the date the executable was compiled. + static char windowCaption[128]; + NSString *versionString = [NSString stringWithFormat:@"Oolite v%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"]]; + + strcpy (windowCaption, [versionString UTF8String]); + strcat (windowCaption, " - "__DATE__); + + mainWindow = SDL_CreateWindow(windowCaption, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 8, 8, + SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); + + [OOJoystickManager setStickHandlerClass:[OOSDLJoystickManager class]]; + // end TODO + + [OOSound setUp]; + if (![OOSound isSoundOK]) OOLog(@"sound.init", @"Sound system disabled."); + + +#if OOLITE_WINDOWS + // needed for enabling system window manager events, which is needed for handling window movement messages + SDL_EventState (SDL_SYSWMEVENT, SDL_ENABLE); + + //capture the window handle for later + static SDL_SysWMinfo wInfo; + SDL_VERSION(&wInfo.version); + SDL_GetWindowWMInfo(mainWindow, &wInfo); + Main_Window = wInfo.window; + + // This must be inited after Main_Window has been set - we need the main window handle in order to get monitor info + if (![self getCurrentMonitorInfo:&monitorInfo]) + { + OOLogWARN(@"display.initGL.monitorInfoWarning", @"Could not get current monitor information."); + } +#endif + + grabMouseStatus = NO; + + imagesDir = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Images"]; + icon = SDL_LoadBMP([[imagesDir stringByAppendingPathComponent:@"WMicon.bmp"] UTF8String]); + + if (icon != NULL) + { + colorkey = SDL_MapRGB(icon->format, 128, 0, 128); + SDL_SetColorKey(icon, SDL_TRUE, colorkey); + SDL_SetWindowIcon(mainWindow, icon); + } + SDL_FreeSurface(icon); + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 32); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + + /* Multisampling significantly improves graphics quality with + * basically no extra programming effort on our part, especially + * for curved surfaces like the planet, but is also expensive - in + * the worst case the entire scene must be rendered four + * times. For now it can be a hidden setting. If early testing + * doesn't give any problems (other than speed on low-end graphics + * cards) a game options entry might be useful. - CIM, 24 Aug 2013*/ + if ([prefs oo_boolForKey:@"anti-aliasing" defaultValue:NO]) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + } + + OOLog(@"display.mode.list", @"%@", @"CREATING MODE LIST"); + [self populateFullScreenModelist]; + currentSize = 0; + + // Find what the full screen and windowed settings are. + fullScreen = NO; + [self loadFullscreenSettings]; + [self loadWindowSize]; + + // Set up the drawing surface's dimensions. + firstScreen= (fullScreen) ? [self modeAsSize: currentSize] : currentWindowSize; + viewSize = firstScreen; // viewSize must be set prior to splash screen initialization + + OOLog(@"display.initGL", @"%@", @"Trying 32-bit depth buffer"); + [self createSurface]; + if (glContext == NULL) + { + // Retry with a 24-bit depth buffer + OOLog(@"display.initGL", @"%@", @"Trying 24-bit depth buffer"); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + [self createSurface]; + if (glContext == NULL) + { + // Still not working? One last go... + // Retry, allowing 16-bit contexts. + OOLog(@"display.initGL", @"%@", @"Trying 16-bit depth buffer"); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + // and if it's this bad, forget even trying to multisample! + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + + [self createSurface]; + + if (glContext == NULL) + { + const char * errStr = SDL_GetError(); + OOLogERR(@"display.mode.error", @"Could not create display GL context: %s", errStr); +#if OOLITE_WINDOWS + if (showSplashScreen) + { + [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"splash-screen"]; + OOLogWARN(@"display.mode.conflict",@"Possible incompatibility between the splash screen and video drivers detected."); + OOLogWARN(@"display.mode.conflict",@"Oolite will start without showing the splash screen from now on. Override with 'oolite.exe -splash'"); + } +#endif + exit(1); + } + } + } + + SDL_GL_SetSwapInterval(vSyncPreference); // V-sync on by default. + OOLog(@"display.initGL", @"V-Sync %@requested.", vSyncPreference ? @"" : @"not "); + + + // Verify V-sync successfully set - report it if not + if (vSyncPreference && SDL_GL_GetSwapInterval() == -1) + { + OOLogWARN(@"display.initGL", @"Could not enable V-Sync. Please check that your graphics driver supports the %@_swap_control extension.", + OOLITE_WINDOWS ? @"WGL_EXT" : @"[GLX_SGI/GLX_MESA]"); + } + + SDL_GL_GetDrawableSize(mainWindow, &drawable.w, &drawable.h); + + bounds.size.width = drawable.w; + bounds.size.height = drawable.h; + + [self autoShowMouse]; + + virtualJoystickPosition = NSMakePoint(0.0,0.0); + mouseWarped = NO; + + typedString = [[NSMutableString alloc] initWithString:@""]; + allowingStringInput = gvStringInputNo; + isAlphabetKeyDown = NO; + + timeIntervalAtLastClick = timeSinceLastMouseWheel = [NSDate timeIntervalSinceReferenceDate]; + + _mouseWheelState = gvMouseWheelNeutral; + + m_glContextInitialized = NO; + + return self; +} + +- (void) endSplashScreen +{ + if (!showSplashScreen) return; + +#if OOLITE_WINDOWS + + wasFullScreen = !fullScreen; + updateContext = YES; + ShowWindow(Main_Window,SW_RESTORE); + [self initialiseGLWithSize: firstScreen]; + +#else + [self setWindowBorderless:NO]; + + SDL_SetWindowSize(mainWindow, (int)firstScreen.width, (int)firstScreen.height); + SDL_SetWindowResizable(mainWindow, SDL_TRUE); + SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED); //stop linux from auto centering on resize + + [self initialiseGLWithSize: firstScreen]; + + if (!(SDL_GetWindowFlags(mainWindow) & SDL_WINDOW_FULLSCREEN) && fullScreen == YES) + { + [self setFullScreenMode: NO]; + } + + + /* MKW 2011.11.11 + * Eat all SDL events to gobble up any resize events while the + * splash-screen was visible. They affected the main window after 1.74. + * TODO: should really process SDL events while showing the splash-screen + + int numEvents = 0; + */ + SDL_Event dummyEvent; + while (SDL_PollEvent(&dummyEvent)) + { + /* Do nothing; the below is for development info + numEvents++; + OOLog(@"display.splash", @"Suppressed splash-screen event %d: %d ", numEvents, dummyEvent.type); + */ + } + + +#endif + + [self updateScreen]; + [self autoShowMouse]; +} + +- (void) dealloc +{ + if (typedString) + [typedString release]; + + if (screenSizes) + [screenSizes release]; + + if (glContext != NULL) + { + SDL_GL_DeleteContext(glContext); + glContext = NULL; + } + + SDL_Quit(); + + if (matrixManager) + { + [matrixManager release]; + } + + [super dealloc]; +} + +- (void) autoShowMouse +{ + //don't touch the 'please wait...' cursor. + if (fullScreen) + { + if (SDL_ShowCursor(SDL_QUERY) == SDL_ENABLE) + SDL_ShowCursor(SDL_DISABLE); + } + else + { + if (SDL_ShowCursor(SDL_QUERY) == SDL_DISABLE) + SDL_ShowCursor(SDL_ENABLE); + } +} + +- (void) setStringInput: (enum StringInput) value +{ + allowingStringInput = value; +} + + +- (void) allowStringInput: (BOOL) value +{ + if (value) + allowingStringInput = gvStringInputAlpha; + else + allowingStringInput = gvStringInputNo; +} + +-(enum StringInput) allowingStringInput +{ + return allowingStringInput; +} + + +- (NSString *) typedString +{ + return typedString; +} + + +- (void) resetTypedString +{ + [typedString setString:@""]; +} + + +- (void) setTypedString:(NSString*) value +{ + [typedString setString:value]; +} + + +- (NSRect) bounds +{ + return bounds; +} + + +- (NSSize) viewSize +{ + return viewSize; +} + + +- (NSSize) backingViewSize +{ + return viewSize; +} + + +- (GLfloat) display_z +{ + return display_z; +} + + +- (GLfloat) x_offset +{ + return x_offset; +} + + +- (GLfloat) y_offset +{ + return y_offset; +} + + +- (GameController *) gameController +{ + return gameController; +} + + +- (void) setGameController:(GameController *) controller +{ + gameController = controller; +} + + +- (void) noteMouseInteractionModeChangedFrom:(OOMouseInteractionMode)oldMode to:(OOMouseInteractionMode)newMode +{ + [self autoShowMouse]; + [self setMouseInDeltaMode:OOMouseInteractionModeIsFlightMode(newMode)]; +} + + +- (BOOL) inFullScreenMode +{ + return fullScreen; +} + +#ifdef GNUSTEP +- (void) setFullScreenMode:(BOOL)fsm +{ + fullScreen = fsm; + + // Save the settings for later. + [[NSUserDefaults standardUserDefaults] + setBool: fullScreen forKey:@"fullscreen"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + + +- (void) toggleScreenMode +{ + [self setFullScreenMode: !fullScreen]; +#if OOLITE_WINDOWS + [self getCurrentMonitorInfo:&monitorInfo]; +#endif + if(fullScreen) + { +#if OOLITE_WINDOWS + if(![self isRunningOnPrimaryDisplayDevice]) + { + [self initialiseGLWithSize:NSMakeSize(monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left, + monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top)]; + } + else [self initialiseGLWithSize:[self modeAsSize: currentSize]]; +#else + [self initialiseGLWithSize:[self modeAsSize: currentSize]]; +#endif + } + else + [self initialiseGLWithSize: currentWindowSize]; + + + // do screen resizing updates + if ([PlayerEntity sharedPlayer]) + { + [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates]; + } +} + + +- (void) setDisplayMode:(int)mode fullScreen:(BOOL)fsm +{ + [self setFullScreenMode: fsm]; + currentSize=mode; + if(fullScreen) + [self initialiseGLWithSize: [self modeAsSize: mode]]; +} + + +- (int) indexOfCurrentSize +{ + return currentSize; +} + + +- (void) setScreenSize: (int)sizeIndex +{ + currentSize=sizeIndex; + if(fullScreen) + [self initialiseGLWithSize: [self modeAsSize: currentSize]]; +} + + +- (NSMutableArray *)getScreenSizeArray +{ + return screenSizes; +} + + +- (NSSize) modeAsSize:(int)sizeIndex +{ + NSDictionary *mode=[screenSizes objectAtIndex: sizeIndex]; + return NSMakeSize([[mode objectForKey: kOODisplayWidth] intValue], + [[mode objectForKey: kOODisplayHeight] intValue]); +} + +#endif + +- (void) display +{ + [self updateScreen]; +} + +- (void) updateScreen +{ + [self drawRect: NSMakeRect(0, 0, viewSize.width, viewSize.height)]; +} + +- (void) drawRect:(NSRect)rect +{ + [self updateScreenWithVideoMode:YES]; +} + +- (void) updateScreenWithVideoMode:(BOOL) v_mode +{ + int v_width, v_height; + + SDL_GetWindowSize(mainWindow, &v_width, &v_height); + if ((viewSize.width != v_width)||(viewSize.height != v_height)) // resized + { +#if OOLITE_LINUX + m_glContextInitialized = NO; //probably not needed +#endif + viewSize.width = v_width; + viewSize.height = v_height; + } + + if (m_glContextInitialized == NO) + { + [self initialiseGLWithSize:viewSize useVideoMode:v_mode]; + } + + if (glContext == NULL) + return; + + // do all the drawing! + // + if (UNIVERSE) [UNIVERSE drawUniverse]; + else + { + // not set up yet, draw a black screen + glClearColor( 0.0, 0.0, 0.0, 0.0); + glClear( GL_COLOR_BUFFER_BIT); + } + + SDL_GL_SwapWindow(mainWindow); +} + +- (void) initSplashScreen +{ + if (!showSplashScreen) return; + + //too early for OOTexture! + SDL_Surface *image=NULL; + SDL_Rect dest; + SDL_Rect drawable; + + NSString *imagesDir = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"Images"]; + + image = SDL_LoadBMP([[imagesDir stringByAppendingPathComponent:@"splash.bmp"] UTF8String]); + + if (image == NULL) + { + SDL_FreeSurface(image); + OOLogWARN(@"sdl.gameStart", @"%@", @"image 'splash.bmp' not found!"); + [self endSplashScreen]; + return; + } + + dest.x = 0; + dest.y = 0; + dest.w = image->w; + dest.h = image->h; + + #if OOLITE_WINDOWS + + dest.x = (GetSystemMetrics(SM_CXSCREEN)- dest.w)/2; + dest.y = (GetSystemMetrics(SM_CYSCREEN)-dest.h)/2; + SetWindowLong(Main_Window,GWL_STYLE,GetWindowLong(Main_Window,GWL_STYLE) & ~WS_CAPTION & ~WS_THICKFRAME); + ShowWindow(Main_Window,SW_RESTORE); + MoveWindow(Main_Window,dest.x,dest.y,dest.w,dest.h,TRUE); + + #else + + /* MKW 2011.11.11 + * According to Marc using the NOFRAME flag causes trouble under Ubuntu 8.04. + * + * The current Ubuntu LTS is 10.04, which doesn't seem to have that problem. + * 12.04 LTS is going to be released soon, also without apparent problems. + * Changed to SDL_NOFRAME, throwing caution to the wind - Kaks 2012.03.23 + * Took SDL_NOFRAME out, since it still causes strange problems here - cim 2012.04.09 + */ + [self setWindowBorderless:YES]; + + SDL_SetWindowResizable(mainWindow, SDL_FALSE); + SDL_SetWindowSize(mainWindow, dest.w, dest.h); + SDL_SetWindowPosition(mainWindow, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED); + + #endif + + if(SDL_GL_MakeCurrent(mainWindow, glContext)) + { + OOLogERR(@"display.mode.error",@"Unable to make GL context current: %s",SDL_GetError()); + exit(1); + } + SDL_GL_GetDrawableSize(mainWindow, &drawable.w, &drawable.h); + + OOSetOpenGLState(OPENGL_STATE_OVERLAY); + + glViewport( 0, 0, drawable.w, drawable.h); + + glEnable( GL_TEXTURE_2D ); + glClearColor( 0.0f, 0.0f, 0.0f, 0.0f ); + glClear( GL_COLOR_BUFFER_BIT ); + + [matrixManager resetProjection]; + [matrixManager orthoLeft: 0.0f right: drawable.w bottom: drawable.h top: 0.0 near: -1.0 far: 1.0]; + [matrixManager syncProjection]; + + [matrixManager resetModelView]; + [matrixManager syncModelView]; + + GLuint texture; + GLenum texture_format; + GLint nOfColors; + + // get the number of channels in the SDL image + nOfColors = image->format->BytesPerPixel; + if (nOfColors == 4) // contains an alpha channel + { + if (image->format->Rmask == 0x000000ff) + texture_format = GL_RGBA; + else + texture_format = GL_BGRA; + } + else if (nOfColors == 3) // no alpha channel + { + if (image->format->Rmask == 0x000000ff) + texture_format = GL_RGB; + else + texture_format = GL_BGR; + } else { + SDL_FreeSurface(image); + OOLog(@"Sdl.GameStart", @"%@", @"----- Encoding error within image 'splash.bmp'"); + [self endSplashScreen]; + return; + } + + glGenTextures( 1, &texture ); + glBindTexture( GL_TEXTURE_2D, texture ); + + // Set the texture's stretching properties + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + // Set the texture image data with the information from SDL_Surface + glTexImage2D( GL_TEXTURE_2D, 0, nOfColors, image->w, image->h, 0, + texture_format, GL_UNSIGNED_BYTE, image->pixels ); + + glBindTexture( GL_TEXTURE_2D, texture ); + glBegin( GL_QUADS ); + + glTexCoord2i( 0, 0 ); + glVertex2i( 0, 0 ); + glTexCoord2i( 1, 0 ); + glVertex2i( drawable.w, 0 ); + glTexCoord2i( 1, 1 ); + glVertex2i( drawable.w, drawable.h ); + glTexCoord2i( 0, 1 ); + glVertex2i( 0, drawable.h ); + + glEnd(); + + SDL_GL_SwapWindow(mainWindow); + + [matrixManager resetModelView]; + [matrixManager syncModelView]; + + if ( image ) { + SDL_FreeSurface( image ); + } + glDeleteTextures(1, &texture); + + glDisable( GL_TEXTURE_2D ); + OOVerifyOpenGLState(); +} + + +#if OOLITE_WINDOWS +- (BOOL) enableDPIAwareness +{ + /* + Declare the application as DPI-aware, so that Windows Vista and later + versions' automatic DPI virtualization does not kick-in. Attempt to + load the SetProcessDpiAwareness WinAPI function and execute it if available. + If not available (as would be the case in Windows versions prior to 8.1), + attempt to load and execute the older SetProcessDPIAware instead. + */ + BOOL dpiAwarenessSet = NO; + BOOL dpiAwareSet = NO; + // the following enum is supposed to be part of the Windows API headers, + // but it looks like MinGW does not declare it + #ifndef DPI_ENUMS_DECLARED + typedef enum PROCESS_DPI_AWARENESS + { + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 + } PROCESS_DPI_AWARENESS; + #endif + HRESULT(WINAPI *setProcessDpiAwareness)(PROCESS_DPI_AWARENESS) = NULL; + setProcessDpiAwareness = (HRESULT(WINAPI*)(PROCESS_DPI_AWARENESS)) + (void*)GetProcAddress(GetModuleHandle("shcore.dll"), "SetProcessDpiAwareness"); + if (setProcessDpiAwareness) + { + dpiAwarenessSet = (setProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) == S_OK); + } + else + { + BOOL(WINAPI *setProcessDPIAware)() = NULL; + setProcessDPIAware = (BOOL(WINAPI*)())(void*)GetProcAddress(GetModuleHandle("user32.dll"), "SetProcessDPIAware"); + if (setProcessDPIAware) + { + dpiAwareSet = setProcessDPIAware(); + } + } + + OOLog(@"display.initGL.dpiAwareness", @"%@ loaded and executed", + dpiAwarenessSet ? @"SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)" : dpiAwareSet ? + @"SetProcessDPIAware()" : @"No DPI awareness method"); + + return dpiAwarenessSet || dpiAwareSet; +} + + +- (MONITORINFOEX) currentMonitorInfo +{ + return monitorInfo; +} + + +- (BOOL) getCurrentMonitorInfo:(MONITORINFOEX *)mInfo +{ + HMONITOR hMon = MonitorFromWindow(Main_Window, MONITOR_DEFAULTTOPRIMARY); + ZeroMemory(mInfo, sizeof(MONITORINFOEX)); + mInfo->cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo (hMon, (LPMONITORINFO)mInfo)) + { + return YES; + } + return NO; +} + + +- (BOOL) isRunningOnPrimaryDisplayDevice +{ + BOOL result = YES; + [self getCurrentMonitorInfo:&monitorInfo]; + if (!(monitorInfo.dwFlags & MONITORINFOF_PRIMARY)) + { + result = NO; + } + return result; +} + + +- (void) grabMouseInsideGameWindow:(BOOL) value +{ + if(value) + { + RECT gameWindowRect; + GetWindowRect(Main_Window, &gameWindowRect); + ClipCursor(&gameWindowRect); + } + else + { + ClipCursor(NULL); + } + grabMouseStatus = !!value; +} + + +- (void) stringToClipboard:(NSString *)stringToCopy +{ + if (stringToCopy) + { + const char *clipboardText = [stringToCopy cStringUsingEncoding:NSUTF8StringEncoding]; + const size_t clipboardTextLength = strlen(clipboardText) + 1; + HGLOBAL clipboardMem = GlobalAlloc(GMEM_MOVEABLE, clipboardTextLength); + if (clipboardMem) + { + memcpy(GlobalLock(clipboardMem), clipboardText, clipboardTextLength); + GlobalUnlock(clipboardMem); + OpenClipboard(0); + EmptyClipboard(); + if (!SetClipboardData(CF_TEXT, clipboardMem)) + { + OOLog(@"stringToClipboard.failed", @"Failed to copy string %@ to clipboard", stringToCopy); + // free global allocated memory if clipboard copy failed + // note: no need to free it if copy succeeded; the OS becomes + // the owner of the copied memory once SetClipboardData has + // been executed successfully + GlobalFree(clipboardMem); + } + CloseClipboard(); + } + } +} + + +- (void) resetSDLKeyModifiers +{ + // this is used when we regain focus to ensure that all + // modifier keys are reset to their correct status + SDLMod modState = SDL_GetModState(); + Uint8 *keyState = SDL_GetKeyState(NULL); + BYTE keyboardStatus[256]; + #define OO_RESET_SDLKEY_MODIFIER(vkCode, kModCode, sdlkCode) do {\ + if (keyboardStatus[vkCode] & 0x0080) \ + { \ + modState |= kModCode; \ + keyState[sdlkCode] = SDL_PRESSED; \ + } \ + else \ + { \ + modState &= ~kModCode; \ + keyState[sdlkCode] = SDL_RELEASED; \ + } \ + } while(0) + if (GetKeyboardState(keyboardStatus)) + { + // Alt key + OO_RESET_SDLKEY_MODIFIER(VK_LMENU, KMOD_LALT, SDLK_LALT); + OO_RESET_SDLKEY_MODIFIER(VK_RMENU, KMOD_RALT, SDLK_RALT); + opt = (modState & KMOD_LALT || modState & KMOD_RALT); + + //Ctrl key + OO_RESET_SDLKEY_MODIFIER(VK_LCONTROL, KMOD_LCTRL, SDLK_LCTRL); + OO_RESET_SDLKEY_MODIFIER(VK_RCONTROL, KMOD_RCTRL, SDLK_RCTRL); + ctrl = (modState & KMOD_LCTRL || modState & KMOD_RCTRL); + + // Shift key + OO_RESET_SDLKEY_MODIFIER(VK_LSHIFT, KMOD_LSHIFT, SDLK_LSHIFT); + OO_RESET_SDLKEY_MODIFIER(VK_RSHIFT, KMOD_RSHIFT, SDLK_RSHIFT); + shift = (modState & KMOD_LSHIFT || modState & KMOD_RSHIFT); + + // Caps Lock key state + if (GetKeyState(VK_CAPITAL) & 0x0001) + { + modState |= KMOD_CAPS; + keyState[SDLK_CAPSLOCK] = SDL_PRESSED; + } + else + { + modState &= ~KMOD_CAPS; + keyState[SDLK_CAPSLOCK] = SDL_RELEASED; + } + } + + SDL_SetModState(modState); +} + + +- (void) setWindowBorderless:(BOOL)borderless +{ + LONG currentWindowStyle = GetWindowLong(Main_Window, GWL_STYLE); + + // window already has the desired style? + if ((!borderless && (currentWindowStyle & WS_CAPTION)) || + (borderless && !(currentWindowStyle & WS_CAPTION))) return; + + if (borderless) + { + SetWindowLong(Main_Window, GWL_STYLE, currentWindowStyle & ~WS_CAPTION & ~WS_THICKFRAME); + } + else + { + SetWindowLong(Main_Window, GWL_STYLE, currentWindowStyle | + WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX ); + } + SetWindowPos(Main_Window, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); +} + + +#else // Linus stub methods + +- (BOOL) enableDPIAwareness +{ + return NO; +} + + +// for Linux we assume we are always on the primary monitor for now +- (BOOL) isRunningOnPrimaryDisplayDevice +{ + return YES; +} + + +- (void) grabMouseInsideGameWindow:(BOOL) value +{ + // do nothing +} + + +- (void) stringToClipboard:(NSString *)stringToCopy +{ + // TODO: implement string clipboard copy for Linux +} + + +- (void) resetSDLKeyModifiers +{ + // probably not needed for Linux +} + + +- (void) setWindowBorderless:(BOOL)borderless +{ + // window already has the desired style? + if ((!borderless && !(SDL_GetWindowFlags(mainWindow) & SDL_WINDOW_BORDERLESS)) || + (borderless && (SDL_GetWindowFlags(mainWindow) & SDL_WINDOW_BORDERLESS))) return; + + if (borderless) + { + SDL_SetWindowBordered(mainWindow, SDL_FALSE); + } + else + { + SDL_SetWindowBordered(mainWindow, SDL_TRUE); + } +} + +#endif //OOLITE_WINDOWS + + +- (void) initialiseGLWithSize:(NSSize) v_size +{ + [self initialiseGLWithSize:v_size useVideoMode:YES]; +} + + +- (void) initialiseGLWithSize:(NSSize) v_size useVideoMode:(BOOL) v_mode +{ +#if OOLITE_LINUX + NSSize oldViewSize = viewSize; + int width, height; +#endif + viewSize = v_size; + OOLog(@"display.initGL", @"Requested a new output of %d x %d, %@.", (int)viewSize.width, (int)viewSize.height,(fullScreen ? @"fullscreen" : @"windowed")); + SDL_GL_SwapWindow(mainWindow); // clear the buffer before resize + +#if OOLITE_WINDOWS + if (!updateContext) return; + + DEVMODE settings; + settings.dmSize = sizeof(DEVMODE); + settings.dmDriverExtra = 0; + EnumDisplaySettings(0, ENUM_CURRENT_SETTINGS, &settings); + + WINDOWPLACEMENT windowPlacement; + windowPlacement.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(Main_Window, &windowPlacement); + + static BOOL lastWindowPlacementMaximized = NO; + if (fullScreen && (windowPlacement.showCmd == SW_SHOWMAXIMIZED)) + { + if (!wasFullScreen) + { + lastWindowPlacementMaximized = YES; + } + } + + if (lastWindowPlacementMaximized) + { + windowPlacement.showCmd = SW_SHOWMAXIMIZED; + } + + // are we attempting to go to a different screen resolution? Note: this also takes care of secondary monitor situations because + // by design the only resolution available for fullscreen on a secondary display device is its native one - Nikos 20150605 + BOOL changingResolution = [self isRunningOnPrimaryDisplayDevice] && + ((fullScreen && (settings.dmPelsWidth != viewSize.width || settings.dmPelsHeight != viewSize.height)) || + (wasFullScreen && (settings.dmPelsWidth != [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayWidth] intValue] + || settings.dmPelsHeight != [[[screenSizes objectAtIndex:0] objectForKey: kOODisplayHeight] intValue]))); + + RECT wDC; + + if (fullScreen) + { + /*NOTE: If we ever decide to change the default behaviour of launching + always on primary monitor to launching on the monitor the program was + started on, all that needs to be done is comment out the line below, as + well as the identical one in the else branch further down. + Nikos 20141222 + */ + [self getCurrentMonitorInfo: &monitorInfo]; + + settings.dmPelsWidth = viewSize.width; + settings.dmPelsHeight = viewSize.height; + settings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; + + // just before going fullscreen, save the location of the current window. It + // may be needed in case of potential attempts to move our fullscreen window + // in a maximized state (yes, in Windows this is entirely possible). + if(lastWindowPlacementMaximized) + { + CopyRect(&lastGoodRect, &windowPlacement.rcNormalPosition); + // if maximized, switch to normal placement before going full screen + windowPlacement.showCmd = SW_SHOWNORMAL; + SetWindowPlacement(Main_Window, &windowPlacement); + } + else GetWindowRect(Main_Window, &lastGoodRect); + + // ok, can go fullscreen now + SetForegroundWindow(Main_Window); + if (changingResolution) + { + if (ChangeDisplaySettingsEx(monitorInfo.szDevice, &settings, NULL, CDS_FULLSCREEN, NULL) != DISP_CHANGE_SUCCESSFUL) + { + m_glContextInitialized = YES; + OOLogERR(@"displayMode.change.error", @"Could not switch to requested display mode."); + return; + } + } + + MoveWindow(Main_Window, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, (int)viewSize.width, (int)viewSize.height, TRUE); + if(!wasFullScreen) + { + [self setWindowBorderless:YES]; + } + } + + else if ( wasFullScreen ) + { + if (changingResolution) ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); + + /*NOTE: If we ever decide to change the default behaviour of launching + always on primary monitor to launching on the monitor the program was + started on, we need to comment out the line below. + For now, this line is needed for correct positioning of our window in case + we return from a non-native resolution fullscreen and has to come after the + display settings have been reverted. + Nikos 20141222 + */ + [self getCurrentMonitorInfo: &monitorInfo]; + + if (lastWindowPlacementMaximized) CopyRect(&windowPlacement.rcNormalPosition, &lastGoodRect); + SetWindowPlacement(Main_Window, &windowPlacement); + if (!lastWindowPlacementMaximized) + { + MoveWindow(Main_Window, (monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left - (int)viewSize.width)/2 + + monitorInfo.rcMonitor.left, + (monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top - (int)viewSize.height)/2 + + monitorInfo.rcMonitor.top, + (int)viewSize.width, (int)viewSize.height, TRUE); + } + + [self setWindowBorderless:NO]; + + lastWindowPlacementMaximized = NO; + ShowWindow(Main_Window,SW_SHOW); + } + + // stop saveWindowSize from reacting to caption & frame if necessary + saveSize = !wasFullScreen; + + GetClientRect(Main_Window, &wDC); + + if (!fullScreen && (bounds.size.width != wDC.right - wDC.left + || bounds.size.height != wDC.bottom - wDC.top)) + { + // Resize the game window if needed. When we ask for a W x H + // window, we intend that the client area be W x H. The actual + // window itself must become big enough to accomodate an area + // of such size. + if (wasFullScreen) // this is true when switching from full screen or when starting in windowed mode + //after the splash screen has ended + { + RECT desiredClientRect; + GetWindowRect(Main_Window, &desiredClientRect); + AdjustWindowRect(&desiredClientRect, WS_CAPTION | WS_THICKFRAME, FALSE); + SetWindowPos(Main_Window, NULL, desiredClientRect.left, desiredClientRect.top, + desiredClientRect.right - desiredClientRect.left, + desiredClientRect.bottom - desiredClientRect.top, 0); + } + GetClientRect(Main_Window, &wDC); + viewSize.width = wDC.right - wDC.left; + viewSize.height = wDC.bottom - wDC.top; + } + + // Reset bounds and viewSize to current values + bounds.size.width = viewSize.width = wDC.right - wDC.left; + bounds.size.height = viewSize.height = wDC.bottom - wDC.top; + if (fullScreen) // bounds on fullscreen coincide with client area, since we are borderless + { + bounds.origin.x = monitorInfo.rcMonitor.left; + bounds.origin.y = monitorInfo.rcMonitor.top; + } + wasFullScreen=fullScreen; + +#else //OOLITE_LINUX + if (v_mode == NO) + [self setWindowBorderless:YES]; + if (fullScreen == YES) + { + SDL_DisplayMode target, closest; + SDL_SetWindowResizable(mainWindow, SDL_FALSE); + target.w = (int)viewSize.width; + target.h = (int)viewSize.height; + target.format = 0; + target.refresh_rate = 0; //FIXME: maybe replace viewSize with SDL_DisplayMode? + target.driverdata = 0; + if (SDL_GetClosestDisplayMode(0, &target, &closest) != NULL) + { + if(SDL_SetWindowDisplayMode(mainWindow, &closest) == 0) + { + OOLog(@"display.initGL", @"Fullscreen resolution set to %d x %d.", (int)viewSize.width, (int)viewSize.height); + if (SDL_SetWindowFullscreen(mainWindow, SDL_WINDOW_FULLSCREEN)) + { + [self setFullScreenMode: NO]; + viewSize = oldViewSize; + } + else + { + SDL_GL_GetDrawableSize(mainWindow, &width, &height); + bounds.size.width = viewSize.width = width; + bounds.size.height = viewSize.height = height; + } + } + } + + } + else + { + SDL_SetWindowFullscreen(mainWindow, 0); + SDL_SetWindowResizable(mainWindow, SDL_TRUE); + SDL_GL_GetDrawableSize(mainWindow, &width, &height); + bounds.size.width = width; + bounds.size.height = height; + } +#endif + OOLog(@"display.initGL", @"Created a new output of %d x %d, %@.", (int)viewSize.width, (int)viewSize.height,(fullScreen ? @"fullscreen" : @"windowed")); + + if (viewSize.width/viewSize.height > 4.0/3.0) { + display_z = 480.0 * bounds.size.width/bounds.size.height; + x_offset = 240.0 * bounds.size.width/bounds.size.height; + y_offset = 240.0; + } else { + display_z = 640.0; + x_offset = 320.0; + y_offset = 320.0 * bounds.size.height/bounds.size.width; + } + + [self autoShowMouse]; + + [[self gameController] setUpBasicOpenGLStateWithSize:viewSize]; + SDL_GL_SwapWindow(mainWindow); + squareX = 0.0f; + + m_glContextInitialized = YES; +} + + +- (BOOL) snapShot:(NSString *)filename +{ + BOOL snapShotOK = YES; + SDL_Surface *surface, *tmpSurface; + + surface = SDL_GetWindowSurface(mainWindow); + + // backup the previous directory + NSString* originalDirectory = [[NSFileManager defaultManager] currentDirectoryPath]; + // use the snapshots directory + [[NSFileManager defaultManager] chdirToSnapshotPath]; + + BOOL withFilename = (filename != nil); + static unsigned imageNo = 0; + unsigned tmpImageNo = 0; + NSString *pathToPic = nil; + NSString *baseName = @"oolite"; + +#if SNAPSHOTS_PNG_FORMAT + NSString *extension = @".png"; +#else + NSString *extension = @".bmp"; +#endif + + if (withFilename) + { + baseName = filename; + pathToPic = [filename stringByAppendingString:extension]; + } + else + { + tmpImageNo = imageNo; + } + + if (withFilename && [[NSFileManager defaultManager] fileExistsAtPath:pathToPic]) + { + OOLog(@"screenshot.filenameExists", @"Snapshot \"%@%@\" already exists - adding numerical sequence.", pathToPic, extension); + pathToPic = nil; + } + + if (pathToPic == nil) + { + do + { + tmpImageNo++; + pathToPic = [NSString stringWithFormat:@"%@-%03d%@", baseName, tmpImageNo, extension]; + } while ([[NSFileManager defaultManager] fileExistsAtPath:pathToPic]); + } + + if (!withFilename) + { + imageNo = tmpImageNo; + } + + OOLog(@"screenshot", @"Saved screen shot \"%@\" (%u x %u pixels).", pathToPic, surface->w, surface->h); + + int pitch = surface->w * 3; + unsigned char *pixls = malloc(pitch * surface->h); + int y; + int off; + + if (surface->w % 4) glPixelStorei(GL_PACK_ALIGNMENT,1); + else glPixelStorei(GL_PACK_ALIGNMENT,4); + for (y=surface->h-1, off=0; y>=0; y--, off+=pitch) + { + glReadPixels(0, y, surface->w, 1, GL_RGB, GL_UNSIGNED_BYTE, pixls + off); + } + + tmpSurface=SDL_CreateRGBSurfaceFrom(pixls,surface->w,surface->h,24,surface->w*3,0xFF,0xFF00,0xFF0000,0x0); +#if SNAPSHOTS_PNG_FORMAT + if(![self pngSaveSurface:pathToPic withSurface:tmpSurface]) + { + OOLog(@"screenshotPNG", @"Failed to save %@", pathToPic); + snapShotOK = NO; + } +#else + if (SDL_SaveBMP(tmpSurface, [pathToPic UTF8String]) == -1) + { + OOLog(@"screenshotBMP", @"Failed to save %@", pathToPic); + snapShotOK = NO; + } +#endif + SDL_FreeSurface(tmpSurface); + SDL_FreeSurface(surface); + free(pixls); + + // return to the previous directory + [[NSFileManager defaultManager] changeCurrentDirectoryPath:originalDirectory]; + return snapShotOK; +} + + +#if SNAPSHOTS_PNG_FORMAT +// This method is heavily based on 'Mars, Land of No Mercy' SDL examples, by Angelo "Encelo" Theodorou, see http://encelo.netsons.org/programming/sdl +- (BOOL) pngSaveSurface:(NSString *)fileName withSurface:(SDL_Surface *)surf +{ + FILE *fp; + png_structp pngPtr; + png_infop infoPtr; + int i, colorType; + png_bytep *rowPointers; + + fp = fopen([fileName UTF8String], "wb"); + if (fp == NULL) + { + OOLog(@"pngSaveSurface.fileCreate.failed", @"Failed to create output screenshot file %@", fileName); + return NO; + } + + // initialize png structures (no callbacks) + pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (pngPtr == NULL) + { + return NO; + } + + infoPtr = png_create_info_struct(pngPtr); + if (infoPtr == NULL) { + png_destroy_write_struct(&pngPtr, (png_infopp)NULL); + OOLog(@"pngSaveSurface.info_struct.failed", @"%@", @"png_create_info_struct error"); + exit(-1); + } + + if (setjmp(png_jmpbuf(pngPtr))) + { + png_destroy_write_struct(&pngPtr, &infoPtr); + fclose(fp); + exit(-1); + } + + png_init_io(pngPtr, fp); + + colorType = PNG_COLOR_MASK_COLOR; /* grayscale not supported */ + if (surf->format->palette) + { + colorType |= PNG_COLOR_MASK_PALETTE; + } + else if (surf->format->Amask) + { + colorType |= PNG_COLOR_MASK_ALPHA; + } + + png_set_IHDR(pngPtr, infoPtr, surf->w, surf->h, 8, colorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + + // write the image + png_write_info(pngPtr, infoPtr); + png_set_packing(pngPtr); + + rowPointers = (png_bytep*) malloc(sizeof(png_bytep)*surf->h); + for (i = 0; i < surf->h; i++) + { + rowPointers[i] = (png_bytep)(Uint8 *)surf->pixels + i*surf->pitch; + } + png_write_image(pngPtr, rowPointers); + png_write_end(pngPtr, infoPtr); + + free(rowPointers); + png_destroy_write_struct(&pngPtr, &infoPtr); + fclose(fp); + + return YES; +} +#endif // SNAPSHOTS_PNG_FORMAT + + +/* Turn the Cocoa ArrowKeys into our arrow key constants. */ +- (int) translateKeyCode: (int) input +{ + int key = input; + switch ( input ) + { + case NSUpArrowFunctionKey: + key = gvArrowKeyUp; + break; + + case NSDownArrowFunctionKey: + key = gvArrowKeyDown; + break; + + case NSLeftArrowFunctionKey: + key = gvArrowKeyLeft; + break; + + case NSRightArrowFunctionKey: + key = gvArrowKeyRight; + break; + + case NSF1FunctionKey: + key = gvFunctionKey1; + break; + + case NSF2FunctionKey: + key = gvFunctionKey2; + break; + + case NSF3FunctionKey: + key = gvFunctionKey3; + break; + + case NSF4FunctionKey: + key = gvFunctionKey4; + break; + + case NSF5FunctionKey: + key = gvFunctionKey5; + break; + + case NSF6FunctionKey: + key = gvFunctionKey6; + break; + + case NSF7FunctionKey: + key = gvFunctionKey7; + break; + + case NSF8FunctionKey: + key = gvFunctionKey8; + break; + + case NSF9FunctionKey: + key = gvFunctionKey9; + break; + + case NSF10FunctionKey: + key = gvFunctionKey10; + break; + + case NSF11FunctionKey: + key = gvFunctionKey11; + break; + + case NSHomeFunctionKey: + key = gvHomeKey; + break; + + default: + break; + } + return key; +} + + +- (void) setVirtualJoystick:(double) vmx :(double) vmy +{ + virtualJoystickPosition.x = vmx; + virtualJoystickPosition.y = vmy; +} + + +- (NSPoint) virtualJoystickPosition +{ + return virtualJoystickPosition; +} + + +///////////////////////////////////////////////////////////// + +- (void) clearKeys +{ + int i; + for (i = 0; i < [self numKeys]; i++) + keys[i] = NO; +} + + +- (void) clearMouse +{ + keys[gvMouseDoubleClick] = NO; + keys[gvMouseLeftButton] = NO; + doubleClick = NO; +} + + +- (void) clearKey: (int)theKey +{ + if (theKey >= 0 && theKey < [self numKeys]) + { + keys[theKey] = NO; + } +} + + +- (void) resetMouse +{ + [self setVirtualJoystick:0.0 :0.0]; + if ([[PlayerEntity sharedPlayer] isMouseControlOn]) + { + SDL_WarpMouseInWindow(mainWindow, [self viewSize].width / 2, [self viewSize].height / 2); + mouseWarped = YES; + } +} + + +- (BOOL) isAlphabetKeyDown +{ + return isAlphabetKeyDown = NO;; +} + +// DJS: When entering submenus in the gui, it is not helpful if the +// key down that brought you into the submenu is still registered +// as down when we're in. This makes isDown return NO until a key up +// event has been received from SDL. +- (void) supressKeysUntilKeyUp +{ + if (keys[gvMouseDoubleClick] == NO) + { + supressKeys = YES; + [self clearKeys]; + } + else + { + [self clearMouse]; + } + +} + + +- (BOOL) isDown: (int) key +{ + if ( supressKeys ) + return NO; + if ( key < 0 ) + return NO; + if ( key >= [self numKeys] ) + return NO; + return keys[key]; +} + + +- (BOOL) isOptDown +{ + return opt; +} + + +- (BOOL) isCtrlDown +{ + return ctrl; +} + + +- (BOOL) isCommandDown +{ + return command; +} + + +- (BOOL) isShiftDown +{ + return shift; +} + + +- (BOOL) isCapsLockOn +{ + /* Caps Lock state check - This effectively gives us + an alternate keyboard state to play with and, in + the future, we could assign different behaviours + to existing controls, depending on the state of + Caps Lock. - Nikos 20160304 + */ + return (SDL_GetModState() & KMOD_CAPS) == KMOD_CAPS; +} + + +- (int) numKeys +{ + return NUM_KEYS; +} + + +- (int) mouseWheelState +{ + return _mouseWheelState; +} + + +- (BOOL) isCommandQDown +{ + return NO; +} + + +- (BOOL) isCommandFDown +{ + return NO; +} + + +- (void) clearCommandF +{ + // SDL stub for the mac function. +} + + +- (void) setKeyboardTo: (NSString *) value +{ +#if OOLITE_WINDOWS + keyboardMap=gvKeyboardAuto; + + if ([value isEqual: @"UK"]) + { + keyboardMap=gvKeyboardUK; + } + else if ([value isEqual: @"US"]) + { + keyboardMap=gvKeyboardUS; + } +#endif +} + +- (void)pollControls +{ + SDL_Event event; + SDL_KeyboardEvent *kbd_event; + SDL_MouseButtonEvent *mbtn_event; + SDL_MouseWheelEvent *mwheel_event; + SDL_WindowEvent *window_event; + SDL_MouseMotionEvent *mmove_event; + int mxdelta, mydelta; + float mouseVirtualStickSensitivityX = viewSize.width * MOUSEVIRTUALSTICKSENSITIVITYFACTOR; + float mouseVirtualStickSensitivityY = viewSize.height * MOUSEVIRTUALSTICKSENSITIVITYFACTOR; + NSTimeInterval timeNow = [NSDate timeIntervalSinceReferenceDate]; + + + while (SDL_PollEvent(&event)) + { + switch (event.type) { + case SDL_JOYAXISMOTION: + case SDL_JOYBUTTONUP: + case SDL_JOYBUTTONDOWN: + case SDL_JOYHATMOTION: + [(OOSDLJoystickManager*)[OOJoystickManager sharedStickHandler] handleSDLEvent: &event]; + break; + + case SDL_MOUSEBUTTONDOWN: + mbtn_event = (SDL_MouseButtonEvent*)&event; + switch(mbtn_event->button) + { + case SDL_BUTTON_LEFT: + keys[gvMouseLeftButton] = YES; + break; + case SDL_BUTTON_RIGHT: + // Cocoa version does this in the GameController + /* + The mouseWarped variable is quite important as far as mouse control is concerned. When we + reset the virtual joystick (mouse) coordinates, we need to send a WarpMouse call because we + must recenter the pointer physically on screen. This goes together with a mouse motion event, + so we use mouseWarped to simply ignore handling of motion events in this case. - Nikos 20110721 + */ + [self resetMouse]; // Will set mouseWarped to YES + break; + } + break; + + case SDL_MOUSEBUTTONUP: + mbtn_event = (SDL_MouseButtonEvent*)&event; + NSTimeInterval timeBetweenClicks = timeNow - timeIntervalAtLastClick; + timeIntervalAtLastClick += timeBetweenClicks; + if (mbtn_event->button == SDL_BUTTON_LEFT) + { + if (!doubleClick) + { + doubleClick = (timeBetweenClicks < MOUSE_DOUBLE_CLICK_INTERVAL); // One fifth of a second + keys[gvMouseDoubleClick] = doubleClick; + } + keys[gvMouseLeftButton] = NO; + } + break; + + case SDL_MOUSEWHEEL: + mwheel_event = (SDL_MouseWheelEvent*)&event; + /* + Mousewheel handling - SDL2 Supports delta movement of mouse wheel. + For now just emulate the old behaviour. + */ + if (mwheel_event->y != 0) + { + NSTimeInterval timeBetweenMouseWheels = timeNow - timeSinceLastMouseWheel; + timeSinceLastMouseWheel += timeBetweenMouseWheels; + } + if (mwheel_event->y <0) + _mouseWheelState = gvMouseWheelDown; + else + _mouseWheelState = gvMouseWheelUp; + break; + + case SDL_MOUSEMOTION: + { + // Delta mode is set when the game is in 'flight' mode. + // In this mode, the mouse movement delta is used rather + // than absolute position. This is because if the user + // clicks the right button to recentre the virtual joystick, + // if we are using absolute joystick positioning, as soon + // as the player touches the mouse again, the virtual joystick + // will snap back to the absolute position (which can be + // annoyingly fatal in battle). + if(mouseInDeltaMode) + { + // possible TODO - make virtual stick sensitivity configurable + SDL_GetRelativeMouseState(&mxdelta, &mydelta); + double mxd=(double)mxdelta / mouseVirtualStickSensitivityX; + double myd=(double)mydelta / mouseVirtualStickSensitivityY; + + if (!mouseWarped) // Standard event, update coordinates + { + virtualJoystickPosition.x += mxd; + virtualJoystickPosition.y += myd; + + // if we excceed the limits, revert changes + if(fabs(virtualJoystickPosition.x) > MOUSEX_MAXIMUM) + { + virtualJoystickPosition.x -= mxd; + } + if(fabs(virtualJoystickPosition.y) > MOUSEY_MAXIMUM) + { + virtualJoystickPosition.y -= myd; + } + } + else + { + // Motion event generated by WarpMouse is ignored and + // we reset mouseWarped for the next time. + mouseWarped = NO; + } + } + else + { + // Windowed mode. Use the absolute position so the + // Oolite mouse pointer appears under the X Window System + // mouse pointer. + mmove_event = (SDL_MouseMotionEvent*)&event; + + int w=bounds.size.width; + int h=bounds.size.height; + + if (!mouseWarped) // standard event, handle it + { + double mx = mmove_event->x - w/2.0; + double my = mmove_event->y - h/2.0; + if (display_z > 640.0) + { + mx /= w * MAIN_GUI_PIXEL_WIDTH / display_z; + my /= h; + } + else + { + mx /= MAIN_GUI_PIXEL_WIDTH * w / 640.0; + my /= MAIN_GUI_PIXEL_HEIGHT * w / 640.0; + } + + [self setVirtualJoystick:mx :my]; + } + else + { + // event coming from WarpMouse ignored, get ready for the next + mouseWarped = NO; + } + } + break; + } + case SDL_KEYDOWN: + kbd_event = (SDL_KeyboardEvent*)&event; + + if(allowingStringInput) + { + [self handleStringInput: kbd_event]; + } + + // Macro KEYCODE_DOWN_EITHER. Detect the keypress state (with shift or without) and assign appropriate values to the + // keys array. This way Oolite can use more keys, since now key '3', for example is a different keypress to '#'. +#define KEYCODE_DOWN_EITHER(a,b) do { \ +if (shift) { keys[a] = YES; keys[b] = NO; } else { keys[a] = NO; keys[b] = YES; } \ +} while (0) + +#if OOLITE_WINDOWS + /* + Windows locale patch - Enable backslash in win/UK + */ + if (EXPECT_NOT(kbd_event->keysym.scancode==86 && (keyboardMap==gvKeyboardAuto || keyboardMap==gvKeyboardUK))) + { + //non-US scancode. If in autodetect, we'll assume UK keyboard. + KEYCODE_DOWN_EITHER (124, 92); // | or \. + } + else switch (kbd_event->keysym.sym) { + + case SDLK_BACKSLASH: + if (keyboardMap==gvKeyboardUK ) + { + KEYCODE_DOWN_EITHER (126, 35); // ~ or # + } + else if (keyboardMap==gvKeyboardAuto || keyboardMap==gvKeyboardUS) + { + KEYCODE_DOWN_EITHER (124, 92); // | or \. + } + break; +#else + switch (kbd_event->keysym.sym) { + + case SDLK_BACKSLASH: KEYCODE_DOWN_EITHER (124, 92); break; // | or \. +#endif + case SDLK_1: KEYCODE_DOWN_EITHER (33, gvNumberKey1); break; // ! or 1 +#if OOLITE_WINDOWS + /* + Windows locale patch - fix shift-2 & shift-3 + */ + case SDLK_2: + if (keyboardMap==gvKeyboardUK) + { + KEYCODE_DOWN_EITHER (34, gvNumberKey2); // " or 2 + } + else + { + KEYCODE_DOWN_EITHER (64, gvNumberKey2); // @ or 2 + } + break; + case SDLK_3: + if (keyboardMap==gvKeyboardUK) + { + KEYCODE_DOWN_EITHER (156, gvNumberKey3); // � or 3 + } + else + { + KEYCODE_DOWN_EITHER (35, gvNumberKey3); // # or 3 + } + break; +#else + case SDLK_2: KEYCODE_DOWN_EITHER (64, gvNumberKey2); break; // @ or 2 + case SDLK_3: KEYCODE_DOWN_EITHER (35, gvNumberKey3); break; // # or 3 +#endif + case SDLK_4: KEYCODE_DOWN_EITHER (36, gvNumberKey4); break; // $ or 4 + case SDLK_5: KEYCODE_DOWN_EITHER (37, gvNumberKey5); break; // % or 5 + case SDLK_6: KEYCODE_DOWN_EITHER (94, gvNumberKey6); break; // ^ or 6 + case SDLK_7: KEYCODE_DOWN_EITHER (38, gvNumberKey7); break; // & or 7 + case SDLK_8: KEYCODE_DOWN_EITHER (42, gvNumberKey8); break; // * or 8 + case SDLK_9: KEYCODE_DOWN_EITHER (40, gvNumberKey9); break; // ( or 9 + case SDLK_0: KEYCODE_DOWN_EITHER (41, gvNumberKey0); break; // ) or 0 + case SDLK_MINUS: KEYCODE_DOWN_EITHER (95, 45); break; // _ or - + case SDLK_COMMA: KEYCODE_DOWN_EITHER (60, 44); break; // < or , + case SDLK_EQUALS: KEYCODE_DOWN_EITHER (43, 61); break; // + or = + case SDLK_PERIOD: KEYCODE_DOWN_EITHER (62, 46); break; // > or . + case SDLK_SLASH: KEYCODE_DOWN_EITHER (63, 47); break; // ? or / + case SDLK_a: KEYCODE_DOWN_EITHER (65, 97); break; // A or a + case SDLK_b: KEYCODE_DOWN_EITHER (66, 98); break; // B or b + case SDLK_c: KEYCODE_DOWN_EITHER (67, 99); break; // C or c + case SDLK_d: KEYCODE_DOWN_EITHER (68, 100); break; // D or d + case SDLK_e: KEYCODE_DOWN_EITHER (69, 101); break; // E or e + case SDLK_f: KEYCODE_DOWN_EITHER (70, 102); break; // F or f + case SDLK_g: KEYCODE_DOWN_EITHER (71, 103); break; // G or g + case SDLK_h: KEYCODE_DOWN_EITHER (72, 104); break; // H or h + case SDLK_i: KEYCODE_DOWN_EITHER (73, 105); break; // I or i + case SDLK_j: KEYCODE_DOWN_EITHER (74, 106); break; // J or j + case SDLK_k: KEYCODE_DOWN_EITHER (75, 107); break; // K or k + case SDLK_l: KEYCODE_DOWN_EITHER (76, 108); break; // L or l + case SDLK_m: KEYCODE_DOWN_EITHER (77, 109); break; // M or m + case SDLK_n: KEYCODE_DOWN_EITHER (78, 110); break; // N or n + case SDLK_o: KEYCODE_DOWN_EITHER (79, 111); break; // O or o + case SDLK_p: KEYCODE_DOWN_EITHER (80, 112); break; // P or p + case SDLK_q: KEYCODE_DOWN_EITHER (81, 113); break; // Q or q + case SDLK_r: KEYCODE_DOWN_EITHER (82, 114); break; // R or r + case SDLK_s: KEYCODE_DOWN_EITHER (83, 115); break; // S or s + case SDLK_t: KEYCODE_DOWN_EITHER (84, 116); break; // T or t + case SDLK_u: KEYCODE_DOWN_EITHER (85, 117); break; // U or u + case SDLK_v: KEYCODE_DOWN_EITHER (86, 118); break; // V or v + case SDLK_w: KEYCODE_DOWN_EITHER (87, 119); break; // W or w + case SDLK_x: KEYCODE_DOWN_EITHER (88, 120); break; // X or x + case SDLK_y: KEYCODE_DOWN_EITHER (89, 121); break; // Y or y + case SDLK_z: KEYCODE_DOWN_EITHER (90, 122); break; // Z or z + case SDLK_SEMICOLON: KEYCODE_DOWN_EITHER(58, 59); break; // : or ; + //SDLK_BACKQUOTE and SDLK_HASH are special cases. No SDLK_ with code 126 exists. + case SDLK_HASH: if (!shift) keys[126] = YES; break; // ~ (really #) + case SDLK_BACKQUOTE: if (!shift) keys[96] = YES; break; // ` + case SDLK_QUOTE: keys[39] = YES; break; // ' + case SDLK_LEFTBRACKET: keys[91] = YES; break; // [ + case SDLK_RIGHTBRACKET: keys[93] = YES; break; // ] + case SDLK_HOME: keys[gvHomeKey] = YES; break; + case SDLK_END: keys[gvEndKey] = YES; break; + case SDLK_INSERT: keys[gvInsertKey] = YES; break; + case SDLK_PAGEUP: keys[gvPageUpKey] = YES; break; + case SDLK_PAGEDOWN: keys[gvPageDownKey] = YES; break; + case SDLK_SPACE: keys[32] = YES; break; + case SDLK_RETURN: keys[13] = YES; break; + case SDLK_TAB: keys[9] = YES; break; + case SDLK_UP: keys[gvArrowKeyUp] = YES; break; + case SDLK_DOWN: keys[gvArrowKeyDown] = YES; break; + case SDLK_LEFT: keys[gvArrowKeyLeft] = YES; break; + case SDLK_RIGHT: keys[gvArrowKeyRight] = YES; break; + + case SDLK_KP_MINUS: keys[45] = YES; break; // numeric keypad - key + case SDLK_KP_PLUS: keys[43] = YES; break; // numeric keypad + key + case SDLK_KP_ENTER: keys[13] = YES; break; + + case SDLK_KP_MULTIPLY: keys[42] = YES; break; // * + + case SDLK_KP_1: keys[gvNumberPadKey1] = YES; break; + case SDLK_KP_2: keys[gvNumberPadKey2] = YES; break; + case SDLK_KP_3: keys[gvNumberPadKey3] = YES; break; + case SDLK_KP_4: keys[gvNumberPadKey4] = YES; break; + case SDLK_KP_5: keys[gvNumberPadKey5] = YES; break; + case SDLK_KP_6: keys[gvNumberPadKey6] = YES; break; + case SDLK_KP_7: keys[gvNumberPadKey7] = YES; break; + case SDLK_KP_8: keys[gvNumberPadKey8] = YES; break; + case SDLK_KP_9: keys[gvNumberPadKey9] = YES; break; + case SDLK_KP_0: keys[gvNumberPadKey0] = YES; break; + + case SDLK_F1: keys[gvFunctionKey1] = YES; break; + case SDLK_F2: keys[gvFunctionKey2] = YES; break; + case SDLK_F3: keys[gvFunctionKey3] = YES; break; + case SDLK_F4: keys[gvFunctionKey4] = YES; break; + case SDLK_F5: keys[gvFunctionKey5] = YES; break; + case SDLK_F6: keys[gvFunctionKey6] = YES; break; + case SDLK_F7: keys[gvFunctionKey7] = YES; break; + case SDLK_F8: keys[gvFunctionKey8] = YES; break; + case SDLK_F9: keys[gvFunctionKey9] = YES; break; + case SDLK_F10: keys[gvFunctionKey10] = YES; break; + + case SDLK_BACKSPACE: + case SDLK_DELETE: + keys[gvDeleteKey] = YES; + break; + + case SDLK_LSHIFT: + case SDLK_RSHIFT: + shift = YES; + break; + + case SDLK_LCTRL: + case SDLK_RCTRL: + ctrl = YES; + break; + + case SDLK_LALT: + case SDLK_RALT: + opt = YES; + break; + + case SDLK_F12: + [self toggleScreenMode]; + break; + + case SDLK_ESCAPE: + if (shift) + { + if (glContext != NULL) SDL_GL_DeleteContext(glContext); + [gameController exitAppWithContext:@"Shift-escape pressed"]; + } + else + keys[27] = YES; + break; + default: + // Numerous cases not handled. + //OOLog(@"keys.test", @"Keydown scancode: %d", kbd_event->keysym.scancode); + ; + } + break; + + case SDL_KEYUP: + supressKeys = NO; // DJS + kbd_event = (SDL_KeyboardEvent*)&event; + +#define KEYCODE_UP_BOTH(a,b) do { \ +keys[a] = NO; keys[b] = NO; \ +} while (0) + +#if OOLITE_WINDOWS + /* + Windows locale patch - Enable backslash in win/UK + */ + if (EXPECT_NOT(kbd_event->keysym.scancode==86 && (keyboardMap==gvKeyboardAuto || keyboardMap==gvKeyboardUK))) + { + //non-US scancode. If in autodetect, we'll assume UK keyboard. + KEYCODE_UP_BOTH (124, 92); // | or \. + } + else switch (kbd_event->keysym.sym) { + + case SDLK_BACKSLASH: + if (keyboardMap==gvKeyboardUK ) + { + KEYCODE_UP_BOTH (126, 35); // ~ or # + } + else if (keyboardMap==gvKeyboardAuto || keyboardMap==gvKeyboardUS) + { + KEYCODE_UP_BOTH (124, 92); // | or \. + } + break; +#else + switch (kbd_event->keysym.sym) { + + case SDLK_BACKSLASH: KEYCODE_UP_BOTH (124, 92); break; // | or \. +#endif + + case SDLK_1: KEYCODE_UP_BOTH (33, gvNumberKey1); break; // ! and 1 +#if OOLITE_WINDOWS + /* + Windows locale patch - fix shift-2 & shift-3 + */ + case SDLK_2: + if (keyboardMap==gvKeyboardUK) + { + KEYCODE_UP_BOTH (34, gvNumberKey2); // " or 2 + } + else + { + KEYCODE_UP_BOTH (64, gvNumberKey2); // @ or 2 + } + break; + case SDLK_3: + if (keyboardMap==gvKeyboardUK) + { + KEYCODE_UP_BOTH (156, gvNumberKey3); // � or 3 + } + else + { + KEYCODE_UP_BOTH (35, gvNumberKey3); // # or 3 + } + break; +#else + case SDLK_2: KEYCODE_UP_BOTH (64, gvNumberKey2); break; // @ or 2 + case SDLK_3: KEYCODE_UP_BOTH (35, gvNumberKey3); break; // # or 3 +#endif + case SDLK_4: KEYCODE_UP_BOTH (36, gvNumberKey4); break; // $ or 4 + case SDLK_5: KEYCODE_UP_BOTH (37, gvNumberKey5); break; // % or 5 + case SDLK_6: KEYCODE_UP_BOTH (94, gvNumberKey6); break; // ^ or 6 + case SDLK_7: KEYCODE_UP_BOTH (38, gvNumberKey7); break; // & or 7 + case SDLK_8: KEYCODE_UP_BOTH (42, gvNumberKey8); break; // * or 8 + case SDLK_9: KEYCODE_UP_BOTH (40, gvNumberKey9); break; // ( or 9 + case SDLK_0: KEYCODE_UP_BOTH (41, gvNumberKey0); break; // ) or 0 + case SDLK_MINUS: KEYCODE_UP_BOTH (95, 45); break; // _ and - + case SDLK_COMMA: KEYCODE_UP_BOTH (60, 44); break; // < and , + case SDLK_EQUALS: KEYCODE_UP_BOTH (43, 61); break; // + and = + case SDLK_PERIOD: KEYCODE_UP_BOTH (62, 46); break; // > and . + case SDLK_SLASH: KEYCODE_UP_BOTH (63, 47); break; // ? and / + case SDLK_a: KEYCODE_UP_BOTH (65, 97); break; // A and a + case SDLK_b: KEYCODE_UP_BOTH (66, 98); break; // B and b + case SDLK_c: KEYCODE_UP_BOTH (67, 99); break; // C and c + case SDLK_d: KEYCODE_UP_BOTH (68, 100); break; // D and d + case SDLK_e: KEYCODE_UP_BOTH (69, 101); break; // E and e + case SDLK_f: KEYCODE_UP_BOTH (70, 102); break; // F and f + case SDLK_g: KEYCODE_UP_BOTH (71, 103); break; // G and g + case SDLK_h: KEYCODE_UP_BOTH (72, 104); break; // H and h + case SDLK_i: KEYCODE_UP_BOTH (73, 105); break; // I and i + case SDLK_j: KEYCODE_UP_BOTH (74, 106); break; // J and j + case SDLK_k: KEYCODE_UP_BOTH (75, 107); break; // K and k + case SDLK_l: KEYCODE_UP_BOTH (76, 108); break; // L and l + case SDLK_m: KEYCODE_UP_BOTH (77, 109); break; // M and m + case SDLK_n: KEYCODE_UP_BOTH (78, 110); break; // N and n + case SDLK_o: KEYCODE_UP_BOTH (79, 111); break; // O and o + case SDLK_p: KEYCODE_UP_BOTH (80, 112); break; // P and p + case SDLK_q: KEYCODE_UP_BOTH (81, 113); break; // Q and q + case SDLK_r: KEYCODE_UP_BOTH (82, 114); break; // R and r + case SDLK_s: KEYCODE_UP_BOTH (83, 115); break; // S and s + case SDLK_t: KEYCODE_UP_BOTH (84, 116); break; // T and t + case SDLK_u: KEYCODE_UP_BOTH (85, 117); break; // U and u + case SDLK_v: KEYCODE_UP_BOTH (86, 118); break; // V and v + case SDLK_w: KEYCODE_UP_BOTH (87, 119); break; // W and w + case SDLK_x: KEYCODE_UP_BOTH (88, 120); break; // X and x + case SDLK_y: KEYCODE_UP_BOTH (89, 121); break; // Y and y + case SDLK_z: KEYCODE_UP_BOTH (90, 122); break; // Z and z + case SDLK_SEMICOLON: KEYCODE_UP_BOTH(58, 59); break; // : and ; + //SDLK_BACKQUOTE and SDLK_HASH are special cases. No SDLK_ with code 126 exists. + case SDLK_HASH: if (!shift) keys[126] = NO; break; // ~ (really #) + case SDLK_BACKQUOTE: keys[96] = NO; break; // ` + case SDLK_QUOTE: keys[39] = NO; break; // ' + case SDLK_LEFTBRACKET: keys[91] = NO; break; // [ + case SDLK_RIGHTBRACKET: keys[93] = NO; break; // ] + case SDLK_HOME: keys[gvHomeKey] = NO; break; + case SDLK_END: keys[gvEndKey] = NO; break; + case SDLK_INSERT: keys[gvInsertKey] = NO; break; + case SDLK_PAGEUP: keys[gvPageUpKey] = NO; break; + case SDLK_PAGEDOWN: keys[gvPageDownKey] = NO; break; + case SDLK_SPACE: keys[32] = NO; break; + case SDLK_RETURN: keys[13] = NO; break; + case SDLK_TAB: keys[9] = NO; break; + case SDLK_UP: keys[gvArrowKeyUp] = NO; break; + case SDLK_DOWN: keys[gvArrowKeyDown] = NO; break; + case SDLK_LEFT: keys[gvArrowKeyLeft] = NO; break; + case SDLK_RIGHT: keys[gvArrowKeyRight] = NO; break; + + case SDLK_KP_MINUS: keys[45] = NO; break; // numeric keypad - key + case SDLK_KP_PLUS: keys[43] = NO; break; // numeric keypad + key + case SDLK_KP_ENTER: keys[13] = NO; break; + + case SDLK_KP_MULTIPLY: keys[42] = NO; break; // * + + case SDLK_KP_1: keys[gvNumberPadKey1] = NO; break; + case SDLK_KP_2: keys[gvNumberPadKey2] = NO; break; + case SDLK_KP_3: keys[gvNumberPadKey3] = NO; break; + case SDLK_KP_4: keys[gvNumberPadKey4] = NO; break; + case SDLK_KP_5: keys[gvNumberPadKey5] = NO; break; + case SDLK_KP_6: keys[gvNumberPadKey6] = NO; break; + case SDLK_KP_7: keys[gvNumberPadKey7] = NO; break; + case SDLK_KP_8: keys[gvNumberPadKey8] = NO; break; + case SDLK_KP_9: keys[gvNumberPadKey9] = NO; break; + case SDLK_KP_0: keys[gvNumberPadKey0] = NO; break; + + case SDLK_F1: keys[gvFunctionKey1] = NO; break; + case SDLK_F2: keys[gvFunctionKey2] = NO; break; + case SDLK_F3: keys[gvFunctionKey3] = NO; break; + case SDLK_F4: keys[gvFunctionKey4] = NO; break; + case SDLK_F5: keys[gvFunctionKey5] = NO; break; + case SDLK_F6: keys[gvFunctionKey6] = NO; break; + case SDLK_F7: keys[gvFunctionKey7] = NO; break; + case SDLK_F8: keys[gvFunctionKey8] = NO; break; + case SDLK_F9: keys[gvFunctionKey9] = NO; break; + case SDLK_F10: keys[gvFunctionKey10] = NO; break; + + case SDLK_BACKSPACE: + case SDLK_DELETE: + keys[gvDeleteKey] = NO; + break; + + case SDLK_LSHIFT: + case SDLK_RSHIFT: + shift = NO; + break; + + case SDLK_LCTRL: + case SDLK_RCTRL: + ctrl = NO; + break; + + case SDLK_LALT: + case SDLK_RALT: + opt = NO; + break; + + case SDLK_ESCAPE: + keys[27] = NO; + break; + + default: + // Numerous cases not handled. + //OOLog(@"keys.test", @"Keyup scancode: %d", kbd_event->keysym.scancode); + ; + } + break; + + case SDL_WINDOWEVENT: + { + window_event = (SDL_WindowEvent*)&event; + switch (window_event->event) { + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + { + NSSize newSize=NSMakeSize(window_event->data1, window_event->data2); +#if OOLITE_WINDOWS + if (!fullScreen && updateContext)( { + if (saveSize == NO) + { + // event triggered by caption & frame + // next event will be a real resize. + saveSize = YES; + } + else + { + [self initialiseGLWithSize: newSize]; + [self saveWindowSize: newSize]; + } + } +#else + if (SDL_GetWindowFlags(mainWindow) & SDL_WINDOW_RESIZABLE) + { + [self initialiseGLWithSize: newSize]; + [self saveWindowSize: newSize]; + } +#endif + // certain gui screens will require an immediate redraw after + // a resize event - Nikos 20140129 + if ([PlayerEntity sharedPlayer]) + { + [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates]; + } + } + break; + } + break; + } + +#if OOLITE_WINDOWS + // if we minimize the window while in fullscreen (e.g. via + // Win+M or Win+DownArrow), restore the non-borderless window + // style before minimuzing and reset it when we return, otherwise + // there might be issues with the game window remaining stuck on + // top in some cases (seen with some Intel gfx chips). + // N.B. active event gain of zero means app is iconified + case SDL_ACTIVEEVENT: + { + if ((event.active.state & SDL_APPACTIVE) && fullScreen) + { + [self setWindowBorderless:event.active.gain]; + } + break; + } + + // need to track this because the user may move the game window + // to a secondary monitor, in which case we must potentially + // refresh the information displayed (e.g. Game Options screen) + // Nikos - 20140920 + case SDL_SYSWMEVENT: + { + switch (event.syswm.msg->msg) + { + case WM_WINDOWPOSCHANGING: + /* if we are in fullscreen mode we normally don't worry about having the window moved. + However, when using multiple monitors, one can use hotkey combinations to make the + window "jump" from one monitor to the next. We don't want this to happen, so if we + detect that our (fullscreen) window has moved, we immediately bring it back to its + original position. Nikos - 20140922 + */ + if (fullScreen) + { + RECT rDC; + + /* attempting to move our fullscreen window while in maximized state can freak + Windows out and the window may not return to its original position properly. + Solution: if such a move takes place, first change the window placement to + normal, move it normally, then restore its placement to maximized again. + Additionally, the last good known window position seems to be lost in such + a case. While at it, update also the coordinates of the non-maximized window + so that it can return to its original position - this is why we need lastGoodRect. + */ + WINDOWPLACEMENT wp; + wp.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(Main_Window, &wp); + + GetWindowRect(Main_Window, &rDC); + if (rDC.left != monitorInfo.rcMonitor.left || rDC.top != monitorInfo.rcMonitor.top) + { + BOOL fullScreenMaximized = NO; + if (wp.showCmd == SW_SHOWMAXIMIZED && !fullScreenMaximized) + { + fullScreenMaximized = YES; + wp.showCmd = SW_SHOWNORMAL; + SetWindowPlacement(Main_Window, &wp); + } + + if (wp.showCmd != SW_SHOWMINIMIZED && wp.showCmd != SW_MINIMIZE) + { + MoveWindow(Main_Window, monitorInfo.rcMonitor.left, monitorInfo.rcMonitor.top, + (int)viewSize.width, (int)viewSize.height, TRUE); + } + + if (fullScreenMaximized) + { + GetWindowPlacement(Main_Window, &wp); + wp.showCmd = SW_SHOWMAXIMIZED; + CopyRect(&wp.rcNormalPosition, &lastGoodRect); + SetWindowPlacement(Main_Window, &wp); + } + } + else if (wp.showCmd == SW_SHOWMAXIMIZED) + { + CopyRect(&wp.rcNormalPosition, &lastGoodRect); + SetWindowPlacement(Main_Window, &wp); + } + } + // it is important that this gets done after we've dealt with possible fullscreen movements, + // because -doGuiScreenResizeUpdates does itself an update on current monitor + if ([PlayerEntity sharedPlayer]) + { + [[PlayerEntity sharedPlayer] doGuiScreenResizeUpdates]; + } + /* + deliberately no break statement here - moving or resizing the window changes its bounds + rectangle. Therefore we must check whether to clip the mouse or not inside the newly + updated rectangle, so just let it fall through + */ + + case WM_ACTIVATEAPP: + if(grabMouseStatus) [self grabMouseInsideGameWindow:YES]; + break; + + case WM_SETFOCUS: + /* + ` make sure that all modifier keys like Shift, Alt, Ctrl and Caps Lock + ` are set correctly to what they should be when we get focus. We have + ` to do it ourselves because SDL on Windows has problems with this + ` when focus change events occur, like e.g. Alt-Tab in/out of the + application + ` */ + [self resetSDLKeyModifiers]; + break; + + default: + ; + } + break; + } +#endif + + // caused by INTR or someone hitting close + case SDL_QUIT: + { + if (glContext != NULL) SDL_GL_DeleteContext(glContext); + [gameController exitAppWithContext:@"SDL_QUIT event received"]; + } + } + } + // check if enough time has passed since last use of the mousewheel and act + // if needed + if (timeNow >= timeSinceLastMouseWheel + OOMOUSEWHEEL_EVENTS_DELAY_INTERVAL) + { + _mouseWheelState = gvMouseWheelNeutral; + } +} + + +// DJS: String input handler. Since for SDL versions we're also handling +// freeform typing this has necessarily got more complex than the non-SDL +// versions. +- (void) handleStringInput: (SDL_KeyboardEvent *) kbd_event; +{ + SDL_Keycode key=kbd_event->keysym.sym; + + // Del, Backspace + if((key == SDLK_BACKSPACE || key == SDLK_DELETE) && [typedString length] > 0) + { + // delete + [typedString deleteCharactersInRange:NSMakeRange([typedString length]-1, 1)]; + } + + isAlphabetKeyDown=NO; + + // TODO: a more flexible mechanism for max. string length ? + if([typedString length] < 40) + { + if (allowingStringInput == gvStringInputAlpha) + { + // inputAlpha - limited input for planet find screen + if(key >= SDLK_a && key <= SDLK_z) + { + isAlphabetKeyDown=YES; + [typedString appendFormat:@"%c", key]; + // if in inputAlpha, keep in lower case. + } + } + else + { + Uint16 unicode = kbd_event->keysym.sym; + // printable range + if (unicode >= 32 && unicode <= 126) + { + if ((char)unicode != '/' || allowingStringInput == gvStringInputAll) + { + isAlphabetKeyDown=YES; + [typedString appendFormat:@"%c", unicode]; + } + } + } + } +} + + +// Full screen mode enumerator. +- (void) populateFullScreenModelist +{ + int i; + static int display_num = 0; // TODO: Support multiple displays? + SDL_DisplayMode display_mode; + int mode_count; + NSMutableDictionary *mode; + + screenSizes=[[NSMutableArray alloc] init]; + + // The default resolution (slot 0) is the resolution we are + // already in since this is guaranteed to work. + mode=[MyOpenGLView getNativeSize]; + [screenSizes addObject: mode]; + + mode_count = SDL_GetNumDisplayModes(display_num); + if (mode_count < 1) + { + OOLog(@"display.mode.list.none", @"SDL didn't return any screen modes"); + return; + } + + // SDL_GetDisplayMode often lists a mode several times, + // because each mode has several refresh rates. + // SDL2 uses SDL_DisplayMode which does support + // refresh rates unlike SDL1 which didn't. This needs + // quite a few code adjustments. TODO. + int lastw=[[mode objectForKey: kOODisplayWidth] intValue]; + int lasth=[[mode objectForKey: kOODisplayHeight] intValue]; + for (i = 0; i < mode_count; ++i) { + if (SDL_GetDisplayMode(display_num, i, &display_mode) == 0) + { + if(display_mode.w != lastw || display_mode.h != lasth) + { + // new resolution, save it + mode=[NSMutableDictionary dictionary]; + [mode setValue: [NSNumber numberWithInt: display_mode.w] + forKey: kOODisplayWidth]; + [mode setValue: [NSNumber numberWithInt: display_mode.h] + forKey: kOODisplayHeight]; + [mode setValue: [NSNumber numberWithInt: 0] + forKey: kOODisplayRefreshRate]; + if (![screenSizes containsObject:mode]) + { + [screenSizes addObject: mode]; + OOLog(@"display.mode.list", @"Added res %d x %d", display_mode.w, display_mode.h); + lastw=display_mode.w; + lasth=display_mode.h; + } + } + } + } +} + +// Save and restore window sizes to/from defaults. +- (void) saveWindowSize: (NSSize) windowSize +{ + NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; + [defaults setInteger: (int)windowSize.width forKey: @"window_width"]; + [defaults setInteger: (int)windowSize.height forKey: @"window_height"]; + currentWindowSize=windowSize; +} + + +- (NSSize) loadWindowSize +{ + NSSize windowSize; + NSUserDefaults *defaults=[NSUserDefaults standardUserDefaults]; + if([defaults objectForKey:@"window_width"] && [defaults objectForKey:@"window_height"]) + { + windowSize=NSMakeSize([defaults integerForKey: @"window_width"], + [defaults integerForKey: @"window_height"]); + } + else + { + windowSize=NSMakeSize(800, 600); + } + currentWindowSize=windowSize; + return windowSize; +} + + +- (int) loadFullscreenSettings +{ + currentSize=0; + int width=0, height=0, refresh=0; + unsigned i; + + NSArray* cmdline_arguments = [[NSProcessInfo processInfo] arguments]; + + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + if ([userDefaults objectForKey:@"display_width"]) + width = [userDefaults integerForKey:@"display_width"]; + if ([userDefaults objectForKey:@"display_height"]) + height = [userDefaults integerForKey:@"display_height"]; + if ([userDefaults objectForKey:@"display_refresh"]) + refresh = [userDefaults integerForKey:@"display_refresh"]; + if([userDefaults objectForKey:@"fullscreen"]) + fullScreen=[userDefaults boolForKey:@"fullscreen"]; + + // Check if -fullscreen or -windowed has been passed on the command line. If yes, + // set it regardless of what is set by .GNUstepDefaults. If both are found in the + // arguments list, the one that comes last wins. + for (i = 0; i < [cmdline_arguments count]; i++) + { + if ([[cmdline_arguments objectAtIndex:i] isEqual:@"-fullscreen"]) fullScreen = YES; + if ([[cmdline_arguments objectAtIndex:i] isEqual:@"-windowed"]) fullScreen = NO; + } + + if(width && height) + { + currentSize=[self findDisplayModeForWidth: width Height: height Refresh: refresh]; + return currentSize; + } + return currentSize; +} + + +- (int) findDisplayModeForWidth:(unsigned int) d_width Height:(unsigned int) d_height Refresh:(unsigned int) d_refresh +{ + int i, modeCount; + NSDictionary *mode; + unsigned int modeWidth, modeHeight, modeRefresh; + + modeCount = [screenSizes count]; + + for (i = 0; i < modeCount; i++) + { + mode = [screenSizes objectAtIndex: i]; + modeWidth = [[mode objectForKey: kOODisplayWidth] intValue]; + modeHeight = [[mode objectForKey: kOODisplayHeight] intValue]; + modeRefresh = [[mode objectForKey: kOODisplayRefreshRate] intValue]; + if ((modeWidth == d_width)&&(modeHeight == d_height)&&(modeRefresh == d_refresh)) + { + OOLog(@"display.mode.found", @"Found mode %@", mode); + return i; + } + } + + OOLog(@"display.mode.found.failed", @"Failed to find mode: width=%d height=%d refresh=%d", d_width, d_height, d_refresh); + OOLog(@"display.mode.found.failed.list", @"Contents of list: %@", screenSizes); + return 0; +} + + +- (NSSize) currentScreenSize +{ + NSDictionary *mode=[screenSizes objectAtIndex: currentSize]; + + if(mode) + { + return NSMakeSize([[mode objectForKey: kOODisplayWidth] intValue], + [[mode objectForKey: kOODisplayHeight] intValue]); + } + OOLog(@"display.mode.unknown", @"%@", @"Screen size unknown!"); + return NSMakeSize(800, 600); +} + + +- (void) setMouseInDeltaMode: (BOOL) inDelta +{ + mouseInDeltaMode=inDelta; +} + + +- (void) setGammaValue: (float) value +{ + Uint16* gamma_ramp; + + if (value < 0.2f) value = 0.2f; + if (value > 4.0f) value = 4.0f; + + gamma_ramp = (Uint16 *)SDL_malloc(256 * sizeof(Uint16)); + _gamma = value; + SDL_CalculateGammaRamp(_gamma, gamma_ramp); + SDL_SetWindowGammaRamp(mainWindow, gamma_ramp, gamma_ramp, gamma_ramp); + + [[NSUserDefaults standardUserDefaults] setFloat:_gamma forKey:@"gamma-value"]; +} + + +- (float) gammaValue +{ + return _gamma; +} + + +- (void) setFov:(float)value fromFraction:(BOOL)fromFraction +{ + _fov = fromFraction ? value : tan((value / 2) * M_PI / 180); +} + + +- (float) fov:(BOOL)inFraction +{ + return inFraction ? _fov : 2 * atan(_fov) * 180 / M_PI; +} + + +- (OOOpenGLMatrixManager *) getOpenGLMatrixManager +{ + return matrixManager; +} + + ++ (BOOL)pollShiftKey +{ + return 0 != (SDL_GetModState() & (KMOD_LSHIFT | KMOD_RSHIFT)); +} + + +#ifndef NDEBUG +- (void) dumpRGBAToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes +{ + if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4) return; + + // use the snapshots directory + NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR]; + dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]]; + + // convert transparency to black before saving to bmp + SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 32, rowBytes, 0xFF, 0xFF00, 0xFF0000, 0xFF000000); + SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]); + SDL_FreeSurface(tmpSurface); +} + + +- (void) dumpRGBToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes +{ + if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 3) return; + + // use the snapshots directory + NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR]; + dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]]; + + SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 24, rowBytes, 0xFF, 0xFF00, 0xFF0000, 0x0); + SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]); + SDL_FreeSurface(tmpSurface); +} + + +- (void) dumpGrayToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes +{ + if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width) return; + + // use the snapshots directory + NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR]; + dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]]; + + SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 8, rowBytes, 0xFF, 0xFF, 0xFF, 0x0); + SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]); + SDL_FreeSurface(tmpSurface); +} + + +- (void) dumpGrayAlphaToFileNamed:(NSString *)name + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes +{ + if (name == nil || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 2) return; + + // use the snapshots directory + NSString *dumpFile = [[NSHomeDirectory() stringByAppendingPathComponent:@SAVEDIR] stringByAppendingPathComponent:@SNAPSHOTDIR]; + dumpFile = [dumpFile stringByAppendingPathComponent: [NSString stringWithFormat:@"%@.bmp", name]]; + + SDL_Surface* tmpSurface = SDL_CreateRGBSurfaceFrom(bytes, width, height, 16, rowBytes, 0xFF, 0xFF, 0xFF, 0xFF); + SDL_SaveBMP(tmpSurface, [dumpFile UTF8String]); + SDL_FreeSurface(tmpSurface); +} + + +- (void) dumpRGBAToRGBFileNamed:(NSString *)rgbName + andGrayFileNamed:(NSString *)grayName + bytes:(uint8_t *)bytes + width:(NSUInteger)width + height:(NSUInteger)height + rowBytes:(NSUInteger)rowBytes +{ + if ((rgbName == nil && grayName == nil) || bytes == NULL || width == 0 || height == 0 || rowBytes < width * 4) return; + + uint8_t *rgbBytes, *rgbPx, *grayBytes, *grayPx, *srcPx; + NSUInteger x, y; + BOOL trivalAlpha = YES; + + rgbPx = rgbBytes = malloc(width * height * 3); + if (rgbBytes == NULL) return; + + grayPx = grayBytes = malloc(width * height); + if (grayBytes == NULL) + { + free(rgbBytes); + return; + } + + for (y = 0; y < height; y++) + { + srcPx = bytes + rowBytes * y; + + for (x = 0; x < width; x++) + { + *rgbPx++ = *srcPx++; + *rgbPx++ = *srcPx++; + *rgbPx++ = *srcPx++; + trivalAlpha = trivalAlpha && ((*srcPx == 0xFF) || (*srcPx == 0x00)); // Look for any "interesting" pixels in alpha. + *grayPx++ = *srcPx++; + } + } + + [self dumpRGBToFileNamed:rgbName + bytes:rgbBytes + width:width + height:height + rowBytes:width * 3]; + free(rgbBytes); + + if (!trivalAlpha) + { + [self dumpGrayToFileNamed:grayName + bytes:grayBytes + width:width + height:height + rowBytes:width]; + } + free(grayBytes); +} +#endif + +@end diff --git a/src/SDL2/OOSDLJoystickManager.h b/src/SDL2/OOSDLJoystickManager.h new file mode 100644 index 000000000..1acb1f3bd --- /dev/null +++ b/src/SDL2/OOSDLJoystickManager.h @@ -0,0 +1,57 @@ +/* + +OOSDLJoystickManager.h +By Dylan Smith + +JoystickHandler handles joystick events from SDL, and translates them +into the appropriate action via a lookup table. The lookup table is +stored as a simple array rather than an ObjC dictionary since this +will be examined fairly often (once per frame during gameplay). + +Conversion methods are provided to convert between the internal +representation and an NSDictionary (for loading/saving user defaults +and for use in areas where portability/ease of coding are more important +than performance such as the GUI) + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + + + +#import +#import +#import "OOJoystickManager.h" + + + + +@interface OOSDLJoystickManager: OOJoystickManager +{ +@private + SDL_Joystick *stick[MAX_STICKS]; + NSUInteger stickCount; +} + +- (id) init; +- (BOOL) handleSDLEvent: (SDL_Event *)evt; +- (NSString *) nameOfJoystick:(NSUInteger)stickNumber; +- (int16_t) getAxisWithStick:(NSUInteger) stickNum axis:(NSUInteger) axisNum ; + +@end diff --git a/src/SDL2/OOSDLJoystickManager.m b/src/SDL2/OOSDLJoystickManager.m new file mode 100644 index 000000000..1fc8216af --- /dev/null +++ b/src/SDL2/OOSDLJoystickManager.m @@ -0,0 +1,113 @@ +/* + +OOSDLJoystickManager.m +By Dylan Smith + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + +#import "OOSDLJoystickManager.h" +#import "OOLogging.h" + +#define kOOLogUnconvertedNSLog @"unclassified.OOSDLJoystickManager" + + +@implementation OOSDLJoystickManager + +- (id) init +{ + int i; + + // Find and open the sticks. Make sure that we don't fail if more joysticks than MAX_STICKS are detected. + stickCount = SDL_NumJoysticks(); + OOLog(@"joystick.init", @"Number of joysticks detected: %ld", (long)stickCount); + if (stickCount > MAX_STICKS) + { + stickCount = MAX_STICKS; + OOLog(@"joystick.init", @"Number of joysticks detected exceeds maximum number of joysticks allowed. Setting number of active joysticks to %d.", MAX_STICKS); + } + if(stickCount) + { + for(i = 0; i < stickCount; i++) + { + // it's doubtful MAX_STICKS will ever get exceeded, but + // we need to be defensive. + if(i > MAX_STICKS) + break; + + stick[i]=SDL_JoystickOpen(i); + if(!stick[i]) + { + OOLog(@"joystick.init", @"Failed to open joystick #%d", i); + } + } + SDL_JoystickEventState(SDL_ENABLE); + } + return [super init]; +} + + +- (BOOL) handleSDLEvent: (SDL_Event *)evt +{ + BOOL rc=NO; + switch(evt->type) + { + case SDL_JOYAXISMOTION: + [self decodeAxisEvent: (JoyAxisEvent *)evt]; + rc=YES; + break; + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + [self decodeButtonEvent: (JoyButtonEvent *)evt]; + rc=YES; + break; + case SDL_JOYHATMOTION: + [self decodeHatEvent: (JoyHatEvent *)evt]; + rc=YES; + break; + default: + OOLog(@"handleSDLEvent.unknownEvent", @"%@", @"JoystickHandler was sent an event it doesn't know"); + } + return rc; +} + + +// Overrides + +- (NSUInteger) joystickCount +{ + return stickCount; +} + + +- (NSString *) nameOfJoystick:(NSUInteger)stickNumber +{ + return [NSString stringWithUTF8String:SDL_JoystickNameForIndex((int)stickNumber)]; +} + + +- (int16_t) getAxisWithStick:(NSUInteger) stickNum axis:(NSUInteger) axisNum +{ + return SDL_JoystickGetAxis(stick[stickNum], axisNum); +} + + + +@end diff --git a/src/SDL2/main.m b/src/SDL2/main.m new file mode 100644 index 000000000..2fcf01bca --- /dev/null +++ b/src/SDL2/main.m @@ -0,0 +1,124 @@ +/* + +main.m + +Oolite +Copyright (C) 2004-2013 Giles C Williams and contributors + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +MA 02110-1301, USA. + +*/ + + +#ifdef GNUSTEP +#import +#if (GNUSTEP_BASE_MAJOR_VERSION == 1 && (GNUSTEP_BASE_MINOR_VERSION == 24 && GNUSTEP_BASE_SUBMINOR_VERSION >= 9) || (GNUSTEP_BASE_MINOR_VERSION > 24)) || (GNUSTEP_BASE_MAJOR_VERSION > 1) +#import +#endif +#import + +#import "GameController.h" +#import "OOLoggingExtended.h" + +#if OOLITE_WINDOWS +#include +#include +#endif +GameController* controller; +#endif + + +#ifndef NDEBUG +uint32_t gDebugFlags = 0; +#endif + + +int main(int argc, char *argv[]) +{ +#ifdef GNUSTEP + int i; + +#if (GNUSTEP_BASE_MAJOR_VERSION == 1 && (GNUSTEP_BASE_MINOR_VERSION == 24 && GNUSTEP_BASE_SUBMINOR_VERSION >= 9) || (GNUSTEP_BASE_MINOR_VERSION > 24)) || (GNUSTEP_BASE_MAJOR_VERSION > 1) + [NSDate class]; // See github issue #202 +#endif + +#if OOLITE_WINDOWS + + // Detect current working directory and set up GNUstep environment variables + #define MAX_PATH_LEN 256 + char currentWorkingDir[MAX_PATH_LEN]; + char envVarString[2 * MAX_PATH_LEN]; + GetCurrentDirectory(MAX_PATH_LEN - 1, currentWorkingDir); + + #define SETENVVAR(var, value) do {\ + sprintf(envVarString, "%s=%s", (var), (value));\ + SDL_putenv (envVarString);\ + } while (0); + + SETENVVAR("GNUSTEP_PATH_HANDLING", "windows"); + SETENVVAR("GNUSTEP_SYSTEM_ROOT", currentWorkingDir); + SETENVVAR("GNUSTEP_LOCAL_ROOT", currentWorkingDir); + SETENVVAR("GNUSTEP_NETWORK_ROOT", currentWorkingDir); + SETENVVAR("GNUSTEP_USERS_ROOT", currentWorkingDir); + SETENVVAR("HOMEPATH", currentWorkingDir); + + /* Windows amibtiously starts apps with the C library locale set to the + system locale rather than the "C" locale as per spec. Fixing here so + numbers don't behave strangely. + */ + setlocale(LC_ALL, "C"); +#endif + + // Need this because we're not using the default run loop's autorelease + // pool. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + OOLoggingInit(); + + @try + { + // dajt: allocate and set the NSApplication delegate manually because not + // using NIB to do this + controller = [[GameController alloc] init]; + + // Release anything allocated during the controller initialisation that + // is no longer required. + + for (i = 1; i < argc; i++) + { + if (strcmp("-load", argv[i]) == 0) + { + i++; + if (i < argc) + [controller setPlayerFileToLoad: [NSString stringWithCString: argv[i]]]; + } + } + + DESTROY(pool); + + // Call applicationDidFinishLaunching because NSApp is not running in + // GNUstep port. + [controller applicationDidFinishLaunching: nil]; + } + @catch (NSException *exception) + { + OOLogERR(kOOLogException, @"Root exception handler hit - terminating. This is an internal error, please report it. Exception name: %@, reason: %@", [exception name], [exception reason]); + return EXIT_FAILURE; + } +#endif + + // never reached + return 0; +}