diff --git a/CMakeLists.txt b/CMakeLists.txt
index 118dc1e..bd9d37f 100755
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -12,11 +12,11 @@ set(CMAKE_C_COMPILER "clang")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
cmake_minimum_required(VERSION 3.18)
project(${PROJ_NAME} LANGUAGES C CXX)
-set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD 23)
file(STRINGS "./other/build.nr" BUILD_NUMBER)
#-----------------------------------------------------------------------------------------
-message(STATUS "Building ${PROJ_NAME} ${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}")
+message("** Building ${PROJ_NAME} ${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Debug Configuration")
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
@@ -35,26 +35,74 @@ list(APPEND SOURCE_FILES
#Targets
#-----------------------------------------------------------------------------------------
-set(APP_TARGET pianolib)
-add_executable(${APP_TARGET} ${SOURCE_FILES})
-target_include_directories(${APP_TARGET} PUBLIC ${INCLUDE_DIRS})
+set(MAIN_TARGET pianolib)
+add_executable(${MAIN_TARGET} ${SOURCE_FILES})
+if(WIN32)
+ target_sources(${MAIN_TARGET} PUBLIC "${CMAKE_CURRENT_LIST_DIR}/other/appIcon.rc")
+endif()
+target_include_directories(${MAIN_TARGET} PUBLIC ${INCLUDE_DIRS})
+
+target_compile_definitions(${MAIN_TARGET} PUBLIC BUILD_NR=${BUILD_NUMBER} MAJ_V=${MAJOR_VER} MIN_V=${MINOR_VER} VERSION_STR="${MAJOR_VER}.${MINOR_VER}.${BUILD_NUMBER}")
+
+if (WIN32)
+ set(MSYS2_UCRT64 "C:/msys64/ucrt64")
+ target_include_directories(${MAIN_TARGET} PRIVATE
+ "${MSYS2_UCRT64}/include"
+ "${MSYS2_UCRT64}/include/c++/13.2.0"
+ "${MSYS2_UCRT64}/include/c++/13.2.0/x86_64-w64-mingw32"
+ )
+endif()
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
- add_compile_options(-O3 -MMD -MP -Wall -ggdb -fsanitize=address)
- target_compile_definitions(${APP_TARGET} PUBLIC PIANOLIB_DEBUG_BUILD)
+ target_compile_definitions(${MAIN_TARGET} PUBLIC BUILD_CONFIG_DEBUG OX_DEBUG_BUILD)
+ add_compile_options(-MMD -MP -Wall -ggdb)
elseif(CMAKE_BUILD_TYPE STREQUAL "Release")
- add_compile_options(-MMD -MP -Wall)
- target_compile_definitions(${APP_TARGET} PUBLIC PIANOLIB_RELEASE_BUILD)
+ target_compile_definitions(${MAIN_TARGET} PUBLIC BUILD_CONFIG_RELEASE OX_RELEASE_BUILD)
+ add_compile_options(-O3 -MMD -MP -Wall)
+endif()
+
+if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Release")
+ set_target_properties(${MAIN_TARGET} PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ )
endif()
#-----------------------------------------------------------------------------------------
#Linking
#-----------------------------------------------------------------------------------------
-target_link_libraries(${APP_TARGET} PUBLIC ostd ogfx)
+if (WIN32)
+ target_link_libraries(${MAIN_TARGET} mingw32 SDL3 SDL3_ttf SDL3_image)
+endif (WIN32)
if (APPLE)
cmake_policy(SET CMP0167 OLD)
+ find_package(Boost REQUIRED COMPONENTS regex)
+ target_link_libraries(${MAIN_TARGET} Boost::regex)
+ find_package(PkgConfig REQUIRED)
+ pkg_check_modules(SDL3 REQUIRED sdl3)
+ pkg_check_modules(SDL3_IMAGE REQUIRED sdl3-image)
+ pkg_check_modules(SDL3_TTF REQUIRED sdl3-ttf)
+ target_include_directories(${MAIN_TARGET} PUBLIC
+ ${SDL3_INCLUDE_DIRS}
+ ${SDL3_IMAGE_INCLUDE_DIRS}
+ ${SDL3_TTF_INCLUDE_DIRS}
+ )
+ target_link_directories(${MAIN_TARGET} PUBLIC
+ ${SDL3_LIBRARY_DIRS}
+ ${SDL3_IMAGE_LIBRARY_DIRS}
+ ${SDL3_TTF_LIBRARY_DIRS}
+ )
+ target_link_libraries(${MAIN_TARGET}
+ ${SDL3_LIBRARIES}
+ ${SDL3_IMAGE_LIBRARIES}
+ ${SDL3_TTF_LIBRARIES}
+ )
elseif (UNIX)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-rpath,'$ORIGIN',-rpath,/usr/lib,-rpath,/usr/local/lib")
+ target_link_libraries(${MAIN_TARGET} xcb xcb-randr boost_regex SDL3 SDL3_image)
endif ()
+
+target_compile_definitions(${MAIN_TARGET} PUBLIC CPPHTTPLIB_MBEDTLS_SUPPORT)
+target_link_libraries(${MAIN_TARGET} mbedtls mbedx509 mbedcrypto)
+target_link_libraries(${MAIN_TARGET} ostd ogfx)
#-----------------------------------------------------------------------------------------
diff --git a/build b/build
index 14583e9..c7bd301 100755
--- a/build
+++ b/build
@@ -22,18 +22,52 @@ if [[ "$1" == "debug" || "$1" == "release" ]]; then
cmake -B bin -S ./ -G "$MAKEFILE_TYPE" -DCMAKE_BUILD_TYPE=$BUILD_TYPE
$MAKE_COMMAND -C bin $NJOBS --no-print-directory
cp -r extra/* bin/
- build_number=$(cat other/build.nr)
- build_number=$((build_number + 1))
+ typeset -i build_number=$(cat other/build.nr)
+ ((build_number++))
truncate -s 0 other/build.nr
echo $build_number >> other/build.nr
+# elif [[ "$1" == "windows_release" ]]; then
+# cd other
+# chmod +x build_windows_release.sh
+# ./build_windows_release.sh
+# cd ../bin/DragonV2_w64
+# cd ../..
+# elif [[ "$1" == "linux_release" ]]; then
+# cd other
+# chmod +x build_linux_release.sh
+# ./build_linux_release.sh
+# cd ../bin/DragonV2_linux64
+# cd ../..
+# elif [[ "$1" == "dependencies" ]]; then
+# cd other
+# chmod +x build_dependencies.sh
+# ./build_dependencies.sh
+# cd ..
else
cp -r extra/* bin/
printf "\n\033[0;32mBuilding Changes...\n\n\033[0m"
$MAKE_COMMAND -C bin $NJOBS --no-print-directory
if [[ "$1" == "run" ]]; then
- printf "\n\033[0;32mRunning PianoLibrary...\n\n\033[0m"
- cd bin
+ printf "\n\033[0;32mRunning Application...\n\n\033[0m"
+ cd bin
./pianolib
cd ..
+ elif [[ "$1" == "gdb" ]]; then
+ printf "\n\033[0;32mDebugging Application...\n\n\033[0m"
+ cd bin
+ gdb -batch \
+ -ex 'set pagination off' \
+ -ex 'set print pretty on' \
+ -ex 'set logging file gdb-last.log' \
+ -ex 'set logging overwrite on' \
+ -ex 'set logging on' \
+ -ex 'handle SIGPIPE nostop noprint pass' \
+ -ex run \
+ -ex 'bt full' \
+ -ex 'info locals' \
+ -ex 'info args' \
+ -ex quit \
+ --args ./pianolib
+ cd ..
fi
fi
diff --git a/extra/res/icons.png b/extra/res/icons.png
new file mode 100644
index 0000000..43768d4
Binary files /dev/null and b/extra/res/icons.png differ
diff --git a/extra/res/loading.png b/extra/res/loading.png
new file mode 100644
index 0000000..b2099ed
Binary files /dev/null and b/extra/res/loading.png differ
diff --git a/extra/res/test.png b/extra/res/test.png
deleted file mode 100644
index ad258d3..0000000
Binary files a/extra/res/test.png and /dev/null differ
diff --git a/extra/res/ttf/Courier Prime.ttf b/extra/res/ttf/Courier Prime.ttf
deleted file mode 100644
index db4e6c1..0000000
Binary files a/extra/res/ttf/Courier Prime.ttf and /dev/null differ
diff --git a/extra/res/ttf/MonospaceTypewriter.ttf b/extra/res/ttf/MonospaceTypewriter.ttf
deleted file mode 100644
index 29e9a67..0000000
Binary files a/extra/res/ttf/MonospaceTypewriter.ttf and /dev/null differ
diff --git a/extra/res/ttf/NK57 Monospace Cd Rg.otf b/extra/res/ttf/NK57 Monospace Cd Rg.otf
deleted file mode 100644
index b9265fd..0000000
Binary files a/extra/res/ttf/NK57 Monospace Cd Rg.otf and /dev/null differ
diff --git a/extra/res/ttf/Roboto-Regular.ttf b/extra/res/ttf/Roboto-Regular.ttf
deleted file mode 100644
index 7d9a6c4..0000000
Binary files a/extra/res/ttf/Roboto-Regular.ttf and /dev/null differ
diff --git a/extra/res/ttf/Roboto-Thin.ttf b/extra/res/ttf/Roboto-Thin.ttf
deleted file mode 100644
index 861d63a..0000000
Binary files a/extra/res/ttf/Roboto-Thin.ttf and /dev/null differ
diff --git a/extra/res/ttf/UbuntuMono-R.ttf b/extra/res/ttf/UbuntuMono-R.ttf
deleted file mode 100644
index fdd309d..0000000
Binary files a/extra/res/ttf/UbuntuMono-R.ttf and /dev/null differ
diff --git a/extra/res/ttf/cmunorm.ttf b/extra/res/ttf/cmunorm.ttf
deleted file mode 100644
index 72cfd42..0000000
Binary files a/extra/res/ttf/cmunorm.ttf and /dev/null differ
diff --git a/extra/res/ttf/digital7mono.ttf b/extra/res/ttf/digital7mono.ttf
deleted file mode 100644
index a481b97..0000000
Binary files a/extra/res/ttf/digital7mono.ttf and /dev/null differ
diff --git a/extra/res/ttf/joystix monospace.otf b/extra/res/ttf/joystix monospace.otf
deleted file mode 100644
index 20a3100..0000000
Binary files a/extra/res/ttf/joystix monospace.otf and /dev/null differ
diff --git a/extra/res/ttf/monofonto rg.otf b/extra/res/ttf/monofonto rg.otf
deleted file mode 100644
index bca1ad6..0000000
Binary files a/extra/res/ttf/monofonto rg.otf and /dev/null differ
diff --git a/extra/themes/DefaultThemeDark.oss b/extra/themes/DefaultThemeDark.oss
new file mode 100644
index 0000000..c4337b8
--- /dev/null
+++ b/extra/themes/DefaultThemeDark.oss
@@ -0,0 +1,424 @@
+% ================== DefaultTheme.oss ==================
+
+
+% ==== Variables ====
+const $accentColor = #2FB000FF
+const $textColor = #CCEECCFF
+const $borderColor = #111111FF
+const $darkColor = #223322FF
+const $accentColorLight = #87FF5CFF
+const $accentColorDark = #3B7500FF
+const $backgroundColor = rgba(30, 30, 30, 255)
+const $crimsonAccent = rgba(220, 20, 60, 150)
+const $blueAccent = #0075C7
+% ===================
+
+
+macro set_focus_border(color, width = 1, show = true) {
+ focusBorderColor = $color;
+ showFocusBorder = $show;
+ focusBorderWidth = $width;
+}
+
+macro set_common_values(_borderColor = $borderColor, _fontSize = 18, _showBorder = true) {
+ borderColor = $_borderColor;
+ fontSize = $_fontSize;
+ showBorder = $_showBorder;
+}
+
+
+% ====== Root Widget ======
+(window) {
+ backgroundColor = $backgroundColor
+ (tooltip) {
+ backgroundColor = #FFF7D6FF
+ borderColor = rgb(30, 30, 30)
+ textColor = rgb(30, 30, 30)
+ borderWidth = 1
+ fontSize = 20
+ }
+}
+% =========================
+
+
+% ====== Panel ======
+(panel) {
+ set_common_values()
+ backgroundColor = $backgroundColor
+ titleColor = $accentColorDark
+ titlebarColor = $borderColor
+ titlebarBorderColor = $borderColor
+ borderRadius = 0
+ borderWidth = 2
+ titlebarBorderWidth = 2
+ showBackground = true
+ titlebarType = full
+ titlebarHeight = 35
+ titlebarFontSize = 18
+ titlebarTextAlign = text_left
+ scrollSpeed = 1.2
+ scrollSmoothFactor = 0.7
+ padding = rect(15, 15, 15, 15)
+ margin = rect(0, 0, 0, 0)
+}
+% ===================
+
+
+
+% ====== TabPanel ======
+(tabPanel) {
+ set_common_values()
+ backgroundColor = $backgroundColor
+ borderRadius = 0
+ borderWidth = 2
+ showBackground = true
+ margin = rect(0, 0, 0, 0)
+ textColor = $textColor
+
+ (tabBar) {
+ height = 35.0
+ backgroundColor = $darkColor
+ borderColor = $borderColor
+ borderWidth = 1
+ sidePadding = 20.0
+ }
+}
+(@panel_tab panel) {
+}
+% ======================
+
+
+
+% ====== Scrollbar ======
+(scrollbar) {
+ (thumb) {
+ color = $darkColor
+ borderRadius = 30.0
+ borderColor = $borderColor
+ showBorder = true
+ }
+ (track) {
+ color = #111111FF
+ }
+ width = 18.0
+}
+(scrollbar:hover) {
+ (thumb) {
+ color = $accentColorDark
+ }
+}
+(scrollbar:pressed) {
+ (thumb) {
+ color = #225500FF
+ }
+ (track) {
+ color = #001100FF
+ }
+}
+% =======================
+
+
+
+% ====== CheckBox ======
+(checkbox) {
+ set_common_values($borderColor, 18, false)
+ checkBorderColor = $accentColorDark
+ checkBoxColor = $accentColor
+ textColor = $textColor
+ borderRadius = 0
+ borderWidth = 0
+ checkBorderRadius = 6
+ checkBorderWidth = 2
+ showBackground = false
+ padding = rect(4, 4, 4, 4)
+ margin = rect(0, 0, 0, 0)
+ set_focus_border($blueAccent)
+}
+(checkbox:hover) {
+ checkBorderColor = $accentColorLight
+}
+(checkbox:pressed) {
+ checkBoxColor = $accentColor
+ textColor = $accentColorDark
+ checkBorderColor = $accentColor
+}
+(checkbox:active) {
+ checkBoxColor = $accentColor
+ checkBorderColor = $accentColor
+ textColor = $accentColorDark
+}
+% ======================
+
+
+
+% ====== RadioButton ======
+(radioButton) {
+ set_common_values($borderColor, 18, false)
+ innerCircleColor = $accentColorDark
+ outerCircleColor = $accentColor
+ outerCircleBorderWidth = 2
+ textColor = $textColor
+ showBackground = false
+ padding = rect(4, 4, 4, 4)
+ margin = rect(0, 0, 0, 0)
+ set_focus_border($blueAccent)
+}
+(radioButton:hover) {
+ outerCircleColor = $accentColorLight
+}
+(radioButton:pressed) {
+ outerCircleColor = $accentColor
+ textColor = $accentColorDark
+ innerCircleColor = $accentColor
+}
+(radioButton:active) {
+ innerCircleColor = $accentColor
+ outerCircleColor = $accentColor
+ textColor = $accentColorDark
+}
+% =========================
+
+
+
+% ====== Label ======
+(label) {
+ set_common_values($borderColor, 18, false)
+ textColor = $textColor
+ borderRadius = 0
+ borderWidth = 0
+ showBackground = false
+ padding = rect(15, 15, 15, 15)
+ margin = rect(0, 0, 0, 0)
+}
+% ===================
+
+
+
+% ====== Button ======
+(button) {
+ set_common_values($accentColorDark)
+ textColor = $textColor
+ backgroundGradient = gradientV($accentColor - $accentColorDark)
+ borderRadius = 0
+ borderWidth = 1
+ showBackground = false
+ useBackgroundGradient = true
+ padding = rect(5, 5, 5, 5)
+ margin = rect(0, 0, 0, 0)
+ showIcon = false
+ (icon) {
+ }
+ set_focus_border($blueAccent)
+}
+(button:hover) {
+ borderColor = $accentColorLight
+}
+(button:pressed) {
+ borderColor = $accentColorDark
+ textColor = #BBBBBBFF
+ backgroundGradient = gradientV($accentColorDark - $accentColor)
+}
+% ====================
+
+
+
+% ====== Image ======
+(image) {
+}
+% ===================
+
+
+
+% ====== ProgressBar ======
+(progressbar) {
+ set_common_values($accentColor)
+ borderWidth = 1
+ showDecimal = false
+ showText = true
+ useBackgroundGradient = true
+ backgroundGradient = gradientV($darkColor - $accentColorDark)
+ useProgressGradient = true
+ progressGradient = gradientV($accentColor - $accentColorDark)
+}
+% =========================
+
+
+
+% ====== Slider ======
+(slider) {
+ (handle) {
+ color = $accentColor
+ size = vec2(10, 18)
+ }
+ (track) {
+ color = $accentColorDark
+ progressColor = $accentColor
+ width = 6.0
+ }
+ showStepTicks = true
+ stepTickHeight = 6.0
+ stepTickColor = $accentColor
+ padding = rect(5, 5, 5, 5)
+ set_focus_border($blueAccent)
+}
+% ===================
+
+
+
+% ====== Context Menu ======
+(context) {
+ set_common_values()
+ padding = rect(16, 0, 16, 0)
+ itemSpacing = 6.0
+ backgroundColor = $darkColor
+ selectionColor = $accentColor
+ selectionTextColor = $accentColorLight
+ separatorLineColor = $borderColor
+ textColor = $textColor
+ submenuIndicatorColor = $accentColorDark
+ useSelectionGradient = true
+ selectionGradient = gradientV($accentColor - $accentColorDark)
+ animateOpen = true
+ animationDelay = 120
+}
+% ==========================
+
+
+
+% ====== MenuBar ======
+(menubar) {
+ set_common_values($borderColor, 16, false)
+ height = 24.0
+ itemPadding = rect(0, 0, 15, 0)
+ backgroundColor = $darkColor
+ textColor = $textColor
+ selectionColor = $accentColor
+ selectionTextColor = $accentColorLight
+}
+% =====================
+
+
+
+% ====== ToolBar ======
+(toolbar) {
+ set_common_values()
+ height = 30.0
+ backgroundColor = $darkColor
+ textColor = $textColor
+ selectionColor = $accentColor
+ selectionTextColor = $accentColorLight
+ showBackground = true
+}
+(@tool_button button) {
+ set_common_values(color_black, 18, false)
+ padding = rect(5, 0, 5, 0)
+ margin = rect(5, 0, 0, 0)
+ showBackground = true
+ backgroundColor = $darkColor
+ useBackgroundGradient = false
+ showIcon = true
+ (icon) {
+ tint = $accentColorLight
+ }
+ set_focus_border($blueAccent)
+}
+(@tool_button button:hover) {
+ borderColor = $textColor
+ backgroundColor = $accentColorDark
+ showBorder = true
+ (icon) {
+ tint = $textColor
+ }
+}
+(@tool_button button:pressed) {
+ borderColor = $accentColorDark
+ showBorder = true
+ (icon) {
+ tint = $accentColorLight
+ }
+}
+% =====================
+
+
+
+% ====== ComboBox ======
+(combo) {
+ set_common_values($accentColor)
+ backgroundColor = $darkColor
+ borderWidth = 1
+ showBackground = true
+ triangleIndicatorColor = $accentColor
+ triangleIndicatorPadding = 8.0
+ textColor = $textColor
+}
+(combo:hover) {
+ triangleIndicatorColor = $accentColorLight
+ borderColor = $accentColorLight
+}
+(combo:active) {
+ triangleIndicatorColor = $accentColorLight
+ borderColor = $accentColorLight
+}
+% ======================
+
+
+
+% ====== TreeView ======
+(tree) {
+ set_common_values($borderColor, 16, true)
+ linePadding = rect(5, 5, 0, 0)
+ selectionColor = $accentColor
+ selectionTextColor = color_white
+ iconSpacing = 0.0
+ iconInset = 4
+ showIcons = true
+ textColor = $textColor
+ backgroundColor = $darkColor
+ backgroundColor2 = $darkColor.lighten(0.06)
+ showAlternatingBackground = true
+ separatorLineColor = $borderColor
+ showSeparatorLine = false
+ iconTintColor = $accentColorLight
+ indentWidth = 16.0
+ chevronColor = $accentColorDark
+ chevronInset = 4.0
+ branchLineColor = $textColor
+ showBranchLines = true
+ branchLineThickness = 1.0
+ set_focus_border($blueAccent)
+}
+% ======================
+
+
+
+% ====== TextEdit ======
+(textEdit) {
+ set_common_values()
+ set_focus_border($blueAccent)
+ backgroundColor = $darkColor
+ textColor = $textColor
+ cursorBlink = true
+ cursorWidth = 2
+ cursorColor = $crimsonAccent
+ selectionColor = $blueAccent
+ cursorStyle = "line"
+}
+% ======================
+
+
+
+% ====== ColorPicker ======
+(colorPicker) {
+ set_common_values()
+ set_focus_border($blueAccent)
+ backgroundColor = $darkColor
+ padding = rect(8, 8, 8, 8)
+ stripWidth = 20
+ regionSpacing = 8
+ cursorRadius = 6
+}
+(colorButton) {
+ set_common_values()
+ set_focus_border($blueAccent)
+}
+% =========================
diff --git a/other/appIcon.ico b/other/appIcon.ico
new file mode 100644
index 0000000..85a06b7
Binary files /dev/null and b/other/appIcon.ico differ
diff --git a/other/appIcon.rc b/other/appIcon.rc
new file mode 100644
index 0000000..47f792a
--- /dev/null
+++ b/other/appIcon.rc
@@ -0,0 +1 @@
+ IDI_ICON1 ICON DISCARDABLE "appIcon.ico"
diff --git a/other/build.nr b/other/build.nr
index 0cfbf08..b8626c4 100644
--- a/other/build.nr
+++ b/other/build.nr
@@ -1 +1 @@
-2
+4
diff --git a/other/deps.txt b/other/deps.txt
new file mode 100644
index 0000000..940141a
--- /dev/null
+++ b/other/deps.txt
@@ -0,0 +1 @@
+mbedtls
diff --git a/src/Metadata.hpp b/src/Metadata.hpp
new file mode 100644
index 0000000..c2fc53b
--- /dev/null
+++ b/src/Metadata.hpp
@@ -0,0 +1,100 @@
+/*
+ PianoLibrary
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of PianoLibrary.
+
+ PianoLibrary 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.
+
+ PianoLibrary 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 PianoLibrary. If not, see .
+*/
+
+#pragma once
+
+#include "data.hpp"
+#include
+
+namespace metadata
+{
+ // Safe conversions — return fallback on any failure.
+ static float str_to_float(const std::string& s, float fallback = 0.0f)
+ {
+ try { return s.empty() ? fallback : std::stof(s); }
+ catch (...) { return fallback; }
+ }
+
+ static uint64_t str_to_u64(const std::string& s, uint64_t fallback = 0)
+ {
+ try { return s.empty() ? fallback : static_cast(std::stoull(s)); }
+ catch (...) { return fallback; }
+ }
+
+ static uint32_t str_to_u32(const std::string& s, uint32_t fallback = 0)
+ {
+ try { return s.empty() ? fallback : static_cast(std::stoul(s)); }
+ catch (...) { return fallback; }
+ }
+
+ // Parse the full list.json body into a PianoLibrary.
+ // Returns false on any structural error (missing "data", bad JSON, etc.).
+ static bool parsePianoLibrary(const std::string& jsonBody, data::PianoLibrary& out)
+ {
+ try
+ {
+ auto j = nlohmann::json::parse(jsonBody);
+
+ // Root-level metadata
+ out.generatedAt = j.value("generated-at", std::string(""));
+
+ if (j.contains("revision"))
+ {
+ if (j["revision"].is_number())
+ out.revision = j["revision"].get();
+ else if (j["revision"].is_string())
+ out.revision = str_to_u64(j["revision"].get());
+ }
+
+ if (!j.contains("data") || !j["data"].is_array())
+ return false;
+
+ out.performances.clear();
+ out.performances.reserve(j["data"].size());
+
+ for (const auto& e : j["data"])
+ {
+ data::Performance p;
+
+ p.date = e.value("date", std::string(""));
+ p.artist = e.value("artist", std::string(""));
+ p.title = e.value("title", std::string(""));
+ p.extraInfo = e.value("extra-info", std::string(""));
+ p.fileName = e.value("file-name", std::string(""));
+ p.midiFileName = e.value("midi-file-name", std::string(""));
+ p.id = e.value("ID", std::string(""));
+
+ p.performanceNumber = str_to_u32(e.value("performance-number", std::string("0")));
+ p.totalPerformances = str_to_u32(e.value("total-performances", std::string("0")));
+ p.fileSize = str_to_u64(e.value("file-size", std::string("0")));
+ p.duration = str_to_float(e.value("duration", std::string("0")));
+ p.soundStart = str_to_float(e.value("sound-start", std::string("0")));
+
+ out.performances.push_back(std::move(p));
+ }
+
+ return true;
+ }
+ catch (...)
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/data.hpp b/src/data.hpp
new file mode 100644
index 0000000..023ed45
--- /dev/null
+++ b/src/data.hpp
@@ -0,0 +1,51 @@
+/*
+ PianoLibrary
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of PianoLibrary.
+
+ PianoLibrary 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.
+
+ PianoLibrary 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 PianoLibrary. If not, see .
+*/
+
+#pragma once
+
+#include
+#include
+
+namespace data
+{
+ struct Performance
+ {
+ String date;
+ String artist;
+ String title;
+ String extraInfo;
+ String fileName;
+ String midiFileName;
+ String id;
+
+ uint32_t performanceNumber { 0 };
+ uint32_t totalPerformances { 0 };
+ uintmax_t fileSize { 0 };
+ float duration { 0.0f };
+ float soundStart { 0.0f };
+ };
+
+ struct PianoLibrary
+ {
+ String generatedAt;
+ uint64_t revision { 0 };
+ stdvec performances;
+ };
+}
diff --git a/src/info.cpp b/src/info.cpp
deleted file mode 100644
index 1910775..0000000
--- a/src/info.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-#include
-#include
-
-int main() {
- cpr::Response r = cpr::Get(cpr::Url{"https://example.com/data.json"});
- auto j = nlohmann::json::parse(r.text);
-}
-
-
-
-
-
-
-
-#include
-#include
-#include
-
-// Portable timegm: temporarily override TZ to UTC, call mktime, restore.
-// Works on POSIX, MSVC, MinGW, MSYS2 — everywhere mktime exists.
-static std::time_t portable_timegm(std::tm* tm)
-{
-#if defined(_WIN32) || defined(__MINGW32__)
- // _mkgmtime is MSVC/UCRT's timegm equivalent, also available in MSYS2 UCRT64.
- return _mkgmtime(tm);
-#else
- return timegm(tm);
-#endif
-}
-
-std::string utcToLocal(const std::string& iso)
-{
- std::tm tm = {};
-
- // Manual parse — avoids strptime, which MSVC lacks entirely.
- // Expected format: "2026-06-20T14:00:00Z"
- if (std::sscanf(iso.c_str(), "%d-%d-%dT%d:%d:%dZ",
- &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
- &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
- return iso; // Unparseable -> return as-is.
-
- tm.tm_year -= 1900;
- tm.tm_mon -= 1;
- tm.tm_isdst = -1;
-
- std::time_t utc = portable_timegm(&tm);
- std::tm* local = std::localtime(&utc);
-
- char buf[64];
- std::strftime(buf, sizeof(buf), "%d %b %Y, %H:%M %Z", local);
- return buf;
-}
diff --git a/src/main.cpp b/src/main.cpp
index a2c8daa..058530f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,167 +1,258 @@
/*
- PianoLibrary - A collection of useful functionality
- Copyright (C) 2025 OmniaX-Dev
+ PianoLibrary
+ Copyright (C) 2025 OmniaX-Dev
- This file is part of PianoLibrary.
+ This file is part of PianoLibrary.
- PianoLibrary 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.
+ PianoLibrary 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.
- PianoLibrary 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.
+ PianoLibrary 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 PianoLibrary. If not, see .
+ You should have received a copy of the GNU General Public License
+ along with PianoLibrary. If not, see .
*/
-#include
-#include
-#include
-#include
-#include
-#include
-#include
+#include
+#include "vendor/httplib.h"
+#include "Metadata.hpp"
+#include "utc.hpp"
-ostd::ConsoleOutputHandler out;
+using namespace ogfx::gui;
-struct PianoPerformance
-{
- ostd::String date { "" };
- ostd::String artist { "" };
- ostd::String name { "" };
- ostd::String filePath { "" };
- ostd::String extraInfo { "" };
- uint32_t performanceNumber { 0 };
-};
-
-int date_to_int(const std::string& s)
-{
- int d, m, y;
- sscanf(s.c_str(), "%d.%d.%d", &d, &m, &y);
- return y * 10000 + m * 100 + d; // YYYYMMDD
-}
-
-class Window : public ogfx::WindowBase
+class TestWindow : public Window
{
public:
- inline Window(void) { }
+ inline TestWindow(void) { }
inline void onInitialize(void) override
{
- enableSignals();
- connectSignal(ostd::tBuiltinSignals::KeyReleased);
+ m_theme.loadFromFile("./themes/DefaultThemeDark.oss", true, getDefaultStylesheetVariableList());
+ setTheme(m_theme);
- m_gfx.init(*this);
- m_gfx.setFont("res/ttf/Courier Prime.ttf");
+ getToolBar().setHeight(30);
+ getToolBar().setLayout(BoxLayout::Orientation::Horizontal);
+ getToolBar().getLayout()->setSpacing(10);
+ getStatusBar().setHeight(24);
+ showMenuBar();
+ showToolBar();
+ showStatusBar();
- std::unordered_map> pianoPerformances;
+ ogfx::AnimationData iconsAD;
+ iconsAD.frameCount = 1;
+ iconsAD.fps = 1;
+ iconsAD.columns = 8;
+ iconsAD.rows = 5;
+ iconsAD.frameWidth = 64;
+ iconsAD.frameHeight = 64;
+ iconsAD.spacingX = 50;
+ iconsAD.spacingY = 48;
+ iconsAD.still = true;
- auto musicList = ostd::FileSystem::listFilesInDirectoryRecursive("/home/sylar/Desktop/Music/");
- for (const auto& path : musicList)
+ auto l_randomIcon = [this](const ogfx::AnimationData& src) -> ogfx::Icon {
+ static ogfx::Image img("./res/icons.png", getGFX());
+ ogfx::AnimationData ad = src;
+ ad.columnOffset = ostd::Random::getui32(0, 7);
+ ad.rowOffset = ostd::Random::getui32(0, 4);
+ if (ad.rowOffset == 3)
+ ad.columnOffset = ostd::Random::getui32(0, 4);
+ return { img, ad };
+ };
+
+ for (i32 i = 0; i < 17; i++)
{
- ostd::String p = path;
- p.trim();
- if (p.contains("/"))
- p.substr(p.lastIndexOf("/") + 1);
-
- PianoPerformance pp;
-
- auto tokens = p.tokenize("-");
- if (tokens.hasNext())
- pp.date = tokens.next();
- if (tokens.hasNext())
- pp.artist = tokens.next();
- if (tokens.hasNext())
- pp.name = tokens.next();
- if (tokens.hasNext())
- {
- ostd::String tmp = tokens.next();
- if (!tmp.isNumeric())
- {
- int32_t i = 0;
- for ( ; i < tmp.len(); i++)
- {
- char c = tmp[i];
- if (c >= '0' && c <= '9')
- continue;
- break;
- }
- if (i == 0)
- pp.performanceNumber = 0;
- else
- pp.performanceNumber = tmp.substr(0, i).toInt();
- }
- else
- pp.performanceNumber = tmp.toInt();
- }
- while (tokens.hasNext())
- pp.extraInfo += tokens.next() + " ";
- pianoPerformances[pp.date].push_back(pp);
+ auto icon = l_randomIcon(iconsAD);
+ auto& btn = getToolBar().addButton("./res/icons.png");
+ btn.enableAnimated();
+ btn.setAnimationData(icon);
+ btn.setTabIndex(i + 100);
}
- std::vector>> sortedPerformances(pianoPerformances.begin(), pianoPerformances.end());
+ m_tabs.setSize(900, 700);
+ m_tabs.addThemeOverride("@.tabPanel.showBorder", false);
+ auto& t1 = m_tabs.addTab("Tab1");
+ auto& t2 = m_tabs.addTab("Tab2 Test");
+ auto& t3 = m_tabs.addTab("Long Tab Test");
- std::sort(sortedPerformances.begin(), sortedPerformances.end(),
- [](auto& a, auto& b) {
- return date_to_int(a.first) < date_to_int(b.first);
- }
- );
+ addWidget(m_tabs, { 0, 0 });
+ getToolBar().reloadTheme(true);
- for (auto[date, performances] : sortedPerformances)
- {
- out.fg(ostd::ConsoleColors::BrightBlue).p(date).nl();
- for (const auto& perf : performances)
+ auto onActivate = [this](const ContextMenu::Entry& e) {
+ out().fg("cyan").p("[MenuBar] Activated: ").fg("yellow").p(e.text)
+ .fg("cyan").p(" (id=").fg("green").p(e.id).fg("cyan").p(")").reset().nl();
+
+ switch (e.id)
{
- out.fg(ostd::ConsoleColors::BrightYellow).p(" ").p(perf.artist).p("\t");
- out.fg(ostd::ConsoleColors::BrightGray).p(perf.name).p(" ");
- out.fg(ostd::ConsoleColors::BrightRed).p("(").p(perf.performanceNumber).p(")").nl();
+ case TestMenuId::File_Exit:
+ close();
+ break;
+ // ... add real handlers as you build them out
+ default:
+ break;
}
- out.nl().nl();
- }
- out.reset();
+ };
- close();
- }
+ fileMenu.onActivate = onActivate;
+ editMenu.onActivate = onActivate;
+ viewMenu.onActivate = onActivate;
+ helpMenu.onActivate = onActivate;
- inline void handleSignal(ostd::tSignal& signal) override
+ getMenuBar().addMenu("File", fileMenu)
+ .addMenu("Edit", editMenu)
+ .addMenu("View", viewMenu)
+ .addMenu("Help", helpMenu);
+ }
+
+ inline void onSignal(ostd::Signal& signal) override
{
- if (signal.ID == ostd::tBuiltinSignals::KeyReleased)
+ if (signal.ID == ostd::BuiltinSignals::KeyReleased)
{
auto& evtData = (ogfx::KeyEventData&)signal.userData;
- if (evtData.keyCode == SDLK_ESCAPE)
+ if (evtData.keyCode == ogfx::KeyCode::Escape)
close();
}
+ else if (signal.ID == ostd::BuiltinSignals::WindowResized)
+ {
+ auto& wrd = cast(signal.userData);
+ m_tabs.setSize(cast(getWindowWidth()), cast(getWindowHeight() - getMenuBar().geth() - getToolBar().geth() - getStatusBar().geth()));
+ m_tabs.setPosition(0, -1);
+ m_tabs.refreshCurrentTab();
+
+ }
}
- inline void onRender(void) override
+ void onRedraw(ogfx::BasicRenderer2D& gfx) override
{
}
- inline void onFixedUpdate(double frameTime_s) override
- {
- }
-
- inline void onUpdate(void) override
+ void onFixedUpdate(void) override
{
}
private:
- ogfx::BasicRenderer2D m_gfx;
+ TabPanel m_tabs { *this };
+
+ enum TestMenuId : i32
+ {
+ // File
+ File_New = 1,
+ File_Open,
+ File_OpenRecent_Project1,
+ File_OpenRecent_Project2,
+ File_OpenRecent_Project3,
+ File_OpenRecent_ClearList,
+ File_Save,
+ File_SaveAs,
+ File_Export_PNG,
+ File_Export_JPG,
+ File_Export_SVG,
+ File_Export_PDF,
+ File_Exit,
+
+ // Edit
+ Edit_Undo,
+ Edit_Redo,
+ Edit_Cut,
+ Edit_Copy,
+ Edit_Paste,
+ Edit_SelectAll,
+
+ // View
+ View_ZoomIn,
+ View_ZoomOut,
+ View_ZoomReset,
+ View_Theme_Dark,
+ View_Theme_Light,
+ View_Theme_Crimson,
+
+ // Help
+ Help_Documentation,
+ Help_About,
+ };
+
+ ContextMenu::Instance fileMenu { {
+ { "New", TestMenuId::File_New },
+ { "Open...", TestMenuId::File_Open },
+ { "Open Recent", {
+ { "Project1.proj", TestMenuId::File_OpenRecent_Project1 },
+ { "Project2.proj", TestMenuId::File_OpenRecent_Project2 },
+ { "Project3.proj", TestMenuId::File_OpenRecent_Project3 },
+ { "Clear Recent List", TestMenuId::File_OpenRecent_ClearList },
+ }},
+ { "Save", TestMenuId::File_Save },
+ { "Save As...",TestMenuId::File_SaveAs },
+ { "Export As", {
+ { "PNG Image", TestMenuId::File_Export_PNG },
+ { "JPG Image", TestMenuId::File_Export_JPG },
+ { "SVG Vector",TestMenuId::File_Export_SVG },
+ { "PDF Document", TestMenuId::File_Export_PDF },
+ }},
+ { "Exit", TestMenuId::File_Exit } },
+ nullptr
+ };
+
+ ContextMenu::Instance editMenu { {
+ { "Undo", TestMenuId::Edit_Undo },
+ { "Redo", TestMenuId::Edit_Redo },
+ { "Cut", TestMenuId::Edit_Cut },
+ { "Copy", TestMenuId::Edit_Copy },
+ { "Paste", TestMenuId::Edit_Paste },
+ { "Select All", TestMenuId::Edit_SelectAll } },
+ nullptr
+ };
+
+ ContextMenu::Instance viewMenu { {
+ { "Zoom In", TestMenuId::View_ZoomIn },
+ { "Zoom Out", TestMenuId::View_ZoomOut },
+ { "Reset Zoom", TestMenuId::View_ZoomReset },
+ { "Theme", {
+ { "Dark", TestMenuId::View_Theme_Dark },
+ { "Light", TestMenuId::View_Theme_Light },
+ { "Crimson", TestMenuId::View_Theme_Crimson },
+ }} },
+ nullptr
+ };
+
+ ContextMenu::Instance helpMenu { {
+ { "Documentation", TestMenuId::Help_Documentation },
+ { "About", TestMenuId::Help_About } },
+ nullptr
+ };
+
+ ostd::Stylesheet m_theme;
};
-int main(int argc, char** argv)
-{
- Window window;
- window.initialize(1280, 720, "PianoLibrary");
- window.setClearColor({ 0, 2 , 15 });
-
- while (window.isRunning())
+void request() {
+ httplib::Client cli("https://timeline.keylightpiano.com");
+ auto res = cli.Get("/music/list.json");
+ if (res && res->status == 200)
{
- window.update();
- }
+ data::PianoLibrary library;
+ if (metadata::parsePianoLibrary(res->body, library))
+ {
+ std::string localTime = utc::toLocal(library.generatedAt);
+ printf("Revision %lu, generated %s\n", library.revision, localTime.c_str());
+ printf("%zu performances loaded\n", library.performances.size());
+ for (const auto& p : library.performances)
+ printf(" %s - %s (%.1fs)\n", p.artist.c_str(), p.title.c_str(), p.duration);
+ }
+ }
+}
+
+i32 main(i32 argc, char** argv)
+{
+ request();
+ ostd::Random::autoSeed();
+ TestWindow window;
+ window.initialize(960, 540, "PianoLibrary");
+ window.setClearColor({ 0, 0, 0 });
+ window.setPosition({ 50, 50 });
+ // window.setWindowState(ogfx::gui::Window::eWindowState::Maximized);
+ window.mainLoop();
return 0;
}
diff --git a/src/utc.hpp b/src/utc.hpp
new file mode 100644
index 0000000..1f24f0b
--- /dev/null
+++ b/src/utc.hpp
@@ -0,0 +1,61 @@
+/*
+ PianoLibrary
+ Copyright (C) 2025 OmniaX-Dev
+
+ This file is part of PianoLibrary.
+
+ PianoLibrary 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.
+
+ PianoLibrary 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 PianoLibrary. If not, see .
+*/
+
+#pragma once
+
+#include
+
+namespace utc
+{
+ // Portable timegm: temporarily override TZ to UTC, call mktime, restore.
+ // Works on POSIX, MSVC, MinGW, MSYS2 — everywhere mktime exists.
+ static std::time_t portable_timegm(std::tm* tm)
+ {
+ #if defined(_WIN32) || defined(__MINGW32__)
+ // _mkgmtime is MSVC/UCRT's timegm equivalent, also available in MSYS2 UCRT64.
+ return _mkgmtime(tm);
+ #else
+ return timegm(tm);
+ #endif
+ }
+
+ static String toLocal(const String& iso)
+ {
+ std::tm tm = {};
+
+ // Manual parse — avoids strptime, which MSVC lacks entirely.
+ // Expected format: "2026-06-20T14:00:00Z"
+ if (std::sscanf(iso.c_str(), "%d-%d-%dT%d:%d:%dZ",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6)
+ return iso; // Unparseable -> return as-is.
+
+ tm.tm_year -= 1900;
+ tm.tm_mon -= 1;
+ tm.tm_isdst = -1;
+
+ std::time_t utc = portable_timegm(&tm);
+ std::tm* local = std::localtime(&utc);
+
+ char buf[64];
+ std::strftime(buf, sizeof(buf), "%d %b %Y, %H:%M %Z", local);
+ return buf;
+ }
+}
diff --git a/src/vendor/httplib.h b/src/vendor/httplib.h
new file mode 100644
index 0000000..58c63d0
--- /dev/null
+++ b/src/vendor/httplib.h
@@ -0,0 +1,20588 @@
+//
+// httplib.h
+//
+// Copyright (c) 2026 Yuji Hirose. All rights reserved.
+// MIT License
+//
+
+#ifndef CPPHTTPLIB_HTTPLIB_H
+#define CPPHTTPLIB_HTTPLIB_H
+
+#define CPPHTTPLIB_VERSION "0.48.0"
+#define CPPHTTPLIB_VERSION_NUM "0x003000"
+
+#ifdef _WIN32
+#if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0A00
+#error \
+ "cpp-httplib doesn't support Windows 8 or lower. Please use Windows 10 or later."
+#endif
+#endif
+
+/*
+ * Configuration
+ */
+
+#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND
+#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
+#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
+#endif
+
+#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
+#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
+#endif
+
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
+#endif
+
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
+#define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
+#define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
+#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
+#define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
+#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
+#endif
+
+#ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
+#define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
+#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
+#define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
+#define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_EXPECT_100_THRESHOLD
+#define CPPHTTPLIB_EXPECT_100_THRESHOLD 1024
+#endif
+
+#ifndef CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND
+#define CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND 1000
+#endif
+
+#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD
+#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_THRESHOLD (1024 * 1024)
+#endif
+
+#ifndef CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND
+#define CPPHTTPLIB_WAIT_EARLY_SERVER_RESPONSE_TIMEOUT_MSECOND 50
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
+#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
+#ifdef _WIN32
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 1000
+#else
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
+#endif
+#endif
+
+#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
+#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_HEADER_MAX_LENGTH
+#define CPPHTTPLIB_HEADER_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_HEADER_MAX_COUNT
+#define CPPHTTPLIB_HEADER_MAX_COUNT 100
+#endif
+
+#ifndef CPPHTTPLIB_REDIRECT_MAX_COUNT
+#define CPPHTTPLIB_REDIRECT_MAX_COUNT 20
+#endif
+
+#ifndef CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT
+#define CPPHTTPLIB_MULTIPART_FORM_DATA_FILE_MAX_COUNT 1024
+#endif
+
+#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
+#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (100 * 1024 * 1024) // 100MB
+#endif
+
+#ifndef CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH
+#define CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH 8192
+#endif
+
+#ifndef CPPHTTPLIB_RANGE_MAX_COUNT
+#define CPPHTTPLIB_RANGE_MAX_COUNT 1024
+#endif
+
+#ifndef CPPHTTPLIB_TCP_NODELAY
+#define CPPHTTPLIB_TCP_NODELAY false
+#endif
+
+#ifndef CPPHTTPLIB_IPV6_V6ONLY
+#define CPPHTTPLIB_IPV6_V6ONLY false
+#endif
+
+#ifndef CPPHTTPLIB_RECV_BUFSIZ
+#define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
+#endif
+
+#ifndef CPPHTTPLIB_SEND_BUFSIZ
+#define CPPHTTPLIB_SEND_BUFSIZ size_t(16384u)
+#endif
+
+#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
+#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
+#endif
+
+#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
+#define CPPHTTPLIB_THREAD_POOL_COUNT \
+ ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
+ ? std::thread::hardware_concurrency() - 1 \
+ : 0))
+#endif
+
+#ifndef CPPHTTPLIB_THREAD_POOL_MAX_COUNT
+#define CPPHTTPLIB_THREAD_POOL_MAX_COUNT (CPPHTTPLIB_THREAD_POOL_COUNT * 4)
+#endif
+
+#ifndef CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT
+#define CPPHTTPLIB_THREAD_POOL_IDLE_TIMEOUT 3 // seconds
+#endif
+
+#ifndef CPPHTTPLIB_RECV_FLAGS
+#define CPPHTTPLIB_RECV_FLAGS 0
+#endif
+
+#ifndef CPPHTTPLIB_SEND_FLAGS
+#define CPPHTTPLIB_SEND_FLAGS 0
+#endif
+
+#ifndef CPPHTTPLIB_LISTEN_BACKLOG
+#define CPPHTTPLIB_LISTEN_BACKLOG 5
+#endif
+
+#ifndef CPPHTTPLIB_MAX_LINE_LENGTH
+#define CPPHTTPLIB_MAX_LINE_LENGTH 32768
+#endif
+
+#ifndef CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH
+#define CPPHTTPLIB_WEBSOCKET_MAX_PAYLOAD_LENGTH 16777216
+#endif
+
+#ifndef CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND
+#define CPPHTTPLIB_WEBSOCKET_READ_TIMEOUT_SECOND 300
+#endif
+
+#ifndef CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND
+#define CPPHTTPLIB_WEBSOCKET_CLOSE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND
+#define CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND 30
+#endif
+
+#ifndef CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS
+#define CPPHTTPLIB_WEBSOCKET_MAX_MISSED_PONGS 0
+#endif
+
+/*
+ * Headers
+ */
+
+#ifdef _WIN32
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS
+#endif //_CRT_SECURE_NO_WARNINGS
+
+#ifndef _CRT_NONSTDC_NO_DEPRECATE
+#define _CRT_NONSTDC_NO_DEPRECATE
+#endif //_CRT_NONSTDC_NO_DEPRECATE
+
+#if defined(_MSC_VER)
+#if _MSC_VER < 1900
+#error Sorry, Visual Studio versions prior to 2015 are not supported
+#endif
+
+#pragma comment(lib, "ws2_32.lib")
+
+#ifndef _SSIZE_T_DEFINED
+using ssize_t = __int64;
+#define _SSIZE_T_DEFINED
+#endif
+#endif // _MSC_VER
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
+#endif // S_ISREG
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
+#endif // S_ISDIR
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif // NOMINMAX
+
+#include
+#include
+#include
+
+#if defined(__has_include)
+#if __has_include()
+// afunix.h uses types declared in winsock2.h, so has to be included after it.
+#include
+#define CPPHTTPLIB_HAVE_AFUNIX_H 1
+#endif
+#endif
+
+#ifndef WSA_FLAG_NO_HANDLE_INHERIT
+#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
+#endif
+
+using nfds_t = unsigned long;
+using socket_t = SOCKET;
+using socklen_t = int;
+
+#else // not _WIN32
+
+#include
+#if !defined(_AIX) && !defined(__MVS__)
+#include
+#endif
+#ifdef __MVS__
+#include
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+#endif
+#include
+#include
+#include
+#ifdef __linux__
+#include
+#undef _res // Undefine _res macro to avoid conflicts with user code (#2278)
+#endif
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using socket_t = int;
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET (-1)
+#endif
+#endif //_WIN32
+
+#if defined(__APPLE__)
+#include
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include