Commit ccc50efa authored by Kai Uwe Broulik's avatar Kai Uwe Broulik 🍇
Browse files

Make it mostly work

* Send delayed reply when we aren't subscribed yet in GetLayout
* Show keyboard shortcuts (crude)
* Add some icons for known actions (even more crude)
* Support GTK actions stuff, including action enabled/visible/checkable
* Action invocation
parent fb72ab85
......@@ -26,7 +26,7 @@
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuItem &item)
{
argument.beginStructure();
argument << item.id << item.count << item.items;
argument << item.id << item.section << item.items;
argument.endStructure();
return argument;
}
......@@ -34,7 +34,24 @@ QDBusArgument &operator<<(QDBusArgument &argument, const GMenuItem &item)
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuItem &item)
{
argument.beginStructure();
argument >> item.id >> item.count >> item.items;
argument >> item.id >> item.section >> item.items;
argument.endStructure();
return argument;
}
// GMenuSection
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuSection &item)
{
argument.beginStructure();
argument << item.subscription << item.menu;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuSection &item)
{
argument.beginStructure();
argument >> item.subscription >> item.menu;
argument.endStructure();
return argument;
}
......@@ -56,6 +73,41 @@ const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuChange &item
return argument;
}
// GMenuActionProperty
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuAction &item)
{
argument.beginStructure();
argument << item.enabled << item.signature << item.arguments;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuAction &item)
{
argument.beginStructure();
argument >> item.enabled >> item.signature >> item.arguments;
argument.endStructure();
return argument;
}
// GMenuActionsChange
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuActionsChange &item)
{
argument.beginStructure();
argument << item.removed << item.enabledChanged << item.stateChanged << item.added;
argument.endStructure();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuActionsChange &item)
{
argument.beginStructure();
argument >> item.removed >> item.enabledChanged >> item.stateChanged >> item.added;
argument.endStructure();
return argument;
}
void GDBusMenuTypes_register()
{
static bool registered = false;
......@@ -66,8 +118,15 @@ void GDBusMenuTypes_register()
qDBusRegisterMetaType<GMenuItem>();
qDBusRegisterMetaType<GMenuItemList>();
qDBusRegisterMetaType<GMenuSection>();
qDBusRegisterMetaType<GMenuChange>();
qDBusRegisterMetaType<GMenuChangeList>();
qDBusRegisterMetaType<GMenuAction>();
qDBusRegisterMetaType<GMenuActionMap>();
qDBusRegisterMetaType<GMenuActionsChange>();
registered = true;
}
......@@ -19,29 +19,41 @@
#pragma once
#include <QHash>
#include <QDBusSignature>
#include <QList>
#include <QMap>
#include <QVariant>
class QDBusArgument;
// Various
using VariantHashList = QList<QVariantHash>;
using VariantMapList = QList<QVariantMap>;
// Menu item itself (Start method)
struct GMenuItem
{
uint id;
uint count;
VariantHashList items;
uint section;
VariantMapList items;
};
Q_DECLARE_METATYPE(GMenuItem)
Q_DECLARE_METATYPE(GMenuItem);
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuItem &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuItem &item);
using GMenuItemList = QList<GMenuItem>;
// Information about what section or submenu to use for a particular entry
struct GMenuSection
{
uint subscription;
uint menu;
};
Q_DECLARE_METATYPE(GMenuSection);
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuSection &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuSection &item);
// Changes of a menu item (Changed signal)
struct GMenuChange
{
......@@ -49,13 +61,39 @@ struct GMenuChange
uint count;
uint changePosition;
uint itemsToRemoveCount;
VariantHashList itemsToInsert;
VariantMapList itemsToInsert;
};
Q_DECLARE_METATYPE(GMenuChange)
Q_DECLARE_METATYPE(GMenuChange);
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuChange &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuChange &item);
using GMenuChangeList = QList<GMenuChange>;
// An application action
struct GMenuAction
{
bool enabled;
QDBusSignature signature;
QVariantList arguments;
};
Q_DECLARE_METATYPE(GMenuAction);
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuAction &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuAction &item);
using GMenuActionMap = QMap<QString, GMenuAction>;
struct GMenuActionsChange
{
QStringList removed;
QMap<QString, bool> enabledChanged;
QMap<QString, QVariant> stateChanged;
QList<GMenuAction> added;
};
Q_DECLARE_METATYPE(GMenuActionsChange);
QDBusArgument &operator<<(QDBusArgument &argument, const GMenuActionsChange &item);
const QDBusArgument &operator>>(const QDBusArgument &argument, GMenuActionsChange &item);
void GDBusMenuTypes_register();
This diff is collapsed.
......@@ -20,16 +20,19 @@
#pragma once
#include <QObject>
#include <QDBusContext>
#include <QString>
#include <QVector>
#include <QWindow> // for WId
#include <functional>
#include "gdbusmenutypes_p.h"
#include "../libdbusmenuqt/dbusmenutypes_p.h"
class QDBusVariant;
class Menu : public QObject
class Menu : public QObject, protected QDBusContext
{
Q_OBJECT
......@@ -38,19 +41,27 @@ class Menu : public QObject
Q_PROPERTY(uint Version READ version)
public:
Menu(WId winId, const QString &serviceName, const QString &objectPath);
Menu(WId winId,
const QString &serviceName,
const QString &applicationObjectPath,
const QString &windowObjectPath,
const QString &menuObjectPath);
~Menu();
WId winId() const;
QString serviceName() const;
QString objectPath() const;
QString applicationObjectPath() const;
QString windowObjectPath() const;
QString menuObjectPath() const;
QString proxyObjectPath() const;
// DBus
bool AboutToShow(int id);
void Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp);
DBusMenuItemList GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames);
uint GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item);
uint GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &dbusItem);
QDBusVariant GetProperty(int id, const QString &property);
QString status() const;
......@@ -68,21 +79,45 @@ signals:
private slots:
void onMenuChanged(const GMenuChangeList &changes);
void onWindowActionsChanged(const GMenuActionsChange &changes);
private:
void start(const QList<uint> &ids);
void stop(const QList<uint> &ids);
void start();
void start(uint id);
void stop(const QList<uint> &id);
bool registerDBusObject();
void getActions(const QString &path, const std::function<void(GMenuActionMap,bool)> &cb);
bool getAction(const QString &name, GMenuAction &action) const;
void triggerAction(const QString &name);
static int treeStructureToInt(int subscription, int section, int index);
static void intToTreeStructure(int source, int &subscription, int &section, int &index);
static GMenuItem findSection(const QList<GMenuItem> &list, int section);
QVariantMap gMenuToDBusMenuProperties(const QVariantMap &source) const;
WId m_winId;
QString m_serviceName; // original GMenu service (the gtk app)
QString m_objectPath; // original GMenu object path (the gtk app)
QString m_applicationObjectPath;
QString m_windowObjectPath;
QString m_menuObjectPath;
QString m_proxyObjectPath; // our object path on this proxy app
// QSet?
QList<uint> m_subscriptions; // keeps track of which menu trees we're subscribed to
QHash<uint, VariantHashList> m_menus; // the menu data we gathered in Start + subsequent updates
QHash<uint, GMenuItemList> m_menus;
QHash<int, QDBusMessage> m_pendingGetLayouts;
bool m_queriedApplicationActions = false;
GMenuActionMap m_applicationActions;
bool m_queriedWindowActions = false;
GMenuActionMap m_windowActions;
};
......@@ -42,6 +42,7 @@ static const QString s_ourServiceName = QStringLiteral("org.kde.plasma.gmenu_dbu
static const QByteArray s_gtkUniqueBusName = QByteArrayLiteral("_GTK_UNIQUE_BUS_NAME");
static const QByteArray s_gtkApplicationObjectPath = QByteArrayLiteral("_GTK_APPLICATION_OBJECT_PATH");
static const QByteArray s_gtkWindowObjectPath = QByteArrayLiteral("_GTK_WINDOW_OBJECT_PATH");
static const QByteArray s_gtkMenuBarObjectPath = QByteArrayLiteral("_GTK_MENUBAR_OBJECT_PATH");
// that's the generic app menu with Help and Options and will be used if window doesn't have a fully-blown menu bar
static const QByteArray s_gtkAppMenuObjectPath = QByteArrayLiteral("_GTK_APP_MENU_OBJECT_PATH");
......@@ -84,7 +85,10 @@ void MenuProxy::onWindowAdded(WId id)
// TODO split that stuff out so we can do early returns and not a kilometer of indentation
const QString serviceName = QString::fromUtf8(getWindowPropertyString(id, s_gtkUniqueBusName));
if (serviceName.isEmpty()) {
const QString applicationObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkApplicationObjectPath));
const QString windowObjectPath = QString::fromUtf8(getWindowPropertyString(id, s_gtkWindowObjectPath));
if (serviceName.isEmpty() || applicationObjectPath.isEmpty() || windowObjectPath.isEmpty()) {
return;
}
......@@ -98,7 +102,7 @@ void MenuProxy::onWindowAdded(WId id)
return;
}
Menu *menu = new Menu(id, serviceName, menuObjectPath);
Menu *menu = new Menu(id, serviceName, applicationObjectPath, windowObjectPath, menuObjectPath);
m_menus.insert(id, menu);
connect(menu, &Menu::requestWriteWindowProperties, this, [this, menu] {
......@@ -107,6 +111,10 @@ void MenuProxy::onWindowAdded(WId id)
writeWindowProperty(menu->winId(), s_kdeNetWmAppMenuServiceName, s_ourServiceName.toUtf8());
writeWindowProperty(menu->winId(), s_kdeNetWmAppMenuObjectPath, menu->proxyObjectPath().toUtf8());
});
connect(menu, &Menu::requestRemoveWindowProperties, this, [this, menu] {
writeWindowProperty(menu->winId(), s_kdeNetWmAppMenuServiceName, QByteArray());
writeWindowProperty(menu->winId(), s_kdeNetWmAppMenuObjectPath, QByteArray());
});
}
//#endif // HAVE_X11
}
......@@ -128,16 +136,16 @@ QByteArray MenuProxy::getWindowPropertyString(WId id, const QByteArray &name)
}
static const long MAX_PROP_SIZE = 10000;
// FIXME figure out what "392" is, xprop says UTF8_STRING but it's NOT XCB_ATOM_STRING :/
auto propertyCookie = xcb_get_property(c, false, id, atom, 392, 0, MAX_PROP_SIZE);
// FIXME figure out what "UT8String" is as atom type, it's 392 or 378 but I don't find that enum
auto propertyCookie = xcb_get_property(c, false, id, atom, XCB_ATOM_ANY, 0, MAX_PROP_SIZE);
QScopedPointer<xcb_get_property_reply_t, QScopedPointerPodDeleter> propertyReply(xcb_get_property_reply(c, propertyCookie, NULL));
if (propertyReply.isNull()) {
qDebug() << "property reply was null";
return value;
}
// FIXME 392
if (propertyReply->type == 392 && propertyReply->format == 8 && propertyReply->value_len > 0) {
// FIXME Check type
if (/*propertyReply->type == 392 && */propertyReply->format == 8 && propertyReply->value_len > 0) {
const char *data = (const char *) xcb_get_property_value(propertyReply.data());
int len = propertyReply->value_len;
if (data) {
......@@ -150,7 +158,6 @@ QByteArray MenuProxy::getWindowPropertyString(WId id, const QByteArray &name)
void MenuProxy::writeWindowProperty(WId id, const QByteArray &name, const QByteArray &value)
{
qDebug() << "Write window property string" << name << "with" << value << "on" << id;
auto *c = QX11Info::connection(); // FIXME cache
auto atom = getAtom(name);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment