/* -*- tab-width: 4; -*- */ /* vi: set sw=2 ts=4 expandtab: */ /* $Id: ac63511da134f2c25a9e1da86a36bc27b6198ae3 $ */ /* * Copyright 2015-2020 Mark Callow. * SPDX-License-Identifier: Apache-2.0 */ /** * @internal * @file GLAppSDLGL.cpp * @~English * * @brief GLAppSDL app class. */ #if defined(_WIN32) #if _MSC_VER < 1900 #define snprintf _snprintf #endif #define _CRT_SECURE_NO_WARNINGS #include "GL/glew.h" #include "SDL2/SDL_loadso.h" #else #define GL_GLEXT_PROTOTYPES 1 #include "GL/glcorearb.h" // for glEnable and FRAMEBUFFER_RGB #endif #include <stdio.h> #include <iomanip> #include <sstream> #include "GLAppSDL.h" #if __WINDOWS__ void setWindowsIcon(SDL_Window *sdlWindow); #endif bool GLAppSDL::initialize(Args& args) { if (!AppBaseSDL::initialize(args)) return false; SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profile); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion); #if !defined(EMSCRIPTEN) if (majorVersion >= 3) SDL_GL_SetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, 1); #endif #if defined(DEBUG) && !defined(EMSCRIPTEN) SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); #endif if (profile == SDL_GL_CONTEXT_PROFILE_ES) { #if 0 int numVideoDrivers = SDL_GetNumVideoDrivers(); int i; const char** drivers; drivers = (const char**)SDL_malloc(sizeof(const char*) * numVideoDrivers); for (i = 0; i < numVideoDrivers; i++) { drivers[i] = SDL_GetVideoDriver(i); } #endif // Only the indicated platforms pay attention to these hints // but they could be set on any platform. #if __WINDOWS__ || __LINUX__ SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1"); #endif #if __WINDOWS__ // If using ANGLE copied from Chrome should set to "d3dcompiler_46.dll" // Should set value via compiler -D definition from gyp file. SDL_SetHint(SDL_HINT_VIDEO_WIN_D3DCOMPILER, "none"); #endif } #if __MACOSX__ SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1"); SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1"); #endif pswMainWindow = SDL_CreateWindow( szName, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, w_width, w_height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE ); if (pswMainWindow == NULL) { (void)SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, szName, SDL_GetError(), NULL); return false; } #if __WINDOWS__ // Set the applications own icon in place of the Windows default set by SDL. // Needs to be done here to avoid change being visible. setWindowsIcon(pswMainWindow); #endif sgcGLContext = SDL_GL_CreateContext(pswMainWindow); // Work around bug in SDL. It returns a 2.x context when 3.x is requested. // It does though internally record an error. const char* error = SDL_GetError(); if (sgcGLContext == NULL || (error[0] != '\0' && majorVersion >= 3 && (profile == SDL_GL_CONTEXT_PROFILE_CORE || profile == SDL_GL_CONTEXT_PROFILE_COMPATIBILITY)) ) { (void)SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, szName, SDL_GetError(), NULL); return false; } #if __WINDOWS__ if (profile != SDL_GL_CONTEXT_PROFILE_ES) { // No choice but to use GLEW for GL on Windows; there is no .lib with static // bindings. For ES we use one of the hardware vendor SDKs all of which have // static bindings. // TODO: Figure out how to support {GLX,WGL}_EXT_create_context_es2_profile // were there are no static bindings. Need a GLEW equivalent for ES and // different compile options. Perhaps can borrow function loading stuff // from SDL's testgles2.c. // So one build of this library can be linked in to applications using GLEW and // applications not using GLEW, do not call any GLEW functions directly. // Call via queried function pointers. void* glewdll = SDL_LoadObject("glew32.dll"); if (glewdll == NULL) { std::string sName(szName); (void)SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, szName, SDL_GetError(), NULL); return false; } typedef GLenum(GLEWAPIENTRY PFNGLEWINIT)(void); typedef const GLubyte * GLEWAPIENTRY PFNGLEWGETERRORSTRING(GLenum error); PFNGLEWINIT* pGlewInit; PFNGLEWGETERRORSTRING* pGlewGetErrorString = nullptr; bool loadError = true; #define STR(s) #s #if defined(_M_IX86) /* Win32 GLEW uses __stdcall. */ #define DNAMESTR(x,n) STR(_##x##@##n) #else /* x64 uses __cdecl. */ #define DNAMESTR(x,n) STR(x) #endif pGlewInit = (PFNGLEWINIT*)SDL_LoadFunction(glewdll, DNAMESTR(glewInit,0)); if (pGlewInit != NULL) { pGlewGetErrorString = (PFNGLEWGETERRORSTRING*)SDL_LoadFunction( glewdll, DNAMESTR(glewGetErrorString,4)); if (pGlewGetErrorString != NULL) { loadError = false; } } if (loadError) { std::string sName(szName); (void)SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, szName, SDL_GetError(), NULL); return false; } int iResult = pGlewInit(); if (iResult != GLEW_OK) { std::string sName(szName); (void)SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, szName, (const char*)pGlewGetErrorString(iResult), NULL); return false; } } #endif int srgb; SDL_GL_GetAttribute(SDL_GL_FRAMEBUFFER_SRGB_CAPABLE, &srgb); if (srgb && profile != SDL_GL_CONTEXT_PROFILE_ES) glEnable(GL_FRAMEBUFFER_SRGB); // In case the window is created with a different size than specified. resizeWindow(); initializeFPSTimer(); return true; } void GLAppSDL::finalize() { SDL_GL_DeleteContext(sgcGLContext); } int GLAppSDL::doEvent(SDL_Event* event) { switch (event->type) { case SDL_WINDOWEVENT: switch (event->window.event) { case SDL_WINDOWEVENT_SIZE_CHANGED: // Size given in event is in 'points' on some platforms. // Resize window will figure out the drawable pixel size. resizeWindow(/*event->window.data1, event->window.data2*/); return 0; } break; } return AppBaseSDL::doEvent(event); } void GLAppSDL::drawFrame(uint32_t /*msTicks*/) { SDL_GL_SwapWindow(pswMainWindow); } void GLAppSDL::windowResized() { // Derived class can override as necessary. } void GLAppSDL::resizeWindow() { SDL_GL_GetDrawableSize(pswMainWindow, &w_width, &w_height); windowResized(); } void GLAppSDL::onFPSUpdate() { // Using onFPSUpdate avoids rewriting the title every frame. setWindowTitle(); } #if 0 void GLAppSDL::setAppTitle(const char* const szExtra) { appTitle = name(); if (szExtra != NULL && szExtra[0] != '\0') { appTitle += ": "; appTitle += szExtra; } setWindowTitle(); } void GLAppSDL::setWindowTitle(const char* const szExtra) { std::stringstream ss; ss << std::fixed << std::setprecision(2) << lastFrameTime << "ms (" << fpsCounter.lastFPS << " fps)" << " - " << szName; if (szExtra != NULL && szExtra[0] != '\0') { ss << ": " << szExtra; } SDL_SetWindowTitle(pswMainWindow, ss.str().c_str()); } void GLAppSDL::setWindowTitle() { SDL_SetWindowTitle(pswMainWindow, appTitle.c_str()); } #endif #if __WINDOWS__ // Windows specific code to use icon in module #define WIN32_LEAN_AND_MEAN #include <Windows.h> #include <SDL2/SDL_syswm.h> void setWindowsIcon(SDL_Window *sdlWindow) { HINSTANCE handle = ::GetModuleHandle(nullptr); // Identify icon by name rather than IDI_ macro to avoid having to // include application's resource.h. HICON icon = ::LoadIcon(handle, "MAIN_ICON");// MAKEINTRESOURCE(IDI_ICON1)); if (icon != nullptr){ SDL_SysWMinfo wminfo; SDL_VERSION(&wminfo.version); if (SDL_GetWindowWMInfo(sdlWindow, &wminfo) == 1){ HWND hwnd = wminfo.info.win.window; ::SetClassLongPtr(hwnd, GCLP_HICON, reinterpret_cast<LONG_PTR>(icon)); } } } #endif