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-03-31 13:17:58 +03:00
# include "GlobalConfig.hpp"
# include <Helpers/Random.hpp>
2026-04-23 20:56:11 +03:00
# include "Helpers/StringHelpers.hpp"
2026-03-31 13:17:58 +03:00
# include "ResourceDisplay/ImageResourceDisplay.hpp"
# include "DrawingProgram/DrawingProgramCache.hpp"
2026-04-20 18:56:39 +03:00
# include <SDL3/SDL_time.h>
2026-03-31 13:17:58 +03:00
GlobalConfig : : GlobalConfig ( ) {
2026-04-12 12:36:00 +03:00
load_default_palette ( ) ;
2026-03-31 13:17:58 +03:00
displayName = Random : : get ( ) . alphanumeric_str ( 10 ) ;
2026-04-20 18:56:39 +03:00
SDL_GetDateTimeLocalePreferences ( & dateFormat , & timeFormat ) ;
2026-05-13 14:31:35 +03:00
if ( dateFormat = = SDL_DATE_FORMAT_YYYYMMDD ) // Can't display this in a friendly way, so changing it for now
dateFormat = SDL_DATE_FORMAT_DDMMYYYY ;
2026-05-14 21:23:36 +03:00
if ( timeFormat = = SDL_TIME_FORMAT_24HR )
timeFormat = SDL_TIME_FORMAT_12HR ; // For now, lock at 12 hour since it's the most used format, and time format detection doesn't work properly all the time
2026-03-31 13:17:58 +03:00
}
nlohmann : : json GlobalConfig : : get_config_json ( const InputManager & input ) const {
using json = nlohmann : : json ;
json toRet ;
json jKeybinds ;
for ( unsigned i = 0 ; i < InputManager : : KEY_ASSIGNABLE_COUNT ; i + + ) {
auto f = std : : find_if ( input . keyAssignments . begin ( ) , input . keyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = i ;
} ) ;
if ( f ! = input . keyAssignments . end ( ) )
jKeybinds [ json ( static_cast < InputManager : : KeyCodeEnum > ( i ) ) ] = input . key_assignment_to_str ( f - > first ) ;
}
toRet [ " keybinds " ] = jKeybinds ;
toRet [ " guiScale " ] = guiScale ;
toRet [ " jumpTransitionTime " ] = jumpTransitionTime ;
toRet [ " disableGraphicsDriverWorkarounds " ] = disableGraphicsDriverWorkarounds ;
2026-05-14 14:39:34 +03:00
toRet [ " viewWebVersionWelcome " ] = viewWebVersionWelcome ;
2026-03-31 13:17:58 +03:00
toRet [ " dragZoomSpeed " ] = dragZoomSpeed ;
toRet [ " scrollZoomSpeed " ] = scrollZoomSpeed ;
toRet [ " vsync " ] = vsyncValue ;
# ifndef __EMSCRIPTEN__
toRet [ " applyDisplayScale " ] = applyDisplayScale ;
# endif
toRet [ " displayName " ] = displayName ;
toRet [ " antialiasing " ] = antialiasing ;
toRet [ " useNativeFilePicker " ] = useNativeFilePicker ;
toRet [ " themeInUse " ] = themeCurrentlyLoaded ;
toRet [ " defaultCanvasBackgroundColor " ] = defaultCanvasBackgroundColor ;
toRet [ " flipZoomToolDirection " ] = flipZoomToolDirection ;
2026-05-25 14:46:22 +03:00
toRet [ " realTimeEraser " ] = realTimeEraser ;
2026-03-31 13:17:58 +03:00
# ifndef __EMSCRIPTEN__
toRet [ " checkForUpdates " ] = checkForUpdates ;
# endif
json tablet ;
2026-05-16 20:40:23 +03:00
tablet [ " brushPressureSmoothingFactor " ] = tabletOptions . brushPressureSmoothingFactor ;
2026-03-31 13:17:58 +03:00
tablet [ " pressureAffectsBrushWidth " ] = tabletOptions . pressureAffectsBrushWidth ;
tablet [ " middleClickButton " ] = tabletOptions . middleClickButton ;
tablet [ " rightClickButton " ] = tabletOptions . rightClickButton ;
tablet [ " ignoreMouseMovementWhenPenInProximity " ] = tabletOptions . ignoreMouseMovementWhenPenInProximity ;
2026-05-15 09:51:25 +03:00
tablet [ " disableTouchWhenPenInProximity " ] = tabletOptions . disableTouchWhenPenInProximity ;
2026-03-31 13:17:58 +03:00
tablet [ " brushMinimumSize " ] = tabletOptions . brushMinimumSize ;
tablet [ " zoomWhilePenDownAndButtonHeld " ] = tabletOptions . zoomWhilePenDownAndButtonHeld ;
toRet [ " tablet " ] = tablet ;
json debugJson ;
2026-05-14 20:21:45 +03:00
debugJson [ " mobileUI " ] = mobileUI ;
2026-03-31 13:17:58 +03:00
debugJson [ " jumpTransitionEasing " ] = jumpTransitionEasing ;
debugJson [ " imageLoadMaxThreads " ] = ImageResourceDisplay : : IMAGE_LOAD_THREAD_COUNT_MAX ;
debugJson [ " cacheNodeResolution " ] = DrawingProgramCache : : CACHE_NODE_RESOLUTION ;
debugJson [ " maxCacheNodes " ] = DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES ;
debugJson [ " maxComponentsInNode " ] = DrawingProgramCache : : MAXIMUM_COMPONENTS_IN_SINGLE_NODE ;
debugJson [ " componentCountToForceCacheRebuild " ] = DrawingProgramCache : : MINIMUM_COMPONENTS_TO_START_REBUILD ;
debugJson [ " maximumFrameTimeToForceCacheRebuild " ] = DrawingProgramCache : : MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH ;
debugJson [ " millisecondMinimumTimeToCheckForCacheRebuild " ] = DrawingProgramCache : : MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH ;
toRet [ " debug " ] = debugJson ;
return toRet ;
}
void GlobalConfig : : set_config_json ( InputManager & input , const nlohmann : : json & j , VersionNumber version ) {
using json = nlohmann : : json ;
input . keyAssignments . clear ( ) ;
try {
const json & jKeybinds = j . at ( " keybinds " ) ;
for ( unsigned i = 0 ; i < InputManager : : KEY_ASSIGNABLE_COUNT ; i + + ) {
try {
Vector2ui32 a = input . key_assignment_from_str ( jKeybinds . at ( json ( static_cast < InputManager : : KeyCodeEnum > ( i ) ) ) ) ;
if ( a ! = Vector2ui32 { 0 , 0 } )
input . keyAssignments . emplace ( a , i ) ;
else
throw ;
}
catch ( . . . ) {
auto f = std : : find_if ( input . defaultKeyAssignments . begin ( ) , input . defaultKeyAssignments . end ( ) , [ & ] ( auto & p ) {
return p . second = = i ;
} ) ;
input . keyAssignments . emplace ( f - > first , i ) ;
}
}
}
catch ( . . . ) {
input . keyAssignments = input . defaultKeyAssignments ;
}
try { j . at ( " displayName " ) . get_to ( displayName ) ; } catch ( . . . ) { }
try { j . at ( " dragZoomSpeed " ) . get_to ( dragZoomSpeed ) ; } catch ( . . . ) { }
try { j . at ( " scrollZoomSpeed " ) . get_to ( scrollZoomSpeed ) ; } catch ( . . . ) { }
try { j . at ( " vsync " ) . get_to ( vsyncValue ) ; } catch ( . . . ) { }
# ifndef __EMSCRIPTEN__
try { j . at ( " applyDisplayScale " ) . get_to ( applyDisplayScale ) ; } catch ( . . . ) { }
# endif
try { j . at ( " guiScale " ) . get_to ( guiScale ) ; } catch ( . . . ) { }
try { j . at ( " jumpTransitionTime " ) . get_to ( jumpTransitionTime ) ; } catch ( . . . ) { }
try { j . at ( " disableGraphicsDriverWorkarounds " ) . get_to ( disableGraphicsDriverWorkarounds ) ; } catch ( . . . ) { }
2026-05-14 14:39:34 +03:00
try { j . at ( " viewWebVersionWelcome " ) . get_to ( viewWebVersionWelcome ) ; } catch ( . . . ) { }
2026-03-31 13:17:58 +03:00
try { j . at ( " useNativeFilePicker " ) . get_to ( useNativeFilePicker ) ; } catch ( . . . ) { }
try { j . at ( " themeInUse " ) . get_to ( themeCurrentlyLoaded ) ; } catch ( . . . ) { }
if ( version > = VersionNumber ( 0 , 3 , 0 ) )
try { j . at ( " defaultCanvasBackgroundColor " ) . get_to ( defaultCanvasBackgroundColor ) ; } catch ( . . . ) { }
try { j . at ( " flipZoomToolDirection " ) . get_to ( flipZoomToolDirection ) ; } catch ( . . . ) { }
2026-05-25 14:46:22 +03:00
try { j . at ( " realTimeEraser " ) . get_to ( realTimeEraser ) ; } catch ( . . . ) { }
2026-03-31 13:17:58 +03:00
# ifndef __EMSCRIPTEN__
try { j . at ( " checkForUpdates " ) . get_to ( checkForUpdates ) ; } catch ( . . . ) { }
# endif
try { j . at ( " antialiasing " ) . get_to ( antialiasing ) ; } catch ( . . . ) { }
2026-05-16 20:40:23 +03:00
try { j . at ( " tablet " ) . at ( " brushPressureSmoothingFactor " ) . get_to ( tabletOptions . brushPressureSmoothingFactor ) ; } catch ( . . . ) { }
2026-03-31 13:17:58 +03:00
try { j . at ( " tablet " ) . at ( " pressureAffectsBrushWidth " ) . get_to ( tabletOptions . pressureAffectsBrushWidth ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " middleClickButton " ) . get_to ( tabletOptions . middleClickButton ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " rightClickButton " ) . get_to ( tabletOptions . rightClickButton ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " ignoreMouseMovementWhenPenInProximity " ) . get_to ( tabletOptions . ignoreMouseMovementWhenPenInProximity ) ; } catch ( . . . ) { }
2026-05-15 09:51:25 +03:00
try { j . at ( " tablet " ) . at ( " disableTouchWhenPenInProximity " ) . get_to ( tabletOptions . disableTouchWhenPenInProximity ) ; } catch ( . . . ) { }
2026-03-31 13:17:58 +03:00
try { j . at ( " tablet " ) . at ( " brushMinimumSize " ) . get_to ( tabletOptions . brushMinimumSize ) ; } catch ( . . . ) { }
try { j . at ( " tablet " ) . at ( " zoomWhilePenDownAndButtonHeld " ) . get_to ( tabletOptions . zoomWhilePenDownAndButtonHeld ) ; } catch ( . . . ) { }
2026-05-14 20:21:45 +03:00
try { j . at ( " debug " ) . at ( " mobileUI " ) . get_to ( mobileUI ) ; } catch ( . . . ) { }
2026-03-31 13:17:58 +03:00
try { j . at ( " debug " ) . at ( " jumpTransitionEasing " ) . get_to ( jumpTransitionEasing ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " imageLoadMaxThreads " ) . get_to ( ImageResourceDisplay : : IMAGE_LOAD_THREAD_COUNT_MAX ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " cacheNodeResolution " ) . get_to ( DrawingProgramCache : : CACHE_NODE_RESOLUTION ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " maxCacheNodes " ) . get_to ( DrawingProgramCache : : MAXIMUM_DRAW_CACHE_SURFACES ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " maxComponentsInNode " ) . get_to ( DrawingProgramCache : : MAXIMUM_COMPONENTS_IN_SINGLE_NODE ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " componentCountToForceCacheRebuild " ) . get_to ( DrawingProgramCache : : MINIMUM_COMPONENTS_TO_START_REBUILD ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " maximumFrameTimeToForceCacheRebuild " ) . get_to ( DrawingProgramCache : : MILLISECOND_FRAME_TIME_TO_FORCE_CACHE_REFRESH ) ; } catch ( . . . ) { }
try { j . at ( " debug " ) . at ( " millisecondMinimumTimeToCheckForCacheRebuild " ) . get_to ( DrawingProgramCache : : MILLISECOND_MINIMUM_TIME_TO_CHECK_FORCE_REFRESH ) ; } catch ( . . . ) { }
}
2026-04-12 12:36:00 +03:00
void GlobalConfig : : save_palettes ( ) {
2026-05-13 14:31:35 +03:00
nlohmann : : json j ;
2026-04-23 20:56:11 +03:00
auto palettesToSave = palettes ;
palettesToSave . erase ( palettesToSave . begin ( ) ) ;
nlohmann : : to_json ( j , palettesToSave ) ;
2026-05-13 14:31:35 +03:00
std : : string saveJson = nlohmann : : to_string ( j ) ;
SDL_SaveFile ( ( configPath / " palettes.json " ) . string ( ) . c_str ( ) , saveJson . c_str ( ) , saveJson . size ( ) ) ;
2026-04-12 12:36:00 +03:00
}
void GlobalConfig : : load_palettes ( ) {
load_default_palette ( ) ;
2026-04-23 20:56:11 +03:00
using json = nlohmann : : json ;
try {
json j ( nlohmann : : json : : parse ( read_file_to_string ( configPath / " palettes.json " ) ) ) ;
2026-05-13 14:31:35 +03:00
std : : vector < Palette > p ;
j . get_to ( p ) ;
palettes . insert ( palettes . end ( ) , p . begin ( ) , p . end ( ) ) ;
2026-04-23 20:56:11 +03:00
} catch ( . . . ) { }
2026-04-12 12:36:00 +03:00
}
void GlobalConfig : : load_default_palette ( ) {
palettes . clear ( ) ;
palettes . emplace_back ( ) ;
auto & palette = palettes . back ( ) . colors ;
palettes . back ( ) . name = " Default " ;
palette = { { 1.0 , 1.0 , 1.0 } , { 0.0 , 0.0 , 0.0 } , { 1.0 , 0.0 , 0.0 } , { 1.0 , 0.529411792755127 , 0.0 } , { 1.0 , 0.8274509906768799 , 0.0 } , { 0.8705882430076599 , 1.0 , 0.03921568766236305 } , { 0.6313725709915161 , 1.0 , 0.03921568766236305 } , { 0.03921568766236305 , 1.0 , 0.6000000238418579 } , { 0.03921568766236305 , 0.9372549057006836 , 1.0 } , { 0.0784313753247261 , 0.4901960790157318 , 0.9607843160629272 } , { 0.3450980484485626 , 0.03921568766236305 , 1.0 } , { 0.7450980544090271 , 0.03921568766236305 , 1.0 } } ;
}
void GlobalConfig : : load_licenses ( ) {
{
int globCount ;
std : : filesystem : : path third_party_license_path ( " data/third_party_licenses " ) ;
char * * filesInPath = SDL_GlobDirectory ( third_party_license_path . string ( ) . c_str ( ) , nullptr , 0 , & globCount ) ;
if ( filesInPath ) {
for ( int i = 0 ; i < globCount ; i + + ) {
std : : filesystem : : path filePath = third_party_license_path / std : : filesystem : : path ( filesInPath [ i ] ) ;
SDL_PathInfo fileInfo ;
if ( SDL_GetPathInfo ( filePath . string ( ) . c_str ( ) , & fileInfo ) & & fileInfo . type = = SDL_PATHTYPE_FILE ) {
thirdPartyLicenses . emplace_back ( filePath . filename ( ) . string ( ) , read_file_to_string ( filePath ) ) ;
}
}
SDL_free ( filesInPath ) ;
}
}
std : : sort ( thirdPartyLicenses . begin ( ) , thirdPartyLicenses . end ( ) , [ ] ( const auto & a1 , const auto & a2 ) {
return std : : lexicographical_compare ( a1 . first . begin ( ) , a1 . first . end ( ) , a2 . first . begin ( ) , a2 . first . end ( ) ) ;
} ) ;
2026-05-17 18:32:54 +03:00
ownLicenseText = " InfiniPaint v " + VersionConstants : : CURRENT_VERSION_STRING + " \n " ;
ownLicenseText + = read_file_to_string ( " data/license " ) ;
2026-04-12 12:36:00 +03:00
}