TrayIcon.hpp

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.hGMainContext and 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 IconPixmap structure

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 LayoutUpdated to 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 Event and EventGroup increases 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.