1. Overview
The First-Run Core Entry Point module (first-run-main.cpp) implements the startup routine for BastionGuard’s first-run experience. Its primary responsibility is to ensure that localization, environment variables, and UI styling are configured deterministically before the GTK application window is created.
The module:
- Loads user language/environment preferences from
~/.config/BastionGuard/lang.conf - Applies locale settings via
setlocale(LC_ALL, "") - Initializes gettext domain bindings for BastionGuard translations
- Initializes the GTK application instance
- Loads the shared CSS/UI theme via
load_style() - Creates and runs the first-run window (
FirstRunServicesWindow)
2. Integrated Components and Dependencies
The entry point integrates the following libraries and internal modules:
- GTKmm – application lifecycle and window creation (
Gtk::Application) - glibmm/i18n – gettext wrapper integration and
_()macro usage - POSIX locale –
std::setlocale()and environment propagation - FirstRunServicesWindow – first-run UI window implementation (
first-run-services-window.hpp) - StyleProvider – shared styling loader (
load_style()) - Resource – resource path helpers used indirectly by style/UI components
The build requires LOCALEDIR to be defined for this target; compilation fails fast if it is missing.
3. Language Configuration Loading
3.1 Configuration File Location
User language/environment configuration is loaded from:
~/.config/BastionGuard/lang.conf
This file is expected to contain key/value assignments similar to:
LANG=it_IT.UTF-8LC_ALL=it_IT.UTF-8LANGUAGE=it_IT:en_US
3.2 Parsing Rules
The loader (load_lang_conf()) applies a conservative parsing model:
- Ignores empty lines
- Ignores comment lines beginning with
# - Requires an
=delimiter - Splits into
keyandval - Performs minimal trimming of surrounding whitespace for both key and value
- Ignores entries where key or value is empty after trimming
3.3 Environment Application
Each valid entry is applied to the current process environment using:
setenv(key.c_str(), val.c_str(), 1);
The overwrite flag is set to 1, ensuring that user configuration takes precedence over inherited environment values.
The function returns:
trueif at least one variable was appliedfalseif the file does not exist, cannot be opened, or contains no valid assignments
4. Locale and gettext Initialization
4.1 Initialization Order
The startup sequence enforces a strict order:
- Load
lang.confand apply environment variables - Apply locale via
std::setlocale(LC_ALL, "") - Initialize gettext domain bindings
This ordering is critical: gettext and locale resolution depend on the effective environment variables (e.g., LANG, LC_ALL, LANGUAGE).
4.2 gettext Domain Configuration
Translations are configured using the BastionGuard text domain and build-provided locale directory:
bindtextdomain("BastionGuard", LOCALEDIR);bind_textdomain_codeset("BastionGuard", "UTF-8");textdomain("BastionGuard");
This ensures:
- Translation lookup is scoped to the BastionGuard domain
- Returned strings are UTF-8 encoded
4.3 Diagnostics
After initialization, the module logs a localized diagnostic indicating whether lang.conf was used:
- If loaded: indicates configuration was read from the user config path
- If not loaded: indicates system locale will be used
Messages are emitted only after gettext initialization to ensure they are safe to wrap with _().
5. GTK Application Lifecycle
5.1 Application Creation
The GTK application instance is created with a fixed application ID:
Gtk::Application::create("org.bastionguard.first-run-test")
This ID is used by GTK for application identity, session integration, and single-instance semantics (where applicable).
5.2 Styling Initialization
The module applies shared styling before presenting UI components:
load_style();
This step typically loads the application CSS provider and ensures consistent visual identity with the rest of BastionGuard.
5.3 Window Creation and Run Loop
The first-run window is created and executed using:
return app->make_window_and_run<FirstRunServicesWindow>(argc, argv);
This API:
- Instantiates the
FirstRunServicesWindowtype - Starts the GTK main loop
- Returns the process exit code when the window closes
6. Build-Time Requirements
6.1 LOCALEDIR Definition
The module requires LOCALEDIR to be defined at compile time. If missing, compilation fails with:
#error "LOCALEDIR not defined for this target"
This ensures gettext is always initialized with a valid translation directory path for the current build target.
7. Runtime and Security Considerations
- Deterministic localization: language configuration is applied before
setlocaleand gettext setup to avoid mismatched translations - Scoped environment changes:
setenv()affects only the current process (and children) and does not modify system-wide locale settings - Minimal parsing surface: the config parser accepts only simple key/value assignments and ignores malformed lines
- UTF-8 enforcement: gettext codeset is fixed to UTF-8 to prevent encoding inconsistencies in the UI
- Operational transparency: localized diagnostics indicate whether user language overrides were applied
- Separation of concerns: the entry point only orchestrates initialization; first-run logic is delegated to
FirstRunServicesWindow