1. Overview
The TrayIcon.hpp header defines the TrayIcon class, a lightweight C++ module responsible for BastionGuard’s system tray integration via the StatusNotifierItem (SNI) ecosystem.
The class exposes two D-Bus endpoints on the user bus using systemd sd-bus:
- SNI object (
org.kde.StatusNotifierItem) for icon/title/tooltip and activation events - DBusMenu object (
com.canonical.dbusmenu) for menu layout and menu event dispatch
Internally, TrayIcon runs a dedicated D-Bus processing loop in a background thread and marshals menu actions back onto the UI thread using a stored GMainContext* (typically GTK’s default context).
2. Dependencies and Includes
#include <systemd/sd-bus.h>
#include <glib.h>
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include <vector>
- systemd/sd-bus.h – low-level D-Bus connection, vtables, messages, slots
- glib.h –
GMainContextand main loop invocation primitives - <thread> – dedicated bus thread for message processing
- <atomic> – lock-free
running_guard to prevent double start/stop - <functional> – callbacks for activation/settings/quit actions
- <vector> – storage of icon pixmap bytes
3. Class Responsibilities
TrayIcon is responsible for:
- Registering a unique SNI service name on the user D-Bus
- Exporting SNI and DBusMenu objects and implementing their handlers
- Providing setters for icon, title and tooltip updates
- Dispatching user actions (show/settings/quit) via injectable callbacks
- Managing lifecycle (start/stop) and safe shutdown of the bus thread
4. Public Interface
4.1 Construction and Lifecycle
TrayIcon();
~TrayIcon();
bool start();
void stop();
- start() – acquires the user bus, requests the service name, exports objects, registers with the watcher, and launches the bus loop thread
- stop() – stops processing, joins the bus thread (when safe), unexports objects, and releases bus resources
4.2 Visual State (Icon/Title/Tooltip)
void set_icon(const std::string& icon_name);
void set_icon_file(const std::string& png_path, int target_px = 22);
void set_tooltip(const std::string& text);
void set_title(const std::string& title);
- set_icon() – sets a theme icon name (SNI
IconName) - set_icon_file() – sets an icon pixmap from a PNG file; the default target size is
22px - set_tooltip() – updates the exposed tooltip property (SNI
ToolTip) - set_title() – updates the exposed SNI title property
4.3 Action Callbacks and UI Context Binding
void set_on_activate(std::function<void()> cb);
void set_on_settings(std::function<void()> cb);
void set_on_quit(std::function<void()> cb);
void bind_ui_context();
- set_on_activate() – callback invoked when the tray icon is activated (e.g., click)
- set_on_settings() – callback invoked when the “Settings” menu item is triggered
- set_on_quit() – callback invoked when the “Quit” menu item is triggered
- bind_ui_context() – captures a
GMainContext*used to marshal callbacks onto the GTK/UI thread
5. D-Bus Handler Entry Points (Static Methods)
The class declares static handlers used by sd-bus vtables. They receive the raw D-Bus message and route execution to the instance passed as userdata.
5.1 SNI Methods
static int sni_method_activate(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int sni_method_context_menu(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int sni_method_secondary_activate(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
5.2 SNI Property Getter
static int sni_prop_get(sd_bus* bus,
const char* path,
const char* interface,
const char* property,
sd_bus_message* reply,
void* userdata,
sd_bus_error* ret_error);
Implements SNI properties such as Category, Id, Title, Status, IconName, IconPixmap, ToolTip, and Menu.
5.3 DBusMenu Methods
static int dbusmenu_method_get_layout(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_get_children(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_get_group_properties(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_get_property(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_get_status(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_event(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_about_to_show(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_event_group(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
static int dbusmenu_method_about_to_show_group(sd_bus_message* m, void* userdata, sd_bus_error* ret_error);
These handlers provide menu discovery (layout/children/properties), status reporting, and event dispatch. Both single-event (Event) and grouped-event (EventGroup) flows are supported for host compatibility.
5.4 DBusMenu Property Getter
static int dbusmenu_prop_get(sd_bus* bus,
const char* path,
const char* interface,
const char* property,
sd_bus_message* reply,
void* userdata,
sd_bus_error* ret_error);
Returns DBusMenu protocol properties (notably Version), as required by compatible tray hosts.
6. Internal State
6.1 SNI Identity and Display Fields
std::string service_name_;
std::string id_ = "bastionguard";
std::string title_ = "BastionGuard";
std::string icon_name_ = "security-high";
std::string tooltip_ = "BastionGuard in esecuzione";
std::string status_ = "Active";
GMainContext* ui_ctx_ = nullptr;
- service_name_ – unique well-known name used to register with the watcher
- id_ – SNI identifier string
- title_ – SNI title property
- icon_name_ – theme icon name (used when pixmap is not provided)
- tooltip_ – tooltip text (localized in the implementation layer)
- status_ – SNI status string (e.g.,
Active) - ui_ctx_ – main context used to dispatch UI work safely
6.2 Pixmap (IconPixmap) State
bool have_pixmap_ = false;
int pix_w_ = 0;
int pix_h_ = 0;
std::vector<uint8_t> pix_rgba_;
- have_pixmap_ – indicates whether a pixmap payload is available
- pix_w_ / pix_h_ – pixmap dimensions
- pix_rgba_ – raw pixel payload serialized into the SNI
IconPixmapstructure
6.3 Callbacks
std::function<void()> on_activate_cb_;
std::function<void()> on_settings_cb_;
std::function<void()> on_quit_cb_;
Action callbacks injected by the application layer. They must be safe to run on the UI thread.
6.4 sd-bus Resources and Execution State
sd_bus* bus_ = nullptr;
sd_bus_slot* sni_slot_ = nullptr;
sd_bus_slot* menu_slot_ = nullptr;
std::thread bus_thread_;
std::atomic_bool running_{false};
- bus_ – user bus connection handle
- sni_slot_ – exported SNI vtable slot
- menu_slot_ – exported DBusMenu vtable slot
- bus_thread_ – background thread that runs
bus_loop() - running_ – atomic guard controlling start/stop and loop termination
7. Menu Identity Constants
static constexpr int MENU_ROOT = 0;
static constexpr int MENU_SHOW = 1;
static constexpr int MENU_SETTINGS = 2;
static constexpr int MENU_QUIT = 3;
Numeric identifiers for DBusMenu items. These IDs must remain stable because D-Bus menu hosts reference them when firing events.
8. Private Control Flow Helpers
static std::string make_service_name();
bool acquire_bus();
bool request_name();
void release_bus();
bool export_objects();
void unexport_objects();
bool register_to_watcher();
void bus_loop();
void emit_sni_signal(const char* signal_name);
void emit_dbusmenu_layout_updated();
- make_service_name() – constructs a unique service name (typically PID + random suffix)
- acquire_bus()/release_bus() – open/close the user bus connection
- request_name() – request ownership of the tray service name
- export_objects()/unexport_objects() – export/unexport SNI + DBusMenu objects and their vtables
- register_to_watcher() – register the SNI with
org.kde.StatusNotifierWatcher - bus_loop() – message processing loop executed by
bus_thread_ - emit_sni_signal() – emits SNI refresh signals (e.g.,
NewIcon) - emit_dbusmenu_layout_updated() – emits DBusMenu
LayoutUpdatedto notify hosts of menu changes
9. Runtime and Security Considerations
- Thread safety: D-Bus method handlers may execute on the bus thread; UI work should be marshaled through
ui_ctx_to avoid GTK thread violations. - Host compatibility: supporting both
EventandEventGroupincreases interoperability across different tray implementations. - Resource lifetime: exported sd-bus slots must be released on shutdown to avoid stale registrations.
- Stable menu IDs: changing numeric IDs breaks host-side dispatch, so IDs must remain stable across releases.
- Pixmap safety: if using file-based icons, input paths should be trusted or sanitized to avoid unexpected file access.