Skip to content

Commit a5cdf94

Browse files
authored
Merge pull request #364 from No0ne558/master
vt_data auto-update, persist setting, add stdlib/fmt fallbacks & CI fixes (7 commits)
2 parents e38d982 + 4147b5a commit a5cdf94

5 files changed

Lines changed: 164 additions & 69 deletions

File tree

‎docs/changelog.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
77
## [Unreleased]
88

99
### Fixed
10+
- **Auto-Update vt_data: Prevent automatic updates when disabled (2026-04-09)**
11+
- Fixed unexpected vt_data downloads/updates triggered when restarting or shutting down from non-server displays even when "Auto-Update vt_data on Startup" is OFF.
12+
- Root Cause: early startup autoupdate script executed based on `.viewtouch_config.autoupdate` before the authoritative fixed settings were consulted; restart flows that use the command file + vtrestart could relaunch `vt_main` which ran the script regardless of the UI toggle.
13+
- Solution:
14+
- Startup autoupdate now requires both `.viewtouch_config.autoupdate` and the fixed settings file (`/usr/viewtouch/dat/settings.dat`) `auto_update_vt_data` flag to be true before running the update script.
15+
- `FindVTData()` now respects `auto_update_vt_data` and will not download `vt_data` when auto-update is disabled.
16+
- The UI switch persists immediately (`settings->Save()`), ensuring restarts honor the user's preference.
17+
- Added logging when autoupdate is suppressed and when restart origin is recorded for auditing.
18+
- Impact: Restarting or shutting down from any display no longer triggers automatic vt_data updates when the setting is OFF.
19+
- Files modified: `main/data/manager.cc`, `zone/settings_zone.cc`
20+
21+
- **Default Employees: Only create preset employees when missing (2026-04-09)**
22+
- Fixed issue where preset/default employees were created every startup even when `employee.dat` already existed.
23+
- Solution: Only create default/preset employees when `employee.dat` is missing.
24+
- Files modified: `main/data/manager.cc`
25+
1026
- **Dialog: Fix crash when opening/using Add Comment dialog (2026-04-08)**
1127
- Prevents a segmentation fault when opening or submitting the Add Comment dialog caused by an uninitialized `key` pointer in `GetTextDialog`.
1228
- `GetTextDialog` constructors now initialize all entries of `key[]` to `nullptr` to avoid dereferencing uninitialized pointers.

