From 06367f157f6686a49ea350a64c1691c18e9c1d01 Mon Sep 17 00:00:00 2001 From: Jan Arne Petersen Date: Wed, 24 May 2017 13:53:55 +0200 Subject: [PATCH] Create a wayland input panel shell integration Move all the wayland input panel code there. Allows to start QT_WAYLAND_SHELL_INTEGRATION=inputpanel-shell maliit-keyboard to get it started as input panel instead of previous hacks. --- CMakeLists.txt | 19 ++ .../shellintegration/inputpanelshell.json | 3 + .../inputpanelshellplugin.cpp | 40 ++++ .../qwaylandinputpanelshellintegration.cpp | 54 ++++++ .../qwaylandinputpanelshellintegration.h | 40 ++++ .../qwaylandinputpanelsurface.cpp | 46 +++++ .../qwaylandinputpanelsurface.h | 39 ++++ src/waylandplatform.cpp | 179 +----------------- 8 files changed, 246 insertions(+), 174 deletions(-) create mode 100644 src/qt/plugins/shellintegration/inputpanelshell.json create mode 100644 src/qt/plugins/shellintegration/inputpanelshellplugin.cpp create mode 100644 src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.cpp create mode 100644 src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.h create mode 100644 src/qt/plugins/shellintegration/qwaylandinputpanelsurface.cpp create mode 100644 src/qt/plugins/shellintegration/qwaylandinputpanelsurface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 28868f17..9b81a568 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ if(enable-wayland) find_package(WaylandProtocols REQUIRED PRIVATE) find_package(QtWaylandScanner REQUIRED) find_package(Wayland REQUIRED) + find_package(Qt5WaylandClient REQUIRED PRIVATE) endif() include_directories(src common) @@ -291,6 +292,19 @@ if(enable-qt5-inputcontext) target_link_libraries(maliitplatforminputcontextplugin maliit-connection Qt5::Quick) endif() +if(enable-wayland) + set(INPUT_PANEL_SHELL_SOURCES + src/qt/plugins/shellintegration/inputpanelshellplugin.cpp + src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.cpp + src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.h src/qt/plugins/shellintegration/qwaylandinputpanelsurface.cpp src/qt/plugins/shellintegration/qwaylandinputpanelsurface.h) + + ecm_add_qtwayland_client_protocol(INPUT_PANEL_SHELL_SOURCES PROTOCOL ${WAYLANDPROTOCOLS_PATH}/unstable/input-method/input-method-unstable-v1.xml BASENAME input-method-unstable-v1) + + add_library(inputpanel-shell MODULE ${INPUT_PANEL_SHELL_SOURCES}) + target_link_libraries(inputpanel-shell Qt5::WaylandClient) + target_include_directories(inputpanel-shell PRIVATE ${Qt5WaylandClient_PRIVATE_INCLUDE_DIRS}) +endif() + add_executable(maliit-exampleapp-plainqt examples/apps/plainqt/mainwindow.cpp examples/apps/plainqt/mainwindow.h @@ -410,6 +424,11 @@ if(enable-qt5-inputcontext) install(TARGETS maliitplatforminputcontextplugin LIBRARY DESTINATION ${QT5_PLUGINS_INSTALL_DIR}/platforminputcontext) endif() +if(enable-wayland) + install(TARGETS inputpanel-shell + LIBRARY DESTINATION ${QT5_PLUGINS_INSTALL_DIR}/wayland-shell-integration) +endif() + if(enable-dbus-activation) configure_file(connection/org.maliit.server.service.in org.maliit.server.service @ONLY) diff --git a/src/qt/plugins/shellintegration/inputpanelshell.json b/src/qt/plugins/shellintegration/inputpanelshell.json new file mode 100644 index 00000000..5f351896 --- /dev/null +++ b/src/qt/plugins/shellintegration/inputpanelshell.json @@ -0,0 +1,3 @@ +{ + "Keys":[ "inputpanel-shell" ] +} \ No newline at end of file diff --git a/src/qt/plugins/shellintegration/inputpanelshellplugin.cpp b/src/qt/plugins/shellintegration/inputpanelshellplugin.cpp new file mode 100644 index 00000000..d26c25c8 --- /dev/null +++ b/src/qt/plugins/shellintegration/inputpanelshellplugin.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * and appearing in the file LICENSE.LGPL included in the packaging + * of this file. + */ + +#include + +#include "qwaylandinputpanelshellintegration.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient +{ + +class QWaylandInputPanelShellIntegrationPlugin: public QWaylandShellIntegrationPlugin +{ +Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "inputpanelshell.json") + +public: + virtual QWaylandShellIntegration *create(const QString &key, const QStringList ¶mList) override; +}; + +QWaylandShellIntegration *QWaylandInputPanelShellIntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(key); + Q_UNUSED(paramList); + return new QWaylandInputPanelShellIntegration(); +} + +} + +QT_END_NAMESPACE + +#include "inputpanelshellplugin.moc" \ No newline at end of file diff --git a/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.cpp b/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.cpp new file mode 100644 index 00000000..98655edc --- /dev/null +++ b/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * and appearing in the file LICENSE.LGPL included in the packaging + * of this file. + */ + +#include "qwaylandinputpanelshellintegration.h" + +#include + +#include "qwaylandinputpanelsurface.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient +{ + +QWaylandInputPanelShellIntegration::QWaylandInputPanelShellIntegration() + : QWaylandShellIntegration() +{ +} + +QWaylandInputPanelShellIntegration::~QWaylandInputPanelShellIntegration() +{ +} + +bool QWaylandInputPanelShellIntegration::initialize(QWaylandDisplay *display) +{ + auto result = QWaylandShellIntegration::initialize(display); + const auto globals = display->globals(); + for (auto global: globals) { + if (global.interface == QLatin1String("zwp_input_panel_v1")) { + m_panel.reset(new QtWayland::zwp_input_panel_v1(display->wl_registry(), global.id, 1)); + break; + } + } + return result; +} + +QWaylandShellSurface * +QWaylandInputPanelShellIntegration::createShellSurface(QWaylandWindow *window) +{ + struct zwp_input_panel_surface_v1 *ip_surface = m_panel->get_input_panel_surface(window->object()); + + return new QWaylandInputPanelSurface(ip_surface, window); +} + +} + +QT_END_NAMESPACE \ No newline at end of file diff --git a/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.h b/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.h new file mode 100644 index 00000000..dccf81ae --- /dev/null +++ b/src/qt/plugins/shellintegration/qwaylandinputpanelshellintegration.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2017 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * and appearing in the file LICENSE.LGPL included in the packaging + * of this file. + */ + +#ifndef QWAYLANDINPUTPANELSHELLINTEGRATION_H +#define QWAYLANDINPUTPANELSHELLINTEGRATION_H + +#include + +#include "qwayland-input-method-unstable-v1.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient +{ + +class QWaylandInputPanelShellIntegration: public QWaylandShellIntegration +{ +public: + QWaylandInputPanelShellIntegration(); + ~QWaylandInputPanelShellIntegration() override; + + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; + +private: + QScopedPointer m_panel; +}; + +} + +QT_END_NAMESPACE + +#endif //QWAYLANDINPUTPANELSHELLINTEGRATION_H diff --git a/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.cpp b/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.cpp new file mode 100644 index 00000000..4f578879 --- /dev/null +++ b/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * and appearing in the file LICENSE.LGPL included in the packaging + * of this file. + */ + +#include "qwaylandinputpanelsurface.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(qLcQpaShellIntegration, "qt.qpa.wayland.shell") + +namespace QtWaylandClient +{ + +QWaylandInputPanelSurface::QWaylandInputPanelSurface(struct ::zwp_input_panel_surface_v1 *object, + QWaylandWindow *window) + : QWaylandShellSurface(window) + , QtWayland::zwp_input_panel_surface_v1(object) +{ + qCDebug(qLcQpaShellIntegration) << Q_FUNC_INFO; +} + +QWaylandInputPanelSurface::~QWaylandInputPanelSurface() +{ + qCDebug(qLcQpaShellIntegration) << Q_FUNC_INFO; +} + +void QWaylandInputPanelSurface::setType(Qt::WindowType type, QWaylandWindow *transientParent) +{ + Q_UNUSED(type) + Q_UNUSED(transientParent) + + set_toplevel(window()->screen()->output(), position_center_bottom); +} + +} + +QT_END_NAMESPACE diff --git a/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.h b/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.h new file mode 100644 index 00000000..07cc2370 --- /dev/null +++ b/src/qt/plugins/shellintegration/qwaylandinputpanelsurface.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Jan Arne Petersen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * and appearing in the file LICENSE.LGPL included in the packaging + * of this file. + */ + +#ifndef QWAYLANDINPUTPANELSURFACE_H +#define QWAYLANDINPUTPANELSURFACE_H + +#include "qwayland-input-method-unstable-v1.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(qLcQpaShellIntegration) + +namespace QtWaylandClient +{ + +class QWaylandInputPanelSurface : public QWaylandShellSurface, public QtWayland::zwp_input_panel_surface_v1 +{ +public: + QWaylandInputPanelSurface(struct ::zwp_input_panel_surface_v1 *object, QWaylandWindow *window); + ~QWaylandInputPanelSurface() override; + + void setType(Qt::WindowType type, QWaylandWindow *transientParent) override; +}; + +} + +QT_END_NAMESPACE + +#endif //QWAYLANDINPUTPANELSURFACE_H diff --git a/src/waylandplatform.cpp b/src/waylandplatform.cpp index b675267c..a8826c33 100644 --- a/src/waylandplatform.cpp +++ b/src/waylandplatform.cpp @@ -12,169 +12,23 @@ */ #include -#include "qwayland-input-method-unstable-v1.h" -#include -#include #include -#include #include -#include #include "waylandplatform.h" #include "windowdata.h" -namespace -{ -QtWayland::zwp_input_panel_surface_v1::position maliitToWestonPosition(Maliit::Position position) -{ - switch (position) { - case Maliit::PositionCenterBottom: - return QtWayland::zwp_input_panel_surface_v1::position_center_bottom; - default: - qWarning() << "Weston only supports center bottom position for top-level surfaces."; - - return QtWayland::zwp_input_panel_surface_v1::position_center_bottom; - } -} -} // unnamed namespace - namespace Maliit { class WaylandPlatformPrivate { public: - WaylandPlatformPrivate(); - ~WaylandPlatformPrivate(); - - void handleRegistryGlobal(uint32_t name, - const char *interface, - uint32_t version); - void handleRegistryGlobalRemove(uint32_t name); - void setupInputSurface(QWindow *window, - Maliit::Position position, - bool avoid_crash = false); - - struct wl_registry *m_registry; - QScopedPointer m_panel; - uint32_t m_panel_name; - QVector m_scheduled_windows; + WaylandPlatformPrivate() = default; + ~WaylandPlatformPrivate() = default; }; -namespace { - -void registryGlobal(void *data, - wl_registry *registry, - uint32_t name, - const char *interface, - uint32_t version) -{ - qDebug() << __PRETTY_FUNCTION__; - WaylandPlatformPrivate *d = static_cast(data); - - Q_UNUSED(registry); - d->handleRegistryGlobal(name, interface, version); -} - -void registryGlobalRemove(void *data, - wl_registry *registry, - uint32_t name) -{ - qDebug() << __PRETTY_FUNCTION__; - WaylandPlatformPrivate *d = static_cast(data); - - Q_UNUSED(registry); - d->handleRegistryGlobalRemove(name); -} - -const wl_registry_listener maliit_registry_listener = { - registryGlobal, - registryGlobalRemove -}; - -} // unnamed namespace - -WaylandPlatformPrivate::WaylandPlatformPrivate() - : m_registry(0), - m_panel(0), - m_panel_name(0), - m_scheduled_windows() -{ - wl_display *display = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("display")); - if (!display) { - qCritical() << __PRETTY_FUNCTION__ << "Failed to get a display."; - return; - } - m_registry = wl_display_get_registry(display); - wl_registry_add_listener(m_registry, &maliit_registry_listener, this); - // QtWayland will do dispatching for us. -} - -WaylandPlatformPrivate::~WaylandPlatformPrivate() -{ - if (m_registry) { - wl_registry_destroy (m_registry); - } -} - -void WaylandPlatformPrivate::handleRegistryGlobal(uint32_t name, - const char *interface, - uint32_t version) -{ - Q_UNUSED(version); - - qDebug() << __PRETTY_FUNCTION__ << "Name:" << name << "Interface:" << interface; - if (!strcmp(interface, "wl_input_panel")) { - m_panel.reset(new QtWayland::zwp_input_panel_v1(m_registry, name, 1)); - m_panel_name = name; - - Q_FOREACH (const WindowData& data, m_scheduled_windows) { - setupInputSurface(data.m_window, data.m_position, true); - } - m_scheduled_windows.clear(); - } -} - -void WaylandPlatformPrivate::handleRegistryGlobalRemove(uint32_t name) -{ - qDebug() << __PRETTY_FUNCTION__ << "Name:" << name; - if (m_panel and m_panel_name == name) { - m_panel.reset(); - } -} - -void WaylandPlatformPrivate::setupInputSurface(QWindow *window, - Maliit::Position position, - bool avoid_crash) -{ - struct wl_surface *surface = avoid_crash ? 0 : static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window)); - - struct wl_output *output = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForScreen("output", QGuiApplication::primaryScreen())); - - if (!surface) { - if (avoid_crash) { - qDebug() << "Creating surface to avoid crash in QtWayland"; - } else { - qDebug() << "No wl_surface, calling create."; - } - qDebug() << __PRETTY_FUNCTION__ << "WId:" << window->winId(); - window->create(); - } - - surface = static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow("surface", window)); - qDebug() << __PRETTY_FUNCTION__ << "WId:" << window->winId(); - if (!surface) { - qDebug() << __PRETTY_FUNCTION__ << "Still no surface, giving up."; - return; - } - - struct zwp_input_panel_surface_v1 *ip_surface = m_panel->get_input_panel_surface(surface); - QtWayland::zwp_input_panel_surface_v1::position weston_position = maliitToWestonPosition(position); - - zwp_input_panel_surface_v1_set_toplevel(ip_surface, output, weston_position); -} - WaylandPlatform::WaylandPlatform() : d_ptr(new WaylandPlatformPrivate) {} @@ -182,20 +36,8 @@ WaylandPlatform::WaylandPlatform() void WaylandPlatform::setupInputPanel(QWindow* window, Maliit::Position position) { - // we ignore non toplevel surfaces - if (not window or window->parent()) { - return; - } - - Q_D(WaylandPlatform); - - // If we have an input panel, we set up the surface now. Otherwise we - // schedule it for later. - if (d->m_panel) { - d->setupInputSurface(window, position); - } else { - d->m_scheduled_windows.append(WindowData(window, position)); - } + Q_UNUSED(window) + Q_UNUSED(position) } void WaylandPlatform::setInputRegion(QWindow* window, @@ -205,18 +47,7 @@ void WaylandPlatform::setInputRegion(QWindow* window, return; } - QPlatformNativeInterface *wliface = QGuiApplication::platformNativeInterface(); - wl_compositor *wlcompositor = static_cast(wliface->nativeResourceForIntegration("compositor")); - wl_region *wlregion = wl_compositor_create_region(wlcompositor); - - Q_FOREACH (const QRect &rect, region.rects()) { - wl_region_add(wlregion, rect.x(), rect.y(), - rect.width(), rect.height()); - } - - wl_surface *wlsurface = static_cast(wliface->nativeResourceForWindow("surface", window)); - wl_surface_set_input_region(wlsurface, wlregion); - wl_region_destroy(wlregion); + window->setMask(region); } WaylandPlatform::~WaylandPlatform()