diff --git a/appmenu/CMakeLists.txt b/appmenu/CMakeLists.txt index 317f62e59..6c6024740 100644 --- a/appmenu/CMakeLists.txt +++ b/appmenu/CMakeLists.txt @@ -1,37 +1,38 @@ remove_definitions(-DQT_NO_CAST_FROM_ASCII -DQT_STRICT_ITERATORS -DQT_NO_CAST_FROM_BYTEARRAY -DQT_NO_KEYWORDS) set(kded_appmenu_SRCS appmenu.cpp menuimporter.cpp appmenu_dbus.cpp verticalmenu.cpp ) qt5_add_dbus_adaptor(kded_appmenu_SRCS com.canonical.AppMenu.Registrar.xml menuimporter.h MenuImporter menuimporteradaptor MenuImporterAdaptor) qt5_add_dbus_adaptor(kded_appmenu_SRCS org.kde.kappmenu.xml appmenu_dbus.h AppmenuDBus appmenuadaptor AppmenuAdaptor) add_library(appmenu MODULE ${kded_appmenu_SRCS}) kcoreaddons_desktop_to_json(appmenu appmenu.desktop) target_link_libraries(appmenu Qt5::DBus KF5::DBusAddons KF5::KIOCore KF5::WindowSystem + KF5::WaylandClient ${X11_LIBRARIES} dbusmenuqt ) if (HAVE_X11) target_link_libraries(appmenu Qt5::X11Extras XCB::XCB) endif() install(TARGETS appmenu DESTINATION ${KDE_INSTALL_PLUGINDIR}/kf5/kded ) ########### install files ############### install( FILES com.canonical.AppMenu.Registrar.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) install( FILES org.kde.kappmenu.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} ) diff --git a/appmenu/appmenu.cpp b/appmenu/appmenu.cpp index 4ab710677..c452e7be8 100644 --- a/appmenu/appmenu.cpp +++ b/appmenu/appmenu.cpp @@ -1,211 +1,217 @@ /* This file is part of the KDE project. Copyright (c) 2011 Lionel Chauvin Copyright (c) 2011,2012 Cédric Bellegarde Copyright (c) 2016 Kai Uwe Broulik Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include "appmenu.h" #include "kdbusimporter.h" #include "menuimporteradaptor.h" #include "appmenuadaptor.h" #include "appmenu_dbus.h" #include "verticalmenu.h" #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #include #endif static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME"); static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH"); K_PLUGIN_FACTORY_WITH_JSON(AppMenuFactory, "appmenu.json", registerPlugin();) AppMenuModule::AppMenuModule(QObject* parent, const QList&) : KDEDModule(parent), m_appmenuDBus(new AppmenuDBus(this)) { reconfigure(); m_appmenuDBus->connectToBus(); connect(m_appmenuDBus, &AppmenuDBus::appShowMenu, this, &AppMenuModule::slotShowMenu); connect(m_appmenuDBus, &AppmenuDBus::reconfigured, this, &AppMenuModule::reconfigure); // transfer our signals to dbus connect(this, &AppMenuModule::showRequest, m_appmenuDBus, &AppmenuDBus::showRequest); connect(this, &AppMenuModule::menuHidden, m_appmenuDBus, &AppmenuDBus::menuHidden); connect(this, &AppMenuModule::menuShown, m_appmenuDBus, &AppmenuDBus::menuShown); m_menuViewWatcher = new QDBusServiceWatcher(QStringLiteral("org.kde.kappmenuview"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForRegistration|QDBusServiceWatcher::WatchForUnregistration, this); auto setupMenuImporter = [this]() { QDBusConnection::sessionBus().connect({}, {}, QStringLiteral("com.canonical.dbusmenu"), QStringLiteral("ItemActivationRequested"), this, SLOT(itemActivationRequested(int,uint))); // Setup a menu importer if needed if (!m_menuImporter) { m_menuImporter = new MenuImporter(this); connect(m_menuImporter, &MenuImporter::WindowRegistered, this, &AppMenuModule::slotWindowRegistered); m_menuImporter->connectToBus(); } }; connect(m_menuViewWatcher, &QDBusServiceWatcher::serviceRegistered, this, setupMenuImporter); connect(m_menuViewWatcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &service) { Q_UNUSED(service) QDBusConnection::sessionBus().disconnect({}, {}, QStringLiteral("com.canonical.dbusmenu"), QStringLiteral("ItemActivationRequested"), this, SLOT(itemActivationRequested(int,uint))); delete m_menuImporter; m_menuImporter = nullptr; }); if (QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.kappmenuview"))) { setupMenuImporter(); } } AppMenuModule::~AppMenuModule() = default; void AppMenuModule::slotWindowRegistered(WId id, const QString &serviceName, const QDBusObjectPath &menuObjectPath) { #if HAVE_X11 if (KWindowSystem::isPlatformX11()) { auto *c = QX11Info::connection(); static xcb_atom_t s_serviceNameAtom = XCB_ATOM_NONE; static xcb_atom_t s_objectPathAtom = XCB_ATOM_NONE; auto setWindowProperty = [c](WId id, xcb_atom_t &atom, const QByteArray &name, const QByteArray &value) { if (atom == XCB_ATOM_NONE) { const xcb_intern_atom_cookie_t cookie = xcb_intern_atom(c, false, name.length(), name.constData()); QScopedPointer reply(xcb_intern_atom_reply(c, cookie, nullptr)); if (reply.isNull()) { return; } atom = reply->atom; if (atom == XCB_ATOM_NONE) { return; } } xcb_change_property(c, XCB_PROP_MODE_REPLACE, id, atom, XCB_ATOM_STRING, 8, value.length(), value.constData()); }; // TODO only set the property if it doesn't already exist setWindowProperty(id, s_serviceNameAtom, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8()); setWindowProperty(id, s_objectPathAtom, s_x11AppMenuObjectPathPropertyName, menuObjectPath.path().toUtf8()); } #endif } void AppMenuModule::slotShowMenu(int x, int y, const QString &serviceName, const QDBusObjectPath &menuObjectPath, int actionId) { if (!m_menuImporter) { return; } // If menu visible, hide it if (m_menu && m_menu.data()->isVisible()) { m_menu.data()->hide(); return; } //dbus call by user (for khotkey shortcut) if (x == -1 || y == -1) { // We do not know kwin button position, so tell kwin to show menu emit showRequest(serviceName, menuObjectPath, actionId); return; } auto *importer = new KDBusMenuImporter(serviceName, menuObjectPath.path(), this); QMetaObject::invokeMethod(importer, "updateMenu", Qt::QueuedConnection); disconnect(importer, nullptr, this, nullptr); // ensure we don't popup multiple times in case the menu updates again later connect(importer, &KDBusMenuImporter::menuUpdated, this, [=](QMenu *m) { QMenu *menu = importer->menu(); if (!menu || menu != m) { return; } m_menu = qobject_cast(menu); m_menu.data()->setServiceName(serviceName); m_menu.data()->setMenuObjectPath(menuObjectPath); connect(m_menu.data(), &QMenu::aboutToHide, this, [this, importer] { hideMenu(); importer->deleteLater(); }); //m_menuImporter->fakeUnityAboutToShow(serviceName, menuObjectPath); - m_menu.data()->popup(QPoint(x, y) / qApp->devicePixelRatio()); + QPoint pos(x, y); +// if (X) { +// pos /= qApp->devicePixelRatio() +// } + m_menu.data()->popup(pos); + qDebug() << "popup!" << x << y; + QAction *actiontoActivate = importer->actionForId(actionId); emit menuShown(serviceName, menuObjectPath); if (actiontoActivate) { m_menu.data()->setActiveAction(actiontoActivate); } }); } void AppMenuModule::hideMenu() { if (m_menu) { emit menuHidden(m_menu.data()->serviceName(), m_menu->menuObjectPath()); } } void AppMenuModule::itemActivationRequested(int actionId, uint timeStamp) { Q_UNUSED(timeStamp); emit showRequest(message().service(), QDBusObjectPath(message().path()), actionId); } // this method is not really used anymore but has to be kept for DBus compatibility void AppMenuModule::reconfigure() { } #include "appmenu.moc" diff --git a/appmenu/verticalmenu.cpp b/appmenu/verticalmenu.cpp index 0d01bdb8d..2c5d34272 100644 --- a/appmenu/verticalmenu.cpp +++ b/appmenu/verticalmenu.cpp @@ -1,77 +1,146 @@ /* This file is part of the KDE project. Copyright (c) 2011 Lionel Chauvin Copyright (c) 2011,2012 Cédric Bellegarde Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "verticalmenu.h" #include #include #include #include #include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace KWayland::Client; VerticalMenu::VerticalMenu(QWidget* parent) : QMenu(parent) { + if (KWindowSystem::isPlatformWayland()) { + // normally inferred from being a menu, but on wayland, we're a toplevel + setWindowFlags(Qt::FramelessWindowHint); + ConnectionThread *connection = ConnectionThread::fromApplication(qApp); + auto registry = new Registry(this); + registry->create(connection); + registry->setup(); + connection->roundtrip(); + + const KWayland::Client::Registry::AnnouncedInterface wmInterface = registry->interface(KWayland::Client::Registry::Interface::PlasmaShell); + + if (wmInterface.name != 0) { + m_plasmaShellInterface = registry->createPlasmaShell(wmInterface.name, wmInterface.version, qApp); + } + } + winId(); //to create a window handle + windowHandle()->installEventFilter(this); +} + +bool VerticalMenu::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::Expose) { + auto ee = static_cast(event); + + if (!KWindowSystem::isPlatformWayland() || ee->region().isNull()) { + return false; + } + if (!m_plasmaShellInterface) { + qDebug() << "booo1"; + return false; + } + if (m_plasmaShellSurface) { + qDebug() << "booo2"; + return false; + } + Surface *s = Surface::fromWindow(windowHandle()); + if (!s) { + qDebug() << "booo3"; + return false; + } + m_plasmaShellSurface = m_plasmaShellInterface->createSurface(s, this); + m_plasmaShellSurface->setPosition(pos()); + qDebug() << "set position" << pos(); + } else if (event->type() == QEvent::Hide) { + delete m_plasmaShellSurface.data(); + } else if (event->type() == QEvent::Move) { + if (m_plasmaShellSurface) { + QMoveEvent *me = static_cast(event); + m_plasmaShellSurface->setPosition(me->pos()); + qDebug() << "move position" << pos(); + + } + } + return false; } VerticalMenu::~VerticalMenu() { } QMenu *VerticalMenu::leafMenu() { QMenu *leaf = this; while (true) { QAction *act = leaf->activeAction(); if (act && act->menu() && act->menu()->isVisible()) { leaf = act->menu(); continue; } return leaf == this ? nullptr : leaf; } return nullptr; // make gcc happy } void VerticalMenu::paintEvent(QPaintEvent *pe) { QMenu::paintEvent(pe); if (QWidget::mouseGrabber() == this) return; if (QWidget::mouseGrabber()) QWidget::mouseGrabber()->releaseMouse(); grabMouse(); grabKeyboard(); } -#define FORWARD(_EVENT_, _TYPE_) \ -void VerticalMenu::_EVENT_##Event(Q##_TYPE_##Event *e) \ -{ \ - if (QMenu *leaf = leafMenu()) \ - QCoreApplication::sendEvent(leaf, e); \ - else \ - QMenu::_EVENT_##Event(e); \ -} \ - -FORWARD(keyPress, Key) -FORWARD(keyRelease, Key) +//DAVE FIXME +// #define FORWARD(_EVENT_, _TYPE_) \ +// void VerticalMenu::_EVENT_##Event(Q##_TYPE_##Event *e) \ +// { \ +// if (QMenu *leaf = leafMenu()) \ +// QCoreApplication::sendEvent(leaf, e); \ +// else \ +// QMenu::_EVENT_##Event(e); \ +// } \ +// +// FORWARD(keyPress, Key) +// FORWARD(keyRelease, Key) diff --git a/appmenu/verticalmenu.h b/appmenu/verticalmenu.h index f9c27692f..acf7841ba 100644 --- a/appmenu/verticalmenu.h +++ b/appmenu/verticalmenu.h @@ -1,58 +1,70 @@ /* This file is part of the KDE project. Copyright (c) 2011 Lionel Chauvin Copyright (c) 2011,2012 Cédric Bellegarde Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef VERTICALMENU_H #define VERTICALMENU_H #include #include +#include + +namespace KWayland { +namespace Client { + class PlasmaShell; + class PlasmaShellSurface; +} +} class VerticalMenu : public QMenu { Q_OBJECT public: explicit VerticalMenu(QWidget * parent = nullptr); ~VerticalMenu() override; QString serviceName() const { return m_serviceName; } void setServiceName(const QString &serviceName) { m_serviceName = serviceName; } QDBusObjectPath menuObjectPath() const { return m_menuObjectPath; } void setMenuObjectPath(const QDBusObjectPath &menuObjectPath) { m_menuObjectPath = menuObjectPath; } + protected: - void keyPressEvent(QKeyEvent*) override; - void keyReleaseEvent(QKeyEvent*) override; + bool eventFilter(QObject *, QEvent *) override; +// void keyPressEvent(QKeyEvent*) override; +// void keyReleaseEvent(QKeyEvent*) override; void paintEvent(QPaintEvent*) override; private: QMenu *leafMenu(); + QPointer m_plasmaShellInterface;; + QPointer m_plasmaShellSurface; QString m_serviceName; QDBusObjectPath m_menuObjectPath; }; #endif //VERTICALMENU_H