31#include <glibmm/main.h>
33#include <gdk-pixbuf/gdk-pixbuf.h>
36#include <glib/gi18n.h>
39#define TI_LOG(fmt, ...) \
40std::fprintf(stderr, "[TrayIcon] " fmt "\n", ##__VA_ARGS__)
43 auto* fn =
static_cast<std::function<
void()
>*>(data);
46 return G_SOURCE_REMOVE;
52 SD_BUS_VTABLE_START(0),
54 SD_BUS_METHOD(
"Activate",
"ii",
"", TrayIcon::sni_method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
55 SD_BUS_METHOD(
"ContextMenu",
"ii",
"", TrayIcon::sni_method_context_menu, SD_BUS_VTABLE_UNPRIVILEGED),
56 SD_BUS_METHOD(
"SecondaryActivate",
"ii",
"", TrayIcon::sni_method_secondary_activate, SD_BUS_VTABLE_UNPRIVILEGED),
58 SD_BUS_PROPERTY(
"Category",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
59 SD_BUS_PROPERTY(
"Id",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
60 SD_BUS_PROPERTY(
"Title",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
61 SD_BUS_PROPERTY(
"Status",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
62 SD_BUS_PROPERTY(
"IconName",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
63 SD_BUS_PROPERTY(
"IconPixmap",
"a(iiay)", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
64 SD_BUS_PROPERTY(
"ToolTip",
"(sa(iiay)ss)", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
65 SD_BUS_PROPERTY(
"Menu",
"o", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
67 SD_BUS_SIGNAL(
"NewIcon",
"", 0),
68 SD_BUS_SIGNAL(
"NewTitle",
"", 0),
69 SD_BUS_SIGNAL(
"NewStatus",
"", 0),
70 SD_BUS_SIGNAL(
"NewToolTip",
"", 0),
77 SD_BUS_VTABLE_START(0),
79 SD_BUS_METHOD(
"GetLayout",
"iias",
"u(ia{sv}av)", TrayIcon::dbusmenu_method_get_layout, SD_BUS_VTABLE_UNPRIVILEGED),
80 SD_BUS_METHOD(
"GetChildren",
"i",
"ai", TrayIcon::dbusmenu_method_get_children, SD_BUS_VTABLE_UNPRIVILEGED),
81 SD_BUS_METHOD(
"GetGroupProperties",
"aias",
"a(ia{sv})", TrayIcon::dbusmenu_method_get_group_properties, SD_BUS_VTABLE_UNPRIVILEGED),
82 SD_BUS_METHOD(
"GetProperty",
"is",
"v", TrayIcon::dbusmenu_method_get_property, SD_BUS_VTABLE_UNPRIVILEGED),
83 SD_BUS_METHOD(
"GetStatus",
"",
"s", TrayIcon::dbusmenu_method_get_status, SD_BUS_VTABLE_UNPRIVILEGED),
84 SD_BUS_METHOD(
"Event",
"isvu",
"", TrayIcon::dbusmenu_method_event, SD_BUS_VTABLE_UNPRIVILEGED),
85 SD_BUS_METHOD(
"AboutToShow",
"i",
"b", TrayIcon::dbusmenu_method_about_to_show, SD_BUS_VTABLE_UNPRIVILEGED),
87 SD_BUS_METHOD(
"EventGroup",
"a(isvu)",
"ai", TrayIcon::dbusmenu_method_event_group, SD_BUS_VTABLE_UNPRIVILEGED),
88 SD_BUS_METHOD(
"AboutToShowGroup",
"ai",
"aiai", TrayIcon::dbusmenu_method_about_to_show_group, SD_BUS_VTABLE_UNPRIVILEGED),
90 SD_BUS_PROPERTY(
"Version",
"u", TrayIcon::dbusmenu_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
92 SD_BUS_SIGNAL(
"LayoutUpdated",
"ui", 0),
93 SD_BUS_SIGNAL(
"ItemsPropertiesUpdated",
"a(ia{sv})a(ias)", 0),
100 case 1:
return _(
"Mostra/Nascondi finestra");
101 case 2:
return _(
"Rendi persistente avvio");
102 case 3:
return _(
"Esci");
112std::string TrayIcon::make_service_name() {
113 std::random_device rd;
114 std::mt19937 gen(rd());
115 std::uniform_int_distribution<int> dis(1000, 9999);
116 return "org.kde.StatusNotifierItem-" + std::to_string(getpid()) +
"-" + std::to_string(dis(gen));
121 id_ =
"bastionguard-sc";
122 title_ =
"BastionGuard™ Secure Connection";
130 tooltip_ =
"BastionGuard™ Secure Connection";
142 if (
running_.exchange(
true))
return true;
144 if (!acquire_bus()) {
running_ =
false;
return false; }
145 if (!request_name()) { release_bus();
running_ =
false;
return false; }
146 if (!export_objects()) { release_bus();
running_ =
false;
return false; }
149 bool ok = register_to_watcher();
150 std::fprintf(stderr,
"[TrayIcon] register_to_watcher: %s (service: %s)\n",
153 std::this_thread::sleep_for(std::chrono::milliseconds(150));
154 emit_dbusmenu_layout_updated();
162int TrayIcon::dbusmenu_method_about_to_show_group(sd_bus_message* m,
void* userdata, sd_bus_error*) {
163 [[maybe_unused]]
auto* self =
static_cast<TrayIcon*
>(userdata);
165 TI_LOG(
"AboutToShowGroup() called");
167 int r = sd_bus_message_enter_container(m,
'a',
"i");
170 std::vector<int32_t> ids;
173 r = sd_bus_message_read(m,
"i", &
id);
179 r = sd_bus_message_exit_container(m);
182 TI_LOG(
"AboutToShowGroup ids count=%zu", ids.size());
184 sd_bus_message* reply =
nullptr;
185 r = sd_bus_message_new_method_return(m, &reply);
188 r = sd_bus_message_open_container(reply,
'a',
"i");
189 if (r < 0) { sd_bus_message_unref(reply);
return r; }
190 r = sd_bus_message_close_container(reply);
191 if (r < 0) { sd_bus_message_unref(reply);
return r; }
193 r = sd_bus_message_open_container(reply,
'a',
"i");
194 if (r < 0) { sd_bus_message_unref(reply);
return r; }
195 r = sd_bus_message_close_container(reply);
196 if (r < 0) { sd_bus_message_unref(reply);
return r; }
198 sd_bus* bus = sd_bus_message_get_bus(m);
199 r = sd_bus_send(bus, reply,
nullptr);
200 sd_bus_message_unref(reply);
201 return (r < 0) ?
r : 1;
204int TrayIcon::dbusmenu_method_event_group(sd_bus_message* m,
void* userdata, sd_bus_error*) {
205 auto* self =
static_cast<TrayIcon*
>(userdata);
207 TI_LOG(
"EventGroup() called");
209 int r = sd_bus_message_enter_container(m,
'a',
"(isvu)");
212 struct Ev { int32_t id; std::string ev; uint32_t ts; };
213 std::vector<Ev> events;
216 r = sd_bus_message_enter_container(m,
'r',
"isvu");
221 const char* ev_c =
nullptr;
224 r = sd_bus_message_read(m,
"is", &
id, &ev_c);
227 r = sd_bus_message_skip(m,
"v");
230 r = sd_bus_message_read(m,
"u", &ts);
233 r = sd_bus_message_exit_container(m);
236 std::string ev = ev_c ? ev_c :
"";
237 TI_LOG(
"EventGroup item: id=%d ev='%s' ts=%" PRIu32, (
int)
id, ev.c_str(), ts);
238 events.push_back({id, ev, ts});
241 r = sd_bus_message_exit_container(m);
244 if (!self->ui_ctx_) {
245 TI_LOG(
"WARNING: ui_ctx_ null (bind_ui_context not called?)");
247 for (
const auto& e : events) {
248 const int32_t
id = e.id;
255 TI_LOG(
"-> MENU_QUIT: SIGTERM");
256 ::kill(::getpid(), SIGTERM);
259 new std::function<
void()>([self,
id]() {
260 TI_LOG(
"Dispatch on UI: id=%d", (int)id);
261 if (id == MENU_SHOW) {
262 TI_LOG(
"-> MENU_SHOW");
263 if (self->on_activate_cb_) self->on_activate_cb_();
264 else TI_LOG(
"on_activate_cb_ is null");
266 TI_LOG(
"-> MENU_PERSIST_STARTUP");
267 if (self->on_make_startup_persistent_cb_) self->on_make_startup_persistent_cb_();
268 else TI_LOG(
"on_make_startup_persistent_cb_ is null");
270 TI_LOG(
"Unknown menu id=%d", (
int)
id);
278 sd_bus_message* reply =
nullptr;
279 r = sd_bus_message_new_method_return(m, &reply);
282 r = sd_bus_message_open_container(reply,
'a',
"i");
283 if (r < 0) { sd_bus_message_unref(reply);
return r; }
284 r = sd_bus_message_close_container(reply);
285 if (r < 0) { sd_bus_message_unref(reply);
return r; }
287 sd_bus* bus = sd_bus_message_get_bus(m);
288 r = sd_bus_send(bus, reply,
nullptr);
289 sd_bus_message_unref(reply);
290 return (r < 0) ?
r : 1;
294 if (!
running_.exchange(
false))
return;
298 std::this_thread::get_id() ==
bus_thread_.get_id()) {
323 ui_ctx_ = g_main_context_ref(g_main_context_default());
327 GError* err =
nullptr;
328 GdkPixbuf* pb = gdk_pixbuf_new_from_file(png_path.c_str(), &err);
330 std::fprintf(stderr,
"[TrayIcon] cannot load icon '%s': %s\n",
331 png_path.c_str(), err ? err->message :
"unknown error");
332 if (err) g_error_free(err);
334 emit_sni_signal(
"NewIcon");
339 int w = gdk_pixbuf_get_width(pb);
340 int h = gdk_pixbuf_get_height(pb);
341 if (w != target_px || h != target_px) {
342 GdkPixbuf* scaled = gdk_pixbuf_scale_simple(pb, target_px, target_px, GDK_INTERP_BILINEAR);
348 GdkPixbuf* rgba = gdk_pixbuf_add_alpha(pb, FALSE, 0, 0, 0);
351 pix_w_ = gdk_pixbuf_get_width(rgba);
352 pix_h_ = gdk_pixbuf_get_height(rgba);
354 const int rowstride = gdk_pixbuf_get_rowstride(rgba);
355 const int nchan = gdk_pixbuf_get_n_channels(rgba);
356 const guchar* pixels = gdk_pixbuf_get_pixels(rgba);
360 for (
int y = 0; y <
pix_h_; ++y) {
361 const guchar* src = pixels + y * rowstride;
364 for (
int x = 0; x <
pix_w_; ++x) {
365 uint8_t
r = src[x * nchan + 0];
366 uint8_t g = src[x * nchan + 1];
367 uint8_t b = src[x * nchan + 2];
368 uint8_t a = (nchan >= 4) ? src[x * nchan + 3] : 255u;
370 uint32_t argb = ((uint32_t)a << 24) |
371 ((uint32_t)r << 16) |
374 argb = htobe32(argb);
375 std::memcpy(dst + x * 4, &argb, 4);
379 g_object_unref(rgba);
384 emit_sni_signal(
"NewIcon");
388 icon_name_ = icon_name.empty() ?
"bastionguard-sc" : icon_name;
390 emit_sni_signal(
"NewIcon");
395 emit_sni_signal(
"NewToolTip");
400 emit_sni_signal(
"NewTitle");
403bool TrayIcon::acquire_bus() {
404 if (sd_bus_open_user(&bus_) < 0) {
411bool TrayIcon::request_name() {
416void TrayIcon::release_bus() {
422bool TrayIcon::export_objects() {
423 int r1 = sd_bus_add_object_vtable(
425 "/StatusNotifierItem",
426 "org.kde.StatusNotifierItem",
430 if (r1 < 0)
return false;
432 int r2 = sd_bus_add_object_vtable(
435 "com.canonical.dbusmenu",
439 if (r2 < 0)
return false;
444void TrayIcon::unexport_objects() {
445 if (sni_slot_) { sd_bus_slot_unref(sni_slot_); sni_slot_ =
nullptr; }
446 if (menu_slot_) { sd_bus_slot_unref(menu_slot_); menu_slot_ =
nullptr; }
449bool TrayIcon::register_to_watcher() {
450 sd_bus_message* m =
nullptr;
451 sd_bus_error er = SD_BUS_ERROR_NULL;
453 int r = sd_bus_message_new_method_call(
455 "org.kde.StatusNotifierWatcher",
456 "/StatusNotifierWatcher",
457 "org.kde.StatusNotifierWatcher",
458 "RegisterStatusNotifierItem"
460 if (r < 0)
return false;
463 if (r < 0) { sd_bus_message_unref(m);
return false; }
465 r = sd_bus_call(bus_, m, 0, &er,
nullptr);
467 std::fprintf(stderr,
"[TrayIcon] RegisterStatusNotifierItem error: %s\n",
468 er.message ? er.message :
"(unknown)");
471 sd_bus_error_free(&er);
472 sd_bus_message_unref(m);
476void TrayIcon::bus_loop() {
479 int r = sd_bus_process(bus_,
nullptr);
492 int w = sd_bus_wait(bus_, 50000);
501void TrayIcon::emit_sni_signal(
const char* signal_name) {
503 (void)sd_bus_emit_signal(
505 "/StatusNotifierItem",
506 "org.kde.StatusNotifierItem",
512void TrayIcon::emit_dbusmenu_layout_updated() {
514 const uint32_t revision = 1u;
516 (void)sd_bus_emit_signal(
519 "com.canonical.dbusmenu",
527int TrayIcon::sni_method_activate(sd_bus_message* m,
void* userdata, sd_bus_error*) {
528 auto* self =
static_cast<TrayIcon*
>(userdata);
530 std::fprintf(stderr,
"[TrayIcon] SNI Activate received\n");
532 if (!self->ui_ctx_) {
533 std::fprintf(stderr,
"[TrayIcon] WARNING: ui_ctx_ null in Activate -> fallback default\n");
534 self->ui_ctx_ = g_main_context_ref(g_main_context_default());
537 if (self->on_activate_cb_) {
539 new std::function<
void()>([self]() {
540 std::fprintf(stderr,
"[TrayIcon] SNI Activate dispatch on UI thread\n");
541 self->on_activate_cb_();
546 return sd_bus_reply_method_return(m,
"");
549int TrayIcon::sni_method_context_menu(sd_bus_message* m,
void*, sd_bus_error*) {
550 return sd_bus_reply_method_return(m,
"");
553int TrayIcon::sni_method_secondary_activate(sd_bus_message* m,
void*, sd_bus_error*) {
554 return sd_bus_reply_method_return(m,
"");
557int TrayIcon::sni_prop_get(sd_bus*,
const char*,
const char*,
558 const char* property, sd_bus_message* reply,
559 void* userdata, sd_bus_error* ret_error) {
560 auto* self =
static_cast<TrayIcon*
>(userdata);
562 if (std::strcmp(property,
"Category") == 0)
563 return sd_bus_message_append(reply,
"s",
"ApplicationStatus");
565 if (std::strcmp(property,
"Id") == 0)
566 return sd_bus_message_append(reply,
"s", self->id_.c_str());
568 if (std::strcmp(property,
"Title") == 0)
569 return sd_bus_message_append(reply,
"s", self->title_.c_str());
571 if (std::strcmp(property,
"Status") == 0)
572 return sd_bus_message_append(reply,
"s", self->status_.c_str());
574 if (std::strcmp(property,
"IconName") == 0) {
575 if (self->have_pixmap_)
576 return sd_bus_message_append(reply,
"s",
"");
577 return sd_bus_message_append(reply,
"s", self->icon_name_.c_str());
580 if (std::strcmp(property,
"IconPixmap") == 0) {
581 int r = sd_bus_message_open_container(reply,
'a',
"(iiay)");
584 if (self->have_pixmap_ && self->pix_w_ > 0 && self->pix_h_ > 0
585 && !self->pix_rgba_.empty())
587 r = sd_bus_message_open_container(reply,
'r',
"iiay");
590 r = sd_bus_message_append(reply,
"ii", self->pix_w_, self->pix_h_);
593 r = sd_bus_message_append_array(reply,
'y',
594 self->pix_rgba_.data(),
595 self->pix_rgba_.size());
598 r = sd_bus_message_close_container(reply);
602 return sd_bus_message_close_container(reply);
605 if (std::strcmp(property,
"Menu") == 0)
606 return sd_bus_message_append(reply,
"o",
"/Menu");
608 if (std::strcmp(property,
"ToolTip") == 0) {
609 int r = sd_bus_message_open_container(reply,
'r',
"sa(iiay)ss");
612 r = sd_bus_message_append(reply,
"s", self->icon_name_.c_str());
615 r = sd_bus_message_open_container(reply,
'a',
"(iiay)");
617 r = sd_bus_message_close_container(reply);
620 r = sd_bus_message_append(reply,
"s", self->title_.c_str());
622 r = sd_bus_message_append(reply,
"s", self->tooltip_.c_str());
625 r = sd_bus_message_close_container(reply);
630 return sd_bus_error_setf(ret_error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
631 "Unknown property '%s'", property);
634 int TrayIcon::dbusmenu_prop_get(sd_bus*,
const char*,
const char*,
635 const char* , sd_bus_message* reply,
636 void*, sd_bus_error*) {
637 return sd_bus_message_append(reply,
"u", 3u);
641 const char* label,
bool enabled =
true) {
643 r = sd_bus_message_open_container(m,
'r',
"ia{sv}av");
if (r < 0)
return r;
644 r = sd_bus_message_append(m,
"i",
id);
if (r < 0)
return r;
646 r = sd_bus_message_open_container(m,
'a',
"{sv}");
if (r < 0)
return r;
648 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
649 r = sd_bus_message_append(m,
"s",
"label");
if (r < 0)
return r;
650 r = sd_bus_message_open_container(m,
'v',
"s");
if (r < 0)
return r;
651 r = sd_bus_message_append(m,
"s", label);
if (r < 0)
return r;
652 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
653 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
655 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
656 r = sd_bus_message_append(m,
"s",
"enabled");
if (r < 0)
return r;
657 r = sd_bus_message_open_container(m,
'v',
"b");
if (r < 0)
return r;
658 r = sd_bus_message_append(m,
"b", enabled ? 1 : 0);
if (r < 0)
return r;
659 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
660 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
662 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
664 r = sd_bus_message_open_container(m,
'a',
"v");
if (r < 0)
return r;
665 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
667 return sd_bus_message_close_container(m);
671 const char* label,
bool enabled) {
673 r = sd_bus_message_open_container(m,
'r',
"ia{sv}");
if (r < 0)
return r;
674 r = sd_bus_message_append(m,
"i",
id);
if (r < 0)
return r;
676 r = sd_bus_message_open_container(m,
'a',
"{sv}");
if (r < 0)
return r;
678 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
679 r = sd_bus_message_append(m,
"s",
"label");
if (r < 0)
return r;
680 r = sd_bus_message_open_container(m,
'v',
"s");
if (r < 0)
return r;
681 r = sd_bus_message_append(m,
"s", label);
if (r < 0)
return r;
682 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
683 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
685 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
686 r = sd_bus_message_append(m,
"s",
"enabled");
if (r < 0)
return r;
687 r = sd_bus_message_open_container(m,
'v',
"b");
if (r < 0)
return r;
688 r = sd_bus_message_append(m,
"b", enabled ? 1 : 0);
if (r < 0)
return r;
689 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
690 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
692 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
693 return sd_bus_message_close_container(m);
696 int TrayIcon::dbusmenu_method_get_status(sd_bus_message* m,
void*, sd_bus_error*) {
697 return sd_bus_reply_method_return(m,
"s",
"normal");
700 int TrayIcon::dbusmenu_method_get_children(sd_bus_message* m,
void* userdata, sd_bus_error*) {
701 [[maybe_unused]]
auto* self =
static_cast<TrayIcon*
>(userdata);
704 int r = sd_bus_message_read(m,
"i", &parent);
707 sd_bus_message* reply =
nullptr;
708 r = sd_bus_message_new_method_return(m, &reply);
711 r = sd_bus_message_open_container(reply,
'a',
"i");
712 if (r < 0) { sd_bus_message_unref(reply);
return r; }
716 for (int32_t
id : ids) {
717 r = sd_bus_message_append(reply,
"i",
id);
718 if (r < 0) { sd_bus_message_unref(reply);
return r; }
722 r = sd_bus_message_close_container(reply);
723 if (r < 0) { sd_bus_message_unref(reply);
return r; }
725 sd_bus* bus = sd_bus_message_get_bus(m);
726 r = sd_bus_send(bus, reply,
nullptr);
727 sd_bus_message_unref(reply);
728 return (r < 0) ?
r : 1;
731 int TrayIcon::dbusmenu_method_get_layout(sd_bus_message* m,
void* userdata, sd_bus_error*) {
732 [[maybe_unused]]
auto* self =
static_cast<TrayIcon*
>(userdata);
734 int32_t parent_id = 0, depth = 0;
735 int r = sd_bus_message_read(m,
"ii", &parent_id, &depth);
738 r = sd_bus_message_skip(m,
"as");
741 sd_bus_message* reply =
nullptr;
742 r = sd_bus_message_new_method_return(m, &reply);
745 r = sd_bus_message_append(reply,
"u", (uint32_t)1);
746 if (r < 0) { sd_bus_message_unref(reply);
return r; }
748 r = sd_bus_message_open_container(reply,
'r',
"ia{sv}av");
749 if (r < 0) { sd_bus_message_unref(reply);
return r; }
751 r = sd_bus_message_append(reply,
"i",
MENU_ROOT);
752 if (r < 0) { sd_bus_message_unref(reply);
return r; }
754 r = sd_bus_message_open_container(reply,
'a',
"{sv}");
755 if (r < 0) { sd_bus_message_unref(reply);
return r; }
756 r = sd_bus_message_close_container(reply);
757 if (r < 0) { sd_bus_message_unref(reply);
return r; }
759 r = sd_bus_message_open_container(reply,
'a',
"v");
760 if (r < 0) { sd_bus_message_unref(reply);
return r; }
762 bool include_children = (depth != 0);
764 if (include_children) {
765 auto push_child = [&](
int id) ->
int {
766 int rr = sd_bus_message_open_container(reply,
'v',
"(ia{sv}av)");
767 if (rr < 0)
return rr;
771 if (rr < 0)
return rr;
772 return sd_bus_message_close_container(reply);
774 r = push_child(
MENU_SHOW);
if (r < 0) { sd_bus_message_unref(reply);
return r; }
776 r = push_child(
MENU_QUIT);
if (r < 0) { sd_bus_message_unref(reply);
return r; }
779 r = sd_bus_message_close_container(reply);
780 if (r < 0) { sd_bus_message_unref(reply);
return r; }
782 r = sd_bus_message_close_container(reply);
783 if (r < 0) { sd_bus_message_unref(reply);
return r; }
785 sd_bus* bus = sd_bus_message_get_bus(m);
786 r = sd_bus_send(bus, reply,
nullptr);
787 sd_bus_message_unref(reply);
788 return (r < 0) ?
r : 1;
791 int TrayIcon::dbusmenu_method_event(sd_bus_message* m,
void* userdata, sd_bus_error*) {
792 auto* self =
static_cast<TrayIcon*
>(userdata);
795 const char* event_id_c =
nullptr;
797 int r = sd_bus_message_read(m,
"is", &
id, &event_id_c);
799 return sd_bus_reply_method_return(m,
"");
801 std::string ev = event_id_c ? event_id_c :
"";
803 (void)sd_bus_message_skip(m,
"vu");
805 std::fprintf(stderr,
"[TrayIcon] Event: id=%d ev='%s'\n", (
int)
id, ev.c_str());
807 if (!self->ui_ctx_) {
808 std::fprintf(stderr,
"[TrayIcon] WARNING: ui_ctx_ null (bind_ui_context?)\n");
809 return sd_bus_reply_method_return(m,
"");
813 std::fprintf(stderr,
"[TrayIcon] -> MENU_QUIT: SIGTERM\n");
814 ::kill(::getpid(), SIGTERM);
817 new std::function<
void()>([self,
id]() {
818 std::fprintf(stderr,
"[TrayIcon] Dispatch on UI: id=%d\n", (int)id);
819 if (id == MENU_SHOW) {
820 if (self->on_activate_cb_) self->on_activate_cb_();
822 if (self->on_make_startup_persistent_cb_) self->on_make_startup_persistent_cb_();
824 std::fprintf(stderr,
"[TrayIcon] Unknown menu id=%d\n", (
int)
id);
830 return sd_bus_reply_method_return(m,
"");
833 int TrayIcon::dbusmenu_method_about_to_show(sd_bus_message* m,
void*, sd_bus_error*) {
835 int r = sd_bus_message_read(m,
"i", &
id);
837 return sd_bus_reply_method_return(m,
"b", 0);
840 int TrayIcon::dbusmenu_method_get_group_properties(sd_bus_message* m,
void* userdata, sd_bus_error*) {
841 [[maybe_unused]]
auto* self =
static_cast<TrayIcon*
>(userdata);
843 int r = sd_bus_message_enter_container(m,
'a',
"i");
846 std::vector<int32_t> ids;
849 r = sd_bus_message_read(m,
"i", &
id);
855 r = sd_bus_message_exit_container(m);
858 r = sd_bus_message_skip(m,
"as");
861 sd_bus_message* reply =
nullptr;
862 r = sd_bus_message_new_method_return(m, &reply);
865 r = sd_bus_message_open_container(reply,
'a',
"(ia{sv})");
866 if (r < 0) { sd_bus_message_unref(reply);
return r; }
868 for (
auto id : ids) {
872 if (r < 0) { sd_bus_message_unref(reply);
return r; }
875 r = sd_bus_message_close_container(reply);
876 if (r < 0) { sd_bus_message_unref(reply);
return r; }
878 sd_bus* bus = sd_bus_message_get_bus(m);
879 r = sd_bus_send(bus, reply,
nullptr);
880 sd_bus_message_unref(reply);
881 return (r < 0) ?
r : 1;
884 int TrayIcon::dbusmenu_method_get_property(sd_bus_message* m,
void* userdata, sd_bus_error*) {
885 [[maybe_unused]]
auto* self =
static_cast<TrayIcon*
>(userdata);
888 const char* prop =
nullptr;
889 int r = sd_bus_message_read(m,
"is", &
id, &prop);
892 std::string prop_str = prop ? prop :
"";
894 sd_bus_message* reply =
nullptr;
895 r = sd_bus_message_new_method_return(m, &reply);
898 if (prop_str ==
"label") {
899 r = sd_bus_message_open_container(reply,
'v',
"s");
900 if (r < 0) { sd_bus_message_unref(reply);
return r; }
902 if (r < 0) { sd_bus_message_unref(reply);
return r; }
903 r = sd_bus_message_close_container(reply);
904 if (r < 0) { sd_bus_message_unref(reply);
return r; }
905 }
else if (prop_str ==
"enabled") {
906 r = sd_bus_message_open_container(reply,
'v',
"b");
907 if (r < 0) { sd_bus_message_unref(reply);
return r; }
909 if (r < 0) { sd_bus_message_unref(reply);
return r; }
910 r = sd_bus_message_close_container(reply);
911 if (r < 0) { sd_bus_message_unref(reply);
return r; }
913 r = sd_bus_message_open_container(reply,
'v',
"s");
914 if (r < 0) { sd_bus_message_unref(reply);
return r; }
915 r = sd_bus_message_append(reply,
"s",
"");
916 if (r < 0) { sd_bus_message_unref(reply);
return r; }
917 r = sd_bus_message_close_container(reply);
918 if (r < 0) { sd_bus_message_unref(reply);
return r; }
921 sd_bus* bus = sd_bus_message_get_bus(m);
922 r = sd_bus_send(bus, reply,
nullptr);
923 sd_bus_message_unref(reply);
924 return (r < 0) ?
r : 1;
static constexpr int MENU_ROOT
void set_title(const std::string &title)
static constexpr int MENU_PERSIST_STARTUP
void set_icon(const std::string &icon_name)
std::vector< uint8_t > pix_rgba_
std::string service_name_
std::atomic_bool running_
void set_icon_file(const std::string &png_path, int target_px=22)
void set_tooltip(const std::string &text)
static constexpr int MENU_QUIT
static constexpr int MENU_SHOW
static const sd_bus_vtable dbusmenu_vtable[]
static int append_menu_item(sd_bus_message *m, int id, const char *label, bool enabled=true)
static const sd_bus_vtable sni_vtable[]
static const char * label_for_menu_id(int id)
static gboolean invoke_std_function(gpointer data)
static int append_item_properties(sd_bus_message *m, int id, const char *label, bool enabled)
static bool enabled_for_menu_id(int)