30#include <glibmm/main.h>
32#include <gdk-pixbuf/gdk-pixbuf.h>
35#include <glib/gi18n.h>
38#define TI_LOG(fmt, ...) \
39 std::fprintf(stderr, "[TrayIcon] " fmt "\n", ##__VA_ARGS__)
42 auto* fn =
static_cast<std::function<
void()
>*>(data);
45 return G_SOURCE_REMOVE;
51 SD_BUS_VTABLE_START(0),
53 SD_BUS_METHOD(
"Activate",
"ii",
"", TrayIcon::sni_method_activate, SD_BUS_VTABLE_UNPRIVILEGED),
54 SD_BUS_METHOD(
"ContextMenu",
"ii",
"", TrayIcon::sni_method_context_menu, SD_BUS_VTABLE_UNPRIVILEGED),
55 SD_BUS_METHOD(
"SecondaryActivate",
"ii",
"", TrayIcon::sni_method_secondary_activate, SD_BUS_VTABLE_UNPRIVILEGED),
57 SD_BUS_PROPERTY(
"Category",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
58 SD_BUS_PROPERTY(
"Id",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
59 SD_BUS_PROPERTY(
"Title",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
60 SD_BUS_PROPERTY(
"Status",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
61 SD_BUS_PROPERTY(
"IconName",
"s", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
62 SD_BUS_PROPERTY(
"IconPixmap",
"a(iiay)", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
63 SD_BUS_PROPERTY(
"ToolTip",
"(sa(iiay)ss)", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
64 SD_BUS_PROPERTY(
"Menu",
"o", TrayIcon::sni_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
66 SD_BUS_SIGNAL(
"NewIcon",
"", 0),
67 SD_BUS_SIGNAL(
"NewTitle",
"", 0),
68 SD_BUS_SIGNAL(
"NewStatus",
"", 0),
69 SD_BUS_SIGNAL(
"NewToolTip",
"", 0),
76 SD_BUS_VTABLE_START(0),
78 SD_BUS_METHOD(
"GetLayout",
"iias",
"u(ia{sv}av)", TrayIcon::dbusmenu_method_get_layout, SD_BUS_VTABLE_UNPRIVILEGED),
79 SD_BUS_METHOD(
"GetChildren",
"i",
"ai", TrayIcon::dbusmenu_method_get_children, SD_BUS_VTABLE_UNPRIVILEGED),
80 SD_BUS_METHOD(
"GetGroupProperties",
"aias",
"a(ia{sv})", TrayIcon::dbusmenu_method_get_group_properties, SD_BUS_VTABLE_UNPRIVILEGED),
81 SD_BUS_METHOD(
"GetProperty",
"is",
"v", TrayIcon::dbusmenu_method_get_property, SD_BUS_VTABLE_UNPRIVILEGED),
82 SD_BUS_METHOD(
"GetStatus",
"",
"s", TrayIcon::dbusmenu_method_get_status, SD_BUS_VTABLE_UNPRIVILEGED),
83 SD_BUS_METHOD(
"Event",
"isvu",
"", TrayIcon::dbusmenu_method_event, SD_BUS_VTABLE_UNPRIVILEGED),
84 SD_BUS_METHOD(
"AboutToShow",
"i",
"b", TrayIcon::dbusmenu_method_about_to_show, SD_BUS_VTABLE_UNPRIVILEGED),
86 SD_BUS_METHOD(
"EventGroup",
"a(isvu)",
"ai", TrayIcon::dbusmenu_method_event_group, SD_BUS_VTABLE_UNPRIVILEGED),
87 SD_BUS_METHOD(
"AboutToShowGroup",
"ai",
"aiai", TrayIcon::dbusmenu_method_about_to_show_group, SD_BUS_VTABLE_UNPRIVILEGED),
89 SD_BUS_PROPERTY(
"Version",
"u", TrayIcon::dbusmenu_prop_get, 0, SD_BUS_VTABLE_PROPERTY_CONST),
91 SD_BUS_SIGNAL(
"LayoutUpdated",
"ui", 0),
92 SD_BUS_SIGNAL(
"ItemsPropertiesUpdated",
"a(ia{sv})a(ias)", 0),
99 case 1:
return _(
"Mostra/Maximizza finestra");
100 case 2:
return _(
"Impostazioni");
101 case 3:
return _(
"Esci");
111std::string TrayIcon::make_service_name() {
112 std::random_device rd;
113 std::mt19937 gen(rd());
114 std::uniform_int_distribution<int> dis(1000, 9999);
115 return "org.kde.StatusNotifierItem-" + std::to_string(getpid()) +
"-" + std::to_string(dis(gen));
120 id_ =
"BastionGuard";
136 if (
running_.exchange(
true))
return true;
138 if (!acquire_bus()) {
running_ =
false;
return false; }
139 if (!request_name()) { release_bus();
running_ =
false;
return false; }
140 if (!export_objects()) { release_bus();
running_ =
false;
return false; }
143 bool ok = register_to_watcher();
144 std::fprintf(stderr,
"[TrayIcon] register_to_watcher: %s (service: %s)\n",
147 std::this_thread::sleep_for(std::chrono::milliseconds(150));
148 emit_dbusmenu_layout_updated();
156int TrayIcon::dbusmenu_method_about_to_show_group(sd_bus_message* m,
void* userdata, sd_bus_error*) {
157 auto* self =
static_cast<TrayIcon*
>(userdata);
159 TI_LOG(
"AboutToShowGroup() called");
161 int r = sd_bus_message_enter_container(m,
'a',
"i");
164 std::vector<int32_t> ids;
167 r = sd_bus_message_read(m,
"i", &
id);
173 r = sd_bus_message_exit_container(m);
176 TI_LOG(
"AboutToShowGroup ids count=%zu", ids.size());
178 sd_bus_message* reply =
nullptr;
179 r = sd_bus_message_new_method_return(m, &reply);
182 r = sd_bus_message_open_container(reply,
'a',
"i");
183 if (r < 0) { sd_bus_message_unref(reply);
return r; }
184 r = sd_bus_message_close_container(reply);
185 if (r < 0) { sd_bus_message_unref(reply);
return r; }
187 r = sd_bus_message_open_container(reply,
'a',
"i");
188 if (r < 0) { sd_bus_message_unref(reply);
return r; }
189 r = sd_bus_message_close_container(reply);
190 if (r < 0) { sd_bus_message_unref(reply);
return r; }
192 sd_bus* bus = sd_bus_message_get_bus(m);
193 r = sd_bus_send(bus, reply,
nullptr);
194 sd_bus_message_unref(reply);
195 return (r < 0) ?
r : 1;
198int TrayIcon::dbusmenu_method_event_group(sd_bus_message* m,
void* userdata, sd_bus_error*) {
199 auto* self =
static_cast<TrayIcon*
>(userdata);
201 TI_LOG(
"EventGroup() called");
203 int r = sd_bus_message_enter_container(m,
'a',
"(isvu)");
206 struct Ev { int32_t id; std::string ev; uint32_t ts; };
207 std::vector<Ev> events;
210 r = sd_bus_message_enter_container(m,
'r',
"isvu");
215 const char* ev_c =
nullptr;
218 r = sd_bus_message_read(m,
"is", &
id, &ev_c);
221 r = sd_bus_message_skip(m,
"v");
224 r = sd_bus_message_read(m,
"u", &ts);
227 r = sd_bus_message_exit_container(m);
230 std::string ev = ev_c ? ev_c :
"";
231 TI_LOG(
"EventGroup item: id=%d ev='%s' ts=%" PRIu32, (
int)
id, ev.c_str(), ts);
232 events.push_back({id, ev, ts});
235 r = sd_bus_message_exit_container(m);
238 if (!self->ui_ctx_) {
239 TI_LOG(
"WARNING: ui_ctx_ null (bind_ui_context not called?)");
241 for (
const auto& e : events) {
242 const int32_t
id = e.id;
244 new std::function<
void()>([self,
id]() {
245 TI_LOG(
"Dispatch on UI: id=%d", (int)id);
247 if (id == MENU_SHOW) {
248 TI_LOG(
"-> MENU_SHOW");
249 if (self->on_activate_cb_) self->on_activate_cb_();
250 else TI_LOG(
"on_activate_cb_ is null");
252 TI_LOG(
"-> MENU_SETTINGS");
253 if (self->on_settings_cb_) self->on_settings_cb_();
254 else TI_LOG(
"on_settings_cb_ is null");
257 if (self->on_quit_cb_) self->on_quit_cb_();
258 else TI_LOG(
"on_quit_cb_ is null");
260 TI_LOG(
"Unknown menu id=%d", (
int)
id);
267 sd_bus_message* reply =
nullptr;
268 r = sd_bus_message_new_method_return(m, &reply);
271 r = sd_bus_message_open_container(reply,
'a',
"i");
272 if (r < 0) { sd_bus_message_unref(reply);
return r; }
273 r = sd_bus_message_close_container(reply);
274 if (r < 0) { sd_bus_message_unref(reply);
return r; }
276 sd_bus* bus = sd_bus_message_get_bus(m);
277 r = sd_bus_send(bus, reply,
nullptr);
278 sd_bus_message_unref(reply);
279 return (r < 0) ?
r : 1;
283 if (!
running_.exchange(
false))
return;
286 std::this_thread::get_id() ==
bus_thread_.get_id()) {
299 ui_ctx_ = g_main_context_ref(g_main_context_default());
303 GError* err =
nullptr;
304 GdkPixbuf* pb = gdk_pixbuf_new_from_file(png_path.c_str(), &err);
306 std::fprintf(stderr,
"[TrayIcon] cannot load icon '%s': %s\n",
307 png_path.c_str(), err ? err->message :
"unknown error");
308 if (err) g_error_free(err);
310 emit_sni_signal(
"NewIcon");
315 int w = gdk_pixbuf_get_width(pb);
316 int h = gdk_pixbuf_get_height(pb);
317 if (w != target_px || h != target_px) {
318 GdkPixbuf* scaled = gdk_pixbuf_scale_simple(pb, target_px, target_px, GDK_INTERP_BILINEAR);
324 GdkPixbuf* rgba = gdk_pixbuf_add_alpha(pb, FALSE, 0, 0, 0);
327 pix_w_ = gdk_pixbuf_get_width(rgba);
328 pix_h_ = gdk_pixbuf_get_height(rgba);
330 const int rowstride = gdk_pixbuf_get_rowstride(rgba);
331 const int nchan = gdk_pixbuf_get_n_channels(rgba);
332 const guchar* pixels = gdk_pixbuf_get_pixels(rgba);
336 for (
int y = 0; y <
pix_h_; ++y) {
337 const guchar* src = pixels + y * rowstride;
340 for (
int x = 0; x <
pix_w_; ++x) {
341 uint8_t r = src[x * nchan + 0];
342 uint8_t g = src[x * nchan + 1];
343 uint8_t b = src[x * nchan + 2];
344 uint8_t a = (nchan >= 4) ? src[x * nchan + 3] : 255u;
346 uint32_t argb = ((uint32_t)a << 24) |
347 ((uint32_t)r << 16) |
350 argb = htobe32(argb);
351 std::memcpy(dst + x * 4, &argb, 4);
355 g_object_unref(rgba);
360 emit_sni_signal(
"NewIcon");
364 icon_name_ = icon_name.empty() ?
"security-high" : icon_name;
365 emit_sni_signal(
"NewIcon");
370 emit_sni_signal(
"NewToolTip");
375 emit_sni_signal(
"NewTitle");
378bool TrayIcon::acquire_bus() {
379 if (sd_bus_open_user(&bus_) < 0) {
386bool TrayIcon::request_name() {
391void TrayIcon::release_bus() {
397bool TrayIcon::export_objects() {
398 int r1 = sd_bus_add_object_vtable(
400 "/StatusNotifierItem",
401 "org.kde.StatusNotifierItem",
405 if (r1 < 0)
return false;
407 int r2 = sd_bus_add_object_vtable(
410 "com.canonical.dbusmenu",
414 if (r2 < 0)
return false;
419void TrayIcon::unexport_objects() {
420 if (sni_slot_) { sd_bus_slot_unref(sni_slot_); sni_slot_ =
nullptr; }
421 if (menu_slot_) { sd_bus_slot_unref(menu_slot_); menu_slot_ =
nullptr; }
424bool TrayIcon::register_to_watcher() {
425 sd_bus_message* m =
nullptr;
426 sd_bus_error er = SD_BUS_ERROR_NULL;
428 int r = sd_bus_message_new_method_call(
430 "org.kde.StatusNotifierWatcher",
431 "/StatusNotifierWatcher",
432 "org.kde.StatusNotifierWatcher",
433 "RegisterStatusNotifierItem"
435 if (r < 0)
return false;
438 if (r < 0) { sd_bus_message_unref(m);
return false; }
440 r = sd_bus_call(bus_, m, 0, &er,
nullptr);
442 std::fprintf(stderr,
"[TrayIcon] RegisterStatusNotifierItem error: %s\n",
443 er.message ? er.message :
"(unknown)");
445 sd_bus_error_free(&er);
446 sd_bus_message_unref(m);
450void TrayIcon::bus_loop() {
453 int r = sd_bus_process(bus_,
nullptr);
463 (void)sd_bus_wait(bus_, 100000);
467void TrayIcon::emit_sni_signal(
const char* signal_name) {
469 (void)sd_bus_emit_signal(
471 "/StatusNotifierItem",
472 "org.kde.StatusNotifierItem",
478void TrayIcon::emit_dbusmenu_layout_updated() {
480 const uint32_t revision = 1u;
482 (void)sd_bus_emit_signal(
485 "com.canonical.dbusmenu",
493int TrayIcon::sni_method_activate(sd_bus_message* m,
void* userdata, sd_bus_error*) {
494 auto* self =
static_cast<TrayIcon*
>(userdata);
496 std::fprintf(stderr,
"[TrayIcon] SNI Activate received\n");
498 if (!self->ui_ctx_) {
499 std::fprintf(stderr,
"[TrayIcon] WARNING: ui_ctx_ null in Activate -> fallback default\n");
500 self->ui_ctx_ = g_main_context_ref(g_main_context_default());
503 if (self->on_activate_cb_) {
505 new std::function<
void()>([self]() {
506 std::fprintf(stderr,
"[TrayIcon] SNI Activate dispatch on UI thread\n");
507 self->on_activate_cb_();
512 return sd_bus_reply_method_return(m,
"");
515int TrayIcon::sni_method_context_menu(sd_bus_message* m,
void*, sd_bus_error*) {
516 return sd_bus_reply_method_return(m,
"");
519int TrayIcon::sni_method_secondary_activate(sd_bus_message* m,
void*, sd_bus_error*) {
520 return sd_bus_reply_method_return(m,
"");
523int TrayIcon::sni_prop_get(sd_bus*,
const char*,
const char*,
524 const char* property, sd_bus_message* reply,
525 void* userdata, sd_bus_error* ret_error) {
526 auto* self =
static_cast<TrayIcon*
>(userdata);
528 if (std::strcmp(property,
"Category") == 0)
529 return sd_bus_message_append(reply,
"s",
"ApplicationStatus");
531 if (std::strcmp(property,
"Id") == 0)
532 return sd_bus_message_append(reply,
"s", self->id_.c_str());
534 if (std::strcmp(property,
"Title") == 0)
535 return sd_bus_message_append(reply,
"s", self->title_.c_str());
537 if (std::strcmp(property,
"Status") == 0)
538 return sd_bus_message_append(reply,
"s", self->status_.c_str());
540 if (std::strcmp(property,
"IconName") == 0) {
541 if (self->have_pixmap_)
542 return sd_bus_message_append(reply,
"s",
"");
543 return sd_bus_message_append(reply,
"s", self->icon_name_.c_str());
546 if (std::strcmp(property,
"IconPixmap") == 0) {
547 int r = sd_bus_message_open_container(reply,
'a',
"(iiay)");
550 if (self->have_pixmap_ && self->pix_w_ > 0 && self->pix_h_ > 0
551 && !self->pix_rgba_.empty())
553 r = sd_bus_message_open_container(reply,
'r',
"iiay");
556 r = sd_bus_message_append(reply,
"ii", self->pix_w_, self->pix_h_);
559 r = sd_bus_message_append_array(reply,
'y',
560 self->pix_rgba_.data(),
561 self->pix_rgba_.size());
564 r = sd_bus_message_close_container(reply);
568 return sd_bus_message_close_container(reply);
571 if (std::strcmp(property,
"Menu") == 0)
572 return sd_bus_message_append(reply,
"o",
"/Menu");
574 if (std::strcmp(property,
"ToolTip") == 0) {
575 int r = sd_bus_message_open_container(reply,
'r',
"sa(iiay)ss");
578 r = sd_bus_message_append(reply,
"s", self->icon_name_.c_str());
581 r = sd_bus_message_open_container(reply,
'a',
"(iiay)");
583 r = sd_bus_message_close_container(reply);
586 r = sd_bus_message_append(reply,
"s", self->title_.c_str());
588 r = sd_bus_message_append(reply,
"s", self->tooltip_.c_str());
591 r = sd_bus_message_close_container(reply);
596 return sd_bus_error_setf(ret_error, SD_BUS_ERROR_UNKNOWN_PROPERTY,
597 "Unknown property '%s'", property);
600int TrayIcon::dbusmenu_prop_get(sd_bus*,
const char*,
const char*,
601 const char* , sd_bus_message* reply,
602 void*, sd_bus_error*) {
603 return sd_bus_message_append(reply,
"u", 3u);
607 const char* label,
bool enabled =
true) {
609 r = sd_bus_message_open_container(m,
'r',
"ia{sv}av");
if (r < 0)
return r;
610 r = sd_bus_message_append(m,
"i",
id);
if (r < 0)
return r;
612 r = sd_bus_message_open_container(m,
'a',
"{sv}");
if (r < 0)
return r;
614 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
615 r = sd_bus_message_append(m,
"s",
"label");
if (r < 0)
return r;
616 r = sd_bus_message_open_container(m,
'v',
"s");
if (r < 0)
return r;
617 r = sd_bus_message_append(m,
"s", label);
if (r < 0)
return r;
618 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
619 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
621 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
622 r = sd_bus_message_append(m,
"s",
"enabled");
if (r < 0)
return r;
623 r = sd_bus_message_open_container(m,
'v',
"b");
if (r < 0)
return r;
624 r = sd_bus_message_append(m,
"b", enabled ? 1 : 0);
if (r < 0)
return r;
625 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
626 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
628 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
630 r = sd_bus_message_open_container(m,
'a',
"v");
if (r < 0)
return r;
631 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
633 return sd_bus_message_close_container(m);
637 const char* label,
bool enabled) {
639 r = sd_bus_message_open_container(m,
'r',
"ia{sv}");
if (r < 0)
return r;
640 r = sd_bus_message_append(m,
"i",
id);
if (r < 0)
return r;
642 r = sd_bus_message_open_container(m,
'a',
"{sv}");
if (r < 0)
return r;
644 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
645 r = sd_bus_message_append(m,
"s",
"label");
if (r < 0)
return r;
646 r = sd_bus_message_open_container(m,
'v',
"s");
if (r < 0)
return r;
647 r = sd_bus_message_append(m,
"s", label);
if (r < 0)
return r;
648 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
649 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
651 r = sd_bus_message_open_container(m,
'e',
"sv");
if (r < 0)
return r;
652 r = sd_bus_message_append(m,
"s",
"enabled");
if (r < 0)
return r;
653 r = sd_bus_message_open_container(m,
'v',
"b");
if (r < 0)
return r;
654 r = sd_bus_message_append(m,
"b", enabled ? 1 : 0);
if (r < 0)
return r;
655 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
656 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
658 r = sd_bus_message_close_container(m);
if (r < 0)
return r;
659 return sd_bus_message_close_container(m);
662int TrayIcon::dbusmenu_method_get_status(sd_bus_message* m,
void*, sd_bus_error*) {
663 return sd_bus_reply_method_return(m,
"s",
"normal");
666int TrayIcon::dbusmenu_method_get_children(sd_bus_message* m,
void* userdata, sd_bus_error*) {
667 auto* self =
static_cast<TrayIcon*
>(userdata);
670 int r = sd_bus_message_read(m,
"i", &parent);
673 sd_bus_message* reply =
nullptr;
674 r = sd_bus_message_new_method_return(m, &reply);
677 r = sd_bus_message_open_container(reply,
'a',
"i");
678 if (r < 0) { sd_bus_message_unref(reply);
return r; }
682 for (int32_t
id : ids) {
683 r = sd_bus_message_append(reply,
"i",
id);
684 if (r < 0) { sd_bus_message_unref(reply);
return r; }
688 r = sd_bus_message_close_container(reply);
689 if (r < 0) { sd_bus_message_unref(reply);
return r; }
691 sd_bus* bus = sd_bus_message_get_bus(m);
692 r = sd_bus_send(bus, reply,
nullptr);
693 sd_bus_message_unref(reply);
694 return (r < 0) ?
r : 1;
697int TrayIcon::dbusmenu_method_get_layout(sd_bus_message* m,
void* userdata, sd_bus_error*) {
698 auto* self =
static_cast<TrayIcon*
>(userdata);
700 int32_t parent_id = 0, depth = 0;
701 int r = sd_bus_message_read(m,
"ii", &parent_id, &depth);
704 r = sd_bus_message_skip(m,
"as");
707 sd_bus_message* reply =
nullptr;
708 r = sd_bus_message_new_method_return(m, &reply);
711 r = sd_bus_message_append(reply,
"u", (uint32_t)1);
712 if (r < 0) { sd_bus_message_unref(reply);
return r; }
714 r = sd_bus_message_open_container(reply,
'r',
"ia{sv}av");
715 if (r < 0) { sd_bus_message_unref(reply);
return r; }
717 r = sd_bus_message_append(reply,
"i",
MENU_ROOT);
718 if (r < 0) { sd_bus_message_unref(reply);
return r; }
720 r = sd_bus_message_open_container(reply,
'a',
"{sv}");
721 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 r = sd_bus_message_open_container(reply,
'a',
"v");
726 if (r < 0) { sd_bus_message_unref(reply);
return r; }
728 bool include_children = (depth != 0);
730 if (include_children) {
731 auto push_child = [&](
int id) ->
int {
732 int rr = sd_bus_message_open_container(reply,
'v',
"(ia{sv}av)");
733 if (rr < 0)
return rr;
737 if (rr < 0)
return rr;
738 return sd_bus_message_close_container(reply);
740 r = push_child(
MENU_SHOW);
if (r < 0) { sd_bus_message_unref(reply);
return r; }
741 r = push_child(
MENU_SETTINGS);
if (r < 0) { sd_bus_message_unref(reply);
return r; }
742 r = push_child(
MENU_QUIT);
if (r < 0) { sd_bus_message_unref(reply);
return r; }
745 r = sd_bus_message_close_container(reply);
746 if (r < 0) { sd_bus_message_unref(reply);
return r; }
748 r = sd_bus_message_close_container(reply);
749 if (r < 0) { sd_bus_message_unref(reply);
return r; }
751 sd_bus* bus = sd_bus_message_get_bus(m);
752 r = sd_bus_send(bus, reply,
nullptr);
753 sd_bus_message_unref(reply);
754 return (r < 0) ?
r : 1;
757int TrayIcon::dbusmenu_method_event(sd_bus_message* m,
void* userdata, sd_bus_error*) {
758 auto* self =
static_cast<TrayIcon*
>(userdata);
761 const char* event_id_c =
nullptr;
763 int r = sd_bus_message_read(m,
"is", &
id, &event_id_c);
765 return sd_bus_reply_method_return(m,
"");
767 std::string ev = event_id_c ? event_id_c :
"";
769 (void)sd_bus_message_skip(m,
"vu");
771 std::fprintf(stderr,
"[TrayIcon] Event: id=%d ev='%s'\n", (
int)
id, ev.c_str());
773 if (!self->ui_ctx_) {
774 std::fprintf(stderr,
"[TrayIcon] WARNING: ui_ctx_ null (bind_ui_context?)\n");
775 return sd_bus_reply_method_return(m,
"");
779 new std::function<
void()>([self,
id]() {
780 std::fprintf(stderr,
"[TrayIcon] Dispatch on UI: id=%d\n", (int)id);
782 if (id == MENU_SHOW) {
783 if (self->on_activate_cb_) self->on_activate_cb_();
785 if (self->on_settings_cb_) self->on_settings_cb_();
787 if (self->on_quit_cb_) self->on_quit_cb_();
789 std::fprintf(stderr,
"[TrayIcon] Unknown menu id=%d\n", (
int)
id);
794 return sd_bus_reply_method_return(m,
"");
797int TrayIcon::dbusmenu_method_about_to_show(sd_bus_message* m,
void*, sd_bus_error*) {
799 int r = sd_bus_message_read(m,
"i", &
id);
801 return sd_bus_reply_method_return(m,
"b", 0);
804int TrayIcon::dbusmenu_method_get_group_properties(sd_bus_message* m,
void* userdata, sd_bus_error*) {
805 auto* self =
static_cast<TrayIcon*
>(userdata);
807 int r = sd_bus_message_enter_container(m,
'a',
"i");
810 std::vector<int32_t> ids;
813 r = sd_bus_message_read(m,
"i", &
id);
819 r = sd_bus_message_exit_container(m);
822 r = sd_bus_message_skip(m,
"as");
825 sd_bus_message* reply =
nullptr;
826 r = sd_bus_message_new_method_return(m, &reply);
829 r = sd_bus_message_open_container(reply,
'a',
"(ia{sv})");
830 if (r < 0) { sd_bus_message_unref(reply);
return r; }
832 for (
auto id : ids) {
836 if (r < 0) { sd_bus_message_unref(reply);
return r; }
839 r = sd_bus_message_close_container(reply);
840 if (r < 0) { sd_bus_message_unref(reply);
return r; }
842 sd_bus* bus = sd_bus_message_get_bus(m);
843 r = sd_bus_send(bus, reply,
nullptr);
844 sd_bus_message_unref(reply);
845 return (r < 0) ?
r : 1;
848int TrayIcon::dbusmenu_method_get_property(sd_bus_message* m,
void* userdata, sd_bus_error*) {
849 auto* self =
static_cast<TrayIcon*
>(userdata);
852 const char* prop =
nullptr;
853 int r = sd_bus_message_read(m,
"is", &
id, &prop);
856 std::string prop_str = prop ? prop :
"";
858 sd_bus_message* reply =
nullptr;
859 r = sd_bus_message_new_method_return(m, &reply);
862 if (prop_str ==
"label") {
863 r = sd_bus_message_open_container(reply,
'v',
"s");
864 if (r < 0) { sd_bus_message_unref(reply);
return r; }
866 if (r < 0) { sd_bus_message_unref(reply);
return r; }
867 r = sd_bus_message_close_container(reply);
868 if (r < 0) { sd_bus_message_unref(reply);
return r; }
869 }
else if (prop_str ==
"enabled") {
870 r = sd_bus_message_open_container(reply,
'v',
"b");
871 if (r < 0) { sd_bus_message_unref(reply);
return r; }
873 if (r < 0) { sd_bus_message_unref(reply);
return r; }
874 r = sd_bus_message_close_container(reply);
875 if (r < 0) { sd_bus_message_unref(reply);
return r; }
877 r = sd_bus_message_open_container(reply,
'v',
"s");
878 if (r < 0) { sd_bus_message_unref(reply);
return r; }
879 r = sd_bus_message_append(reply,
"s",
"");
880 if (r < 0) { sd_bus_message_unref(reply);
return r; }
881 r = sd_bus_message_close_container(reply);
882 if (r < 0) { sd_bus_message_unref(reply);
return r; }
885 sd_bus* bus = sd_bus_message_get_bus(m);
886 r = sd_bus_send(bus, reply,
nullptr);
887 sd_bus_message_unref(reply);
888 return (r < 0) ?
r : 1;
static constexpr int MENU_ROOT
static constexpr int MENU_SETTINGS
void set_title(const std::string &title)
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)