From 09726348b35a315a9ba1ea24a17c859de37ced80 Mon Sep 17 00:00:00 2001 From: Aloshi Date: Sun, 18 Aug 2013 12:17:24 -0500 Subject: [PATCH] Moved to SDL2. Renderer on the Pi doesn't work at the moment. --- CMake/Packages/FindSDL2.cmake | 163 ++++++++++++++++++++++++++++++++++ CMakeLists.txt | 8 +- src/AudioManager.cpp | 4 + src/InputConfig.cpp | 5 +- src/InputConfig.h | 5 +- src/InputManager.cpp | 8 +- src/Renderer.h | 3 +- src/Renderer_init_sdlgl.cpp | 92 ++++++++++--------- 8 files changed, 228 insertions(+), 60 deletions(-) create mode 100644 CMake/Packages/FindSDL2.cmake diff --git a/CMake/Packages/FindSDL2.cmake b/CMake/Packages/FindSDL2.cmake new file mode 100644 index 000000000..236d6b4b9 --- /dev/null +++ b/CMake/Packages/FindSDL2.cmake @@ -0,0 +1,163 @@ +# Locate SDL2 library +# This module defines +# SDL2_LIBRARY, the name of the library to link against +# SDL2_FOUND, if false, do not try to link to SDL2 +# SDL2_INCLUDE_DIR, where to find SDL.h +# +# This module responds to the the flag: +# SDL2_BUILDING_LIBRARY +# If this is defined, then no SDL2main will be linked in because +# only applications need main(). +# Otherwise, it is assumed you are building an application and this +# module will attempt to locate and set the the proper link flags +# as part of the returned SDL2_LIBRARY variable. +# +# Don't forget to include SDLmain.h and SDLmain.m your project for the +# OS X framework based version. (Other versions link to -lSDL2main which +# this module will try to find on your behalf.) Also for OS X, this +# module will automatically add the -framework Cocoa on your behalf. +# +# +# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration +# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library +# (SDL2.dll, libsdl2.so, SDL2.framework, etc). +# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. +# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value +# as appropriate. These values are used to generate the final SDL2_LIBRARY +# variable, but when these values are unset, SDL2_LIBRARY does not get created. +# +# +# $SDL2DIR is an environment variable that would +# correspond to the ./configure --prefix=$SDL2DIR +# used in building SDL2. +# l.e.galup 9-20-02 +# +# Modified by Eric Wing. +# Added code to assist with automated building by using environmental variables +# and providing a more controlled/consistent search behavior. +# Added new modifications to recognize OS X frameworks and +# additional Unix paths (FreeBSD, etc). +# Also corrected the header search path to follow "proper" SDL guidelines. +# Added a search for SDL2main which is needed by some platforms. +# Added a search for threads which is needed by some platforms. +# Added needed compile switches for MinGW. +# +# On OSX, this will prefer the Framework version (if found) over others. +# People will have to manually change the cache values of +# SDL2_LIBRARY to override this selection or set the CMake environment +# CMAKE_INCLUDE_PATH to modify the search paths. +# +# Note that the header path has changed from SDL2/SDL.h to just SDL.h +# This needed to change because "proper" SDL convention +# is #include "SDL.h", not . This is done for portability +# reasons because not all systems place things in SDL2/ (see FreeBSD). + +#============================================================================= +# Copyright 2003-2009 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +SET(SDL2_SEARCH_PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +FIND_PATH(SDL2_INCLUDE_DIR SDL.h + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES include/SDL2 include + PATHS ${SDL2_SEARCH_PATHS} +) + +FIND_LIBRARY(SDL2_LIBRARY_TEMP + NAMES SDL2 + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} +) + +IF(NOT SDL2_BUILDING_LIBRARY) + IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") + # Non-OS X framework versions expect you to also dynamically link to + # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms + # seem to provide SDL2main for compatibility even though they don't + # necessarily need it. + FIND_LIBRARY(SDL2MAIN_LIBRARY + NAMES SDL2main + HINTS + $ENV{SDL2DIR} + PATH_SUFFIXES lib64 lib + PATHS ${SDL2_SEARCH_PATHS} + ) + ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") +ENDIF(NOT SDL2_BUILDING_LIBRARY) + +# SDL2 may require threads on your system. +# The Apple build may not need an explicit flag because one of the +# frameworks may already provide it. +# But for non-OSX systems, I will use the CMake Threads package. +IF(NOT APPLE) + FIND_PACKAGE(Threads) +ENDIF(NOT APPLE) + +# MinGW needs an additional library, mwindows +# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows +# (Actually on second look, I think it only needs one of the m* libraries.) +IF(MINGW) + SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") +ENDIF(MINGW) + +IF(SDL2_LIBRARY_TEMP) + # For SDL2main + IF(NOT SDL2_BUILDING_LIBRARY) + IF(SDL2MAIN_LIBRARY) + SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(SDL2MAIN_LIBRARY) + ENDIF(NOT SDL2_BUILDING_LIBRARY) + + # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. + # CMake doesn't display the -framework Cocoa string in the UI even + # though it actually is there if I modify a pre-used variable. + # I think it has something to do with the CACHE STRING. + # So I use a temporary variable until the end so I can set the + # "real" variable in one-shot. + IF(APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") + ENDIF(APPLE) + + # For threads, as mentioned Apple doesn't need this. + # In fact, there seems to be a problem if I used the Threads package + # and try using this line, so I'm just skipping it entirely for OS X. + IF(NOT APPLE) + SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) + ENDIF(NOT APPLE) + + # For MinGW library + IF(MINGW) + SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) + ENDIF(MINGW) + + # Set the final string here so the GUI reflects the final state. + SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") + # Set the temp variable to INTERNAL so it is not seen in the CMake GUI + SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") +ENDIF(SDL2_LIBRARY_TEMP) + +INCLUDE(FindPackageHandleStandardArgs) + +FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/CMakeLists.txt b/CMakeLists.txt index f73a555a7..597e3b670 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,7 @@ else() endif() find_package(FreeType REQUIRED) find_package(FreeImage REQUIRED) -find_package(SDL REQUIRED) +find_package(SDL2 REQUIRED) find_package(Boost REQUIRED COMPONENTS system filesystem) find_package(Eigen3 REQUIRED) @@ -81,7 +81,7 @@ add_definitions(-DEIGEN_DONT_ALIGN) set(ES_INCLUDE_DIRS ${FREETYPE_INCLUDE_DIRS} ${FreeImage_INCLUDE_DIRS} - ${SDL_INCLUDE_DIR} + ${SDL2_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${EIGEN3_INCLUDE_DIR} ) @@ -252,8 +252,8 @@ set(ES_LIBRARIES ${Boost_LIBRARIES} ${FREETYPE_LIBRARIES} ${FreeImage_LIBRARIES} - ${SDL_LIBRARY} - ${SDLMAIN_LIBRARY} + ${SDL2_LIBRARY} + ${SDL2MAIN_LIBRARY} ) #add ALSA for Linux diff --git a/src/AudioManager.cpp b/src/AudioManager.cpp index 47887ede7..2c6af704b 100644 --- a/src/AudioManager.cpp +++ b/src/AudioManager.cpp @@ -14,6 +14,9 @@ void AudioManager::mixAudio(void *unused, Uint8 *stream, int len) { bool stillPlaying = false; + //initialize the buffer to "silence" + SDL_memset(stream, 0, len); + //iterate through all our samples std::vector>::const_iterator soundIt = sSoundVector.cbegin(); while (soundIt != sSoundVector.cend()) @@ -40,6 +43,7 @@ void AudioManager::mixAudio(void *unused, Uint8 *stream, int len) //advance to next sound ++soundIt; } + //we have processed all samples. check if some will still be playing if (!stillPlaying) { //no. pause audio till a Sound::play() wakes us up diff --git a/src/InputConfig.cpp b/src/InputConfig.cpp index 0036e3d11..e7c4add6e 100644 --- a/src/InputConfig.cpp +++ b/src/InputConfig.cpp @@ -4,6 +4,7 @@ #include #include #include "Log.h" +#include "InputManager.h" //some util functions std::string inputTypeToString(InputType type) @@ -48,7 +49,7 @@ std::string toLower(std::string str) } //end util functions -InputConfig::InputConfig(int deviceId) : mDeviceId(deviceId) +InputConfig::InputConfig(int deviceId, const std::string& deviceName) : mDeviceId(deviceId), mDeviceName(deviceName) { mPlayerNum = -1; } @@ -162,7 +163,7 @@ void InputConfig::writeToXML(pugi::xml_node parent) cfg.append_attribute("type") = "keyboard"; }else{ cfg.append_attribute("type") = "joystick"; - cfg.append_attribute("deviceName") = SDL_JoystickName(mDeviceId); + cfg.append_attribute("deviceName") = mDeviceName.c_str(); } typedef std::map::iterator it_type; diff --git a/src/InputConfig.h b/src/InputConfig.h index 99d13e84b..01957d3cd 100644 --- a/src/InputConfig.h +++ b/src/InputConfig.h @@ -72,7 +72,7 @@ public: stream << "Hat " << id << " " << getHatDir(value); break; case TYPE_KEY: - stream << "Key " << SDL_GetKeyName((SDLKey)id); + stream << "Key " << SDL_GetKeyName((SDL_Keycode)id); break; default: stream << "Input to string error"; @@ -86,7 +86,7 @@ public: class InputConfig { public: - InputConfig(int deviceId); + InputConfig(int deviceId, const std::string& deviceName); void clear(); void mapInput(const std::string& name, Input input); @@ -109,6 +109,7 @@ public: private: std::map mNameMap; const int mDeviceId; + const std::string mDeviceName; int mPlayerNum; }; diff --git a/src/InputManager.cpp b/src/InputManager.cpp index fb76e48b1..2b13fc992 100644 --- a/src/InputManager.cpp +++ b/src/InputManager.cpp @@ -126,7 +126,7 @@ Uint32 InputManager::devicePollingCallback(Uint32 interval, void* param) event.user.code = SDL_USEREVENT_POLLDEVICES; event.user.data1 = nullptr; event.user.data2 = nullptr; - if (SDL_PushEvent(&event) != 0) { + if (!SDL_PushEvent(&event)) { LOG(LogError) << "InputManager::devicePollingCallback - SDL event queue is full!"; } @@ -151,7 +151,7 @@ void InputManager::init() for(int i = 0; i < mNumJoysticks; i++) { mJoysticks[i] = SDL_JoystickOpen(i); - mInputConfigs[i] = new InputConfig(i); + mInputConfigs[i] = new InputConfig(i, SDL_JoystickName(mJoysticks[i])); for(int k = 0; k < SDL_JoystickNumAxes(mJoysticks[i]); k++) { @@ -159,7 +159,7 @@ void InputManager::init() } } - mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD); + mKeyboardInputConfig = new InputConfig(DEVICE_KEYBOARD, "Keyboard"); SDL_JoystickEventState(SDL_ENABLE); @@ -370,7 +370,7 @@ void InputManager::loadConfig() std::string devName = node.attribute("deviceName").as_string(); for(int i = 0; i < mNumJoysticks; i++) { - if(!configuredDevice[i] && SDL_JoystickName(i) == devName) + if(!configuredDevice[i] && SDL_JoystickName(mJoysticks[i]) == devName) { mInputConfigs[i]->loadFromXML(node, mNumPlayers); mNumPlayers++; diff --git a/src/Renderer.h b/src/Renderer.h index fc5b64dd6..ed7134a3e 100644 --- a/src/Renderer.h +++ b/src/Renderer.h @@ -12,7 +12,8 @@ class GuiComponent; class Font; //The Renderer provides several higher-level functions for drawing (rectangles, text, etc.). -//Defined in multiple files - Renderer.cpp has the GuiComponent stuff, Renderer_draw_* includes renderer-specific drawing implementations, and Renderer_init_* includes renderer-specific init/deinit. +//Renderer_draw_gl.cpp has most of the higher-level functions and wrappers. +//Renderer_init_*.cpp has platform-specific renderer initialziation/deinitialziation code. (e.g. the Raspberry Pi sets up dispmanx/OpenGL ES) namespace Renderer { bool init(int w, int h); diff --git a/src/Renderer_init_sdlgl.cpp b/src/Renderer_init_sdlgl.cpp index 254d890c0..6a362c7ac 100644 --- a/src/Renderer_init_sdlgl.cpp +++ b/src/Renderer_init_sdlgl.cpp @@ -1,16 +1,9 @@ #include "Renderer.h" #include #include "platform.h" - -#ifdef _WINDOWS_ - #include -#endif - #include GLHEADER - #include "Font.h" #include -#include "InputManager.h" #include "Log.h" #include "ImageIO.h" #include "../data/Resources.h" @@ -27,9 +20,10 @@ namespace Renderer unsigned int getScreenWidth() { return display_width; } unsigned int getScreenHeight() { return display_height; } - SDL_Surface* sdlScreen = NULL; + SDL_Window* sdlWindow = NULL; + SDL_GLContext sdlContext = NULL; - bool createSurface() //unsigned int display_width, unsigned int display_height) + bool createSurface() { LOG(LogInfo) << "Creating surface..."; @@ -39,53 +33,54 @@ namespace Renderer return false; } - //ATM it is best to just leave the window icon alone on windows. - //When compiled as a Windows application, ES at least has an icon in the taskbar - //The method below looks pretty shite as alpha isn't taken into account... -#ifndef WIN32 - //try loading PNG from memory - size_t width = 0; - size_t height = 0; - std::vector rawData = ImageIO::loadFromMemoryRGBA32(ES_logo_32_png_data, ES_logo_32_png_size, width, height); - if (!rawData.empty()) { - //SDL interprets each pixel as a 32-bit number, so our masks must depend on the endianness (byte order) of the machine -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - Uint32 rmask = 0xff000000; Uint32 gmask = 0x0000ff00; Uint32 bmask = 0x00ff0000; Uint32 amask = 0x000000ff; -#else - Uint32 rmask = 0x000000ff; Uint32 gmask = 0x00ff0000; Uint32 bmask = 0x0000ff00; Uint32 amask = 0xff000000; -#endif - //try creating SDL surface from logo data - SDL_Surface * logoSurface = SDL_CreateRGBSurfaceFrom((void *)rawData.data(), width, height, 32, width*4, rmask, gmask, bmask, amask); - if (logoSurface != nullptr) { - //change window icon. this sucks atm, but there's nothing better we can do. SDL 1.3 or 2.0 should sort this out... - SDL_WM_SetIcon(logoSurface, nullptr); - } - } -#endif - - SDL_WM_SetCaption("EmulationStation", "EmulationStation"); - 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, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - //SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1); //vsync - sdlScreen = SDL_SetVideoMode(display_width, display_height, 16, SDL_OPENGL | (Settings::getInstance()->getBool("WINDOWED") ? 0 : SDL_FULLSCREEN)); + //SDL_GL_SetSwapInterval(1); //0 for immediate updates, 1 for updates synchronized with the vertical retrace, -1 for late swap tearing - if(sdlScreen == NULL) + sdlWindow = SDL_CreateWindow("EmulationStation", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + display_width, display_height, + SDL_WINDOW_OPENGL | (Settings::getInstance()->getBool("WINDOWED") ? 0 : SDL_WINDOW_FULLSCREEN)); + + if(sdlWindow == NULL) { - LOG(LogError) << "Error creating SDL video surface!"; + LOG(LogError) << "Error creating SDL window!"; return false; } + LOG(LogInfo) << "Created window successfully."; + + //set an icon for the window + size_t width = 0; + size_t height = 0; + std::vector rawData = ImageIO::loadFromMemoryRGBA32(ES_logo_32_png_data, ES_logo_32_png_size, width, height); + if (!rawData.empty()) + { + //SDL interprets each pixel as a 32-bit number, so our masks must depend on the endianness (byte order) of the machine + #if SDL_BYTEORDER == SDL_BIG_ENDIAN + Uint32 rmask = 0xff000000; Uint32 gmask = 0x00ff0000; Uint32 bmask = 0x0000ff00; Uint32 amask = 0x000000ff; + #else + Uint32 rmask = 0x000000ff; Uint32 gmask = 0x0000ff00; Uint32 bmask = 0x00ff0000; Uint32 amask = 0xff000000; + #endif + //try creating SDL surface from logo data + SDL_Surface * logoSurface = SDL_CreateRGBSurfaceFrom((void *)rawData.data(), width, height, 32, width * 4, rmask, gmask, bmask, amask); + if (logoSurface != NULL) + { + SDL_SetWindowIcon(sdlWindow, logoSurface); + SDL_FreeSurface(logoSurface); + } + } + + sdlContext = SDL_GL_CreateContext(sdlWindow); + //usually display width/height are not specified, i.e. zero, which SDL automatically takes as "native resolution" //so, since other things rely on the size of the screen (damn currently unnormalized coordinate system), we set it here - //even though the system was already initialized - display_width = sdlScreen->w; - display_height = sdlScreen->h; - - LOG(LogInfo) << "Created surface successfully."; + //even though the system was already initialized - this makes sure it gets reinitialized to the original resolution when we return from a game + //display_width = sdlWindow->w; + //display_height = sdlWindow->h; //hide mouse cursor initialCursorState = SDL_ShowCursor(0) == 1; @@ -95,14 +90,17 @@ namespace Renderer void swapBuffers() { - SDL_GL_SwapBuffers(); + SDL_GL_SwapWindow(sdlWindow); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void destroySurface() { - SDL_FreeSurface(sdlScreen); - sdlScreen = NULL; + SDL_GL_DeleteContext(sdlContext); + sdlContext = NULL; + + SDL_DestroyWindow(sdlWindow); + sdlWindow = NULL; //show mouse cursor SDL_ShowCursor(initialCursorState);