2026-05-11 23:06:39 +03:00
/*
* InfiniPaint
* Copyright ( C ) 2025 - 2026 Yousef Khadadeh
*
* 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 3 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 , see < https : //www.gnu.org/licenses/>.
*/
2026-04-04 17:52:43 +03:00
# include "CustomEvents.hpp"
2025-07-06 20:43:53 +03:00
# include "Helpers/SCollision.hpp"
# include "Helpers/StringHelpers.hpp"
2025-10-01 17:54:23 +03:00
# include "Helpers/FileDownloader.hpp"
2026-04-23 20:56:11 +03:00
# include "Screens/FileSelectScreen.hpp"
2026-04-26 09:54:58 +03:00
# include "Screens/DesktopDrawingProgramScreen.hpp"
2026-04-26 13:21:05 +03:00
# include "Screens/PhoneDrawingProgramScreen.hpp"
2026-03-01 12:09:27 +03:00
# include "VersionConstants.hpp"
2025-07-06 20:43:53 +03:00
# include "include/gpu/GpuTypes.h"
2026-02-24 17:21:41 +03:00
# include <SDL3/SDL_filesystem.h>
2025-07-06 20:43:53 +03:00
# include <SDL3/SDL_hints.h>
# include <SDL3/SDL_oldnames.h>
2025-09-29 17:05:53 +03:00
# include <chrono>
2025-07-06 20:43:53 +03:00
# include <filesystem>
2025-08-20 11:17:05 +03:00
# include <include/core/SkAlphaType.h>
# include <include/core/SkColorType.h>
2026-02-25 18:57:55 +03:00
# include <thread>
2025-07-06 20:43:53 +03:00
# ifdef USE_BACKEND_OPENGL
# ifndef __EMSCRIPTEN__
2025-08-09 10:45:39 +03:00
# ifdef USE_BACKEND_OPENGLES_3_0
# define GLAD_GLES2_IMPLEMENTATION
# include <glad/gles3_0.h>
# elif USE_BACKEND_OPENGL_2_1
# define GLAD_GL_IMPLEMENTATION
# include <glad/gl2_1.h>
2025-08-01 16:16:40 +03:00
# else
2025-08-09 10:45:39 +03:00
# define GLAD_GL_IMPLEMENTATION
# include <glad/gl3_3.h>
2025-08-01 16:16:40 +03:00
# endif
2025-07-06 20:43:53 +03:00
# endif
# endif
# include "cereal/archives/portable_binary.hpp"
# include <include/core/SkCanvas.h>
# include <include/core/SkSurface.h>
# include <include/core/SkSurfaceProps.h>
# ifdef USE_SKIA_BACKEND_GANESH
# include <include/gpu/ganesh/GrDirectContext.h>
# include <include/gpu/ganesh/SkSurfaceGanesh.h>
# include <include/gpu/ganesh/GrBackendSurface.h>
# elif USE_SKIA_BACKEND_GRAPHITE
# include <include/gpu/graphite/Surface.h>
# endif
# include <iostream>
# include <cereal/types/string.hpp>
# include "MainProgram.hpp"
# include <include/codec/SkPngDecoder.h>
# define SDL_MAIN_USE_CALLBACKS
# include <SDL3/SDL_main.h>
# include <SDL3/SDL_init.h>
# include <SDL3/SDL_pen.h>
# include <SDL3/SDL_events.h>
# include <SDL3/SDL_keyboard.h>
# include <SDL3/SDL_mouse.h>
# include <SDL3/SDL_scancode.h>
# include <SDL3/SDL_video.h>
# include <SDL3/SDL.h>
2026-02-04 19:15:58 +03:00
# include "DrawingProgram/DrawingProgramCache.hpp"
2025-07-06 20:43:53 +03:00
# include <fstream>
# ifdef __EMSCRIPTEN__
# include <SDL3/SDL_opengl.h>
# include <include/gpu/ganesh/gl/GrGLDirectContext.h>
# include <include/gpu/ganesh/gl/GrGLInterface.h>
# include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
# include <include/gpu/ganesh/gl/GrGLTypes.h>
# include <emscripten/html5.h>
# include <emscripten.h>
# include <emscripten/emscripten.h>
# elif USE_BACKEND_VULKAN
# ifdef USE_SKIA_BACKEND_GRAPHITE
# include "VulkanContext/GraphiteNativeVulkanWindowContext.h"
# elif USE_SKIA_BACKEND_GANESH
# include "VulkanContext/VulkanWindowContext.h"
# endif
# include <tools/window/DisplayParams.h>
# include <SDL3/SDL_vulkan.h>
# elif USE_BACKEND_OPENGL
# include <include/gpu/ganesh/gl/GrGLDirectContext.h>
# include <include/gpu/ganesh/gl/GrGLInterface.h>
# include <include/gpu/ganesh/gl/GrGLBackendSurface.h>
# include <include/gpu/ganesh/gl/GrGLTypes.h>
# endif
# ifdef __linux__
# include <unistd.h>
# include <pwd.h>
# elif _WIN32
# include <shlobj.h>
# endif
# include <Helpers/Logger.hpp>
# include "SwitchCWD.hpp"
2026-04-04 17:52:43 +03:00
# include "CustomEvents.hpp"
2025-07-06 20:43:53 +03:00
2025-09-07 09:50:56 +03:00
// Use dedicated graphics card on Windows
# ifdef _WIN32
extern " C "
{
__declspec ( dllexport ) unsigned long NvOptimusEnablement = 0x00000001 ;
__declspec ( dllexport ) int AmdPowerXpressRequestHighPerformance = 1 ;
}
# endif
2026-05-07 21:48:47 +03:00
# include "AndroidJNICalls.hpp"
2025-10-13 18:54:33 +03:00
# include <unicode/udata.h>
std : : string icudt ; // Put this in global space so that it isn't deallocated
2025-07-06 20:43:53 +03:00
struct MainStruct {
# ifdef USE_BACKEND_VULKAN
# ifdef USE_SKIA_BACKEND_GANESH
std : : unique_ptr < skwindow : : internal : : VulkanWindowContext > vulkanWindowContext ;
# elif USE_SKIA_BACKEND_GRAPHITE
std : : unique_ptr < skwindow : : internal : : GraphiteVulkanWindowContext > vulkanWindowContext ;
# endif
# elif USE_BACKEND_OPENGL
unsigned kStencilBits = 8 ;
SDL_GLContext gl_context ;
GLint defaultFBO = 0 ;
# endif
# ifdef USE_SKIA_BACKEND_GRAPHITE
# elif USE_SKIA_BACKEND_GANESH
sk_sp < GrDirectContext > ctx ;
GrBackendRenderTarget target ;
# endif
std : : unique_ptr < MainProgram > m ;
SDL_Window * window ;
std : : array < SDL_Cursor * , SDL_SYSTEM_CURSOR_COUNT > systemCursors ;
unsigned currentCursor = 0 ;
SkCanvas * canvas ;
SDL_Surface * iconSurface = nullptr ;
std : : string iconData ;
SDL_Cursor * hiddenCursor = nullptr ;
std : : filesystem : : path configPath ;
std : : filesystem : : path homePath ;
std : : ofstream logFile ;
2026-02-25 15:25:00 +03:00
std : : chrono : : steady_clock : : time_point lastRenderTimePoint = std : : chrono : : steady_clock : : now ( ) ;
2026-02-25 18:57:55 +03:00
std : : chrono : : steady_clock : : time_point lastUpdateTimePoint = std : : chrono : : steady_clock : : now ( ) ;
2026-02-25 15:25:00 +03:00
std : : chrono : : steady_clock : : duration frameRefreshDuration = std : : chrono : : steady_clock : : duration : : zero ( ) ;
2025-07-06 20:43:53 +03:00
} MainData ;
bool load_file_to_string ( std : : string & toRet , std : : string_view fileName ) {
// https://nullptr.org/cpp-read-file-into-string/
std : : ifstream file ( fileName . data ( ) , std : : ios : : in | std : : ios : : binary | std : : ios : : ate ) ;
if ( ! file . is_open ( ) ) {
std : : cout < < " [load_file_to_string] Could not open file " < < fileName < < std : : endl ;
return false ;
}
size_t fileSize ;
auto tellgResult = file . tellg ( ) ;
if ( tellgResult = = - 1 ) {
std : : cout < < " [load_file_to_string] tellg failed for file " < < fileName < < std : : endl ;
return false ;
}
fileSize = static_cast < size_t > ( tellgResult ) ;
file . seekg ( 0 , std : : ios_base : : beg ) ;
toRet . resize ( fileSize ) ;
file . read ( toRet . data ( ) , fileSize ) ;
file . close ( ) ;
return true ;
}
2026-02-25 15:25:00 +03:00
void get_refresh_rate ( MainStruct & mS ) {
mS . frameRefreshDuration = std : : chrono : : seconds ( 0 ) ;
SDL_DisplayID displayID = SDL_GetDisplayForWindow ( mS . window ) ;
if ( displayID = = 0 )
return ;
const SDL_DisplayMode * displayMode = SDL_GetCurrentDisplayMode ( displayID ) ;
if ( ! displayMode )
return ;
if ( displayMode - > refresh_rate ! = 0.0f )
mS . frameRefreshDuration = std : : chrono : : microseconds ( static_cast < int > ( ( 1.0f / displayMode - > refresh_rate ) * 1000000.0f ) ) ;
}
2025-07-06 20:43:53 +03:00
void initialize_sdl ( MainStruct & mS , int wWidth , int wHeight ) {
2026-03-01 12:09:27 +03:00
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_NAME_STRING , " InfiniPaint " ) ;
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_VERSION_STRING , VersionConstants : : CURRENT_VERSION_STRING . c_str ( ) ) ;
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_IDENTIFIER_STRING , " com.infinipaint.infinipaint " ) ;
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_CREATOR_STRING , " Yousef Khadadeh (ErrorAtLine0) " ) ;
2026-05-13 19:56:14 +03:00
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_COPYRIGHT_STRING , " Copyright (C) 2025-2026 Yousef Khadadeh " ) ;
2026-03-01 12:09:27 +03:00
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_URL_STRING , " https://infinipaint.com/ " ) ;
SDL_SetAppMetadataProperty ( SDL_PROP_APP_METADATA_TYPE_STRING , " application " ) ;
2025-07-06 20:43:53 +03:00
SDL_SetHint ( SDL_HINT_APP_NAME , " InfiniPaint " ) ;
2026-05-15 17:38:42 +03:00
SDL_SetHint ( SDL_HINT_PEN_TOUCH_EVENTS , " 0 " ) ;
2025-07-06 20:43:53 +03:00
SDL_SetHint ( SDL_HINT_PEN_MOUSE_EVENTS , " 0 " ) ;
2026-02-27 16:16:51 +03:00
SDL_SetHint ( SDL_HINT_MOUSE_TOUCH_EVENTS , " 0 " ) ;
SDL_SetHint ( SDL_HINT_TOUCH_MOUSE_EVENTS , " 0 " ) ;
2026-04-27 17:20:16 +03:00
SDL_SetHint ( SDL_HINT_ANDROID_TRAP_BACK_BUTTON , " 1 " ) ;
2025-07-06 20:43:53 +03:00
SDL_SetHint ( SDL_HINT_EMSCRIPTEN_KEYBOARD_ELEMENT , " #canvas " ) ; // Ensures that SDL only grabs input when browser is focused on canvas
2026-03-04 12:15:24 +03:00
if ( ! SDL_Init ( SDL_INIT_VIDEO ) )
2025-07-06 20:43:53 +03:00
throw std : : runtime_error ( " [SDL_Init] " + std : : string ( SDL_GetError ( ) ) ) ;
Uint32 window_flags = SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN | SDL_WINDOW_HIGH_PIXEL_DENSITY ;
# ifdef USE_BACKEND_VULKAN
window_flags | = SDL_WINDOW_VULKAN ;
# elif USE_BACKEND_OPENGL
window_flags | = SDL_WINDOW_OPENGL ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_FLAGS , 0 ) ;
2025-08-09 10:45:39 +03:00
# if defined(__EMSCRIPTEN__) || defined(USE_BACKEND_OPENGLES_3_0)
2025-07-06 20:43:53 +03:00
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_ES ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , 3 ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , 0 ) ;
2025-08-09 10:45:39 +03:00
# elif defined(USE_BACKEND_OPENGL_2_1)
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_PROFILE_MASK , SDL_GL_CONTEXT_PROFILE_COMPATIBILITY ) ;
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MAJOR_VERSION , 2 ) ;
2025-08-01 16:16:40 +03:00
SDL_GL_SetAttribute ( SDL_GL_CONTEXT_MINOR_VERSION , 1 ) ;
2025-07-06 20:43:53 +03:00
# else
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 , 3 ) ;
# endif
SDL_GL_SetAttribute ( SDL_GL_DOUBLEBUFFER , 1 ) ;
SDL_GL_SetAttribute ( SDL_GL_DEPTH_SIZE , 0 ) ;
SDL_GL_SetAttribute ( SDL_GL_STENCIL_SIZE , mS . kStencilBits ) ;
SDL_GL_SetAttribute ( SDL_GL_ACCELERATED_VISUAL , 1 ) ;
# endif
mS . window = SDL_CreateWindow ( " InfiniPaint " , wWidth , wHeight , window_flags ) ;
if ( mS . window = = nullptr )
throw std : : runtime_error ( " [SDL_CreateWindow] " + std : : string ( SDL_GetError ( ) ) ) ;
if ( load_file_to_string ( mS . iconData , " data/progicons/icon.png " ) ) {
sk_sp < SkData > newData = SkData : : MakeWithoutCopy ( mS . iconData . c_str ( ) , mS . iconData . size ( ) ) ;
std : : unique_ptr < SkCodec > codec = SkCodec : : MakeFromData ( newData , { SkPngDecoder : : Decoder ( ) } ) ;
if ( codec ) {
sk_sp < SkImage > newImage = std : : get < 0 > ( codec - > getImage ( ) ) ;
SkPixmap px ;
newImage - > peekPixels ( & px ) ;
mS . iconSurface = SDL_CreateSurfaceFrom ( newImage - > width ( ) , newImage - > height ( ) , SDL_PIXELFORMAT_RGBA32 , px . writable_addr ( ) , px . rowBytes ( ) ) ;
SDL_SetWindowIcon ( mS . window , mS . iconSurface ) ;
}
}
SDL_SetWindowPosition ( mS . window , SDL_WINDOWPOS_CENTERED , SDL_WINDOWPOS_CENTERED ) ;
# ifdef USE_BACKEND_OPENGL
mS . gl_context = SDL_GL_CreateContext ( mS . window ) ;
if ( mS . gl_context = = nullptr )
throw std : : runtime_error ( " [SDL_GL_CreateContext] " + std : : string ( SDL_GetError ( ) ) ) ;
SDL_GL_MakeCurrent ( mS . window , mS . gl_context ) ;
# ifndef __EMSCRIPTEN__
2025-08-09 10:45:39 +03:00
# ifdef USE_BACKEND_OPENGLES_3_0
2025-08-01 16:16:40 +03:00
if ( ! gladLoadGLES2 ( SDL_GL_GetProcAddress ) )
2025-08-09 10:45:39 +03:00
throw std : : runtime_error ( " [gladLoadGLES2] Failed to load GLAD OpenGLES 3.0 Loader " ) ;
# elif defined(USE_BACKEND_OPENGL_2_1)
if ( ! gladLoadGL ( SDL_GL_GetProcAddress ) )
throw std : : runtime_error ( " [gladLoadGL] Failed to load GLAD OpenGL 2.1 Loader " ) ;
2025-08-01 16:16:40 +03:00
# else
if ( ! gladLoadGL ( SDL_GL_GetProcAddress ) )
throw std : : runtime_error ( " [gladLoadGL] Failed to load GLAD OpenGL 3.3 Loader " ) ;
# endif
2025-08-09 10:45:39 +03:00
# ifndef USE_BACKEND_OPENGL_2_1
glGetIntegerv ( GL_FRAMEBUFFER_BINDING , & mS . defaultFBO ) ;
# endif
2025-08-01 16:16:40 +03:00
Logger : : get ( ) . log ( " INFO " , " GL Version: " + std : : string ( reinterpret_cast < const char * > ( glGetString ( GL_VERSION ) ) ) ) ;
2025-07-06 20:43:53 +03:00
# endif
# endif
SDL_SetWindowSize ( mS . window , wWidth , wHeight ) ;
SDL_ShowWindow ( mS . window ) ;
for ( unsigned i = 0 ; i < SDL_SYSTEM_CURSOR_COUNT ; i + + )
mS . systemCursors [ i ] = SDL_CreateSystemCursor ( ( SDL_SystemCursor ) i ) ;
2026-02-25 15:25:00 +03:00
get_refresh_rate ( mS ) ;
2025-07-06 20:43:53 +03:00
}
void sdl_terminate ( MainStruct & mS ) {
for ( unsigned i = 0 ; i < SDL_SYSTEM_CURSOR_COUNT ; i + + )
SDL_DestroyCursor ( mS . systemCursors [ i ] ) ;
if ( mS . iconSurface )
SDL_DestroySurface ( mS . iconSurface ) ;
if ( mS . hiddenCursor )
SDL_DestroyCursor ( mS . hiddenCursor ) ;
//SDL_GL_DestroyContext(mS.gl_context);
//SDL_DestroyWindow(mS.window);
//SDL_Quit();
}
void resize_window ( MainStruct & mS ) {
// Having intermediate surface allows for changing options more easily
2026-03-20 20:53:59 +03:00
mS . m - > refresh_draw_surfaces ( ) ;
2025-07-06 20:43:53 +03:00
# ifdef USE_BACKEND_VULKAN
mS . vulkanWindowContext - > resize ( mS . m - > window . size . x ( ) , mS . m - > window . size . y ( ) ) ;
# elif USE_BACKEND_OPENGL
GrGLFramebufferInfo info ;
info . fFBOID = ( GrGLuint ) mS . defaultFBO ;
info . fFormat = GL_RGBA8 ;
SkSurfaceProps props2 ;
mS . target = GrBackendRenderTargets : : MakeGL ( mS . m - > window . size . x ( ) , mS . m - > window . size . y ( ) , 0 , mS . kStencilBits , info ) ;
2026-04-09 01:05:52 +03:00
mS . m - > window . nativeSurface = SkSurfaces : : WrapBackendRenderTarget ( mS . ctx . get ( ) , mS . target , kBottomLeft_GrSurfaceOrigin , kRGBA_8888_SkColorType , SkColorSpace : : MakeSRGB ( ) , & props2 ) ;
2025-07-06 20:43:53 +03:00
2026-04-09 01:05:52 +03:00
if ( ! mS . m - > window . nativeSurface )
2025-07-06 20:43:53 +03:00
throw std : : runtime_error ( " [resize_window] Could not make surface " ) ;
2026-04-09 01:05:52 +03:00
mS . canvas = mS . m - > window . nativeSurface - > getCanvas ( ) ;
2025-07-06 20:43:53 +03:00
if ( ! mS . canvas )
throw std : : runtime_error ( " [resize_window] No canvas made " ) ;
# endif
}
# ifdef __EMSCRIPTEN__
const char * emscripten_before_unload ( int eventType , const void * reserved , void * userData ) {
MainStruct & mS = * ( ( MainStruct * ) userData ) ;
mS . m - > save_config ( ) ;
return " " ;
}
# endif
void init_logs ( MainStruct & mS ) {
2026-02-24 17:21:41 +03:00
char * homePathSDL = SDL_GetCurrentDirectory ( ) ;
mS . homePath = std : : filesystem : : path ( homePathSDL ) ;
SDL_free ( homePathSDL ) ;
2026-04-12 17:31:32 +03:00
# ifdef CONFIG_NEXT_TO_EXECUTABLE
2025-10-02 14:30:11 +03:00
std : : string CONFIG_FOLDER_NAME = " config " ;
std : : filesystem : : create_directory ( CONFIG_FOLDER_NAME ) ;
mS . configPath = mS . homePath / CONFIG_FOLDER_NAME ;
2025-07-06 20:43:53 +03:00
# else
2026-02-24 17:21:41 +03:00
char * configPathSDL = SDL_GetPrefPath ( " ErrorAtLine0 " , " infinipaint " ) ;
mS . configPath = std : : filesystem : : path ( configPathSDL ) ;
SDL_free ( configPathSDL ) ;
2025-07-06 20:43:53 +03:00
# endif
mS . logFile = std : : ofstream ( mS . configPath / " log.txt " ) ;
Logger : : get ( ) . add_log ( " FATAL " , [ & , mS = & mS ] ( const std : : string & text ) {
mS - > logFile < < " [FATAL] " < < text < < std : : endl ;
std : : cerr < < " [FATAL] " < < text < < std : : endl ;
mS - > logFile . close ( ) ;
} ) ;
Logger : : get ( ) . add_log ( " INFO " , [ & , mS = & mS ] ( const std : : string & text ) {
mS - > logFile < < " [INFO] " < < text < < std : : endl ;
2026-02-24 17:21:41 +03:00
Logger : : get ( ) . cross_platform_println ( " [INFO] " + text ) ;
2025-07-06 20:43:53 +03:00
} ) ;
Logger : : get ( ) . log ( " INFO " , " Home Path: " + mS . homePath . string ( ) ) ;
Logger : : get ( ) . log ( " INFO " , " Config Path: " + mS . configPath . string ( ) ) ;
}
SDL_AppResult SDL_AppInit ( void * * appstate , int argc , char * * argv ) {
std : : vector < std : : filesystem : : path > listOfFilesToOpenFromCommand ;
for ( int i = 1 ; i < argc ; i + + )
2025-11-21 08:35:47 +03:00
listOfFilesToOpenFromCommand . emplace_back ( std : : filesystem : : canonical ( std : : filesystem : : path ( std : : u8string_view ( reinterpret_cast < char8_t * > ( argv [ i ] ) ) ) ) ) ;
2025-07-06 20:43:53 +03:00
switch_cwd ( ) ;
2025-10-13 18:54:33 +03:00
UErrorCode uerr = U_ZERO_ERROR ;
2025-11-15 09:48:02 +03:00
icudt = read_file_to_string ( " data/icudt77l-small.dat " ) ;
2025-10-13 18:54:33 +03:00
udata_setCommonData ( ( void * ) icudt . data ( ) , & uerr ) ;
if ( U_FAILURE ( uerr ) ) {
char * errorName = const_cast < char * > ( u_errorName ( uerr ) ) ;
2025-11-15 09:48:02 +03:00
std : : cout < < " Failed to load icudt77l-small.dat " < < std : : endl ;
2025-10-13 18:54:33 +03:00
std : : cout < < errorName < < std : : endl ;
}
2025-07-06 20:43:53 +03:00
MainStruct * mSPtr = new MainStruct ;
* appstate = ( void * ) mSPtr ;
MainStruct & mS = * mSPtr ;
init_logs ( mS ) ;
2025-10-01 17:54:23 +03:00
FileDownloader : : init ( ) ;
2025-07-06 20:43:53 +03:00
# ifdef NDEBUG
try {
# endif
int initWidth = 1000 ;
int initHeight = 900 ;
2026-04-12 17:31:32 +03:00
# ifdef __EMSCRIPTEN__
{
Vector2d sizeD ;
EMSCRIPTEN_RESULT result = emscripten_get_element_css_size ( " #canvas " , & sizeD . x ( ) , & sizeD . y ( ) ) ;
if ( result ! = EMSCRIPTEN_RESULT_SUCCESS )
std : : cout < < " Failed to get canvas size " < < std : : endl ;
else {
initWidth = sizeD . x ( ) ;
initHeight = sizeD . y ( ) ;
std : : cout < < " Initial window size: " < < initWidth < < " " < < initHeight < < std : : endl ;
}
2025-07-06 20:43:53 +03:00
}
2026-04-12 17:31:32 +03:00
# endif
2025-07-06 20:43:53 +03:00
initialize_sdl ( mS , initWidth , initHeight ) ;
2026-04-05 20:26:03 +03:00
CustomEvents : : init ( ) ;
2025-07-06 20:43:53 +03:00
int32_t cursorData [ 2 ] = { 0 , 0 } ;
mS . hiddenCursor = SDL_CreateCursor ( ( Uint8 * ) cursorData , ( Uint8 * ) cursorData , 8 , 8 , 4 , 4 ) ;
mS . m = std : : make_unique < MainProgram > ( ) ;
mS . m - > logFile = & mS . logFile ;
2026-04-12 12:36:00 +03:00
mS . m - > conf . configPath = mS . configPath ;
2025-07-06 20:43:53 +03:00
mS . m - > homePath = mS . homePath ;
2026-04-12 17:31:32 +03:00
# ifndef __EMSCRIPTEN__
const char * documentsPathSDL = SDL_GetUserFolder ( SDL_FOLDER_DOCUMENTS ) ;
if ( ! documentsPathSDL ) {
documentsPathSDL = SDL_GetUserFolder ( SDL_FOLDER_DESKTOP ) ;
if ( ! documentsPathSDL )
mS . m - > documentsPath = mS . m - > conf . configPath ;
else
mS . m - > documentsPath = std : : filesystem : : path ( documentsPathSDL ) ;
}
2026-02-24 17:21:41 +03:00
else
mS . m - > documentsPath = std : : filesystem : : path ( documentsPathSDL ) ;
2026-04-12 17:31:32 +03:00
# endif
2025-07-06 20:43:53 +03:00
mS . m - > window . sdlWindow = mS . window ;
2025-11-23 12:53:50 +03:00
mS . m - > update_scale_and_density ( ) ;
2025-07-06 20:43:53 +03:00
mS . m - > load_config ( ) ;
2026-04-12 17:31:32 +03:00
# ifdef __EMSCRIPTEN__
emscripten_set_beforeunload_callback ( ( void * ) mSPtr , emscripten_before_unload ) ;
# endif
2025-07-06 20:43:53 +03:00
# ifdef USE_BACKEND_VULKAN
# ifdef USE_SKIA_BACKEND_GRAPHITE
std : : unique_ptr < const skwindow : : DisplayParams > displayParams = skwindow : : DisplayParamsBuilder ( ) . build ( ) ;
mS . vulkanWindowContext = std : : make_unique < skwindow : : internal : : GraphiteVulkanWindowContext > ( std : : move ( displayParams ) ,
[ window = mS . window ] ( VkInstance instance ) {
VkSurfaceKHR toRet ;
if ( ! SDL_Vulkan_CreateSurface ( window , instance , nullptr , & toRet ) )
throw std : : runtime_error ( " Failed to create SDL vulkan surface " ) ;
else
std : : cout < < " Successfully created surface! " < < std : : endl ;
return toRet ;
} ,
SDL_Vulkan_GetPresentationSupport ,
( PFN_vkGetInstanceProcAddr ) SDL_Vulkan_GetVkGetInstanceProcAddr ( ) ) ;
2026-04-09 01:05:52 +03:00
mS . m - > window . nativeSurface = mS . vulkanWindowContext - > getBackbufferSurface ( ) ;
2025-07-06 20:43:53 +03:00
# elif USE_SKIA_BACKEND_GANESH
2026-02-04 10:53:23 +03:00
std : : unique_ptr < const skwindow : : DisplayParams > displayParams = skwindow : : DisplayParamsBuilder ( ) . detach ( ) ;
2025-07-06 20:43:53 +03:00
mS . vulkanWindowContext = std : : make_unique < skwindow : : internal : : VulkanWindowContext > ( std : : move ( displayParams ) ,
[ window = mS . window ] ( VkInstance instance ) {
VkSurfaceKHR toRet ;
if ( ! SDL_Vulkan_CreateSurface ( window , instance , nullptr , & toRet ) )
throw std : : runtime_error ( " Failed to create SDL vulkan surface " ) ;
else
std : : cout < < " Successfully created surface! " < < std : : endl ;
return toRet ;
} ,
SDL_Vulkan_GetPresentationSupport ,
( PFN_vkGetInstanceProcAddr ) SDL_Vulkan_GetVkGetInstanceProcAddr ( ) ) ;
mS . ctx = mS . vulkanWindowContext - > fContext ; // fContext is supposed to be private, but put it in public for convenience for this app
2026-04-09 01:05:52 +03:00
mS . m - > window . nativeSurface = mS . vulkanWindowContext - > getBackbufferSurface ( ) ;
2025-07-06 20:43:53 +03:00
# endif
# elif USE_BACKEND_OPENGL
sk_sp < const GrGLInterface > iface = GrGLMakeNativeInterface ( ) ;
2026-02-04 12:32:07 +03:00
GrContextOptions opts ;
opts . fSuppressPrints = true ;
2026-03-31 13:17:58 +03:00
opts . fDisableDriverCorrectnessWorkarounds = mS . m - > conf . disableGraphicsDriverWorkarounds ; // Driver workarounds causing glitches on intel drivers on windows, but are required in the Emscripten version. Good idea to have a toggle for it
2026-02-04 12:32:07 +03:00
2025-07-06 20:43:53 +03:00
mS . ctx = GrDirectContexts : : MakeGL ( iface , opts ) ;
if ( ! mS . ctx )
throw std : : runtime_error ( " [GrDirectContexts::MakeGL] Could not make context " ) ;
# endif
# ifdef USE_SKIA_BACKEND_GRAPHITE
mS . m - > window . recorder = [ & ] ( ) { return mS . vulkanWindowContext - > graphiteRecorder ( ) ; } ;
# elif USE_SKIA_BACKEND_GANESH
mS . m - > window . ctx = mS . ctx ;
# endif
# ifdef __EMSCRIPTEN__
SDL_GetWindowPosition ( mS . window , & mS . m - > window . pos . x ( ) , & mS . m - > window . pos . y ( ) ) ;
mS . m - > window . writtenPos . x ( ) = mS . m - > window . pos . x ( ) ;
mS . m - > window . writtenPos . y ( ) = mS . m - > window . pos . y ( ) ;
mS . m - > window . size . x ( ) = initWidth ;
mS . m - > window . size . y ( ) = initHeight ;
mS . m - > window . writtenSize . x ( ) = initWidth ;
mS . m - > window . writtenSize . y ( ) = initHeight ;
# else
if ( mS . m - > window . pos . x ( ) > = 0 & & mS . m - > window . pos . y ( ) > = 0 & & mS . m - > window . size . x ( ) > = 0 & & mS . m - > window . size . y ( ) > = 0 ) {
SDL_SetWindowSize ( mS . window , mS . m - > window . size . x ( ) , mS . m - > window . size . y ( ) ) ;
SDL_SetWindowPosition ( mS . window , mS . m - > window . pos . x ( ) , mS . m - > window . pos . y ( ) ) ;
}
else {
SDL_GetWindowPosition ( mS . window , & mS . m - > window . pos . x ( ) , & mS . m - > window . pos . y ( ) ) ;
mS . m - > window . writtenPos . x ( ) = mS . m - > window . pos . x ( ) ;
mS . m - > window . writtenPos . y ( ) = mS . m - > window . pos . y ( ) ;
mS . m - > window . size . x ( ) = initWidth ;
mS . m - > window . size . y ( ) = initHeight ;
mS . m - > window . writtenSize . x ( ) = initWidth ;
mS . m - > window . writtenSize . y ( ) = initHeight ;
}
if ( mS . m - > window . maximized )
SDL_MaximizeWindow ( mS . window ) ;
if ( mS . m - > window . fullscreen )
SDL_SetWindowFullscreen ( mS . window , true ) ;
# endif
2026-04-25 20:29:51 +03:00
mS . m - > input . update_safe_area ( ) ;
2026-03-20 20:53:59 +03:00
mS . m - > window . canCreateSurfaces = true ;
2025-07-06 20:43:53 +03:00
resize_window ( mS ) ;
2026-05-14 20:21:45 +03:00
if ( mS . m - > conf . mobileUI )
mS . m - > set_first_screen ( std : : make_unique < FileSelectScreen > ( * mS . m ) ) ;
2025-07-06 20:43:53 +03:00
else {
2026-05-14 20:21:45 +03:00
mS . m - > set_first_screen ( std : : make_unique < DesktopDrawingProgramScreen > ( * mS . m ) ) ;
if ( listOfFilesToOpenFromCommand . empty ( ) ) {
2026-04-05 20:26:03 +03:00
mS . m - > create_new_tab ( {
2026-05-14 20:21:45 +03:00
. isClient = false
2026-04-05 20:26:03 +03:00
} ) ;
2025-07-06 20:43:53 +03:00
}
2026-05-14 20:21:45 +03:00
else {
for ( std : : filesystem : : path & f : listOfFilesToOpenFromCommand ) {
mS . m - > create_new_tab ( {
. isClient = false ,
. filePathSource = f
} ) ;
}
}
2025-07-06 20:43:53 +03:00
}
2026-04-02 10:15:22 +03:00
mS . m - > update ( ) ; // Run update once to make sure that callbacks dont crash
2025-07-06 20:43:53 +03:00
# ifdef NDEBUG
}
catch ( const std : : exception & e ) {
Logger : : get ( ) . log ( " FATAL " , e . what ( ) ) ;
return SDL_APP_FAILURE ;
}
# endif
return SDL_APP_CONTINUE ;
}
2026-04-09 01:05:52 +03:00
void regular_draw ( MainStruct & mS ) {
mS . lastRenderTimePoint = std : : chrono : : steady_clock : : now ( ) ;
if ( mS . m - > window . intermediateSurfaceMSAA ) {
SkCanvas * intermediateCanvas = mS . m - > window . intermediateSurfaceMSAA - > getCanvas ( ) ;
intermediateCanvas - > save ( ) ;
2026-05-04 18:13:47 +03:00
intermediateCanvas - > translate ( mS . m - > input . screenOffset . x ( ) , mS . m - > input . screenOffset . y ( ) ) ;
2026-04-17 19:32:49 +03:00
mS . m - > draw ( intermediateCanvas ) ;
2026-04-09 01:05:52 +03:00
intermediateCanvas - > restore ( ) ;
2026-02-25 18:57:55 +03:00
2026-02-25 15:25:00 +03:00
# ifdef USE_BACKEND_VULKAN
2026-04-09 01:05:52 +03:00
mS . vulkanWindowContext - > getBackbufferSurface ( ) - > getCanvas ( ) - > drawImage ( mS . m - > window . intermediateSurfaceMSAA - > makeTemporaryImage ( ) , 0 , 0 ) ;
mS . ctx - > flushAndSubmit ( ) ;
2026-02-25 15:25:00 +03:00
mS . vulkanWindowContext - > swapBuffers ( ) ;
# elif USE_BACKEND_OPENGL
2026-04-09 01:05:52 +03:00
mS . canvas - > drawImage ( mS . m - > window . intermediateSurfaceMSAA - > makeTemporaryImage ( ) , 0 , 0 ) ;
mS . ctx - > flushAndSubmit ( ) ;
2026-02-25 15:25:00 +03:00
SDL_GL_SwapWindow ( mS . window ) ;
# endif
2026-04-09 01:05:52 +03:00
}
else {
2026-02-25 15:25:00 +03:00
# ifdef USE_BACKEND_VULKAN
2026-04-17 19:32:49 +03:00
mS . m - > draw ( mS . vulkanWindowContext - > getBackbufferSurface ( ) - > getCanvas ( ) ) ;
2026-02-25 15:25:00 +03:00
mS . ctx - > flushAndSubmit ( ) ;
2026-04-09 01:05:52 +03:00
mS . vulkanWindowContext - > swapBuffers ( ) ;
2026-02-25 15:25:00 +03:00
# elif USE_BACKEND_OPENGL
2026-05-04 18:13:47 +03:00
mS . canvas - > save ( ) ;
mS . canvas - > translate ( mS . m - > input . screenOffset . x ( ) , mS . m - > input . screenOffset . y ( ) ) ;
2026-04-17 19:32:49 +03:00
mS . m - > draw ( mS . canvas ) ;
2026-05-04 18:13:47 +03:00
mS . canvas - > restore ( ) ;
2026-02-25 15:25:00 +03:00
mS . ctx - > flushAndSubmit ( ) ;
2026-04-09 01:05:52 +03:00
SDL_GL_SwapWindow ( mS . window ) ;
2026-02-25 15:25:00 +03:00
# endif
}
}
2025-07-06 20:43:53 +03:00
SDL_AppResult SDL_AppIterate ( void * appstate ) {
2025-08-26 12:03:09 +03:00
std : : chrono : : steady_clock : : time_point frameTimeStart = std : : chrono : : steady_clock : : now ( ) ;
2025-07-06 20:43:53 +03:00
MainStruct & mS = * ( ( MainStruct * ) appstate ) ;
2026-02-25 18:57:55 +03:00
mS . lastUpdateTimePoint = std : : chrono : : steady_clock : : now ( ) ;
2025-07-06 20:43:53 +03:00
# ifdef NDEBUG
try {
# endif
mS . m - > update ( ) ;
2026-05-13 14:31:35 +03:00
if ( mS . m - > setToQuit ) {
# ifdef __ANDROID__
mS . m - > input_app_about_to_go_to_background_callback ( ) ;
# endif
2025-07-06 20:43:53 +03:00
return SDL_APP_SUCCESS ;
2026-05-13 14:31:35 +03:00
}
2025-07-06 20:43:53 +03:00
if ( mS . m - > input . hideCursor ) {
SDL_HideCursor ( ) ;
SDL_SetCursor ( mS . hiddenCursor ) ; // Using this because SDL hidden cursor sometimes doesnt work
}
else {
SDL_ShowCursor ( ) ;
SDL_SetCursor ( mS . systemCursors [ static_cast < unsigned > ( mS . m - > input . cursorIcon ) ] ) ;
}
2026-02-27 16:16:51 +03:00
regular_draw ( mS ) ;
2025-07-06 20:43:53 +03:00
mS . m - > input . frame_reset ( mS . m - > window . size ) ;
# ifdef NDEBUG
}
catch ( const std : : exception & e ) {
Logger : : get ( ) . log ( " FATAL " , e . what ( ) ) ;
return SDL_APP_FAILURE ;
}
# endif
2025-08-26 12:03:09 +03:00
mS . m - > window . lastFrameTime = std : : chrono : : steady_clock : : now ( ) - frameTimeStart ;
2025-07-06 20:43:53 +03:00
return SDL_APP_CONTINUE ;
}
SDL_AppResult SDL_AppEvent ( void * appstate , SDL_Event * event ) {
MainStruct & mS = * ( ( MainStruct * ) appstate ) ;
# ifdef NDEBUG
try {
# endif
switch ( event - > type ) {
case SDL_EVENT_QUIT :
2026-04-27 21:30:31 +03:00
Logger : : get ( ) . log ( " INFO " , " [SDL_AppEvent] Quit " ) ;
2026-01-18 13:39:49 +03:00
if ( mS . m - > app_close_requested ( ) )
2025-07-06 20:43:53 +03:00
return SDL_APP_SUCCESS ;
break ;
2026-04-28 14:51:35 +03:00
case SDL_EVENT_WILL_ENTER_BACKGROUND :
Logger : : get ( ) . log ( " INFO " , " [SDL_AppEvent] Entering background " ) ;
mS . m - > input_app_about_to_go_to_background_callback ( ) ;
break ;
2026-05-14 11:53:36 +03:00
case SDL_EVENT_WILL_ENTER_FOREGROUND :
Logger : : get ( ) . log ( " INFO " , " [SDL_AppEvent] Entering foreground " ) ;
mS . m - > input_app_about_to_go_to_foreground_callback ( ) ;
break ;
2026-01-18 13:39:49 +03:00
case SDL_EVENT_WINDOW_CLOSE_REQUESTED :
if ( event - > window . windowID = = SDL_GetWindowID ( mS . window ) ) {
if ( mS . m - > app_close_requested ( ) )
return SDL_APP_SUCCESS ;
}
break ;
2025-07-06 20:43:53 +03:00
case SDL_EVENT_WINDOW_MOVED :
mS . m - > window . pos . x ( ) = event - > window . data1 ;
mS . m - > window . pos . y ( ) = event - > window . data2 ;
if ( ! mS . m - > window . fullscreen & & ! mS . m - > window . maximized ) {
mS . m - > window . writtenPos . x ( ) = event - > window . data1 ;
mS . m - > window . writtenPos . y ( ) = event - > window . data2 ;
}
2026-02-25 15:25:00 +03:00
get_refresh_rate ( mS ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED :
mS . m - > window . size . x ( ) = event - > window . data1 ;
mS . m - > window . size . y ( ) = event - > window . data2 ;
if ( ! mS . m - > window . fullscreen & & ! mS . m - > window . maximized ) {
mS . m - > window . writtenSize . x ( ) = event - > window . data1 ;
mS . m - > window . writtenSize . y ( ) = event - > window . data2 ;
}
resize_window ( mS ) ;
2026-04-07 20:21:41 +03:00
mS . m - > update_scale_and_density ( ) ;
2026-04-25 20:29:51 +03:00
mS . m - > input . backend_window_resize_update ( ) ;
2026-04-07 20:21:41 +03:00
get_refresh_rate ( mS ) ;
break ;
case SDL_EVENT_WINDOW_DISPLAY_SCALE_CHANGED :
mS . m - > update_scale_and_density ( ) ;
mS . m - > input . backend_window_scale_update ( event - > window ) ;
2026-02-25 15:25:00 +03:00
get_refresh_rate ( mS ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_WINDOW_MAXIMIZED :
mS . m - > window . maximized = true ;
2026-02-25 15:25:00 +03:00
get_refresh_rate ( mS ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_WINDOW_RESTORED :
mS . m - > window . maximized = false ;
2026-02-25 15:25:00 +03:00
get_refresh_rate ( mS ) ;
2025-07-06 20:43:53 +03:00
break ;
2026-04-25 20:29:51 +03:00
case SDL_EVENT_WINDOW_SAFE_AREA_CHANGED : {
mS . m - > input . backend_window_resize_update ( ) ;
break ;
}
2025-07-06 20:43:53 +03:00
case SDL_EVENT_MOUSE_MOTION :
2025-07-08 15:12:35 +03:00
# ifdef _WIN32
2026-04-11 11:17:26 +03:00
if ( ! mS . m - > conf . tabletOptions . ignoreMouseMovementWhenPenInProximity | | ! mS . m - > input . pen . inProximity )
2025-07-08 15:12:35 +03:00
# endif
{
2026-03-01 17:28:43 +03:00
mS . m - > input . backend_mouse_motion_update ( event - > motion ) ;
2025-07-08 15:12:35 +03:00
}
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_MOUSE_BUTTON_UP :
2026-03-01 17:28:43 +03:00
mS . m - > input . backend_mouse_button_up_update ( event - > button ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_MOUSE_BUTTON_DOWN :
2026-03-01 17:28:43 +03:00
mS . m - > input . backend_mouse_button_down_update ( event - > button ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_MOUSE_WHEEL :
2026-03-01 17:28:43 +03:00
mS . m - > input . backend_mouse_wheel_update ( event - > wheel ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_KEY_DOWN :
mS . m - > input . backend_key_down_update ( event - > key ) ;
break ;
case SDL_EVENT_KEY_UP :
mS . m - > input . backend_key_up_update ( event - > key ) ;
break ;
case SDL_EVENT_TEXT_INPUT :
2026-04-03 23:02:33 +03:00
mS . m - > input . backend_input_text_event ( event - > text . text ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_DROP_FILE :
2026-03-04 11:03:05 +03:00
mS . m - > input . backend_drop_file_event ( event - > drop ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_DROP_TEXT :
2026-03-04 11:03:05 +03:00
mS . m - > input . backend_drop_text_event ( event - > drop ) ;
2025-07-06 20:43:53 +03:00
break ;
case SDL_EVENT_PEN_PROXIMITY_IN : {
mS . m - > input . pen . inProximity = true ;
break ;
}
case SDL_EVENT_PEN_PROXIMITY_OUT : {
mS . m - > input . pen . inProximity = false ;
break ;
}
case SDL_EVENT_PEN_MOTION : {
2026-03-03 14:59:18 +03:00
mS . m - > input . backend_pen_motion_update ( event - > pmotion ) ;
2025-07-06 20:43:53 +03:00
break ;
}
case SDL_EVENT_PEN_AXIS : {
2026-03-03 22:31:48 +03:00
mS . m - > input . backend_pen_axis_update ( event - > paxis ) ;
2025-07-06 20:43:53 +03:00
break ;
}
2026-03-03 14:59:18 +03:00
case SDL_EVENT_PEN_DOWN : {
mS . m - > input . backend_pen_touch_down_update ( event - > ptouch ) ;
break ;
}
2025-07-06 20:43:53 +03:00
case SDL_EVENT_PEN_UP : {
2026-03-03 14:59:18 +03:00
mS . m - > input . backend_pen_touch_up_update ( event - > ptouch ) ;
2025-07-06 20:43:53 +03:00
break ;
}
case SDL_EVENT_PEN_BUTTON_UP : {
2026-03-03 14:59:18 +03:00
mS . m - > input . backend_pen_button_up_update ( event - > pbutton ) ;
2025-07-06 20:43:53 +03:00
break ;
}
case SDL_EVENT_PEN_BUTTON_DOWN : {
2026-03-03 14:59:18 +03:00
mS . m - > input . backend_pen_button_down_update ( event - > pbutton ) ;
2025-07-06 20:43:53 +03:00
break ;
}
2026-02-27 16:16:51 +03:00
case SDL_EVENT_FINGER_DOWN : {
2026-05-15 09:51:25 +03:00
if ( ! mS . m - > conf . tabletOptions . disableTouchWhenPenInProximity | | ! mS . m - > input . pen . inProximity )
mS . m - > input . backend_touch_finger_down_update ( event - > tfinger ) ;
2026-02-27 16:16:51 +03:00
break ;
}
case SDL_EVENT_FINGER_UP : {
2026-05-15 09:51:25 +03:00
if ( ! mS . m - > conf . tabletOptions . disableTouchWhenPenInProximity | | ! mS . m - > input . pen . inProximity )
mS . m - > input . backend_touch_finger_up_update ( event - > tfinger ) ;
2026-02-27 16:16:51 +03:00
break ;
}
case SDL_EVENT_FINGER_MOTION : {
2026-05-15 09:51:25 +03:00
if ( ! mS . m - > conf . tabletOptions . disableTouchWhenPenInProximity | | ! mS . m - > input . pen . inProximity )
mS . m - > input . backend_touch_finger_motion_update ( event - > tfinger ) ;
2026-02-27 16:16:51 +03:00
break ;
}
2026-02-25 15:25:00 +03:00
case SDL_EVENT_DISPLAY_ORIENTATION :
case SDL_EVENT_DISPLAY_ADDED :
case SDL_EVENT_DISPLAY_REMOVED :
case SDL_EVENT_DISPLAY_MOVED :
case SDL_EVENT_DISPLAY_DESKTOP_MODE_CHANGED :
case SDL_EVENT_DISPLAY_CURRENT_MODE_CHANGED :
case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED :
case SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED : {
get_refresh_rate ( mS ) ;
break ;
}
2026-03-22 20:16:15 +03:00
case SDL_EVENT_SCREEN_KEYBOARD_SHOWN :
2026-03-07 13:00:10 +03:00
break ;
2026-03-22 20:16:15 +03:00
case SDL_EVENT_SCREEN_KEYBOARD_HIDDEN :
2026-03-07 13:00:10 +03:00
break ;
2026-04-04 17:52:43 +03:00
default : {
2026-04-17 11:12:48 +03:00
if ( event - > type = = CustomEvents : : PasteEvent : : EVENT_NUM )
mS . m - > input_paste_callback ( * CustomEvents : : get_event < CustomEvents : : PasteEvent > ( ) ) ;
else if ( event - > type = = CustomEvents : : OpenInfiniPaintFileEvent : : EVENT_NUM )
mS . m - > input_open_infinipaint_file_callback ( * CustomEvents : : get_event < CustomEvents : : OpenInfiniPaintFileEvent > ( ) ) ;
else if ( event - > type = = CustomEvents : : AddFileToCanvasEvent : : EVENT_NUM )
mS . m - > input_add_file_to_canvas_callback ( * CustomEvents : : get_event < CustomEvents : : AddFileToCanvasEvent > ( ) ) ;
2026-05-11 23:06:39 +03:00
else if ( event - > type = = CustomEvents : : RefreshTextBoxInputEvent : : EVENT_NUM ) {
CustomEvents : : get_event < CustomEvents : : RefreshTextBoxInputEvent > ( ) ;
2026-05-07 21:48:47 +03:00
mS . m - > input . refresh_receiving_text_box_input ( ) ;
2026-05-11 23:06:39 +03:00
}
else if ( event - > type = = CustomEvents : : AndroidTextBoxInputEvent : : EVENT_NUM )
mS . m - > input_android_text_box_input_callback ( * CustomEvents : : get_event < CustomEvents : : AndroidTextBoxInputEvent > ( ) ) ;
2025-07-06 20:43:53 +03:00
break ;
2026-04-04 17:52:43 +03:00
}
2025-07-06 20:43:53 +03:00
}
# ifdef NDEBUG
}
catch ( const std : : exception & e ) {
Logger : : get ( ) . log ( " FATAL " , e . what ( ) ) ;
return SDL_APP_FAILURE ;
}
# endif
2026-05-13 14:31:35 +03:00
if ( mS . m - > setToQuit ) {
# ifdef __ANDROID__
mS . m - > input_app_about_to_go_to_background_callback ( ) ;
# endif
return SDL_APP_SUCCESS ;
}
return SDL_APP_CONTINUE ;
2025-07-06 20:43:53 +03:00
}
2026-04-28 14:51:35 +03:00
// NOTE: On Android, SDL_AppQuit is triggered by onDestroy. onDestroy may or may not be called, and even if it is, it may only be partially called. You should not rely on it.
2025-07-06 20:43:53 +03:00
void SDL_AppQuit ( void * appstate , SDL_AppResult result ) {
2026-04-28 14:51:35 +03:00
# ifndef __ANDROID__
2026-02-04 19:19:29 +03:00
DrawingProgramCache : : delete_all_draw_cache ( ) ;
2026-02-04 19:15:58 +03:00
2025-07-06 20:43:53 +03:00
MainStruct & mS = * ( ( MainStruct * ) appstate ) ;
try {
mS . m - > save_config ( ) ;
mS . m - > early_destroy ( ) ;
sdl_terminate ( mS ) ;
}
catch ( const std : : exception & e ) {
Logger : : get ( ) . log ( " FATAL " , e . what ( ) ) ;
}
delete ( & mS ) ;
SDL_Quit ( ) ;
2025-11-20 13:26:09 +03:00
FileDownloader : : cleanup ( ) ;
2026-04-28 14:51:35 +03:00
# endif
2025-07-06 20:43:53 +03:00
}