2023-08-25 14:38:06 -07:00
# include "RendererBackend.h"
2024-03-23 18:41:26 -07:00
# include <assets/assets.h>
2023-08-25 14:38:06 -07:00
# include <vector>
# include "IconsForkAwesome.h"
# include "config.h"
# include <SDL_image.h>
# include <string>
# include "theme.h"
# include "imgui_stdlib.h"
# include "imgui_impl_sdl2.h"
# include "imgui_impl_opengl3.h"
# include "base85.h"
# include <thread>
# include "translation.h"
2024-03-23 18:41:26 -07:00
# include <log.hpp>
2023-08-25 14:38:06 -07:00
using std : : vector ;
2024-04-24 09:59:51 -07:00
# ifdef __EMSCRIPTEN__
extern " C " {
extern void get_size ( int32_t * x , int32_t * y ) ;
}
# endif
void RendererBackend : : on_resize ( ) {
# ifdef __EMSCRIPTEN__
int32_t x , y ;
get_size ( & x , & y ) ;
SDL_SetWindowSize ( window , ( int ) x , ( int ) y ) ;
# endif
}
static RendererBackend * renderer_backend ;
void RendererBackend : : resize_static ( ) {
renderer_backend - > resize_needed = true ;
}
void main_loop ( ) {
renderer_backend - > LoopFunction ( ) ;
# ifdef __EMSCRIPTEN__
if ( renderer_backend - > done ) {
renderer_backend - > BackendDeinit ( ) ;
emscripten_cancel_main_loop ( ) ;
}
# endif
}
void RendererBackend : : BackendDeinit ( ) {
ImGuiIO & io = ImGui : : GetIO ( ) ; ( void ) io ;
// Cleanup
ImGui_ImplOpenGL3_Shutdown ( ) ;
ImGui_ImplSDL2_Shutdown ( ) ;
ImGui : : DestroyContext ( ) ;
SDL_GL_DeleteContext ( gl_context ) ;
SDL_DestroyWindow ( window ) ;
IMG_Quit ( ) ;
SDL_Quit ( ) ;
free ( ( void * ) io . IniFilename ) ;
Deinit ( ) ;
renderer_backend = nullptr ;
}
void RendererBackend : : LoopFunction ( ) {
ImGuiIO & io = ImGui : : GetIO ( ) ; ( void ) io ;
if ( resize_needed ) {
on_resize ( ) ;
}
auto next_frame = std : : chrono : : steady_clock : : now ( ) + std : : chrono : : milliseconds ( 1000 / framerate ) ;
// Poll and handle events (inputs, window resize, etc.)
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
SDL_Event event ;
while ( SDL_PollEvent ( & event ) )
{
ImGui_ImplSDL2_ProcessEvent ( & event ) ;
if ( event . type = = SDL_QUIT )
done = true ;
if ( event . type = = SDL_WINDOWEVENT ) {
if ( event . window . event = = SDL_WINDOWEVENT_RESIZED ) {
window_width = event . window . data1 / scale ;
window_height = event . window . data2 / scale ;
//SDL_GetWindowSize(window, &window_width, &window_height);
}
if ( event . window . event = = SDL_WINDOWEVENT_DISPLAY_CHANGED ) {
UpdateScale ( ) ;
}
if ( event . window . event = = SDL_WINDOWEVENT_CLOSE & & event . window . windowID = = SDL_GetWindowID ( window ) ) {
done = true ;
}
}
if ( event . type = = SDL_DROPFILE ) {
if ( event . drop . file ! = NULL ) {
Drop ( std : : string ( event . drop . file ) ) ;
free ( event . drop . file ) ;
}
}
}
// Start the Dear ImGui frame
ImGui_ImplOpenGL3_NewFrame ( ) ;
ImGui_ImplSDL2_NewFrame ( ) ;
ImGui : : NewFrame ( ) ;
// Run the GUI
GuiFunction ( ) ;
// Rendering
ImGui : : Render ( ) ;
// Update the window size.
glViewport ( 0 , 0 , ( int ) io . DisplaySize . x , ( int ) io . DisplaySize . y ) ;
// Clear the screen.
glClearColor ( clear_color . x * clear_color . w , clear_color . y * clear_color . w , clear_color . z * clear_color . w , clear_color . w ) ;
glClear ( GL_COLOR_BUFFER_BIT ) ;
// Tell ImGui to render.
ImGui_ImplOpenGL3_RenderDrawData ( ImGui : : GetDrawData ( ) ) ;
2023-08-25 14:38:06 -07:00
2024-04-24 09:59:51 -07:00
// Update and Render additional Platform Windows
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
// For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly)
if ( io . ConfigFlags & ImGuiConfigFlags_ViewportsEnable )
{
SDL_Window * backup_current_window = SDL_GL_GetCurrentWindow ( ) ;
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext ( ) ;
ImGui : : UpdatePlatformWindows ( ) ;
ImGui : : RenderPlatformWindowsDefault ( ) ;
SDL_GL_MakeCurrent ( backup_current_window , backup_current_context ) ;
}
// Swap the buffers, and do VSync if enabled.
SDL_GL_SwapWindow ( window ) ;
// If not doing VSync, wait until the next frame needs to be rendered.
if ( ! vsync ) {
std : : this_thread : : sleep_until ( next_frame ) ;
}
}
2023-08-25 14:38:06 -07:00
struct FontData {
const char * data ;
const ImWchar * ranges ;
} ;
ImFont * add_font ( vector < FontData > data_vec , int size = 13 ) {
ImFont * font = nullptr ;
ImGuiIO & io = ImGui : : GetIO ( ) ;
for ( auto data : data_vec ) {
ImFontConfig font_cfg = ImFontConfig ( ) ;
font_cfg . SizePixels = size ;
font_cfg . OversampleH = font_cfg . OversampleV = 1 ;
font_cfg . PixelSnapH = true ;
if ( font_cfg . SizePixels < = 0.0f )
font_cfg . SizePixels = 13.0f * 1.0f ;
if ( font ! = nullptr ) {
font_cfg . DstFont = font ;
font_cfg . MergeMode = true ;
}
//font_cfg.EllipsisChar = (ImWchar)0x0085;
//font_cfg.GlyphOffset.y = 1.0f * IM_FLOOR(font_cfg.SizePixels / 13.0f); // Add +1 offset per 13 units
const char * ttf_compressed_base85 = data . data ;
const ImWchar * glyph_ranges = data . ranges ;
auto new_font = io . Fonts - > AddFontFromMemoryCompressedBase85TTF ( ttf_compressed_base85 , font_cfg . SizePixels , & font_cfg , glyph_ranges ) ;
if ( font = = nullptr ) font = new_font ;
}
{
ImFontConfig config ;
config . MergeMode = true ;
config . GlyphMinAdvanceX = size ;
config . SizePixels = size ;
config . DstFont = font ;
static const ImWchar icon_ranges [ ] = { ICON_MIN_FK , ICON_MAX_FK , 0 } ;
io . Fonts - > AddFontFromMemoryCompressedBase85TTF ( forkawesome_compressed_data_base85 , float ( size ) , & config , icon_ranges ) ;
}
return font ;
}
RendererBackend : : RendererBackend ( ) {
}
RendererBackend : : ~ RendererBackend ( ) {
}
void RendererBackend : : SetWindowTitle ( const char * title ) {
SDL_SetWindowTitle ( window , title ) ;
}
void RendererBackend : : GuiFunction ( ) {
// Do nothing by default.
}
2023-09-03 11:54:07 -07:00
void RendererBackend : : UpdateScale ( ) {
double prevScale = scale ;
const double defaultDPI =
# ifdef __APPLE__
72.0 ;
# else
96.0 ;
# endif
float dpi = defaultDPI ;
if ( SDL_GetDisplayDPI ( SDL_GetWindowDisplayIndex ( window ) , NULL , & dpi , NULL ) = = 0 ) {
scale = dpi / defaultDPI ;
} else {
2024-03-23 18:41:26 -07:00
WARNING . writeln ( " DPI couldn't be determined! " ) ;
2023-09-03 11:54:07 -07:00
scale = 1.0 ;
}
SDL_SetWindowSize ( window , window_width * scale , window_height * scale ) ;
AddFonts ( ) ;
}
void RendererBackend : : SetWindowSize ( int w , int h ) {
window_width = w ;
window_height = h ;
SDL_SetWindowSize ( window , w * scale , h * scale ) ;
}
void RendererBackend : : GetWindowsize ( int * w , int * h ) {
int ww , wh ;
SDL_GetWindowSize ( window , & ww , & wh ) ;
ww / = scale ;
wh / = scale ;
if ( w ) * w = ww ;
if ( h ) * h = wh ;
}
void RendererBackend : : AddFonts ( ) {
ImGui_ImplOpenGL3_DestroyFontsTexture ( ) ;
auto & io = ImGui : : GetIO ( ) ; ( void ) io ;
io . Fonts - > Clear ( ) ;
2024-03-23 18:41:26 -07:00
add_font ( vector < FontData > ( { FontData { notosans_regular_compressed_data_base85 , io . Fonts - > GetGlyphRangesDefault ( ) } , FontData { notosansjp_regular_compressed_data_base85 , io . Fonts - > GetGlyphRangesJapanese ( ) } } ) , 13 * scale ) ;
title = add_font ( vector < FontData > ( { FontData { notosans_thin_compressed_data_base85 , io . Fonts - > GetGlyphRangesDefault ( ) } , FontData { notosansjp_thin_compressed_data_base85 , io . Fonts - > GetGlyphRangesJapanese ( ) } } ) , 48 * scale ) ;
2023-09-03 11:54:07 -07:00
ImGui_ImplOpenGL3_CreateFontsTexture ( ) ;
}
2024-04-24 10:05:02 -07:00
# ifdef __EMSCRIPTEN__
2024-04-24 09:59:51 -07:00
static EM_BOOL resize_callback ( int event_type , const EmscriptenUiEvent * event , void * userdata ) {
RendererBackend : : resize_static ( ) ;
}
2024-04-24 10:05:02 -07:00
# endif
2023-08-25 14:38:06 -07:00
int RendererBackend : : Run ( ) {
setlocale ( LC_ALL , " " ) ;
bindtextdomain ( " neko_player " , LOCALE_DIR ) ;
textdomain ( " neko_player " ) ;
2024-03-23 18:41:26 -07:00
DEBUG . writefln ( " Loaded locale '%s' from '%s'... " , CURRENT_LANGUAGE , LOCALE_DIR ) ;
DEBUG . writefln ( " Locale name: %s " , _TR_CTX ( " Language name " , " English (United States) " ) ) ;
2023-08-25 14:38:06 -07:00
bool enable_kms = std : : getenv ( " LAP_KMS " ) ! = nullptr ;
SDL_SetHint ( SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR , " false " ) ;
SDL_SetHint ( SDL_HINT_APP_NAME , NAME ) ;
// Setup SDL
if ( SDL_Init ( SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER ) ! = 0 )
{
2024-03-23 18:41:26 -07:00
ERROR . writefln ( " Error: %s " , SDL_GetError ( ) ) ;
2023-08-25 14:38:06 -07:00
return - 1 ;
}
if ( std : : string ( SDL_GetCurrentVideoDriver ( ) ) = = " KMSDRM " ) {
enable_kms = true ;
}
IMG_Init ( IMG_INIT_PNG | IMG_INIT_WEBP ) ;
prefPath = SDL_GetPrefPath ( " Catmeow72 " , NAME ) ;
Theme : : prefPath = prefPath ;
// Decide GL+GLSL versions
# if defined(IMGUI_IMPL_OPENGL_ES2)
// GL ES 2.0 + GLSL 100
const char * glsl_version = " #version 100 " ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_FLAGS , 0 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_ES ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , 2 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , 0 ) ;
# elif defined(__APPLE__)
// GL 3.2 Core + GLSL 150
const char * glsl_version = " #version 150 " ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_FLAGS , SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG ) ; // Always required on Mac
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_CORE ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , 3 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , 2 ) ;
# else
// GL 3.0 + GLSL 130
const char * glsl_version = " #version 130 " ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_FLAGS , 0 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_CORE ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , 3 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , 0 ) ;
# endif
// From 2.0.18: Enable native IME.
# ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint ( SDL_HINT_IME_SHOW_UI , " 1 " ) ;
# endif
// Create window with graphics context
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER , 1 ) ;
SDL_GL_SetAttribute ( SDL_GL_DEPTH_SIZE , 24 ) ;
SDL_GL_SetAttribute ( SDL_GL_STENCIL_SIZE , 8 ) ;
2023-10-16 10:44:25 -07:00
SDL_WindowFlags window_flags = ( SDL_WindowFlags ) ( SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN ) ;
2023-08-25 14:38:06 -07:00
window = SDL_CreateWindow ( NAME , SDL_WINDOWPOS_CENTERED , SDL_WINDOWPOS_CENTERED , window_width , window_height , window_flags ) ;
SDL_SetWindowMinimumSize ( window , window_width , window_height ) ;
if ( enable_kms ) {
SDL_SetWindowFullscreen ( window , SDL_WINDOW_FULLSCREEN_DESKTOP ) ;
}
2023-10-16 10:44:25 -07:00
SDL_EventState ( SDL_DROPFILE , SDL_ENABLE ) ;
2023-08-25 14:38:06 -07:00
const vector < unsigned char > icon_data = DecodeBase85 ( icon_compressed_data_base85 ) ;
SDL_Surface * icon = IMG_Load_RW ( SDL_RWFromConstMem ( icon_data . data ( ) , icon_data . size ( ) ) , 1 ) ;
SDL_SetWindowIcon ( window , icon ) ;
2024-04-24 09:59:51 -07:00
gl_context = SDL_GL_CreateContext ( window ) ;
2023-08-25 14:38:06 -07:00
SDL_GL_MakeCurrent ( window , gl_context ) ;
// Setup Dear ImGui context
IMGUI_CHECKVERSION ( ) ;
ImGui : : CreateContext ( ) ;
ImGuiIO & io = ImGui : : GetIO ( ) ; ( void ) io ;
io . ConfigFlags | = ImGuiConfigFlags_NavEnableKeyboard ; // Enable Keyboard Controls
io . ConfigFlags | = ImGuiConfigFlags_NavEnableGamepad ; // Enable Gamepad Controls
io . ConfigFlags | = ImGuiConfigFlags_DockingEnable ;
//io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
io . IniFilename = strdup ( ( std : : string ( prefPath ) + " imgui.ini " ) . c_str ( ) ) ;
if ( enable_kms ) {
io . MouseDrawCursor = true ;
}
//io.ConfigViewportsNoAutoMerge = true;
//io.ConfigViewportsNoTaskBarIcon = true;
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplSDL2_InitForOpenGL ( window , gl_context ) ;
ImGui_ImplOpenGL3_Init ( glsl_version ) ;
// Load Fonts
// - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them.
// - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple.
// - If the file cannot be loaded, the function will return a nullptr. Please handle those errors in your application (e.g. use an assertion, or display an error and quit).
// - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call.
// - Use '#define IMGUI_ENABLE_FREETYPE' in your imconfig file to use Freetype for higher quality font rendering.
// - Read 'docs/FONTS.md' for more instructions and details.
// - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ !
//io.Fonts->AddFontDefault();
//io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\segoeui.ttf", 18.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f);
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
//IM_ASSERT(font != nullptr);
2023-09-03 11:54:07 -07:00
UpdateScale ( ) ;
2023-08-25 14:38:06 -07:00
theme = new Theme ( false ) ;
userdir = std : : getenv (
# ifdef _WIN32
" UserProfile "
# else
" HOME "
# endif
) ;
2024-04-24 09:59:51 -07:00
# ifndef __EMSCRIPTEN__
2023-08-25 14:38:06 -07:00
SDL_GL_SetSwapInterval ( vsync ? 1 : 0 ) ;
2024-04-24 09:59:51 -07:00
# endif
2023-08-25 14:38:06 -07:00
theme - > Apply ( accent_color ) ;
Init ( ) ;
2023-10-16 10:44:25 -07:00
SDL_ShowWindow ( window ) ;
2024-04-24 09:59:51 -07:00
renderer_backend = this ;
2023-08-25 14:38:06 -07:00
# ifdef __EMSCRIPTEN__
// For an Emscripten build we are disabling file-system access, so let's not attempt to do a fopen() of the imgui.ini file.
// You may manually call LoadIniSettingsFromMemory() to load settings from your own storage.
io . IniFilename = nullptr ;
2024-04-24 09:59:51 -07:00
emscripten_set_resize_callback ( EMSCRIPTEN_EVENT_TARGET_WINDOW , 0 , 0 , resize_callback ) ;
emscripten_set_main_loop ( & main_loop , 0 , 1 ) ;
2023-08-25 14:38:06 -07:00
# else
while ( ! done )
{
2024-04-24 09:59:51 -07:00
LoopFunction ( ) ;
2023-08-25 14:38:06 -07:00
}
2024-04-24 09:59:51 -07:00
BackendDeinit ( ) ;
2023-08-25 14:38:06 -07:00
# endif
return 0 ;
}
void RendererBackend : : Init ( ) {
// Do nothing by default.
}
void RendererBackend : : Deinit ( ) {
// Do nothing by default.
2023-10-16 10:44:25 -07:00
}
void RendererBackend : : Drop ( std : : string file ) {
// Do nothing by default.
2023-08-25 14:38:06 -07:00
}