Files
how-to-vulkan/ktx/tests/loadtests/appfwSDL/GLAppSDL.cpp
T
2026-06-14 19:09:18 +01:00

331 lines
9.2 KiB
C++

/* -*- 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
* @~English
*
* @brief GLAppSDL app class.
*/
#if defined(_WIN32)
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include "GL/glew.h"
#include "SDL3/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 SDL_PLATFORM_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);
// On SDL3 this defaults to 8. On SDL2 it was 0.
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0);
#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 SDL_PLATFORM_WINDOWS || SDL_PLATFORM_LINUX
SDL_SetHint(SDL_HINT_OPENGL_ES_DRIVER, "1");
#endif
#if SDL_PLATFORM_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
}
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
// CAUTION: Setting this to 0 (the default) on macOS causes loss of all touch events
// from a trackpad not just those corresponding to mouse clicks.
SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "1");
#if 0
const char* mt = SDL_GetHint(SDL_HINT_MOUSE_TOUCH_EVENTS);
SDL_Log("MOUSE_TOUCH_EVENTS = %s", mt);
const char* tm = SDL_GetHint(SDL_HINT_TOUCH_MOUSE_EVENTS);
SDL_Log("TOUCH_MOUSE_EVENTS = %s", tm);
const char* tto = SDL_GetHint(SDL_HINT_TRACKPAD_IS_TOUCH_ONLY);
SDL_Log("TRACKPAD_IS_TOUCH_ONLY = %s", tto);
#endif
pswMainWindow = SDL_CreateWindow(
szName,
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 SDL_PLATFORM_WINDOWS
// Set the application's 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 SDL_PLATFORM_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.
SDL_SharedObject* glewdll;
#if defined(_DEBUG)
glewdll = SDL_LoadObject("glew32d.dll");
// KTX-Software repo only contains non-debug library for x64 hence this.
if (glewdll == NULL) {
glewdll = SDL_LoadObject("glew32.dll");
}
#else
glewdll = SDL_LoadObject("glew32.dll");
#endif
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.
int actualWidth, actualHeight;
SDL_GetWindowSizeInPixels(pswMainWindow, &actualWidth, &actualHeight);
resizeWindow(actualWidth, actualHeight);
initializeFPSTimer();
return true;
}
void
GLAppSDL::finalize()
{
SDL_GL_DestroyContext(sgcGLContext);
}
bool
GLAppSDL::doEvent(SDL_Event* event)
{
switch (event->type) {
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
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(int width, int height)
{
w_width = width;
w_height = 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 SDL_PLATFORM_WINDOWS
// Windows specific code to use icon in module
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){
HWND hwnd = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(sdlWindow), SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
if (hwnd) {
::SetClassLongPtr(hwnd, GCLP_HICON, reinterpret_cast<LONG_PTR>(icon));
}
}
}
#endif
// }