110 lines
5.0 KiB
C
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
|