Files
gKeyMapper/src/trayicon.h
2026-10-18 18:26:06 +02:00

110 lines
5.0 KiB
C++

// gKeyMapper - the tray icon, spoken as a StatusNotifierItem.
#ifndef GKEYMAPPER_TRAYICON_H
#define GKEYMAPPER_TRAYICON_H
#include <functional>
#include <string>
#include <vector>
#include <gio/gio.h>
namespace gkeymapper {
// The tray icon. GTK4 has no tray widget (GtkStatusIcon was GTK3 and is gone), so
// the icon is exported on the session bus as a StatusNotifierItem plus a
// com.canonical.dbusmenu menu - exactly the protocol libxapp and libappindicator
// wrap, only without their GTK3 dependency (a GTK4 process must not load GTK3).
// The panel hosts it: Cinnamon's xapp-status applet, KDE, Unity, or the GNOME
// AppIndicator extension.
//
// The panel is a different process, so the icon travels as a pixmap: it cannot
// read the icons compiled into this binary.
class TrayIcon {
public:
using Callback = std::function<void()>;
TrayIcon() = default;
~TrayIcon();
TrayIcon(const TrayIcon&) = delete;
TrayIcon& operator=(const TrayIcon&) = delete;
// Owns the item's bus name, exports the item and its menu and registers with
// the panel's watcher - and again whenever the panel restarts (the watcher
// owns the icon list, a restart loses it). `icon` is ARGB32 in network byte
// order, which is the layout the protocol asks for.
void start(std::vector<unsigned char> icon, int width, int height, Callback onShow,
Callback onQuit);
// True once a panel hosts the icon, i.e. the icon is really on screen. Only
// then hiding the window is safe: without a tray there is no way back.
bool ready() const { return registered_; }
// The panel mirrors the menu once, so it has to be told when the texts in it
// changed - a language switch. Nothing else about the icon changes.
void refresh_labels();
private:
static void on_bus_acquired(GDBusConnection* connection, const gchar* name, gpointer userData);
static void on_name_lost(GDBusConnection* connection, const gchar* name, gpointer userData);
static void on_watcher_appeared(GDBusConnection* connection, const gchar* name,
const gchar* nameOwner, gpointer userData);
static void on_watcher_vanished(GDBusConnection* connection, const gchar* name,
gpointer userData);
static void on_register_reply(GObject* source, GAsyncResult* result, gpointer userData);
static void on_item_method_call(GDBusConnection* connection, const gchar* sender,
const gchar* objectPath, const gchar* interfaceName,
const gchar* methodName, GVariant* parameters,
GDBusMethodInvocation* invocation, gpointer userData);
static GVariant* on_item_get_property(GDBusConnection* connection, const gchar* sender,
const gchar* objectPath, const gchar* interfaceName,
const gchar* propertyName, GError** error,
gpointer userData);
static void on_menu_method_call(GDBusConnection* connection, const gchar* sender,
const gchar* objectPath, const gchar* interfaceName,
const gchar* methodName, GVariant* parameters,
GDBusMethodInvocation* invocation, gpointer userData);
static GVariant* on_menu_get_property(GDBusConnection* connection, const gchar* sender,
const gchar* objectPath, const gchar* interfaceName,
const gchar* propertyName, GError** error,
gpointer userData);
void export_objects(GDBusConnection* connection);
// Runs what a click on a menu entry means ("clicked" on Show or Close).
void handle_event(int id, const char* eventId);
// Notes which of the two watcher names is on the bus and registers there.
void set_watcher_available(const char* name, bool available);
void register_with_watcher();
GVariant* icon_pixmaps() const;
GVariant* tooltip() const;
GVariant* layout(int parentId, int depth) const;
GVariant* node(int id, int depth) const;
GVariant* group_properties(GVariant* ids) const;
GVariant* menu_property(int id, const char* name) const;
GDBusConnection* connection_ = nullptr;
guint itemRegistration_ = 0;
guint menuRegistration_ = 0;
guint ownerId_ = 0;
guint watcherId_ = 0;
guint watcherIdX_ = 0;
std::string serviceName_;
std::vector<unsigned char> icon_;
int iconWidth_ = 0;
int iconHeight_ = 0;
bool watcher_ = false;
bool watcherAlias_ = false;
bool registering_ = false;
bool registered_ = false;
const char* registeringWith_ = nullptr;
std::string registeredWith_;
// Bumped whenever the menu texts change; the panel compares it to know that
// what it mirrored is stale.
guint menuRevision_ = 1;
Callback onShow_;
Callback onQuit_;
};
} // namespace gkeymapper
#endif // GKEYMAPPER_TRAYICON_H