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/>.
*/
2025-07-06 20:43:53 +03:00
# include "MainProgram.hpp"
2026-04-05 20:26:03 +03:00
# include "CustomEvents.hpp"
2025-11-17 16:08:42 +03:00
# include "VersionConstants.hpp"
2025-11-20 12:38:28 +03:00
# include <SDL3/SDL_render.h>
# include <SDL3/SDL_video.h>
2025-07-06 20:43:53 +03:00
# include <include/core/SkAlphaType.h>
# include <include/core/SkColor.h>
# include <include/core/SkPaint.h>
# include <filesystem>
# include <iostream>
# include <include/core/SkFont.h>
# include <include/core/SkData.h>
# include <include/core/SkSurfaceProps.h>
# include "InputManager.hpp"
2026-05-14 11:53:36 +03:00
# include "NetThreadManager.hpp"
2025-07-06 20:43:53 +03:00
# include <cereal/types/vector.hpp>
# include <cereal/types/unordered_map.hpp>
# include <Eigen/Core>
# include <cereal/archives/json.hpp>
# include <Helpers/Serializers.hpp>
# include <nlohmann/json.hpp>
# include <include/core/SkSurface.h>
# ifdef USE_SKIA_BACKEND_GRAPHITE
# include <include/gpu/graphite/Surface.h>
# elif USE_SKIA_BACKEND_GANESH
# include <include/gpu/ganesh/GrDirectContext.h>
# include <include/gpu/ganesh/SkSurfaceGanesh.h>
# endif
# ifdef __EMSCRIPTEN__
# include <emscripten.h>
# endif
# include <Helpers/Logger.hpp>
2026-05-20 10:33:32 +03:00
# ifdef __ANDROID__
# define UPDATE_NOTIFICATION_URL "https: //infinipaint.com/updateNotificationVersionAndroid.txt"
# else
# define UPDATE_NOTIFICATION_URL "https: //infinipaint.com/updateNotificationVersion.txt"
# endif
2026-05-17 17:32:05 +03:00
2025-07-06 20:43:53 +03:00
MainProgram : : MainProgram ( ) :
2026-03-01 17:28:43 +03:00
input ( * this ) ,
2025-11-16 10:42:01 +03:00
fonts ( std : : make_shared < FontData > ( ) ) ,
2026-03-31 13:33:15 +03:00
g ( * this )
2025-07-06 20:43:53 +03:00
{
2026-04-27 21:30:31 +03:00
g . gui . io . layoutRun = [ & ] {
screen - > gui_layout_run ( ) ;
} ;
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . set_log_function ( Logger : : LogType : : WORLDFATAL , [ & ] ( const std : : string & text ) {
2025-07-06 20:43:53 +03:00
* logFile < < " [WORLDFATAL] " < < text < < std : : endl ;
std : : cout < < " [WORLDFATAL] " < < text < < std : : endl ;
2026-06-01 15:42:33 +03:00
logMessages . emplace_front ( UserLogMessage { text , UserLogMessage : : COLOR_ERROR , UserLogMessage : : DISPLAY_FOR_ALL } ) ;
2025-07-06 20:43:53 +03:00
if ( logMessages . size ( ) = = LOG_SIZE )
logMessages . pop_back ( ) ;
2026-04-05 22:01:30 +03:00
g . gui . set_to_layout ( ) ;
2025-07-06 20:43:53 +03:00
} ) ;
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . set_log_function ( Logger : : LogType : : USERINFO , [ & ] ( const std : : string & text ) {
2025-07-06 20:43:53 +03:00
* logFile < < " [USERINFO] " < < text < < std : : endl ;
std : : cout < < " [USERINFO] " < < text < < std : : endl ;
2026-05-16 21:56:58 +03:00
logMessages . emplace_front ( UserLogMessage { text , UserLogMessage : : COLOR_NORMAL , UserLogMessage : : DISPLAY_FOR_ALL } ) ;
2025-07-06 20:43:53 +03:00
if ( logMessages . size ( ) = = LOG_SIZE )
logMessages . pop_back ( ) ;
2026-04-05 22:01:30 +03:00
g . gui . set_to_layout ( ) ;
2025-07-06 20:43:53 +03:00
} ) ;
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . set_log_function ( Logger : : LogType : : DESKTOP_USERINFO , [ & ] ( const std : : string & text ) {
* logFile < < " [DESKTOP_USERINFO] " < < text < < std : : endl ;
std : : cout < < " [DESKTOP_USERINFO] " < < text < < std : : endl ;
logMessages . emplace_front ( UserLogMessage { text , UserLogMessage : : COLOR_NORMAL , UserLogMessage : : DISPLAY_DESKTOP_ONLY } ) ;
if ( logMessages . size ( ) = = LOG_SIZE )
logMessages . pop_back ( ) ;
g . gui . set_to_layout ( ) ;
} ) ;
Logger : : get ( ) . set_log_function ( Logger : : LogType : : PHONE_USERINFO , [ & ] ( const std : : string & text ) {
* logFile < < " [PHONE_USERINFO] " < < text < < std : : endl ;
std : : cout < < " [PHONE_USERINFO] " < < text < < std : : endl ;
logMessages . emplace_front ( UserLogMessage { text , UserLogMessage : : COLOR_NORMAL , UserLogMessage : : DISPLAY_PHONE_ONLY } ) ;
if ( logMessages . size ( ) = = LOG_SIZE )
logMessages . pop_back ( ) ;
g . gui . set_to_layout ( ) ;
} ) ;
Logger : : get ( ) . set_log_function ( Logger : : LogType : : CHAT , [ & ] ( const std : : string & text ) {
2025-07-06 20:43:53 +03:00
* logFile < < " [CHAT] " < < text < < std : : endl ;
std : : cout < < " [CHAT] " < < text < < std : : endl ;
} ) ;
2025-10-16 21:22:08 +03:00
}
2025-07-06 20:43:53 +03:00
2025-10-16 21:22:08 +03:00
void MainProgram : : update ( ) {
2025-10-20 17:30:36 +03:00
deltaTime . update_time_since ( ) ;
deltaTime . update_time_point ( ) ;
2026-04-09 10:20:40 +03:00
input . update ( ) ;
2026-04-07 22:21:06 +03:00
g . update ( ) ;
2026-05-17 17:32:05 +03:00
update_notification_check ( ) ;
2026-04-12 12:36:00 +03:00
screen - > update ( ) ;
2026-05-14 11:53:36 +03:00
background_update ( ) ;
2026-05-14 14:39:34 +03:00
NetThreadManager : : get ( ) . synchronous_update ( ) ;
2026-04-05 22:01:30 +03:00
post_callback ( ) ;
2025-07-06 20:43:53 +03:00
}
2026-05-17 17:32:05 +03:00
void MainProgram : : update_notification_check ( ) {
# ifndef __EMSCRIPTEN__
if ( ! updateCheckerData . updateCheckDone ) {
if ( conf . checkForUpdates ) {
if ( ! updateCheckerData . versionFile )
updateCheckerData . versionFile = FileDownloader : : download_data_from_url ( UPDATE_NOTIFICATION_URL ) ;
else {
switch ( updateCheckerData . versionFile - > status ) {
case FileDownloader : : DownloadData : : Status : : IN_PROGRESS :
break ;
case FileDownloader : : DownloadData : : Status : : SUCCESS : {
updateCheckerData . updateCheckDone = true ;
std : : optional < VersionNumber > newVersion = version_str_to_version_numbers ( updateCheckerData . versionFile - > str ) ;
std : : optional < VersionNumber > currentVersion = VersionConstants : : CURRENT_VERSION_NUMBER ;
if ( newVersion . has_value ( ) & & currentVersion . has_value ( ) ) {
VersionNumber & newV = newVersion . value ( ) ;
VersionNumber & currentV = currentVersion . value ( ) ;
updateCheckerData . newVersionStr = version_numbers_to_version_str ( newV ) ;
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Latest online version is v " + updateCheckerData . newVersionStr ) ;
if ( newV > currentV ) {
updateCheckerData . showGui = true ;
Logger : : get ( ) . log ( Logger : : LogType : : PHONE_USERINFO , " New version v " + updateCheckerData . newVersionStr + " available " ) ;
g . gui . set_to_layout ( ) ;
}
else if ( newV = = currentV )
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Current version is up to date " ) ;
else
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Local version has larger version number than the latest online one " ) ;
}
else
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Update notification file couldn't be converted to version numbers " ) ;
updateCheckerData . versionFile = nullptr ;
break ;
}
case FileDownloader : : DownloadData : : Status : : FAILURE :
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Failed to check for updates " ) ;
updateCheckerData . updateCheckDone = true ;
updateCheckerData . versionFile = nullptr ;
break ;
}
}
}
else
updateCheckerData . updateCheckDone = true ;
}
# endif
}
2026-05-14 11:53:36 +03:00
void MainProgram : : background_update ( ) {
bool isAnyWorldConnected = false ;
for ( auto & w : worlds ) {
w - > update ( ) ;
isAnyWorldConnected | = ( ( w - > netServer & & ! w - > netServer - > is_disconnected ( ) ) | | ( w - > netClient & & ! w - > netClient - > is_disconnected ( ) ) ) ;
}
if ( ! isAnyWorldConnected )
NetThreadManager : : get ( ) . destroy ( ) ;
}
2026-01-18 13:39:49 +03:00
bool MainProgram : : app_close_requested ( ) {
2026-04-12 12:36:00 +03:00
return screen - > app_close_requested ( ) ;
2026-01-18 13:39:49 +03:00
}
2025-07-08 12:18:55 +03:00
void MainProgram : : update_display_names ( ) {
for ( auto & w : worlds ) {
2026-01-01 22:46:02 +03:00
if ( ! w - > netObjMan . is_connected ( ) )
2026-03-31 13:17:58 +03:00
w - > ownClientData - > set_display_name ( conf . displayName ) ;
2025-07-08 12:18:55 +03:00
}
}
2025-10-27 20:19:47 +03:00
void MainProgram : : init_net_library ( ) {
2026-05-14 11:53:36 +03:00
NetThreadManager : : get ( ) . init ( this ) ;
2025-10-27 20:19:47 +03:00
}
2025-07-06 20:43:53 +03:00
void MainProgram : : save_config ( ) {
2026-04-23 20:56:11 +03:00
using json = nlohmann : : json ;
json j ;
j [ " version " ] = VersionConstants : : CURRENT_VERSION_STRING ;
j [ " settings " ] = conf . get_config_json ( input ) ;
j [ " window " ] [ " pos " ] = window . writtenPos ;
j [ " window " ] [ " size " ] = window . writtenSize ;
j [ " window " ] [ " maximized " ] = window . maximized ;
j [ " window " ] [ " fullscreen " ] = window . fullscreen ;
j [ " fileselectorpath " ] = conf . currentSearchPath ;
j [ " toolConfig " ] = toolConfig ;
std : : stringstream f ;
f < < std : : setw ( 4 ) < < j ;
SDL_SaveFile ( ( conf . configPath / " config.json " ) . string ( ) . c_str ( ) , f . view ( ) . data ( ) , f . view ( ) . size ( ) ) ;
2026-05-13 14:31:35 +03:00
2026-04-12 12:36:00 +03:00
conf . save_palettes ( ) ;
2025-07-06 20:43:53 +03:00
}
void MainProgram : : load_config ( ) {
2026-04-12 12:36:00 +03:00
conf . currentSearchPath = homePath ;
2026-04-23 20:56:11 +03:00
using json = nlohmann : : json ;
try {
json j ( nlohmann : : json : : parse ( read_file_to_string ( conf . configPath / " config.json " ) ) ) ;
2025-11-17 16:08:42 +03:00
2026-04-23 20:56:11 +03:00
VersionNumber version ( 0 , 0 , 1 ) ;
2025-07-06 20:43:53 +03:00
try {
2026-04-23 20:56:11 +03:00
std : : string versionStr ;
j . at ( " version " ) . get_to ( versionStr ) ;
auto optVersion = version_str_to_version_numbers ( versionStr ) ;
if ( optVersion . has_value ( ) )
version = optVersion . value ( ) ;
}
catch ( . . . ) { }
2025-11-17 16:08:42 +03:00
2026-04-23 20:56:11 +03:00
try {
conf . set_config_json ( input , j [ " settings " ] , version ) ;
}
catch ( . . . ) { }
2025-07-06 20:43:53 +03:00
# ifndef __EMSCRIPTEN__
2026-04-23 20:56:11 +03:00
try {
j . at ( " window " ) . at ( " size " ) . get_to ( window . size ) ;
window . writtenSize = window . size ;
j . at ( " window " ) . at ( " pos " ) . get_to ( window . pos ) ;
window . writtenPos = window . pos ;
j . at ( " window " ) . at ( " maximized " ) . get_to ( window . maximized ) ;
j . at ( " window " ) . at ( " fullscreen " ) . get_to ( window . fullscreen ) ;
}
catch ( . . . ) { }
try {
j . at ( " fileselectorpath " ) . get_to ( conf . currentSearchPath ) ;
}
catch ( . . . ) {
conf . currentSearchPath = homePath ;
}
2025-07-06 20:43:53 +03:00
# endif
2026-04-23 20:56:11 +03:00
try {
j . at ( " toolConfig " ) . get_to ( toolConfig ) ;
}
catch ( . . . ) { }
} catch ( . . . ) { }
2026-04-12 12:36:00 +03:00
conf . load_palettes ( ) ;
g . load_theme ( conf . configPath , conf . themeCurrentlyLoaded ) ;
conf . load_licenses ( ) ;
2025-10-27 20:19:47 +03:00
2026-04-12 12:36:00 +03:00
NetLibrary : : copy_default_p2p_config_to_path ( conf . configPath / " p2p.json " ) ;
2026-05-14 11:53:36 +03:00
NetLibrary : : init_config ( conf . configPath / " p2p.json " ) ;
2026-03-20 20:53:59 +03:00
2026-03-31 13:17:58 +03:00
update_display_names ( ) ;
2025-07-06 20:43:53 +03:00
}
2025-11-20 12:38:28 +03:00
void MainProgram : : set_vsync_value ( int vsyncValue ) {
if ( ! SDL_GL_SetSwapInterval ( vsyncValue ) ) {
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . log ( Logger : : LogType : : INFO , " Vsync value " + std : : to_string ( vsyncValue ) + " not available. Setting to 1 " ) ;
2025-11-20 13:12:41 +03:00
if ( vsyncValue = = - 1 ) {
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . log ( Logger : : LogType : : DESKTOP_USERINFO , " Adaptive VSync not available. Setting Vsync to On " ) ;
2025-11-20 13:12:41 +03:00
vsyncValue = 1 ;
}
2025-11-20 12:38:28 +03:00
SDL_GL_SetSwapInterval ( 1 ) ;
}
2026-03-31 13:17:58 +03:00
conf . vsyncValue = vsyncValue ;
2025-11-20 12:38:28 +03:00
}
2025-11-23 12:53:50 +03:00
void MainProgram : : update_scale_and_density ( ) {
2025-11-23 20:51:14 +03:00
# ifdef __EMSCRIPTEN__
window . scale = 1.0f ;
# else
2026-03-31 13:17:58 +03:00
window . scale = conf . applyDisplayScale ? SDL_GetWindowDisplayScale ( window . sdlWindow ) : 1.0f ;
2025-11-23 20:51:14 +03:00
# endif
2025-11-23 12:53:50 +03:00
window . density = SDL_GetWindowPixelDensity ( window . sdlWindow ) ;
}
float MainProgram : : get_scale_and_density_factor_gui ( ) {
return window . scale * window . density ;
}
2026-03-20 20:53:59 +03:00
void MainProgram : : refresh_draw_surfaces ( ) {
if ( window . canCreateSurfaces ) {
window . defaultMSAASampleCount = 0 ;
2026-03-31 13:17:58 +03:00
window . defaultMSAASurfaceProps = SkSurfaceProps ( conf . antialiasing = = GlobalConfig : : AntiAliasing : : DYNAMIC_MSAA ? SkSurfaceProps : : kDynamicMSAA_Flag : SkSurfaceProps : : kDefault_Flag , kUnknown_SkPixelGeometry ) ;
2026-04-09 01:05:52 +03:00
if ( conf . antialiasing = = GlobalConfig : : AntiAliasing : : DYNAMIC_MSAA )
window . intermediateSurfaceMSAA = create_native_surface ( window . size , true ) ;
else
window . intermediateSurfaceMSAA = nullptr ;
2026-03-20 20:53:59 +03:00
2026-04-08 17:34:50 +03:00
g . delete_cache_surface ( ) ;
2026-03-20 20:53:59 +03:00
DrawingProgramCache : : delete_all_draw_cache ( ) ;
}
}
2026-04-17 19:32:49 +03:00
void MainProgram : : draw_world ( SkCanvas * canvas , std : : shared_ptr < World > worldToDraw , const DrawData & drawData ) {
2026-02-25 13:26:35 +03:00
canvas - > clear ( drawData . transparentBackground ? SkColor4f { 0.0f , 0.0f , 0.0f , 0.0f } : worldToDraw - > canvasTheme . get_back_color ( ) ) ;
2026-03-20 20:53:59 +03:00
DrawData drawDataCopy = drawData ;
2026-03-31 13:17:58 +03:00
drawDataCopy . skiaAA = conf . antialiasing = = GlobalConfig : : AntiAliasing : : SKIA ;
2026-03-20 20:53:59 +03:00
worldToDraw - > draw ( canvas , drawDataCopy ) ;
2026-04-17 19:32:49 +03:00
}
void MainProgram : : draw ( SkCanvas * canvas ) {
screen - > draw ( canvas ) ;
g . draw ( canvas , conf . antialiasing = = GlobalConfig : : AntiAliasing : : SKIA ) ;
2025-07-06 20:43:53 +03:00
}
2026-02-16 21:27:42 +03:00
sk_sp < SkSurface > MainProgram : : create_native_surface ( Vector2i resolution , bool isMSAA ) {
2026-03-20 20:53:59 +03:00
if ( window . canCreateSurfaces ) {
2026-04-09 01:52:19 +03:00
SkImageInfo imgInfo = SkImageInfo : : MakeN32Premul ( resolution . x ( ) , resolution . y ( ) ) ;
2026-03-20 20:53:59 +03:00
SkSurfaceProps defaultProps ;
# ifdef USE_SKIA_BACKEND_GRAPHITE
auto surfaceToRet = SkSurfaces : : RenderTarget ( window . recorder ( ) , imgInfo , skgpu : : Mipmapped : : kNo , isMSAA ? & window . defaultMSAASurfaceProps : & defaultProps ) ;
# elif USE_SKIA_BACKEND_GANESH
auto surfaceToRet = SkSurfaces : : RenderTarget ( window . ctx . get ( ) , skgpu : : Budgeted : : kNo , imgInfo , isMSAA ? window . defaultMSAASampleCount : 0 , isMSAA ? & window . defaultMSAASurfaceProps : & defaultProps ) ;
# endif
if ( ! surfaceToRet )
throw std : : runtime_error ( " [MainProgram::create_native_surface] Could not make native surface " ) ;
return surfaceToRet ;
}
return nullptr ;
2026-02-16 21:27:42 +03:00
}
2026-04-11 16:36:24 +03:00
void MainProgram : : create_new_tab ( const CustomEvents : : OpenInfiniPaintFileEvent & openFile ) {
2026-04-05 20:26:03 +03:00
std : : shared_ptr < World > newWorld ;
try {
newWorld = std : : make_shared < World > ( * this , openFile ) ;
}
catch ( const std : : runtime_error & e ) {
2026-06-01 15:42:33 +03:00
# ifdef __ANDROID__
Logger : : get ( ) . log ( Logger : : LogType : : WORLDFATAL , std : : string ( " Failed to open canvas: " ) + e . what ( ) ) ;
# else
2026-05-16 21:56:58 +03:00
Logger : : get ( ) . log ( Logger : : LogType : : WORLDFATAL , " Failed to open canvas: " + ( openFile . filePathSource . has_value ( ) ? openFile . filePathSource . value ( ) . string ( ) : " NO PATH " ) + " with error: " + e . what ( ) ) ;
2026-06-01 15:42:33 +03:00
# endif
2026-04-05 20:26:03 +03:00
return ;
}
worlds . emplace_back ( newWorld ) ;
switch_to_tab ( worlds . size ( ) - 1 ) ;
g . gui . set_to_layout ( ) ;
}
void MainProgram : : set_tab_to_close ( World * world ) {
tabsToClose . emplace ( world ) ;
}
void MainProgram : : switch_to_tab ( size_t wIndex ) {
world = worlds [ wIndex ] ;
}
void MainProgram : : post_callback ( ) {
2026-04-28 14:51:35 +03:00
g . gui . run_post_callback_func ( ) ;
2026-04-05 20:26:03 +03:00
close_set_to_close_tabs ( ) ;
2026-05-14 11:53:36 +03:00
run_new_screen_func ( ) ;
2026-04-28 14:51:35 +03:00
g . gui . layout_if_necessary ( ) ;
}
2026-05-17 22:18:53 +03:00
void MainProgram : : input_app_about_to_go_to_background_callback ( ) {
2026-05-18 09:45:17 +03:00
screen - > input_app_about_to_go_to_background_callback ( ) ;
save_config ( ) ;
2026-05-14 11:53:36 +03:00
NetThreadManager : : get ( ) . go_to_background ( ) ;
}
void MainProgram : : input_app_about_to_go_to_foreground_callback ( ) {
NetThreadManager : : get ( ) . go_to_foreground ( ) ;
2026-04-05 20:26:03 +03:00
}
2026-04-11 16:36:24 +03:00
void MainProgram : : input_add_file_to_canvas_callback ( const CustomEvents : : AddFileToCanvasEvent & addFile ) {
2026-04-12 12:36:00 +03:00
screen - > input_add_file_to_canvas_callback ( addFile ) ;
2026-04-11 16:36:24 +03:00
post_callback ( ) ;
}
void MainProgram : : input_open_infinipaint_file_callback ( const CustomEvents : : OpenInfiniPaintFileEvent & openFile ) {
2026-04-12 12:36:00 +03:00
screen - > input_open_infinipaint_file_callback ( openFile ) ;
2026-04-27 21:30:31 +03:00
g . gui . set_to_layout ( ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
}
2026-06-01 15:42:33 +03:00
void MainProgram : : input_mobile_import_canvas_callback ( const CustomEvents : : MobileImportCanvasEvent & mobileImport ) {
screen - > input_mobile_import_canvas_callback ( mobileImport ) ;
g . gui . set_to_layout ( ) ;
post_callback ( ) ;
}
2026-04-11 16:36:24 +03:00
void MainProgram : : input_paste_callback ( const CustomEvents : : PasteEvent & paste ) {
2026-04-04 17:52:43 +03:00
g . input_paste_callback ( paste ) ;
2026-04-12 12:36:00 +03:00
screen - > input_paste_callback ( paste ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-04-04 17:52:43 +03:00
}
2026-05-11 23:06:39 +03:00
void MainProgram : : input_android_text_box_input_callback ( const CustomEvents : : AndroidTextBoxInputEvent & textboxInput ) {
g . input_android_text_box_input_callback ( textboxInput ) ;
screen - > input_android_text_box_input_callback ( textboxInput ) ;
post_callback ( ) ;
}
2026-04-27 17:20:16 +03:00
void MainProgram : : input_global_back_button_callback ( ) {
screen - > input_global_back_button_callback ( ) ;
post_callback ( ) ;
}
2026-04-02 19:18:39 +03:00
bool MainProgram : : input_keybind_callback ( const Vector2ui32 & newKey ) {
if ( keybindWaiting . has_value ( ) ) {
unsigned v = keybindWaiting . value ( ) ;
input . keyAssignments . erase ( newKey ) ;
auto f = std : : find_if ( input . keyAssignments . begin ( ) , input . keyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = v ;
} ) ;
if ( f ! = input . keyAssignments . end ( ) )
input . keyAssignments . erase ( f ) ;
input . keyAssignments . emplace ( newKey , v ) ;
keybindWaiting = std : : nullopt ;
g . gui . set_to_layout ( ) ;
2026-04-05 22:01:30 +03:00
post_callback ( ) ;
2026-04-02 19:18:39 +03:00
return true ;
}
return false ;
}
2026-03-04 11:03:05 +03:00
void MainProgram : : input_drop_file_callback ( const InputManager : : DropCallbackArgs & drop ) {
2026-04-12 12:36:00 +03:00
screen - > input_drop_file_callback ( drop ) ;
post_callback ( ) ;
2026-03-04 11:03:05 +03:00
}
void MainProgram : : input_drop_text_callback ( const InputManager : : DropCallbackArgs & drop ) {
2026-04-12 12:36:00 +03:00
screen - > input_drop_text_callback ( drop ) ;
post_callback ( ) ;
2026-03-04 11:03:05 +03:00
}
2026-03-02 16:36:34 +03:00
void MainProgram : : input_key_callback ( const InputManager : : KeyCallbackArgs & key ) {
2026-04-12 12:36:00 +03:00
if ( key . key = = InputManager : : KEY_FULLSCREEN ) {
if ( key . down & & ! key . repeat )
toggle_full_screen ( ) ;
2026-03-02 16:36:34 +03:00
}
2026-03-31 17:33:09 +03:00
g . input_key_callback ( key ) ;
2026-04-12 12:36:00 +03:00
screen - > input_key_callback ( key ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-03-02 16:36:34 +03:00
}
2026-04-03 23:02:33 +03:00
void MainProgram : : input_text_key_callback ( const InputManager : : KeyCallbackArgs & key ) {
g . input_text_key_callback ( key ) ;
2026-04-12 12:36:00 +03:00
screen - > input_text_key_callback ( key ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-04-02 10:15:22 +03:00
}
2026-04-04 17:52:43 +03:00
void MainProgram : : input_text_callback ( const InputManager : : TextCallbackArgs & text ) {
g . input_text_callback ( text ) ;
2026-04-12 12:36:00 +03:00
screen - > input_text_callback ( text ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-04-02 10:15:22 +03:00
}
2026-03-02 16:36:34 +03:00
void MainProgram : : input_mouse_button_callback ( const InputManager : : MouseButtonCallbackArgs & button ) {
2026-03-31 17:33:09 +03:00
g . input_mouse_button_callback ( button ) ;
2026-04-12 12:36:00 +03:00
screen - > input_mouse_button_callback ( button ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-03-02 16:36:34 +03:00
}
void MainProgram : : input_mouse_motion_callback ( const InputManager : : MouseMotionCallbackArgs & motion ) {
2026-03-31 17:33:09 +03:00
g . input_mouse_motion_callback ( motion ) ;
2026-04-12 12:36:00 +03:00
screen - > input_mouse_motion_callback ( motion ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-03-02 16:36:34 +03:00
}
2026-03-02 23:09:48 +03:00
void MainProgram : : input_mouse_wheel_callback ( const InputManager : : MouseWheelCallbackArgs & wheel ) {
2026-03-31 17:33:09 +03:00
g . input_mouse_wheel_callback ( wheel ) ;
2026-04-12 12:36:00 +03:00
screen - > input_mouse_wheel_callback ( wheel ) ;
2026-04-05 20:26:03 +03:00
post_callback ( ) ;
2026-03-02 23:09:48 +03:00
}
2026-03-03 14:59:18 +03:00
void MainProgram : : input_pen_button_callback ( const InputManager : : PenButtonCallbackArgs & button ) {
2026-04-12 12:36:00 +03:00
screen - > input_pen_button_callback ( button ) ;
post_callback ( ) ;
2026-03-03 14:59:18 +03:00
}
void MainProgram : : input_pen_touch_callback ( const InputManager : : PenTouchCallbackArgs & touch ) {
2026-04-12 12:36:00 +03:00
screen - > input_pen_touch_callback ( touch ) ;
post_callback ( ) ;
2026-03-03 14:59:18 +03:00
}
void MainProgram : : input_pen_motion_callback ( const InputManager : : PenMotionCallbackArgs & motion ) {
2026-04-12 12:36:00 +03:00
screen - > input_pen_motion_callback ( motion ) ;
post_callback ( ) ;
2026-03-03 16:01:21 +03:00
}
void MainProgram : : input_pen_axis_callback ( const InputManager : : PenAxisCallbackArgs & axis ) {
2026-04-12 12:36:00 +03:00
screen - > input_pen_axis_callback ( axis ) ;
post_callback ( ) ;
2026-03-03 14:59:18 +03:00
}
2026-03-05 21:09:30 +03:00
void MainProgram : : input_multi_finger_touch_callback ( const InputManager : : MultiFingerTouchCallbackArgs & touch ) {
2026-04-12 12:36:00 +03:00
screen - > input_multi_finger_touch_callback ( touch ) ;
post_callback ( ) ;
2026-03-05 12:03:10 +03:00
}
2026-03-05 21:09:30 +03:00
void MainProgram : : input_multi_finger_motion_callback ( const InputManager : : MultiFingerMotionCallbackArgs & motion ) {
2026-04-12 12:36:00 +03:00
screen - > input_multi_finger_motion_callback ( motion ) ;
post_callback ( ) ;
2026-03-05 12:03:10 +03:00
}
2026-03-05 21:09:30 +03:00
void MainProgram : : input_finger_touch_callback ( const InputManager : : FingerTouchCallbackArgs & touch ) {
2026-04-28 20:13:47 +03:00
g . input_finger_touch_callback ( touch ) ;
2026-04-12 12:36:00 +03:00
screen - > input_finger_touch_callback ( touch ) ;
post_callback ( ) ;
2026-03-05 21:09:30 +03:00
}
void MainProgram : : input_finger_motion_callback ( const InputManager : : FingerMotionCallbackArgs & motion ) {
2026-04-28 20:13:47 +03:00
g . input_finger_motion_callback ( motion ) ;
2026-04-12 12:36:00 +03:00
screen - > input_finger_motion_callback ( motion ) ;
post_callback ( ) ;
2026-03-05 21:09:30 +03:00
}
2026-04-07 20:21:41 +03:00
void MainProgram : : input_window_resize_callback ( const InputManager : : WindowResizeCallbackArgs & w ) {
2026-04-09 20:44:45 +03:00
for ( auto & wor : worlds )
wor - > drawData . cam . set_viewing_area ( window . size . cast < float > ( ) ) ;
2026-04-07 20:21:41 +03:00
g . window_update ( ) ;
2026-04-12 12:36:00 +03:00
screen - > input_window_resize_callback ( w ) ;
2026-04-07 20:21:41 +03:00
post_callback ( ) ;
}
void MainProgram : : input_window_scale_callback ( const InputManager : : WindowScaleCallbackArgs & w ) {
g . window_update ( ) ;
2026-04-12 12:36:00 +03:00
screen - > input_window_scale_callback ( w ) ;
2026-04-07 20:21:41 +03:00
post_callback ( ) ;
}
2026-06-04 09:21:53 +03:00
void MainProgram : : input_window_mouse_focus_gained ( ) {
# ifndef __ANDROID__
update_main_loop_call_rate ( conf . mainCallbackRate ) ;
# endif
}
void MainProgram : : input_window_mouse_focus_lost ( ) {
# ifndef __ANDROID__
if ( ! window . mouseFocus & & ! window . windowFocus )
update_main_loop_call_rate ( conf . mainCallbackRateBackground ) ;
# endif
}
void MainProgram : : input_window_focus_gained ( ) {
# ifndef __ANDROID__
update_main_loop_call_rate ( conf . mainCallbackRate ) ;
# endif
}
void MainProgram : : input_window_focus_lost ( ) {
# ifndef __ANDROID__
if ( ! window . mouseFocus & & ! window . windowFocus )
update_main_loop_call_rate ( conf . mainCallbackRateBackground ) ;
# endif
}
2026-05-07 21:48:47 +03:00
std : : optional < InputManager : : TextBoxStartInfo > MainProgram : : get_text_box_start_info ( ) {
auto toRet = g . get_text_box_start_info ( ) ;
if ( toRet )
return toRet ;
return screen - > get_text_box_start_info ( ) ;
}
2026-04-07 20:33:48 +03:00
void MainProgram : : toggle_full_screen ( ) {
window . fullscreen = ! window . fullscreen ;
SDL_SetWindowFullscreen ( window . sdlWindow , window . fullscreen ) ;
}
2026-06-04 09:21:53 +03:00
void MainProgram : : update_main_loop_call_rate ( unsigned newCallbackRate ) {
SDL_SetHint ( SDL_HINT_MAIN_CALLBACK_RATE , std : : to_string ( newCallbackRate ) . c_str ( ) ) ;
}
2026-04-28 14:51:35 +03:00
void MainProgram : : set_first_screen ( std : : unique_ptr < Screen > firstScreen ) {
set_screen ( [ & ] ( std : : unique_ptr < Screen > ) { return std : : move ( firstScreen ) ; } ) ;
run_new_screen_func ( ) ;
}
void MainProgram : : run_new_screen_func ( ) {
if ( newScreenFunc ) {
screen = newScreenFunc ( std : : move ( screen ) ) ;
g . gui . io . redrawSurface = true ;
g . gui . set_to_layout ( ) ;
newScreenFunc = nullptr ;
}
}
2026-05-17 19:14:03 +03:00
float MainProgram : : calculate_gui_scale ( ) {
return screen - > calculate_gui_scale ( ) ;
}
2026-04-28 14:51:35 +03:00
void MainProgram : : set_screen ( std : : function < std : : unique_ptr < Screen > ( std : : unique_ptr < Screen > ) > screenFunc ) {
newScreenFunc = screenFunc ;
2026-04-27 21:30:31 +03:00
}
2026-04-05 20:26:03 +03:00
void MainProgram : : close_set_to_close_tabs ( ) {
if ( ! tabsToClose . empty ( ) ) {
std : : erase_if ( worlds , [ & ] ( auto & w ) {
if ( tabsToClose . contains ( w . get ( ) ) ) {
if ( w = = world )
world = nullptr ;
return true ;
}
return false ;
} ) ;
tabsToClose . clear ( ) ;
2026-05-14 11:53:36 +03:00
screen - > on_tab_close ( ) ;
2026-04-05 20:26:03 +03:00
g . gui . set_to_layout ( ) ;
}
}
2025-07-06 20:43:53 +03:00
bool MainProgram : : network_being_used ( ) {
for ( auto & w : worlds ) {
2026-01-01 22:46:02 +03:00
if ( w - > netObjMan . is_connected ( ) )
2025-07-06 20:43:53 +03:00
return true ;
}
return false ;
}
bool MainProgram : : net_server_hosted ( ) {
for ( auto & w : worlds ) {
2026-01-01 22:46:02 +03:00
if ( w - > netServer )
2025-07-06 20:43:53 +03:00
return true ;
}
return false ;
}
void MainProgram : : early_destroy ( ) {
for ( auto & w : worlds )
w - > early_destroy ( ) ;
}
MainProgram : : ~ MainProgram ( ) {
2026-04-23 20:56:11 +03:00
screen = nullptr ; // Destroy screen first to let destructor run
2026-05-14 11:53:36 +03:00
NetThreadManager : : get ( ) . destroy ( ) ;
2025-07-06 20:43:53 +03:00
}