From 6b5425ebd35c28e20680f5f28c887b705827ba1c Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 18:53:32 +0100 Subject: GUI: add on-screen toasts and atomic config writes - Toast queue drawn in the top-right by the overlay, independent of the GUI being open. Info/warn/error colors and 6/9/12 s lifetimes; timers start on the first painted frame so startup messages are not missed. - Queue a load toast after install_menu, plus warnings/errors for missing modules/interfaces/netvars, input hooks, fonts, ISurface, server datamap, and spread seed support. - Toast on config reload, reset, bind/unbind and config save failure. - Config saves now write `config.tmp`, flush, then rename it over the real file; failures leave the previous config intact. - config_load reports whether a file was found so Reload can distinguish a successful reload from a missing file. - Update CONTEXT and ISURFACE_STATUS for the single GUI, toasts and atomic config writes. --- src/menu.cpp | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) (limited to 'src/menu.cpp') diff --git a/src/menu.cpp b/src/menu.cpp index 5fc325e..2021218 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -116,12 +116,14 @@ void register_bundled_font() { } if (path.empty()) { logf("font: bundled LiberationSans-Regular.ttf not found"); + toastf(TOAST_WARN, "Bundled font missing; overlay may not render"); return; } void *fc = dlopen("libfontconfig.so.1", RTLD_LAZY | RTLD_GLOBAL); if (!fc) { logf("font: libfontconfig.so.1 not available"); + toastf(TOAST_WARN, "fontconfig unavailable; overlay font not registered"); return; } using InitFn = int (*)(); @@ -132,20 +134,27 @@ void register_bundled_font() { auto add = (AddFileFn)dlsym(fc, "FcConfigAppFontAddFile"); if (!init || !get || !add) { logf("font: fontconfig symbols missing"); + toastf(TOAST_WARN, "fontconfig symbols missing; overlay font not registered"); return; } init(); void *cfg = get(); if (cfg && add(cfg, path.c_str())) logf("font: registered %s", path.c_str()); - else + else { logf("font: failed to register %s", path.c_str()); + toastf(TOAST_WARN, "Could not register the overlay font"); + } } void config_save() { - FILE *f = fopen(config_path().c_str(), "w"); - if (!f) + std::string path = config_path(); + std::string tmp = path + ".tmp"; + FILE *f = fopen(tmp.c_str(), "w"); + if (!f) { + toastf(TOAST_ERROR, "Config save failed: %s", path.c_str()); return; + } fprintf(f, "[bhop]\nactive=%d\nbind=%d\nautostrafe=%d\n", g_bhop.active, g_bhop.bind, g_bhop_autostrafe); fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); @@ -175,13 +184,23 @@ void config_save() { g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); gui_config_write(f); - fclose(f); + bool ok = ferror(f) == 0; + if (fflush(f) != 0) + ok = false; + if (ok) + (void)fsync(fileno(f)); // best effort; the rename is what matters + if (fclose(f) != 0) + ok = false; + if (!ok || rename(tmp.c_str(), path.c_str()) != 0) { + remove(tmp.c_str()); + toastf(TOAST_ERROR, "Config save failed: %s", path.c_str()); + } } -static void config_load() { +static bool config_load() { FILE *f = fopen(config_path().c_str(), "r"); if (!f) - return; + return false; char line[256]; FeatureState *cur = nullptr; bool in_esp = false, in_bhop = false, in_aim = false, in_silent = false, @@ -288,9 +307,15 @@ static void config_load() { } } fclose(f); + return true; } -void config_reload() { config_load(); } +void config_reload() { + if (config_load()) + toastf(TOAST_INFO, "Config reloaded"); + else + toastf(TOAST_WARN, "Config not found; kept current settings"); +} const char *config_path_cstr() { static std::string path; @@ -583,7 +608,8 @@ void outer_draw() { // HL2H_DRAW_TEST=1 draws a probe rect without needing the GUI open static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; bool esp_on = feature_active(FEAT_ESP); - if (!g_surface || (!gui_is_open() && !draw_test && !esp_on)) + bool toasts = gui_toasts_active(); + if (!g_surface || (!gui_is_open() && !draw_test && !esp_on && !toasts)) return; // Level/menu -> game transitions can invalidate the VGUI font handle. @@ -622,6 +648,7 @@ void outer_draw() { } if (!font_ok) { logf("outer_draw: no usable font"); + toastf(TOAST_ERROR, "No usable overlay font"); return; } } @@ -639,6 +666,8 @@ void outer_draw() { } if (gui_is_open()) gui_draw(); + if (toasts) + gui_draw_toasts(); surf_pop_make_current(panel); } @@ -697,13 +726,15 @@ static ISurface *find_surface_interface() { void install_menu() { register_bundled_font(); logf("config: %s", config_path().c_str()); - config_load(); + if (!config_load()) + toastf(TOAST_INFO, "No config found; using defaults"); gui_config_init(); // resolve ISurface once (used by the DrawPrintText hook in hl2h.cpp and // by outer_draw). DrawPrintText hook is installed from hkdCreateMove. g_surface = find_surface_interface(); if (!g_surface) { logf("ISurface not found; menu disabled"); + toastf(TOAST_ERROR, "Overlay unavailable: ISurface not found"); return; } logf("ISurface = %p", g_surface); -- cgit v1.2.3