diff --git a/ext_components/cp0_lvgl/assets/cp0_status_time_background_wide.inc b/ext_components/cp0_lvgl/assets/cp0_status_time_background_wide.inc new file mode 100644 index 00000000..80dc1ae1 --- /dev/null +++ b/ext_components/cp0_lvgl/assets/cp0_status_time_background_wide.inc @@ -0,0 +1,371 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +static const uint8_t cp0_status_time_background_wide_map[] = { + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x98, 0xf6, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xf8, + 0x33, 0x65, 0x98, 0xc2, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x34, 0x66, 0x99, 0xc6, 0x32, 0x64, 0x98, 0x52, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x34, 0x67, 0x98, 0x54, 0x00, 0x00, 0x00, 0x00, 0x32, 0x66, 0x98, 0x8a, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x32, 0x66, 0x99, 0x89, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x65, 0x9a, 0x51, + 0x33, 0x65, 0x98, 0xc2, 0x33, 0x66, 0x98, 0xf6, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, 0x33, 0x66, 0x99, 0xff, + 0x33, 0x66, 0x98, 0xf6, 0x33, 0x65, 0x98, 0xc2, 0x32, 0x65, 0x97, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; diff --git a/ext_components/cp0_lvgl/include/cp0_lvgl_app.h b/ext_components/cp0_lvgl/include/cp0_lvgl_app.h index a7760693..efb89b5b 100644 --- a/ext_components/cp0_lvgl/include/cp0_lvgl_app.h +++ b/ext_components/cp0_lvgl/include/cp0_lvgl_app.h @@ -213,6 +213,10 @@ cp0_battery_info_t cp0_battery_read(void); int cp0_backlight_read(void); int cp0_backlight_max(void); int cp0_backlight_write(int val); +/* Config key (cp0_signal_config_api GetInt/SetInt) selecting how clock values are + presented: 0 = 24-hour "HH:MM" (default), 1 = US 12-hour "h:MM AM/PM". + cp0_time_str() itself always returns the documented 24-hour "HH:MM" text. */ +#define CP0_CONFIG_KEY_CLOCK_12H "clock_12h" void cp0_time_str(char *buf, int buf_size); #ifdef __cplusplus diff --git a/ext_components/cp0_lvgl/include/cp0_lvgl_app_page_assets.h b/ext_components/cp0_lvgl/include/cp0_lvgl_app_page_assets.h index 1cdd9719..f2e19103 100644 --- a/ext_components/cp0_lvgl/include/cp0_lvgl_app_page_assets.h +++ b/ext_components/cp0_lvgl/include/cp0_lvgl_app_page_assets.h @@ -9,6 +9,7 @@ #include "lvgl/lvgl.h" LV_IMAGE_DECLARE(cp0_status_time_background); +LV_IMAGE_DECLARE(cp0_status_time_background_wide); LV_IMAGE_DECLARE(cp0_status_battery_background); LV_IMAGE_DECLARE(cp0_status_ethernet); LV_IMAGE_DECLARE(setting_cross); diff --git a/ext_components/cp0_lvgl/include/ui_app_page.hpp b/ext_components/cp0_lvgl/include/ui_app_page.hpp index aba9c310..721656e7 100644 --- a/ext_components/cp0_lvgl/include/ui_app_page.hpp +++ b/ext_components/cp0_lvgl/include/ui_app_page.hpp @@ -282,6 +282,11 @@ class AppTopBarTitleComponent final : public AppTopBarComponent class AppTopBarTimeComponent final : public AppTopBarComponent { public: + // Pill widths in pixels. The 24-hour pill fits "HH:MM"; the US 12-hour + // form ("h:MM AM/PM") needs room for its widest value, "10:44 AM". + static constexpr int kTwentyFourHourWidth = 40; + static constexpr int kTwelveHourWidth = 68; + AppTopBarTimeComponent(); ~AppTopBarTimeComponent() override; void refresh() override; @@ -293,7 +298,11 @@ class AppTopBarTimeComponent final : public AppTopBarComponent private: static void child_delete_cb(lv_event_t *event); + // Resizes the pill when the CP0_CONFIG_KEY_CLOCK_12H preference changes. + void apply_clock_format(bool twelve_hour); lv_obj_t *label_ = nullptr; + bool twelve_hour_ = false; + bool clock_format_applied_ = false; }; /** @brief Built-in top-bar component that displays battery state. */ diff --git a/ext_components/cp0_lvgl/src/cp0_lvgl_app_page_assets.c b/ext_components/cp0_lvgl/src/cp0_lvgl_app_page_assets.c index 8658b1ba..d1bc535b 100644 --- a/ext_components/cp0_lvgl/src/cp0_lvgl_app_page_assets.c +++ b/ext_components/cp0_lvgl/src/cp0_lvgl_app_page_assets.c @@ -7,6 +7,7 @@ #include "cp0_lvgl_app_page_assets.h" #include "../assets/cp0_status_time_background.inc" +#include "../assets/cp0_status_time_background_wide.inc" #include "../assets/cp0_status_battery_background.inc" #include "../assets/setting_cross.inc" #include "../assets/setting_ok.inc" @@ -27,6 +28,20 @@ const lv_image_dsc_t cp0_status_time_background = { .data = cp0_status_time_background_map, }; +/* Same pill, widened for the US 12-hour clock text ("h:MM AM/PM"). */ +const lv_image_dsc_t cp0_status_time_background_wide = { + .header = { + .magic = LV_IMAGE_HEADER_MAGIC, + .cf = LV_COLOR_FORMAT_ARGB8888, + .flags = 0, + .w = 68, + .h = 16, + .stride = 68 * 4, + }, + .data_size = sizeof(cp0_status_time_background_wide_map), + .data = cp0_status_time_background_wide_map, +}; + const lv_image_dsc_t cp0_status_battery_background = { .header = { .magic = LV_IMAGE_HEADER_MAGIC, diff --git a/ext_components/cp0_lvgl/src/cp0_time_format.cpp b/ext_components/cp0_lvgl/src/cp0_time_format.cpp new file mode 100644 index 00000000..15bf80fe --- /dev/null +++ b/ext_components/cp0_lvgl/src/cp0_time_format.cpp @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#include "cp0_time_format.hpp" + +#include + +namespace cp0::time { + +namespace { + +bool two_digits(std::string_view text, std::size_t offset, int &value) noexcept +{ + if (offset + 1 >= text.size()) return false; + const char high = text[offset]; + const char low = text[offset + 1]; + if (high < '0' || high > '9' || low < '0' || low > '9') return false; + value = (high - '0') * 10 + (low - '0'); + return true; +} + +} // namespace + +bool format_12_hour(std::string_view hh_mm, char *out, std::size_t out_size) noexcept +{ + // "12:59 PM" is the widest result; requiring room for it up front keeps a + // failed call from touching the caller's buffer at all. + constexpr std::size_t kMaxFormattedBytes = sizeof("12:59 PM"); + if (!out || out_size < kMaxFormattedBytes) return false; + + int hour = 0; + int minute = 0; + if (hh_mm.size() != 5 || hh_mm[2] != ':' || !two_digits(hh_mm, 0, hour) || + !two_digits(hh_mm, 3, minute) || hour > 23 || minute > 59) + return false; + + const char *meridiem = hour < 12 ? "AM" : "PM"; + const int display_hour = hour % 12 == 0 ? 12 : hour % 12; + const int written = + std::snprintf(out, out_size, "%d:%02d %s", display_hour, minute, meridiem); + return written > 0 && static_cast(written) < out_size; +} + +} // namespace cp0::time diff --git a/ext_components/cp0_lvgl/src/cp0_time_format.hpp b/ext_components/cp0_lvgl/src/cp0_time_format.hpp new file mode 100644 index 00000000..4d854f86 --- /dev/null +++ b/ext_components/cp0_lvgl/src/cp0_time_format.hpp @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#pragma once + +#include +#include + +namespace cp0::time { + +/** + * @brief Presents 24-hour clock text as US 12-hour time. + * + * cp0_time_str() keeps its documented "HH:MM" contract because callers such as + * the Date & Time page parse that value. This helper only changes how the same + * value is presented, e.g. on the application top bar. + * + * @param hh_mm Exactly five characters: a two-digit hour 00..23, ':' and a + * two-digit minute 00..59. + * @param out Destination buffer. Untouched when the call fails. + * @param out_size Destination capacity in bytes, including the terminator. + * @return true when hh_mm was a valid 24-hour clock and out holds the 12-hour + * text ("h:MM AM" / "h:MM PM"); false otherwise. + */ +bool format_12_hour(std::string_view hh_mm, char *out, std::size_t out_size) noexcept; + +} // namespace cp0::time diff --git a/ext_components/cp0_lvgl/src/ui_app_page.cpp b/ext_components/cp0_lvgl/src/ui_app_page.cpp index fc9d9f1a..6c81a704 100644 --- a/ext_components/cp0_lvgl/src/ui_app_page.cpp +++ b/ext_components/cp0_lvgl/src/ui_app_page.cpp @@ -9,6 +9,11 @@ #include "cp0_pointer_lifecycle.hpp" #include "cp0_status_lifecycle.hpp" #include "cp0_status_component_contract.hpp" +#include "cp0_sync_signal.hpp" +#include "cp0_time_format.hpp" + +#include +#include namespace { @@ -397,10 +402,30 @@ lv_obj_t *create_centered_status_label(lv_obj_t *obj) return label; } +/* The clock presentation is a user preference stored in the shared config + service; an unavailable service keeps the 24-hour default. */ +bool clock_prefers_twelve_hour() +{ + int preference = 0; + cp0::signal::invoke_noexcept([&preference] { + cp0_signal_config_api( + {"GetInt", CP0_CONFIG_KEY_CLOCK_12H, "0"}, + [&preference](int code, std::string data) { + int parsed = 0; + const char *end = data.data() + data.size(); + const auto result = std::from_chars(data.data(), end, parsed); + if (code == 0 && result.ec == std::errc{} && result.ptr == end && + (parsed == 0 || parsed == 1)) + preference = parsed; + }); + }); + return preference != 0; +} + } // namespace AppTopBarTimeComponent::AppTopBarTimeComponent() - : AppTopBarComponent("cp0.time", 40) + : AppTopBarComponent("cp0.time", kTwentyFourHourWidth) { } @@ -427,11 +452,35 @@ void AppTopBarTimeComponent::child_delete_cb(lv_event_t *event) void AppTopBarTimeComponent::refresh() { if (!label_) return; + + // cp0_time_str() always yields the documented 24-hour "HH:MM" value; the + // preference only changes how the pill presents it. + const bool twelve_hour = clock_prefers_twelve_hour(); + if (!clock_format_applied_ || twelve_hour != twelve_hour_) apply_clock_format(twelve_hour); + char text[16] = "--:--"; cp0_time_str(text, sizeof(text)); + if (twelve_hour) { + char formatted[16] = {}; + if (cp0::time::format_12_hour(text, formatted, sizeof(formatted))) { + lv_label_set_text(label_, formatted); + return; + } + } lv_label_set_text(label_, text[0] ? text : "--:--"); } +void AppTopBarTimeComponent::apply_clock_format(bool twelve_hour) +{ + twelve_hour_ = twelve_hour; + clock_format_applied_ = true; + if (!obj()) return; + set_width(twelve_hour ? kTwelveHourWidth : kTwentyFourHourWidth); + configure_status_component( + obj(), twelve_hour ? &cp0_status_time_background_wide : &cp0_status_time_background); + if (label_) lv_obj_center(label_); +} + void AppTopBarTimeComponent::on_object_deleted() { label_ = nullptr; diff --git a/ext_components/cp0_lvgl/tests/run_tests.sh b/ext_components/cp0_lvgl/tests/run_tests.sh index 8d428cea..e3bbd325 100755 --- a/ext_components/cp0_lvgl/tests/run_tests.sh +++ b/ext_components/cp0_lvgl/tests/run_tests.sh @@ -419,6 +419,12 @@ fi "$root/tests/test_settings_policy.cpp" -o "$binary" "$binary" +"${CXX:-c++}" -std=c++17 -Wall -Wextra -Werror \ + -I"$root/src" \ + "$root/src/cp0_time_format.cpp" \ + "$root/tests/test_time_format.cpp" -o "$binary" +"$binary" + "${CXX:-c++}" -std=c++17 -Wall -Wextra -Werror \ -I"$root/src" \ "$root/src/cp0_pty_contract.cpp" \ diff --git a/ext_components/cp0_lvgl/tests/test_time_format.cpp b/ext_components/cp0_lvgl/tests/test_time_format.cpp new file mode 100644 index 00000000..97683c1d --- /dev/null +++ b/ext_components/cp0_lvgl/tests/test_time_format.cpp @@ -0,0 +1,99 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#include "cp0_time_format.hpp" + +#include +#include +#include + +using cp0::time::format_12_hour; + +namespace { + +std::string formatted(const char *text) +{ + char buffer[16] = {}; + if (!format_12_hour(text, buffer, sizeof(buffer))) return ""; + return buffer; +} + +void expect(const char *input, const char *expected) +{ + char buffer[16] = {}; + assert(format_12_hour(input, buffer, sizeof(buffer))); + assert(std::strcmp(buffer, expected) == 0); +} + +} // namespace + +int main() +{ + // Midnight and noon are the two cases that are easy to get wrong: neither + // maps to hour 0. + expect("00:00", "12:00 AM"); + expect("00:05", "12:05 AM"); + expect("00:59", "12:59 AM"); + expect("12:00", "12:00 PM"); + expect("12:34", "12:34 PM"); + + // Morning keeps the AM suffix; the leading hour zero is dropped. + expect("01:05", "1:05 AM"); + expect("09:09", "9:09 AM"); + expect("11:59", "11:59 AM"); + + // Afternoon/evening wrap back to a 1..12 hour. + expect("13:05", "1:05 PM"); + expect("19:44", "7:44 PM"); + expect("23:59", "11:59 PM"); + expect("10:44", "10:44 AM"); + + // Minutes are always two digits, even when they contain zeros. + expect("07:00", "7:00 AM"); + expect("21:07", "9:07 PM"); + + // Anything that is not a valid 24-hour clock is rejected, and the caller's + // buffer is left untouched so the previous text cannot be half-overwritten. + const char *invalid[] = { + "", + "1:05", + "01:5", + "0105", + "24:00", + "23:60", + "aa:bb", + " 1:05", + "-1:05", + "01-05", + "01:005", + }; + for (const char *input : invalid) { + char buffer[16] = "keep"; + assert(!format_12_hour(input, buffer, sizeof(buffer))); + assert(std::strcmp(buffer, "keep") == 0); + } + + // A too-small buffer fails instead of truncating the result. + char small[8] = "keep"; + assert(!format_12_hour("10:44", small, sizeof(small))); + assert(std::strcmp(small, "keep") == 0); + + // A one-byte buffer cannot hold even an empty string; make sure the helper + // does not write a truncated result. + char tiny[4] = "abc"; + assert(!format_12_hour("13:05", tiny, 1)); + assert(tiny[0] == 'a'); + + // Null/zero-size output is rejected rather than crashing. + char buffer[16] = {}; + assert(!format_12_hour("10:44", nullptr, sizeof(buffer))); + assert(!format_12_hour("10:44", buffer, 0)); + + assert(formatted("10:44") == "10:44 AM"); + assert(formatted("nope!") == ""); + + return 0; +} diff --git a/projects/APPLaunch/main/ui/settings/settings_page.cpp b/projects/APPLaunch/main/ui/settings/settings_page.cpp index 2ab89903..f017dc81 100644 --- a/projects/APPLaunch/main/ui/settings/settings_page.cpp +++ b/projects/APPLaunch/main/ui/settings/settings_page.cpp @@ -578,6 +578,72 @@ void bluetooth_named_only_api(int cmd, void *data) bluetooth_named_only_state = previous; } +// CP0_CONFIG_KEY_CLOCK_12H is the same key the platform top bar reads, so the +// setting and its presentation cannot drift apart. +constexpr const char *kClock12HourConfigKey = CP0_CONFIG_KEY_CLOCK_12H; + +bool read_clock_12h_config() +{ + int preference = 0; + try { + cp0_signal_config_api( + {"GetInt", kClock12HourConfigKey, "0"}, + [&preference](int code, std::string data) { + int parsed = 0; + const char *end = data.data() + data.size(); + const auto result = std::from_chars(data.data(), end, parsed); + if (code == 0 && result.ec == std::errc{} && result.ptr == end && + (parsed == 0 || parsed == 1)) + preference = parsed; + }); + } catch (...) { + } + return preference != 0; +} + +bool write_clock_12h_config(bool twelve_hour) +{ + bool succeeded = false; + try { + cp0_signal_config_api( + {"SetInt", kClock12HourConfigKey, twelve_hour ? "1" : "0"}, + [&succeeded](int code, std::string) { succeeded = code == 0; }); + } catch (...) { + } + return succeeded; +} + +bool save_clock_12h_config() +{ + bool succeeded = false; + try { + cp0_signal_config_api({"Save"}, [&succeeded](int code, std::string) { succeeded = code == 0; }); + } catch (...) { + } + return succeeded; +} + +void clock_12h_api(int cmd, void *data) +{ + if ((cmd == SettingApiReadFlag || cmd == SettingApiReadFlagTimeStart) && data) { + const bool twelve_hour = read_clock_12h_config(); + if (cmd == SettingApiReadFlag) + *static_cast(data) = twelve_hour; + else + std::get<0>(*static_cast(data)) = twelve_hour; + return; + } + if (cmd != SettingApiActivate) return; + + const bool previous = read_clock_12h_config(); + if (!write_clock_12h_config(!previous)) return; + if (save_clock_12h_config()) return; + + // SetInt mutates the live config immediately. Restore it when Save fails so + // a later page rebuild cannot observe an unpersisted value. + if (write_clock_12h_config(previous)) (void)save_clock_12h_config(); +} + static void append_numeric_options(Tree &tree, const NodeIter &parent, int first, int last) { for (int value = first; value <= last; ++value) tree.append_child(parent, SettingEntry{std::to_string(value)}); @@ -738,6 +804,11 @@ void UISettingTreePage::create_page_detail() mode_tree.append_child(write_rtc, SettingEntry{"Yes"}); mode_tree.append_child(write_rtc, SettingEntry{"No"}); } + // Presentation only: the stored clock stays 24-hour, the top bar shows + // "h:MM AM/PM" once this is on. + SettingEntry clock_format{"12H Clock", clock_12h_api, true}; + clock_format.status_read_policy = SettingStatusReadPolicy::Direct; + mode_tree.append_child(date_time, std::move(clock_format)); } { diff --git a/projects/APPLaunch/setup.ini b/projects/APPLaunch/setup.ini index 30ec93d6..ea122e76 100644 --- a/projects/APPLaunch/setup.ini +++ b/projects/APPLaunch/setup.ini @@ -1,9 +1,9 @@ [ssh] local_file_path = dist remote_file_path = /home/pi/dist -remote_host = 192.168.28.237 +remote_host = 192.168.28.97 remote_port = 22 username = pi password = pi ; before_cmd = 'echo pi | sudo -S systemctl stop APPLaunch.service' -after_cmd = 'systemctl --user stop APPLaunch.service; echo pi | sudo -S mkdir -p /usr/share/APPLaunch/bin; echo pi | sudo -S cp -a /home/pi/dist/APPLaunch/. /usr/share/APPLaunch/; echo pi | sudo -S install -m 755 /home/pi/dist/M5CardputerZero-APPLaunch /usr/share/APPLaunch/bin/M5CardputerZero-APPLaunch; systemctl --user start APPLaunch.service' +after_cmd = 'systemctl --user stop APPLaunch.service; echo {password} | sudo -S mkdir -p /usr/share/APPLaunch/bin; echo {password} | sudo -S cp -a {remote_file_path}/APPLaunch/. /usr/share/APPLaunch/; echo {password} | sudo -S install -m 755 {remote_file_path}/M5CardputerZero-APPLaunch /usr/share/APPLaunch/bin/M5CardputerZero-APPLaunch; systemctl --user start APPLaunch.service' diff --git a/projects/APPLaunch/tests/run_tests.sh b/projects/APPLaunch/tests/run_tests.sh index b98b75fc..b745254b 100755 --- a/projects/APPLaunch/tests/run_tests.sh +++ b/projects/APPLaunch/tests/run_tests.sh @@ -12,6 +12,7 @@ python3 "$test_root/test_settings_menu_order.py" python3 "$test_root/test_settings_wifi_enable_copy_contract.py" python3 "$test_root/test_settings_ethernet_layout_contract.py" python3 "$test_root/test_settings_rtc_layout_contract.py" +python3 "$test_root/test_settings_clock_format_contract.py" python3 "$test_root/test_settings_volume_initial_selection.py" python3 "$test_root/test_preinstalled_app_packaging.py" python3 "$test_root/test_settings_storage_build_contract.py" diff --git a/projects/APPLaunch/tests/test_settings_clock_format_contract.py b/projects/APPLaunch/tests/test_settings_clock_format_contract.py new file mode 100644 index 00000000..f3194362 --- /dev/null +++ b/projects/APPLaunch/tests/test_settings_clock_format_contract.py @@ -0,0 +1,103 @@ +# SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD +# +# SPDX-License-Identifier: MIT + +"""Contracts for the launcher's US 12-hour clock option. + +The setting lives in the APPLaunch settings tree, but the clock is drawn by the +cp0_lvgl top bar, so the two sides must not drift apart: + +* both read the same shared config key (``CP0_CONFIG_KEY_CLOCK_12H``) and + neither hard-codes the key string; +* ``cp0_time_str()`` keeps its documented 24-hour ``HH:MM`` contract, because + the Date & Time page parses that value; the option only changes presentation; +* the toggle persists through ``SetInt`` + ``Save`` with a rollback, like the + other config-backed toggles in the settings tree. + +The checks are static (regex over the sources) so they need no toolchain. +""" + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REPO = Path(__file__).resolve().parents[3] +SETTINGS_PAGE = (ROOT / "main/ui/settings/settings_page.cpp").read_text(encoding="utf-8") +TOP_BAR = (REPO / "ext_components/cp0_lvgl/src/ui_app_page.cpp").read_text( + encoding="utf-8" +) +PUBLIC_API = (REPO / "ext_components/cp0_lvgl/include/cp0_lvgl_app.h").read_text( + encoding="utf-8" +) +TIME_FORMAT = (REPO / "ext_components/cp0_lvgl/src/cp0_time_format.cpp").read_text( + encoding="utf-8" +) +ASSETS_HEADER = ( + REPO / "ext_components/cp0_lvgl/include/cp0_lvgl_app_page_assets.h" +).read_text(encoding="utf-8") +TOP_BAR_HEADER = ( + REPO / "ext_components/cp0_lvgl/include/ui_app_page.hpp" +).read_text(encoding="utf-8") + + +def date_time_block() -> str: + start = SETTINGS_PAGE.index("NodeIter date_time =") + system = SETTINGS_PAGE.index("NodeIter system =", start) + return SETTINGS_PAGE[start:system] + + +def test_time_format_toggle_is_under_date_and_time(): + block = date_time_block() + assert 'SettingEntry clock_format{"12H Clock", clock_12h_api, true};' in block + assert "clock_format.status_read_policy = SettingStatusReadPolicy::Direct;" in block + assert "mode_tree.append_child(date_time, std::move(clock_format));" in block + # It must sit after the manual editor: the "Set Manually" workflow is + # guarded by its own activation gate and must not be reordered. + assert block.index('SettingEntry clock_format{"12H Clock"') > block.index( + "NodeIter write_rtc" + ) + + +def test_setting_and_top_bar_share_one_config_key(): + assert '#define CP0_CONFIG_KEY_CLOCK_12H "clock_12h"' in PUBLIC_API + # Neither side hard-codes the key string; both use the shared constant. + assert '"clock_12h"' not in SETTINGS_PAGE + assert '"clock_12h"' not in TOP_BAR + assert "CP0_CONFIG_KEY_CLOCK_12H" in SETTINGS_PAGE + assert "CP0_CONFIG_KEY_CLOCK_12H" in TOP_BAR + + +def test_toggle_persists_with_a_rollback(): + assert '{"GetInt", kClock12HourConfigKey, "0"}' in SETTINGS_PAGE + assert '{"SetInt", kClock12HourConfigKey, twelve_hour ? "1" : "0"}' in SETTINGS_PAGE + assert 'cp0_signal_config_api({"Save"}' in SETTINGS_PAGE + # SetInt mutates the live config immediately, so a failed Save must restore + # the previous value instead of leaving an unpersisted preference. + assert ( + "if (write_clock_12h_config(previous)) (void)save_clock_12h_config();" + in SETTINGS_PAGE + ) + + +def test_top_bar_only_changes_the_presentation(): + # The 24-hour value keeps coming from the documented cp0_time_str() API. + assert "cp0_time_str(text, sizeof(text));" in TOP_BAR + assert "cp0::time::format_12_hour(text, formatted, sizeof(formatted))" in TOP_BAR + assert "void cp0_time_str(char *buf, int buf_size);" in PUBLIC_API + # The formatter only reformats the five-character HH:MM text. + assert "hh_mm.size() != 5" in TIME_FORMAT + assert "hh_mm[2] != ':'" in TIME_FORMAT + + +def test_12_hour_pill_uses_the_wider_background(): + assert "LV_IMAGE_DECLARE(cp0_status_time_background_wide);" in ASSETS_HEADER + assert "cp0_status_time_background_wide" in TOP_BAR + assert "static constexpr int kTwelveHourWidth = 68;" in TOP_BAR_HEADER + assert "static constexpr int kTwentyFourHourWidth = 40;" in TOP_BAR_HEADER + + +if __name__ == "__main__": + for _name, _test in sorted(globals().items()): + if _name.startswith("test_") and callable(_test): + _test() diff --git a/projects/CardputerZero-Emulator/CMakeLists.txt b/projects/CardputerZero-Emulator/CMakeLists.txt index 93bc95ef..d7c96ca4 100644 --- a/projects/CardputerZero-Emulator/CMakeLists.txt +++ b/projects/CardputerZero-Emulator/CMakeLists.txt @@ -159,6 +159,7 @@ set(CP0_LVGL_APP_COMMON_SRCS ${CP0_LVGL}/src/cp0_network_api_contract.cpp ${CP0_LVGL}/src/cp0_status_ethernet.c ${CP0_LVGL}/src/cp0_status_lifecycle.cpp + ${CP0_LVGL}/src/cp0_time_format.cpp ${CP0_LVGL}/src/ui_app_page.cpp )