Fix size restore upon keyboard close in XdgSurfaceClient
in XdgSurfaceClient setFrameGeometry is async, so we can't rely on it having the final value immediately. make setVirtualKeyboardGeometry a virtual. in the implementation on setVirtualKeyboardGeometry use requestedFrameGeometry() instead of frameGeometry()
This commit is contained in:
parent
f894976e2e
commit
97f4712f29
12 changed files with 382 additions and 9 deletions
|
@ -827,6 +827,10 @@ install(FILES scripting/kwinscript.desktop DESTINATION ${SERVICETYPES_INSTALL_DI
|
|||
add_subdirectory(qml)
|
||||
|
||||
if (BUILD_TESTING)
|
||||
find_package(WaylandProtocols REQUIRED)
|
||||
find_package(QtWaylandScanner ${QT_MIN_VERSION} REQUIRED)
|
||||
find_package(Wayland REQUIRED COMPONENTS Client)
|
||||
|
||||
add_subdirectory(autotests)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
|
|
@ -2571,6 +2571,16 @@ void AbstractClient::setVirtualKeyboardGeometry(const QRect &geo)
|
|||
setFrameGeometry(newWindowGeometry);
|
||||
}
|
||||
|
||||
QRect AbstractClient::keyboardGeometryRestore() const
|
||||
{
|
||||
return m_keyboardGeometryRestore;
|
||||
}
|
||||
|
||||
void AbstractClient::setKeyboardGeometryRestore(const QRect &geom)
|
||||
{
|
||||
m_keyboardGeometryRestore = geom;
|
||||
}
|
||||
|
||||
bool AbstractClient::dockWantsInput() const
|
||||
{
|
||||
return false;
|
||||
|
|
|
@ -757,7 +757,7 @@ public:
|
|||
* Sets the geometry of the virtual keyboard, The window may resize itself in order to make space for the keybaord
|
||||
* This geometry is in global coordinates
|
||||
*/
|
||||
void setVirtualKeyboardGeometry(const QRect &geo);
|
||||
virtual void setVirtualKeyboardGeometry(const QRect &geo);
|
||||
|
||||
/**
|
||||
* Restores the AbstractClient after it had been hidden due to show on screen edge functionality.
|
||||
|
@ -1210,6 +1210,11 @@ protected:
|
|||
void startShadeHoverTimer();
|
||||
void startShadeUnhoverTimer();
|
||||
|
||||
// The geometry that the client should be restored when the virtual keyboard closes
|
||||
QRect keyboardGeometryRestore() const;
|
||||
void setKeyboardGeometryRestore(const QRect &geom);
|
||||
|
||||
QRect m_virtualKeyboardGeometry;
|
||||
private Q_SLOTS:
|
||||
void shadeHover();
|
||||
void shadeUnhover();
|
||||
|
@ -1264,7 +1269,6 @@ private:
|
|||
QRect m_bufferGeometryBeforeUpdateBlocking;
|
||||
QRect m_frameGeometryBeforeUpdateBlocking;
|
||||
QRect m_clientGeometryBeforeUpdateBlocking;
|
||||
QRect m_virtualKeyboardGeometry;
|
||||
QRect m_keyboardGeometryRestore;
|
||||
QRect m_maximizeGeometryRestore;
|
||||
|
||||
|
|
|
@ -1,7 +1,20 @@
|
|||
add_subdirectory(helper)
|
||||
|
||||
add_library(KWinIntegrationTestFramework STATIC kwin_wayland_test.cpp test_helpers.cpp generic_scene_opengl_test.cpp ../../cursor.cpp ${kwin_XWAYLAND_SRCS})
|
||||
target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test)
|
||||
set(KWinIntegrationTestFramework_SOURCES
|
||||
../../cursor.cpp
|
||||
|
||||
generic_scene_opengl_test.cpp
|
||||
kwin_wayland_test.cpp
|
||||
test_helpers.cpp
|
||||
|
||||
${kwin_XWAYLAND_SRCS}
|
||||
)
|
||||
ecm_add_qtwayland_client_protocol(KWinIntegrationTestFramework_SOURCES
|
||||
PROTOCOL ${WaylandProtocols_DATADIR}/unstable/input-method/input-method-unstable-v1.xml
|
||||
BASENAME input-method-unstable-v1
|
||||
)
|
||||
add_library(KWinIntegrationTestFramework STATIC ${KWinIntegrationTestFramework_SOURCES})
|
||||
target_link_libraries(KWinIntegrationTestFramework kwin Qt5::Test Wayland::Client)
|
||||
|
||||
function(integrationTest)
|
||||
set(optionArgs WAYLAND_ONLY)
|
||||
|
@ -12,7 +25,7 @@ function(integrationTest)
|
|||
target_link_libraries(${ARGS_NAME} KWinIntegrationTestFramework kwin Qt5::Test ${ARGS_LIBS})
|
||||
add_test(NAME kwin-${ARGS_NAME} COMMAND dbus-run-session ${CMAKE_BINARY_DIR}/bin/${ARGS_NAME})
|
||||
if (${ARGS_WAYLAND_ONLY})
|
||||
add_executable(${ARGS_NAME}_waylandonly ${ARGS_SRCS})
|
||||
add_executable(${ARGS_NAME}_waylandonly ${ARGS_SRCS} )
|
||||
set_target_properties(${ARGS_NAME}_waylandonly PROPERTIES COMPILE_DEFINITIONS "NO_XWAYLAND")
|
||||
target_link_libraries(${ARGS_NAME}_waylandonly KWinIntegrationTestFramework kwin Qt5::Test ${ARGS_LIBS})
|
||||
add_test(NAME kwin-${ARGS_NAME}-waylandonly COMMAND dbus-run-session ${CMAKE_BINARY_DIR}/bin/${ARGS_NAME}_waylandonly)
|
||||
|
@ -60,6 +73,7 @@ integrationTest(WAYLAND_ONLY NAME testNoGlobalShortcuts SRCS no_global_shortcuts
|
|||
integrationTest(WAYLAND_ONLY NAME testBufferSizeChange SRCS buffer_size_change_test.cpp )
|
||||
integrationTest(WAYLAND_ONLY NAME testPlacement SRCS placement_test.cpp)
|
||||
integrationTest(WAYLAND_ONLY NAME testActivation SRCS activation_test.cpp)
|
||||
integrationTest(WAYLAND_ONLY NAME testVirtualKeyboard SRCS virtualkeyboard_test.cpp)
|
||||
|
||||
if (XCB_ICCCM_FOUND)
|
||||
integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "../../workspace.h"
|
||||
#include "../../xcbutils.h"
|
||||
#include "../../xwl/xwayland.h"
|
||||
#include "../../virtualkeyboard.h"
|
||||
|
||||
#include <KPluginMetaData>
|
||||
|
||||
|
@ -85,6 +86,38 @@ WaylandTestApplication::~WaylandTestApplication()
|
|||
|
||||
void WaylandTestApplication::performStartup()
|
||||
{
|
||||
if (!m_inputMethodServerToStart.isEmpty()) {
|
||||
VirtualKeyboard::create();
|
||||
if (m_inputMethodServerToStart != QStringLiteral("internal")) {
|
||||
int socket = dup(waylandServer()->createInputMethodConnection());
|
||||
if (socket >= 0) {
|
||||
QProcessEnvironment environment = processStartupEnvironment();
|
||||
environment.insert(QStringLiteral("WAYLAND_SOCKET"), QByteArray::number(socket));
|
||||
environment.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("wayland"));
|
||||
environment.remove("DISPLAY");
|
||||
environment.remove("WAYLAND_DISPLAY");
|
||||
QProcess *p = new Process(this);
|
||||
p->setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
||||
connect(p, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this,
|
||||
[p] {
|
||||
if (waylandServer()) {
|
||||
waylandServer()->destroyInputMethodConnection();
|
||||
}
|
||||
p->deleteLater();
|
||||
}
|
||||
);
|
||||
p->setProcessEnvironment(environment);
|
||||
p->setProgram(m_inputMethodServerToStart);
|
||||
// p->setArguments(arguments);
|
||||
p->start();
|
||||
connect(waylandServer(), &WaylandServer::terminatingInternalClientConnection, p, [p] {
|
||||
p->kill();
|
||||
p->waitForFinished();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// first load options - done internally by a different thread
|
||||
createOptions();
|
||||
waylandServer()->createInternalConnection();
|
||||
|
|
|
@ -37,9 +37,15 @@ class SubSurface;
|
|||
class Surface;
|
||||
class XdgDecorationManager;
|
||||
class OutputManagement;
|
||||
class TextInputManager;
|
||||
}
|
||||
}
|
||||
|
||||
namespace QtWayland
|
||||
{
|
||||
class zwp_input_panel_surface_v1;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
namespace Xwl
|
||||
|
@ -56,6 +62,9 @@ public:
|
|||
WaylandTestApplication(OperationMode mode, int &argc, char **argv);
|
||||
~WaylandTestApplication() override;
|
||||
|
||||
void setInputMethodServerToStart(const QString &inputMethodServer) {
|
||||
m_inputMethodServerToStart = inputMethodServer;
|
||||
}
|
||||
protected:
|
||||
void performStartup() override;
|
||||
|
||||
|
@ -66,11 +75,14 @@ private:
|
|||
void finalizeStartup();
|
||||
|
||||
Xwl::Xwayland *m_xwayland = nullptr;
|
||||
QString m_inputMethodServerToStart;
|
||||
};
|
||||
|
||||
namespace Test
|
||||
{
|
||||
|
||||
class MockInputMethod;
|
||||
|
||||
enum class AdditionalWaylandInterface {
|
||||
Seat = 1 << 0,
|
||||
Decoration = 1 << 1,
|
||||
|
@ -82,6 +94,8 @@ enum class AdditionalWaylandInterface {
|
|||
ShadowManager = 1 << 7,
|
||||
XdgDecoration = 1 << 8,
|
||||
OutputManagement = 1 << 9,
|
||||
TextInputManagerV2 = 1 << 10,
|
||||
InputMethodV1 = 1 << 11
|
||||
};
|
||||
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
|
||||
/**
|
||||
|
@ -114,6 +128,7 @@ KWayland::Client::IdleInhibitManager *waylandIdleInhibitManager();
|
|||
KWayland::Client::AppMenuManager *waylandAppMenuManager();
|
||||
KWayland::Client::XdgDecorationManager *xdgDecorationManager();
|
||||
KWayland::Client::OutputManagement *waylandOutputManagement();
|
||||
KWayland::Client::TextInputManager *waylandTextInputManager();
|
||||
|
||||
bool waitForWaylandPointer();
|
||||
bool waitForWaylandTouch();
|
||||
|
@ -133,6 +148,9 @@ enum class CreationSetup {
|
|||
CreateAndConfigure, /// commit and wait for the configure event, making this surface ready to commit buffers
|
||||
};
|
||||
|
||||
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(KWayland::Client::Surface *surface,
|
||||
KWayland::Client::Output *output);
|
||||
|
||||
KWayland::Client::XdgShellSurface *createXdgShellSurface(XdgShellSurfaceType type,
|
||||
KWayland::Client::Surface *surface,
|
||||
QObject *parent = nullptr,
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include "screenlockerwatcher.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "qwayland-input-method-unstable-v1.h"
|
||||
#include "virtualkeyboard.h"
|
||||
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/connection_thread.h>
|
||||
|
@ -28,6 +30,7 @@
|
|||
#include <KWayland/Client/subcompositor.h>
|
||||
#include <KWayland/Client/subsurface.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/textinput.h>
|
||||
#include <KWayland/Client/appmenu.h>
|
||||
#include <KWayland/Client/xdgshell.h>
|
||||
#include <KWayland/Client/xdgdecoration.h>
|
||||
|
@ -71,8 +74,60 @@ static struct {
|
|||
IdleInhibitManager *idleInhibit = nullptr;
|
||||
AppMenuManager *appMenu = nullptr;
|
||||
XdgDecorationManager *xdgDecoration = nullptr;
|
||||
TextInputManager *textInputManager = nullptr;
|
||||
QtWayland::zwp_input_panel_v1 *inputPanelV1 = nullptr;
|
||||
MockInputMethod *inputMethodV1 = nullptr;
|
||||
QtWayland::zwp_input_method_context_v1 *inputMethodContextV1 = nullptr;
|
||||
} s_waylandConnection;
|
||||
|
||||
class MockInputMethod : public QtWayland::zwp_input_method_v1
|
||||
{
|
||||
public:
|
||||
MockInputMethod(struct wl_registry *registry, int id, int version);
|
||||
~MockInputMethod();
|
||||
|
||||
protected:
|
||||
void zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *context) override;
|
||||
void zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context) override;
|
||||
|
||||
private:
|
||||
Surface *m_inputSurface = nullptr;
|
||||
QtWayland::zwp_input_panel_surface_v1 *m_inputMethodSurface = nullptr;
|
||||
AbstractClient *m_client = nullptr;
|
||||
};
|
||||
|
||||
MockInputMethod::MockInputMethod(struct wl_registry *registry, int id, int version)
|
||||
: QtWayland::zwp_input_method_v1(registry, id, version)
|
||||
{
|
||||
|
||||
}
|
||||
MockInputMethod::~MockInputMethod()
|
||||
{
|
||||
}
|
||||
|
||||
void MockInputMethod::zwp_input_method_v1_activate(struct ::zwp_input_method_context_v1 *context)
|
||||
{
|
||||
if (!m_inputSurface) {
|
||||
m_inputSurface = Test::createSurface();
|
||||
m_inputMethodSurface = Test::createInputPanelSurfaceV1(m_inputSurface, s_waylandConnection.outputs.first());
|
||||
}
|
||||
m_client = Test::renderAndWaitForShown(m_inputSurface, QSize(1280, 400), Qt::blue);
|
||||
}
|
||||
|
||||
void MockInputMethod::zwp_input_method_v1_deactivate(struct ::zwp_input_method_context_v1 *context)
|
||||
{
|
||||
zwp_input_method_context_v1_destroy(context);
|
||||
|
||||
if (m_inputSurface) {
|
||||
m_inputSurface->release();
|
||||
m_inputSurface->destroy();
|
||||
delete m_inputSurface;
|
||||
m_inputSurface = nullptr;
|
||||
delete m_inputMethodSurface;
|
||||
m_inputMethodSurface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
||||
{
|
||||
if (s_waylandConnection.connection) {
|
||||
|
@ -123,6 +178,16 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
|||
});
|
||||
});
|
||||
|
||||
QObject::connect(registry, &Registry::interfaceAnnounced, [=](const QByteArray &interface, quint32 name, quint32 version) {
|
||||
if (flags & AdditionalWaylandInterface::InputMethodV1) {
|
||||
if (interface == QByteArrayLiteral("zwp_input_method_v1")) {
|
||||
s_waylandConnection.inputMethodV1 = new MockInputMethod(*registry, name, version);
|
||||
} else if (interface == QByteArrayLiteral("zwp_input_panel_v1")) {
|
||||
s_waylandConnection.inputPanelV1 = new QtWayland::zwp_input_panel_v1(*registry, name, version);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
QSignalSpy allAnnounced(registry, &Registry::interfacesAnnounced);
|
||||
if (!allAnnounced.isValid()) {
|
||||
return false;
|
||||
|
@ -219,6 +284,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (flags.testFlag(AdditionalWaylandInterface::TextInputManagerV2)) {
|
||||
s_waylandConnection.textInputManager = registry->createTextInputManager(registry->interface(Registry::Interface::TextInputManagerUnstableV2).name, registry->interface(Registry::Interface::TextInputManagerUnstableV2).version);
|
||||
if (!s_waylandConnection.textInputManager->isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -257,6 +328,10 @@ void destroyWaylandConnection()
|
|||
s_waylandConnection.appMenu = nullptr;
|
||||
delete s_waylandConnection.xdgDecoration;
|
||||
s_waylandConnection.xdgDecoration = nullptr;
|
||||
delete s_waylandConnection.textInputManager;
|
||||
s_waylandConnection.textInputManager = nullptr;
|
||||
delete s_waylandConnection.inputPanelV1;
|
||||
s_waylandConnection.inputPanelV1 = nullptr;
|
||||
if (s_waylandConnection.thread) {
|
||||
QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed);
|
||||
s_waylandConnection.connection->deleteLater();
|
||||
|
@ -341,6 +416,11 @@ OutputManagement *waylandOutputManagement()
|
|||
return s_waylandConnection.outputManagement;
|
||||
}
|
||||
|
||||
TextInputManager *waylandTextInputManager()
|
||||
{
|
||||
return s_waylandConnection.textInputManager;
|
||||
}
|
||||
|
||||
|
||||
bool waitForWaylandPointer()
|
||||
{
|
||||
|
@ -467,6 +547,24 @@ XdgShellSurface *createXdgShellStableSurface(Surface *surface, QObject *parent,
|
|||
return s;
|
||||
}
|
||||
|
||||
QtWayland::zwp_input_panel_surface_v1 *createInputPanelSurfaceV1(Surface *surface, Output *output)
|
||||
{
|
||||
if (!s_waylandConnection.inputPanelV1) {
|
||||
qWarning() << "Unable to create the input panel surface. The interface input_panel global is not bound";
|
||||
return nullptr;
|
||||
}
|
||||
QtWayland::zwp_input_panel_surface_v1 *s = new QtWayland::zwp_input_panel_surface_v1(s_waylandConnection.inputPanelV1->get_input_panel_surface(*surface));
|
||||
|
||||
if (!s->isInitialized()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s->set_toplevel(output->output(), QtWayland::zwp_input_panel_surface_v1::position_center_bottom);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
XdgShellPopup *createXdgShellStablePopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent, CreationSetup creationSetup)
|
||||
{
|
||||
if (!s_waylandConnection.xdgShellStable) {
|
||||
|
|
154
autotests/integration/virtualkeyboard_test.cpp
Normal file
154
autotests/integration/virtualkeyboard_test.cpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2020 Marco Martin <mart@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
#include "kwin_wayland_test.h"
|
||||
#include "abstract_client.h"
|
||||
#include "cursor.h"
|
||||
#include "effects.h"
|
||||
#include "deleted.h"
|
||||
#include "platform.h"
|
||||
#include "screens.h"
|
||||
#include "wayland_server.h"
|
||||
#include "workspace.h"
|
||||
#include "virtualkeyboard.h"
|
||||
#include "virtualkeyboard_dbus.h"
|
||||
#include "qwayland-input-method-unstable-v1.h"
|
||||
|
||||
#include <QTest>
|
||||
#include <QSignalSpy>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusPendingReply>
|
||||
#include <KWaylandServer/clientconnection.h>
|
||||
#include <KWaylandServer/display.h>
|
||||
#include <KWaylandServer/surface_interface.h>
|
||||
|
||||
#include <KWayland/Client/compositor.h>
|
||||
#include <KWayland/Client/output.h>
|
||||
#include <KWayland/Client/surface.h>
|
||||
#include <KWayland/Client/xdgshell.h>
|
||||
#include <KWayland/Client/textinput.h>
|
||||
|
||||
using namespace KWin;
|
||||
using namespace KWayland::Client;
|
||||
using KWin::VirtualKeyboardDBus;
|
||||
|
||||
static const QString s_socketName = QStringLiteral("wayland_test_kwin_virtualkeyboard-0");
|
||||
|
||||
class VirtualKeyboardTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
void testOpenClose();
|
||||
};
|
||||
|
||||
|
||||
void VirtualKeyboardTest::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<KWin::Deleted *>();
|
||||
qRegisterMetaType<KWin::AbstractClient *>();
|
||||
qRegisterMetaType<KWayland::Client::Output *>();
|
||||
|
||||
QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
|
||||
QVERIFY(applicationStartedSpy.isValid());
|
||||
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
|
||||
QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
|
||||
QMetaObject::invokeMethod(kwinApp()->platform(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(int, 2));
|
||||
|
||||
static_cast<WaylandTestApplication *>(kwinApp())->setInputMethodServerToStart("internal");
|
||||
kwinApp()->start();
|
||||
QVERIFY(applicationStartedSpy.wait());
|
||||
QCOMPARE(screens()->count(), 2);
|
||||
QCOMPARE(screens()->geometry(0), QRect(0, 0, 1280, 1024));
|
||||
QCOMPARE(screens()->geometry(1), QRect(1280, 0, 1280, 1024));
|
||||
waylandServer()->initWorkspace();
|
||||
|
||||
}
|
||||
|
||||
void VirtualKeyboardTest::init()
|
||||
{
|
||||
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat |
|
||||
Test::AdditionalWaylandInterface::TextInputManagerV2 | Test::AdditionalWaylandInterface::InputMethodV1));
|
||||
|
||||
|
||||
screens()->setCurrent(0);
|
||||
KWin::Cursors::self()->mouse()->setPos(QPoint(1280, 512));
|
||||
|
||||
const QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kwin.testvirtualkeyboard"),
|
||||
QStringLiteral("/VirtualKeyboard"),
|
||||
QStringLiteral("org.kde.kwin.VirtualKeyboard"),
|
||||
"enable");
|
||||
QDBusConnection::sessionBus().call(message);
|
||||
}
|
||||
|
||||
void VirtualKeyboardTest::cleanup()
|
||||
{
|
||||
Test::destroyWaylandConnection();
|
||||
}
|
||||
|
||||
void VirtualKeyboardTest::testOpenClose()
|
||||
{
|
||||
QSignalSpy clientAddedSpy(workspace(), &Workspace::clientAdded);
|
||||
QSignalSpy clientRemovedSpy(workspace(), &Workspace::clientRemoved);
|
||||
QVERIFY(clientAddedSpy.isValid());
|
||||
|
||||
// Create an xdg_toplevel surface and wait for the compositor to catch up.
|
||||
QScopedPointer<Surface> surface(Test::createSurface());
|
||||
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
|
||||
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(1280, 1024), Qt::red);
|
||||
QVERIFY(client);
|
||||
QVERIFY(client->isActive());
|
||||
QCOMPARE(client->frameGeometry().size(), QSize(1280, 1024));
|
||||
QSignalSpy frameGeometryChangedSpy(client, &AbstractClient::frameGeometryChanged);
|
||||
QVERIFY(frameGeometryChangedSpy.isValid());
|
||||
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
|
||||
QVERIFY(configureRequestedSpy.isValid());
|
||||
|
||||
QScopedPointer<TextInput> textInput(Test::waylandTextInputManager()->createTextInput(Test::waylandSeat()));
|
||||
|
||||
QVERIFY(!textInput.isNull());
|
||||
textInput->enable(surface.data());
|
||||
QVERIFY(configureRequestedSpy.wait());
|
||||
|
||||
// Show the keyboard
|
||||
textInput->showInputPanel();
|
||||
QVERIFY(clientAddedSpy.wait());
|
||||
|
||||
AbstractClient *keyboardClient = clientAddedSpy.last().first().value<AbstractClient *>();
|
||||
QVERIFY(keyboardClient);
|
||||
QVERIFY(keyboardClient->isInputMethod());
|
||||
|
||||
// Do the actual resize
|
||||
QVERIFY(configureRequestedSpy.wait());
|
||||
|
||||
Test::render(surface.data(), configureRequestedSpy.last().first().value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(client->frameGeometry().height(), 1024 - keyboardClient->inputGeometry().height() + 1);
|
||||
|
||||
// Hide the keyboard
|
||||
textInput->hideInputPanel();
|
||||
|
||||
QVERIFY(configureRequestedSpy.wait());
|
||||
Test::render(surface.data(), configureRequestedSpy.last().first().value<QSize>(), Qt::red);
|
||||
QVERIFY(frameGeometryChangedSpy.wait());
|
||||
|
||||
QCOMPARE(client->frameGeometry().height(), 1024);
|
||||
|
||||
// Destroy the test client.
|
||||
shellSurface.reset();
|
||||
QVERIFY(Test::waitForWindowDestroyed(client));
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(VirtualKeyboardTest)
|
||||
|
||||
#include "virtualkeyboard_test.moc"
|
|
@ -123,6 +123,9 @@ NET::WindowType InputPanelV1Client::windowType(bool, int) const
|
|||
|
||||
QRect InputPanelV1Client::inputGeometry() const
|
||||
{
|
||||
if (surface()->inputIsInfinite()) {
|
||||
return frameGeometry();
|
||||
}
|
||||
return surface()->input().boundingRect().translated(pos());
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,9 @@ void VirtualKeyboard::init()
|
|||
return;
|
||||
}
|
||||
|
||||
m_trackedClient->setVirtualKeyboardGeometry(m_inputClient ? m_inputClient->inputGeometry() : QRect());
|
||||
if (m_inputClient && !m_inputClient->inputGeometry().isEmpty()) {
|
||||
m_trackedClient->setVirtualKeyboardGeometry(m_inputClient->inputGeometry());
|
||||
}
|
||||
};
|
||||
connect(surface->surface(), &SurfaceInterface::inputChanged, this, refreshFrame);
|
||||
connect(surface->surface(), &QObject::destroyed, this, [this] {
|
||||
|
@ -120,8 +122,7 @@ void VirtualKeyboard::init()
|
|||
m_trackedClient->setVirtualKeyboardGeometry({});
|
||||
}
|
||||
});
|
||||
updateInputPanelState();
|
||||
refreshFrame();
|
||||
connect(m_inputClient, &AbstractClient::frameGeometryChanged, this, refreshFrame);
|
||||
});
|
||||
|
||||
connect(waylandServer()->seat(), &SeatInterface::focusedTextInputChanged, this,
|
||||
|
@ -158,6 +159,7 @@ void VirtualKeyboard::init()
|
|||
});
|
||||
m_waylandEnabledConnection = connect(t, &TextInputInterface::enabledChanged, this, [t, this] {
|
||||
if (t->isEnabled()) {
|
||||
//FIXME This sendDeactivate shouldn't be necessary?
|
||||
waylandServer()->inputMethod()->sendDeactivate();
|
||||
waylandServer()->inputMethod()->sendActivate();
|
||||
adoptInputMethodContext();
|
||||
|
@ -177,7 +179,6 @@ void VirtualKeyboard::init()
|
|||
}
|
||||
m_trackedClient = newClient;
|
||||
}
|
||||
|
||||
updateInputPanelState();
|
||||
} else {
|
||||
m_waylandShowConnection = QMetaObject::Connection();
|
||||
|
@ -198,6 +199,7 @@ void VirtualKeyboard::show()
|
|||
{
|
||||
auto t = waylandServer()->seat()->focusedTextInput();
|
||||
if (t) {
|
||||
//FIXME: this shouldn't be necessary and causes double emits?
|
||||
Q_EMIT t->enabledChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -518,6 +518,38 @@ void XdgSurfaceClient::destroyClient()
|
|||
delete this;
|
||||
}
|
||||
|
||||
void XdgSurfaceClient::setVirtualKeyboardGeometry(const QRect &geo)
|
||||
{
|
||||
// No keyboard anymore
|
||||
if (geo.isEmpty() && !keyboardGeometryRestore().isEmpty()) {
|
||||
setFrameGeometry(keyboardGeometryRestore());
|
||||
setKeyboardGeometryRestore(QRect());
|
||||
} else if (geo.isEmpty()) {
|
||||
return;
|
||||
// The keyboard has just been opened (rather than resized) save client geometry for a restore
|
||||
} else if (keyboardGeometryRestore().isEmpty()) {
|
||||
setKeyboardGeometryRestore(requestedFrameGeometry().isEmpty() ? frameGeometry() : requestedFrameGeometry());
|
||||
}
|
||||
|
||||
m_virtualKeyboardGeometry = geo;
|
||||
|
||||
// Don't resize Desktop and fullscreen windows
|
||||
if (isFullScreen() || isDesktop()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!geo.intersects(keyboardGeometryRestore())) {
|
||||
return;
|
||||
}
|
||||
|
||||
const QRect availableArea = workspace()->clientArea(MaximizeArea, this);
|
||||
QRect newWindowGeometry = keyboardGeometryRestore();
|
||||
newWindowGeometry.moveBottom(geo.top());
|
||||
newWindowGeometry.setTop(qMax(newWindowGeometry.top(), availableArea.top()));
|
||||
|
||||
setFrameGeometry(newWindowGeometry);
|
||||
}
|
||||
|
||||
void XdgSurfaceClient::cleanGrouping()
|
||||
{
|
||||
if (transientFor()) {
|
||||
|
|
|
@ -61,6 +61,7 @@ public:
|
|||
void move(int x, int y, ForceGeometry_t force = NormalGeometrySet) override;
|
||||
bool isInitialPositionSet() const override;
|
||||
void destroyClient() override;
|
||||
void setVirtualKeyboardGeometry(const QRect &geo) override;
|
||||
|
||||
QRect frameRectToBufferRect(const QRect &rect) const;
|
||||
QRect requestedFrameGeometry() const;
|
||||
|
|
Loading…
Reference in a new issue