‎main/data/manager.cc‎

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,21 @@ int main(int argc, genericChar* argv[])
667667
vt::Logger::info("Using default data path: {}", VIEWTOUCH_PATH "/dat");
668668
MasterSystem->SetDataPath(VIEWTOUCH_PATH "/dat");
669669
}
670+
// Respect fixed settings file when deciding whether to run startup update script
671+
bool fixed_auto_update_allowed = true;
672+
std::string fixed_settings_path = std::string(VIEWTOUCH_PATH) + "/dat/settings.dat";
673+
if (fs::exists(fixed_settings_path)) {
674+
Settings temp_settings;
675+
if (temp_settings.Load(fixed_settings_path.c_str()) == 0) {
676+
fixed_auto_update_allowed = temp_settings.auto_update_vt_data;
677+
if (!fixed_auto_update_allowed)
678+
ReportError(GlobalTranslate("Startup autoupdate suppressed by fixed settings"));
679+
} else {
680+
ReportError(GlobalTranslate("Warning: Could not load fixed settings file for startup update decision"));
681+
}
682+
}
670683
// Check for updates from server if not disabled
671-
if (autoupdate)
684+
if (autoupdate && fixed_auto_update_allowed)
672685
{
673686
ReportError(GlobalTranslate("Automatic check for updates..."));
674687
unlink(VIEWTOUCH_UPDATE_COMMAND); // out with the old
@@ -681,26 +694,12 @@ int main(int argc, genericChar* argv[])
681694
// Check if vt_data exists locally first
682695
bool vt_data_updated = false;
683696

684-
// Check if auto-update is enabled by loading settings from a fixed location
685-
// to ensure consistency across all displays
686-
bool auto_update_enabled = true; // Default to enabled for backward compatibility
687-
688-
std::string fixed_settings_path = std::string(VIEWTOUCH_PATH) + "/dat/settings.dat";
689-
690-
if (fs::exists(fixed_settings_path)) {
691-
Settings temp_settings;
692-
if (temp_settings.Load(fixed_settings_path.c_str()) == 0) {
693-
auto_update_enabled = temp_settings.auto_update_vt_data;
694-
if (!auto_update_enabled) {
695-
ReportError(GlobalTranslate("Auto-update of vt_data is disabled in settings"));
696-
} else {
697-
ReportError(GlobalTranslate("Auto-update of vt_data is enabled in settings"));
698-
}
699-
} else {
700-
ReportError(GlobalTranslate("Warning: Could not load settings file, defaulting to auto-update enabled"));
701-
}
697+
// Determine whether vt_data auto-update is enabled (reuse earlier fixed setting)
698+
bool auto_update_enabled = fixed_auto_update_allowed;
699+
if (!auto_update_enabled) {
700+
ReportError(GlobalTranslate("Auto-update of vt_data is disabled in settings"));
702701
} else {
703-
ReportError(GlobalTranslate("Warning: Settings file not found, defaulting to auto-update enabled"));
702+
ReportError(GlobalTranslate("Auto-update of vt_data is enabled in settings"));
704703
}
705704

706705
if (!fs::exists(SYSTEM_DATA_FILE)) {
@@ -948,7 +947,12 @@ void UserSignal1(int /*my_signal*/)
948947
}
949948
}
950949

951-
// Exit immediately to trigger restart
950+
// Log origin of restart if available, then exit to trigger restart
951+
std::string origin = displaystr.data();
952+
if (!origin.empty())
953+
ReportError(std::string("UserSignal1: Restart requested from display: ") + origin);
954+
else
955+
ReportError("UserSignal1: Restart requested (unknown display)");
952956
ReportError("UserSignal1: Exiting for restart");
953957
exit(0);
954958
}
@@ -1317,11 +1321,19 @@ int StartSystem(int my_use_net)
13171321
// set developer key (this should be done somewhere else)
13181322
sys->user_db.developer->key = settings->developer_key;
13191323

1320-
// Create default users if settings file was just created
1321-
if (settings_just_created)
1324+
// Create default users only if no employee database file exists
13221325
{
1323-
ReportLoader("Creating Default Users");
1324-
CreateDefaultUsers(sys, settings);
1326+
std::array<genericChar, STRLENGTH> user_db_path{};
1327+
sys->FullPath(MASTER_USER_DB, user_db_path.data());
1328+
if (!DoesFileExist(user_db_path.data()))
1329+
{
1330+
ReportLoader("Creating Default Users");
1331+
CreateDefaultUsers(sys, settings);
1332+
}
1333+
else
1334+
{
1335+
ReportLoader("Skipping Default Users creation; employee database exists");
1336+
}
13251337
}
13261338

13271339
vt_safe_string::safe_format(msg.data(), msg.size(), "%s OK", MASTER_USER_DB);
@@ -1919,9 +1931,17 @@ int FindVTData(InputDataFile *infile)
19191931
if (infile->Open(vt_data_path, version) == 0)
19201932
return version;
19211933

1922-
// Only download if we don't have any vt_data file anywhere
1923-
// This prevents overwriting existing files when offline
1924-
if (!fs::exists(SYSTEM_DATA_FILE) && !fs::exists(vt_data_path)) {
1934+
// Only download if we don't have any vt_data file anywhere AND auto-update is allowed
1935+
// This prevents overwriting existing files when offline or when auto-update is disabled
1936+
bool allow_download = true;
1937+
if (MasterSystem != nullptr) {
1938+
if (MasterSystem->settings.auto_update_vt_data == 0) {
1939+
allow_download = false;
1940+
}
1941+
}
1942+
if (!allow_download) {
1943+
fprintf(stderr, "Auto-update disabled by settings, skipping vt_data download in FindVTData\n");
1944+
} else if (!fs::exists(SYSTEM_DATA_FILE) && !fs::exists(vt_data_path)) {
19251945
// download to official location and then try to read again
19261946
// Try both HTTPS and HTTP for reliable downloads on Raspberry Pi
19271947
const std::string vtdata_url = "www.viewtouch.com/vt_data";
@@ -3560,8 +3580,10 @@ int RunUserCommand()
35603580
AllowLogins = 0;
35613581
else if (strcmp(key.data(), "allowlogin") == 0)
35623582
AllowLogins = 1;
3563-
else if (strcmp(key.data(), "exitsystem") == 0)
3583+
else if (strcmp(key.data(), "exitsystem") == 0) {
35643584
exit_system = 1;
3585+
ReportError("RunUserCommand: External command requested system exit");
3586+
}
35653587
else if (strcmp(key.data(), "endday") == 0)
35663588
endday = RunEndDay();
35673589
else if (strcmp(key.data(), "runmacros") == 0)

‎src/utils/cpp23_utils.hh‎

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,16 @@
2727
#include <system_error>
2828
#include <stdexcept>
2929
#include <type_traits>
30+
#include <algorithm> // std::clamp
3031

3132
// Feature detection for C++20/C++23 features
3233
#ifdef __has_include
3334
# if __has_include(<format>)
3435
# include <format>
3536
# define VT_HAS_STD_FORMAT 1
37+
# elif __has_include(<fmt/format.h>)
38+
# include <fmt/format.h>
39+
# define VT_HAS_FMTLIB 1
3640
# endif
3741
#endif
3842

@@ -111,62 +115,32 @@ template<typename E>
111115
// String Formatting (C++20 std::format, enhanced in C++23)
112116
// ============================================================================
113117

114-
/**
115-
* @brief Type-safe string formatting using std::format
116-
*
117-
* Modern replacement for sprintf/snprintf with compile-time format checking.
118-
*
119-
* @param fmt Format string
120-
* @param args Arguments to format
121-
* @return Formatted string
122-
*
123-
* Example:
124-
* auto str = format("Account {} of {}", account_no, total);
125-
* auto price = format("Price: ${:.2f}", 19.99);
126-
*/
118+
// String formatting utilities: provide implementations using std::format (C++20/23)
119+
// or fall back to {fmt} if available. If neither is present, provide a
120+
// minimal snprintf-based fallback for simple formatting.
121+
122+
#if defined(VT_HAS_STD_FORMAT)
123+
127124
template<typename... Args>
128125
[[nodiscard]] std::string format(std::format_string<Args...> fmt, Args&&... args) {
129126
return std::format(fmt, std::forward<Args>(args)...);
130127
}
131128

132-
/**
133-
* @brief Format to an existing string (more efficient, no allocation)
134-
*
135-
* @param str Output string (will be cleared and reused)
136-
* @param fmt Format string
137-
* @param args Arguments to format
138-
*/
139129
template<typename... Args>
140130
void format_to(std::string& str, std::format_string<Args...> fmt, Args&&... args) {
141131
str.clear();
142132
std::format_to(std::back_inserter(str), fmt, std::forward<Args>(args)...);
143133
}
144134

145-
/**
146-
* @brief Format with fixed-size buffer (for stack allocation)
147-
*
148-
* Safe replacement for snprintf() with automatic truncation.
149-
* Returns number of characters that would have been written (like snprintf).
150-
*
151-
* @param buffer Output buffer
152-
* @param size Buffer size
153-
* @param fmt Format string
154-
* @param args Arguments to format
155-
* @return Number of characters written (excluding null terminator)
156-
*
157-
* Example:
158-
* char buffer[256];
159-
* format_to_buffer(buffer, sizeof(buffer), "Account {}", acct_num);
160-
*/
161135
template<typename... Args>
162136
[[nodiscard]] std::size_t format_to_buffer(
163-
char* buffer,
137+
char* buffer,
164138
std::size_t size,
165-
std::format_string<Args...> fmt,
166-
Args&&... args)
139+
std::format_string<Args...> fmt,
140+
Args&&... args)
167141
{
168142
if (size == 0) return 0;
169-
143+
170144
try {
171145
auto result = std::format_to_n(buffer, size - 1, fmt, std::forward<Args>(args)...);
172146
buffer[result.size < size ? result.size : size - 1] = '\0';
@@ -177,6 +151,82 @@ template<typename... Args>
177151
}
178152
}
179153

154+
#elif defined(VT_HAS_FMTLIB)
155+
156+
// Use {fmt} library as a compatible fallback.
157+
template<typename... Args>
158+
[[nodiscard]] std::string format(fmt::format_string<Args...> fmt_s, Args&&... args) {
159+
return fmt::format(fmt_s, std::forward<Args>(args)...);
160+
}
161+
162+
template<typename... Args>
163+
void format_to(std::string& str, fmt::format_string<Args...> fmt_s, Args&&... args) {
164+
str.clear();
165+
fmt::format_to(std::back_inserter(str), fmt_s, std::forward<Args>(args)...);
166+
}
167+
168+
template<typename... Args>
169+
[[nodiscard]] std::size_t format_to_buffer(
170+
char* buffer,
171+
std::size_t size,
172+
fmt::format_string<Args...> fmt_s,
173+
Args&&... args)
174+
{
175+
if (size == 0) return 0;
176+
177+
try {
178+
auto result = fmt::format_to_n(buffer, size - 1, fmt_s, std::forward<Args>(args)...);
179+
buffer[result.size < size ? result.size : size - 1] = '\0';
180+
return result.size;
181+
} catch (...) {
182+
buffer[0] = '\0';
183+
return 0;
184+
}
185+
}
186+
187+
#else
188+
189+
// Minimal fallback: use snprintf-style formatting. This does NOT support
190+
// Python-style formatting ("{}") and is a degraded fallback for older
191+
// compilers without std::format or {fmt}. It keeps the code compiling but
192+
// callers should prefer environments with std::format or fmt available.
193+
#include <cstdio>
194+
195+
template<typename... Args>
196+
[[nodiscard]] std::string format(const char* fmt_cstr, Args&&... args) {
197+
// Estimate required size
198+
int size = std::snprintf(nullptr, 0, fmt_cstr, std::forward<Args>(args)...);
199+
if (size <= 0) return std::string();
200+
std::string out;
201+
out.resize(static_cast<std::size_t>(size));
202+
std::snprintf(out.data(), out.size() + 1, fmt_cstr, std::forward<Args>(args)...);
203+
return out;
204+
}
205+
206+
template<typename... Args>
207+
void format_to(std::string& str, const char* fmt_cstr, Args&&... args) {
208+
str = format(fmt_cstr, std::forward<Args>(args)...);
209+
}
210+
211+
template<typename... Args>
212+
[[nodiscard]] std::size_t format_to_buffer(
213+
char* buffer,
214+
std::size_t size,
215+
const char* fmt_cstr,
216+
Args&&... args)
217+
{
218+
if (size == 0) return 0;
219+
int n = std::snprintf(buffer, size, fmt_cstr, std::forward<Args>(args)...);
220+
if (n < 0) {
221+
buffer[0] = '\0';
222+
return 0;
223+
}
224+
if (static_cast<std::size_t>(n) >= size) buffer[size - 1] = '\0';
225+
return static_cast<std::size_t>(n);
226+
}
227+
228+
#endif
229+
180230
// ============================================================================
181231
// Error Handling with std::expected
182232
// ============================================================================

‎src/utils/vt_enum_utils.hh‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <optional>
2828
#include <vector>
2929
#include <utility> // std::to_underlying (C++23)
30+
#include <type_traits>
3031

3132
namespace vt {
3233

@@ -132,7 +133,11 @@ std::optional<E> IntToEnum(int value) {
132133
template<typename E>
133134
requires std::is_enum_v<E>
134135
constexpr auto EnumToUnderlying(E value) noexcept {
136+
#if defined(__cpp_lib_to_underlying)
135137
return std::to_underlying(value);
138+
#else
139+
return static_cast<std::underlying_type_t<E>>(value);
140+
#endif
136141
}
137142

138143
/**

‎zone/settings_zone.cc‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,6 +623,8 @@ SignalResult SwitchZone::Touch(Terminal *term, int /*tx*/, int /*ty*/)
623623
break;
624624
case SWITCH_AUTO_UPDATE_VT_DATA:
625625
settings->auto_update_vt_data ^= 1;
626+
// Persist change immediately so startup/restart reads the updated preference
627+
settings->Save();
626628
break;
627629
case SWITCH_BUTTON_IMAGES:
628630
settings->show_button_images_default ^= 1;

0 commit comments

Comments
 (0)