Implemented get request + parsing for list.json
This commit is contained in:
parent
893ddf3a83
commit
45c22ffe99
26 changed files with 21536 additions and 189 deletions
|
|
@ -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)
|
||||
#-----------------------------------------------------------------------------------------
|
||||
|
|
|
|||
40
build
40
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"
|
||||
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
|
||||
|
|
|
|||
BIN
extra/res/icons.png
Normal file
BIN
extra/res/icons.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
BIN
extra/res/loading.png
Normal file
BIN
extra/res/loading.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 352 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 251 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
424
extra/themes/DefaultThemeDark.oss
Normal file
424
extra/themes/DefaultThemeDark.oss
Normal file
|
|
@ -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)
|
||||
}
|
||||
% =========================
|
||||
BIN
other/appIcon.ico
Normal file
BIN
other/appIcon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.3 KiB |
1
other/appIcon.rc
Normal file
1
other/appIcon.rc
Normal file
|
|
@ -0,0 +1 @@
|
|||
IDI_ICON1 ICON DISCARDABLE "appIcon.ico"
|
||||
|
|
@ -1 +1 @@
|
|||
2
|
||||
4
|
||||
|
|
|
|||
1
other/deps.txt
Normal file
1
other/deps.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
mbedtls
|
||||
100
src/Metadata.hpp
Normal file
100
src/Metadata.hpp
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "data.hpp"
|
||||
#include <ostd/vendor/nlohmann/json.hpp>
|
||||
|
||||
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<uint64_t>(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<uint32_t>(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<uint64_t>();
|
||||
else if (j["revision"].is_string())
|
||||
out.revision = str_to_u64(j["revision"].get<std::string>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
src/data.hpp
Normal file
51
src/data.hpp
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostd/data/Types.hpp>
|
||||
#include <ostd/string/String.hpp>
|
||||
|
||||
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<Performance> performances;
|
||||
};
|
||||
}
|
||||
52
src/info.cpp
52
src/info.cpp
|
|
@ -1,52 +0,0 @@
|
|||
#include <cpr/cpr.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
int main() {
|
||||
cpr::Response r = cpr::Get(cpr::Url{"https://example.com/data.json"});
|
||||
auto j = nlohmann::json::parse(r.text);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
// 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;
|
||||
}
|
||||
309
src/main.cpp
309
src/main.cpp
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
PianoLibrary - A collection of useful functionality
|
||||
PianoLibrary
|
||||
Copyright (C) 2025 OmniaX-Dev
|
||||
|
||||
This file is part of PianoLibrary.
|
||||
|
|
@ -18,150 +18,241 @@
|
|||
along with PianoLibrary. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <ogfx/WindowBase.hpp>
|
||||
#include <ogfx/BasicRenderer.hpp>
|
||||
#include <ostd/io/IOHandlers.hpp>
|
||||
#include <ostd/ostd.hpp>
|
||||
#include <ostd/string/String.hpp>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <ogfx/ogfx.hpp>
|
||||
#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>(BoxLayout::Orientation::Horizontal);
|
||||
getToolBar().getLayout()->setSpacing(10);
|
||||
getStatusBar().setHeight(24);
|
||||
showMenuBar();
|
||||
showToolBar();
|
||||
showStatusBar();
|
||||
|
||||
std::unordered_map<ostd::String, std::vector<PianoPerformance>> 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);
|
||||
auto icon = l_randomIcon(iconsAD);
|
||||
auto& btn = getToolBar().addButton("./res/icons.png");
|
||||
btn.enableAnimated();
|
||||
btn.setAnimationData(icon);
|
||||
btn.setTabIndex(i + 100);
|
||||
}
|
||||
|
||||
PianoPerformance pp;
|
||||
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");
|
||||
|
||||
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())
|
||||
addWidget(m_tabs, { 0, 0 });
|
||||
getToolBar().reloadTheme(true);
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
case TestMenuId::File_Exit:
|
||||
close();
|
||||
break;
|
||||
// ... add real handlers as you build them out
|
||||
default:
|
||||
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);
|
||||
};
|
||||
|
||||
fileMenu.onActivate = onActivate;
|
||||
editMenu.onActivate = onActivate;
|
||||
viewMenu.onActivate = onActivate;
|
||||
helpMenu.onActivate = onActivate;
|
||||
|
||||
getMenuBar().addMenu("File", fileMenu)
|
||||
.addMenu("Edit", editMenu)
|
||||
.addMenu("View", viewMenu)
|
||||
.addMenu("Help", helpMenu);
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::vector<PianoPerformance>>> sortedPerformances(pianoPerformances.begin(), pianoPerformances.end());
|
||||
|
||||
std::sort(sortedPerformances.begin(), sortedPerformances.end(),
|
||||
[](auto& a, auto& b) {
|
||||
return date_to_int(a.first) < date_to_int(b.first);
|
||||
}
|
||||
);
|
||||
|
||||
for (auto[date, performances] : sortedPerformances)
|
||||
inline void onSignal(ostd::Signal& signal) override
|
||||
{
|
||||
out.fg(ostd::ConsoleColors::BrightBlue).p(date).nl();
|
||||
for (const auto& perf : performances)
|
||||
{
|
||||
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();
|
||||
}
|
||||
out.nl().nl();
|
||||
}
|
||||
out.reset();
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
inline void handleSignal(ostd::tSignal& 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<ogfx::WindowResizedData&>(signal.userData);
|
||||
m_tabs.setSize(cast<f32>(getWindowWidth()), cast<f32>(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,
|
||||
};
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Window window;
|
||||
window.initialize(1280, 720, "PianoLibrary");
|
||||
window.setClearColor({ 0, 2 , 15 });
|
||||
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
|
||||
};
|
||||
|
||||
while (window.isRunning())
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
61
src/utc.hpp
Normal file
61
src/utc.hpp
Normal file
|
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ostd/string/String.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
20588
src/vendor/httplib.h
vendored
Normal file
20588
src/vendor/httplib.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue