1. Overview
LiveScanDialog is a GTKmm (GTK4) modal dialog used to display live scanning progress (typically for USB device scans) in BastionGuard. It provides a branded UI experience consistent with other application dialogs, including a custom header bar, animated progress feedback, a scrolling live log view, and a user-controlled cancellation workflow.
The dialog is designed for safe cross-thread interaction: scanning threads can append output without touching GTK objects directly, while UI updates are marshaled back onto the GTK main loop using Glib::signal_idle().
2. Window Initialization and Styling
2.1 Modal Dialog Configuration
The constructor configures the dialog as a fixed-size, non-resizable, modal window:
set_default_size(620, 420)set_resizable(false)set_modal(true)set_transient_for(parent)(when provided)
Multiple CSS classes are applied to integrate with BastionGuard’s design system:
app-dialogmain-windowapp-window
2.2 Global CSS Injection
The dialog loads a global application stylesheet:
resource("BastionGuard.css")
The CSS provider is registered at application priority using:
Gtk::StyleContext::add_provider_for_display(..., GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)
Errors during CSS loading are intentionally ignored to avoid startup failure if the stylesheet is missing or unavailable.
3. Custom Header Bar
3.1 Header Layout and Buttons
The dialog replaces standard title buttons with a custom Gtk::HeaderBar:
set_show_title_buttons(false)set_decoration_layout("")- CSS class:
custom-headerbar
A close button is created using an image resource:
resource("icon-close.png")scaled to 28×28- CSS class:
header-button
Clicking the close button hides the dialog:
signal_clicked() → hide()
4. Dialog Content Composition
4.1 Root Layout Container
The dialog content is arranged in a vertical box:
Gtk::Box(Gtk::Orientation::VERTICAL, 16)- Content margin:
root_.set_margin(20)
4.2 Title and Contextual Path Display
The main title is a centered label styled with the title class:
- Text: “USB device scan in progress” (localized via
_()) Gtk::Align::CENTER
The scanned target is displayed beneath as a wrapped, centered label:
- Text includes the analyzed path:
_("Analyzed path:\n") + path set_wrap(true)
4.3 Scan Icon (Optional)
An optional center-aligned scan icon is displayed using Gtk::Picture:
- Source:
resource("icons/scan.png") - Scaled to 64×64 using bilinear interpolation
Errors are ignored to keep the dialog functional even when the icon is missing.
4.4 Progress Bar Behavior
The dialog uses a pulsing progress bar for indeterminate scan operations:
set_pulse_step(0.1)set_show_text(true)- Initial text:
_("Scanning in progress...")
4.5 Live Output View (TextView + Scroller)
Scan output is displayed using:
Gtk::TextView(read-only)Gtk::ScrolledWindow(expanded vertically)
The scroller is styled with scroller and configured to expand:
scroller_.set_vexpand(true)
4.6 Cancel Workflow
A centered “Cancel” button is provided with BastionGuard danger styling:
- Label:
_("Cancel") - CSS class:
btn-danger Gtk::Align::CENTER
On click:
- Sets
cancelled_to true (atomic flag) - Disables the button (
set_sensitive(false)) - Updates progress text to:
_("Stopping...")
This establishes a cooperative cancellation model: the scanning worker is expected to periodically check cancelled_ and stop gracefully.
5. Progress Animation Timer
To provide visual feedback while the scan runs, the dialog starts a periodic timer:
- Interval: 120ms
- Action:
progress_.pulse()
The timer stops automatically once the scan is marked as finished:
- If
finished_ == true, the timeout returns false
6. Thread-Safe Live Output Handling
6.1 append_output() – Producer Side
Worker threads call append_output(line) to report progress/log lines without touching GTK objects directly:
- Acquires
mtx_and appendsline + "\n"topending_lines_ - Uses
flush_pending_(atomic) to avoid scheduling redundant UI flushes - If no flush is pending, schedules a single idle callback:
Glib::signal_idle().connect_once([this]() { flush_output_idle(); });
This pattern prevents UI flooding and ensures batching, improving responsiveness for high-volume output.
6.2 flush_output_idle() – Consumer Side (GTK Main Loop)
flush_output_idle() runs on the GTK main loop and performs the actual UI mutation:
- Resets
flush_pending_to false - Moves (swaps) pending lines into a local vector under lock to minimize lock duration
- Appends lines to the
Gtk::TextBufferat the end iterator - Accumulates the same content into
full_output_for later retrieval - Auto-scrolls to bottom by setting the vertical adjustment to its upper bound
7. Scan Completion
7.1 mark_finished()
mark_finished() finalizes the dialog state:
- Sets
finished_ = true - Schedules a final idle flush
- Sets progress to complete:
progress_.set_fraction(1.0)progress_.set_text(_("Completed"))
This ensures that any lines queued near the end of scanning are still rendered before the UI transitions to a completed state.
7.2 Output Retrieval
get_full_output() returns the buffered output text collected during the scan. Access is protected by mtx_ to avoid races with concurrent append operations.
8. Runtime and Security Considerations
- Thread safety: GTK widgets are updated only on the main loop; worker threads push output into a queue protected by a mutex.
- UI throttling: batching output via
flush_pending_prevents excessive idle callbacks and improves responsiveness. - Cooperative cancellation: the dialog only signals cancellation; the scanning engine must enforce it by checking
cancelled_during work. - Resilience: resource loading (CSS, icons) is wrapped in try/catch so missing assets do not crash the dialog.
- UX consistency: the custom header bar and CSS classes match BastionGuard’s alert and window styling system.