Merge remote-tracking branch 'origin/5.13' into dev

Conflicts:
	src/client/qwaylanddisplay_p.h
	src/client/qwaylandwindow.cpp

Change-Id: I50eb5c83a8b81e4bdb032b68d41f429b17d0a74d
This commit is contained in:
Johan Klokkhammer Helsing 2019-05-07 03:01:52 +02:00
commit 8607ca5ca2
24 changed files with 856 additions and 773 deletions

View File

@ -178,7 +178,7 @@ Copyright © 2015, 2016 Jan Arne Petersen"
"Name": "Wayland EGLStream Controller Protocol",
"QDocModule": "qtwaylandcompositor",
"QtUsage": "Used in the Qt Wayland Compositor",
"Files": "wayland-eglstream-controller.xml",
"Files": "wl-eglstream-controller.xml",
"Description": "Allows clients to request that the compositor creates its EGLStream.",
"Homepage": "https://github.com/NVIDIA/egl-wayland",

View File

@ -15,8 +15,9 @@ use_gold_linker: CONFIG += no_linker_version_script
CONFIG -= precompile_header
CONFIG += link_pkgconfig wayland-scanner
qtConfig(xkbcommon): \
QMAKE_USE_PRIVATE += xkbcommon
qtConfig(xkbcommon) {
QT_FOR_PRIVATE += xkbcommon_support-private
}
qtHaveModule(linuxaccessibility_support_private): \
QT += linuxaccessibility_support_private
@ -49,7 +50,6 @@ SOURCES += qwaylandintegration.cpp \
qwaylandtouch.cpp \
qwaylandqtkey.cpp \
../shared/qwaylandmimehelper.cpp \
../shared/qwaylandxkb.cpp \
../shared/qwaylandinputmethodeventbuilder.cpp \
qwaylandabstractdecoration.cpp \
qwaylanddecorationfactory.cpp \
@ -84,7 +84,6 @@ HEADERS += qwaylandintegration_p.h \
qtwaylandclientglobal_p.h \
../shared/qwaylandinputmethodeventbuilder_p.h \
../shared/qwaylandmimehelper_p.h \
../shared/qwaylandxkb_p.h \
../shared/qwaylandsharedmemoryformathelper_p.h \
qtConfig(clipboard) {

View File

@ -109,21 +109,21 @@ QWaylandBradientDecoration::QWaylandBradientDecoration()
QRectF QWaylandBradientDecoration::closeButtonRect() const
{
const int windowRight = waylandWindow()->windowGeometry().right() + 1;
const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
return QRectF(windowRight - BUTTON_WIDTH - BUTTON_SPACING * 0 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::maximizeButtonRect() const
{
const int windowRight = waylandWindow()->windowGeometry().right() + 1;
const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
return QRectF(windowRight - BUTTON_WIDTH * 2 - BUTTON_SPACING * 1 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
QRectF QWaylandBradientDecoration::minimizeButtonRect() const
{
const int windowRight = waylandWindow()->windowGeometry().right() + 1;
const int windowRight = waylandWindow()->windowContentGeometry().right() + 1;
return QRectF(windowRight - BUTTON_WIDTH * 3 - BUTTON_SPACING * 2 - BUTTONS_RIGHT_MARGIN,
(margins().top() - BUTTON_WIDTH) / 2, BUTTON_WIDTH, BUTTON_WIDTH);
}
@ -136,7 +136,7 @@ QMargins QWaylandBradientDecoration::margins() const
void QWaylandBradientDecoration::paint(QPaintDevice *device)
{
bool active = window()->handle()->isActive();
QRect wg = waylandWindow()->windowGeometry();
QRect wg = waylandWindow()->windowContentGeometry();
QRect clips[] =
{
QRect(wg.left(), wg.top(), wg.width(), margins().top()),
@ -267,7 +267,7 @@ bool QWaylandBradientDecoration::handleMouse(QWaylandInputDevice *inputDevice, c
Q_UNUSED(global);
// Figure out what area mouse is in
QRect wg = waylandWindow()->windowGeometry();
QRect wg = waylandWindow()->windowContentGeometry();
if (local.y() <= wg.top() + margins().top()) {
processMouseTop(inputDevice,local,b,mods);
} else if (local.y() > wg.bottom() - margins().bottom()) {
@ -312,7 +312,7 @@ bool QWaylandBradientDecoration::handleTouch(QWaylandInputDevice *inputDevice, c
void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods)
{
QRect wg = waylandWindow()->windowGeometry();
QRect wg = waylandWindow()->windowContentGeometry();
Q_UNUSED(mods);
if (local.y() <= wg.top() + margins().bottom()) {
if (local.x() <= margins().left()) {

View File

@ -70,6 +70,8 @@
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/QAbstractEventDispatcher>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
@ -91,13 +93,6 @@ struct wl_surface *QWaylandDisplay::createSurface(void *handle)
return surface;
}
QWaylandShellSurface *QWaylandDisplay::createShellSurface(QWaylandWindow *window)
{
if (!mWaylandIntegration->shellIntegration())
return nullptr;
return mWaylandIntegration->shellIntegration()->createShellSurface(window);
}
struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
{
struct ::wl_region *region = mCompositor.create_region();
@ -111,12 +106,18 @@ struct ::wl_region *QWaylandDisplay::createRegion(const QRegion &qregion)
::wl_subsurface *QWaylandDisplay::createSubSurface(QWaylandWindow *window, QWaylandWindow *parent)
{
if (!mSubCompositor) {
qCWarning(lcQpaWayland) << "Can't create subsurface, not supported by the compositor.";
return nullptr;
}
return mSubCompositor->get_subsurface(window->wlSurface(), parent->wlSurface());
}
QWaylandShellIntegration *QWaylandDisplay::shellIntegration() const
{
return mWaylandIntegration->shellIntegration();
}
QWaylandClientBufferIntegration * QWaylandDisplay::clientBufferIntegration() const
{
return mWaylandIntegration->clientBufferIntegration();
@ -143,7 +144,18 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration)
mWindowManagerIntegration.reset(new QWaylandWindowManagerIntegration(this));
#if QT_CONFIG(xkbcommon)
mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
if (!mXkbContext)
qCWarning(lcQpaWayland, "failed to create xkb context");
#endif
forceRoundTrip();
if (!mWaitingScreens.isEmpty()) {
// Give wl_output.done and zxdg_output_v1.done events a chance to arrive
forceRoundTrip();
}
}
QWaylandDisplay::~QWaylandDisplay(void)
@ -158,6 +170,7 @@ QWaylandDisplay::~QWaylandDisplay(void)
QWindowSystemInterface::handleScreenRemoved(screen);
}
mScreens.clear();
qDeleteAll(mWaitingScreens);
#if QT_CONFIG(wayland_datadevice)
delete mDndSelectionHandler.take();
@ -194,7 +207,6 @@ void QWaylandDisplay::flushRequests()
wl_display_flush(mDisplay);
}
void QWaylandDisplay::blockingReadEvents()
{
if (wl_display_dispatch(mDisplay) < 0) {
@ -208,6 +220,41 @@ void QWaylandDisplay::exitWithError()
::exit(1);
}
wl_event_queue *QWaylandDisplay::createEventQueue()
{
return wl_display_create_queue(mDisplay);
}
void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
{
if (!condition())
return;
QElapsedTimer timer;
timer.start();
struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
while (timeout == -1 || timer.elapsed() < timeout) {
while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
wl_display_dispatch_queue_pending(mDisplay, queue);
wl_display_flush(mDisplay);
const int remaining = qMax(timeout - timer.elapsed(), 0ll);
const int pollTimeout = timeout == -1 ? -1 : remaining;
if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
wl_display_read_events(mDisplay);
else
wl_display_cancel_read(mDisplay);
if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) {
checkError();
exitWithError();
}
if (!condition())
break;
}
}
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
for (int i = 0; i < mScreens.size(); ++i) {
@ -218,6 +265,14 @@ QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
return nullptr;
}
void QWaylandDisplay::handleScreenInitialized(QWaylandScreen *screen)
{
if (!mWaitingScreens.removeOne(screen))
return;
mScreens.append(screen);
QWindowSystemInterface::handleScreenAdded(screen);
}
void QWaylandDisplay::waitForScreens()
{
flushRequests();
@ -242,11 +297,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
struct ::wl_registry *registry = object();
if (interface == QStringLiteral("wl_output")) {
QWaylandScreen *screen = new QWaylandScreen(this, version, id);
mScreens.append(screen);
// We need to get the output events before creating surfaces
forceRoundTrip();
QWindowSystemInterface::handleScreenAdded(screen);
mWaitingScreens << new QWaylandScreen(this, version, id);
} else if (interface == QStringLiteral("wl_compositor")) {
mCompositorVersion = qMin((int)version, 3);
mCompositor.init(registry, id, mCompositorVersion);
@ -267,11 +318,11 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
mTouchExtension.reset(new QWaylandTouchExtension(this, id));
} else if (interface == QStringLiteral("zqt_key_v1")) {
mQtKeyExtension.reset(new QWaylandQtKeyExtension(this, id));
} else if (interface == QStringLiteral("zwp_text_input_manager_v2")) {
} else if (interface == QStringLiteral("zwp_text_input_manager_v2") && !mClientSideInputContextRequested) {
mTextInputManager.reset(new QtWayland::zwp_text_input_manager_v2(registry, id, 1));
foreach (QWaylandInputDevice *inputDevice, mInputDevices) {
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat())));
}
mWaylandIntegration->reconfigureInputContext();
} else if (interface == QStringLiteral("qt_hardware_integration")) {
bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION");
if (!disableHardwareIntegration) {
@ -282,7 +333,7 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin
}
} else if (interface == QLatin1String("zxdg_output_manager_v1")) {
mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, qMin(2, int(version))));
for (auto *screen : qAsConst(mScreens))
for (auto *screen : qAsConst(mWaitingScreens))
screen->initXdgOutput(xdgOutputManager());
forceRoundTrip();
}
@ -299,6 +350,14 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
RegistryGlobal &global = mGlobals[i];
if (global.id == id) {
if (global.interface == QStringLiteral("wl_output")) {
for (auto *screen : mWaitingScreens) {
if (screen->outputId() == id) {
mWaitingScreens.removeOne(screen);
delete screen;
break;
}
}
foreach (QWaylandScreen *screen, mScreens) {
if (screen->outputId() == id) {
mScreens.removeOne(screen);
@ -307,6 +366,12 @@ void QWaylandDisplay::registry_global_remove(uint32_t id)
}
}
}
if (global.interface == QStringLiteral("zwp_text_input_manager_v2")) {
mTextInputManager.reset();
for (QWaylandInputDevice *inputDevice : qAsConst(mInputDevices))
inputDevice->setTextInput(nullptr);
mWaylandIntegration->reconfigureInputContext();
}
mGlobals.removeAt(i);
break;
}

View File

@ -63,6 +63,12 @@
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
#include <QtWaylandClient/private/qwaylandshm_p.h>
#include <qpa/qplatforminputcontextfactory_p.h>
#if QT_CONFIG(xkbcommon)
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif
struct wl_cursor_image;
QT_BEGIN_NAMESPACE
@ -92,8 +98,8 @@ class QWaylandQtKeyExtension;
class QWaylandWindow;
class QWaylandIntegration;
class QWaylandHardwareIntegration;
class QWaylandShellSurface;
class QWaylandSurface;
class QWaylandShellIntegration;
class QWaylandCursor;
class QWaylandCursorTheme;
@ -110,18 +116,23 @@ public:
QWaylandDisplay(QWaylandIntegration *waylandIntegration);
~QWaylandDisplay(void) override;
#if QT_CONFIG(xkbcommon)
struct xkb_context *xkbContext() const { return mXkbContext.get(); }
#endif
QList<QWaylandScreen *> screens() const { return mScreens; }
QWaylandScreen *screenForOutput(struct wl_output *output) const;
void handleScreenInitialized(QWaylandScreen *screen);
struct wl_surface *createSurface(void *handle);
QWaylandShellSurface *createShellSurface(QWaylandWindow *window);
struct ::wl_region *createRegion(const QRegion &qregion);
struct ::wl_subsurface *createSubSurface(QWaylandWindow *window, QWaylandWindow *parent);
QWaylandShellIntegration *shellIntegration() const;
QWaylandClientBufferIntegration *clientBufferIntegration() const;
QWaylandWindowManagerIntegration *windowManagerIntegration() const;
#if QT_CONFIG(cursor)
QWaylandCursor *waylandCursor();
QWaylandCursorTheme *loadCursorTheme(const QString &name, int pixelSize);
@ -145,6 +156,7 @@ public:
QWaylandHardwareIntegration *hardwareIntegration() const { return mHardwareIntegration.data(); }
QtWayland::zxdg_output_manager_v1 *xdgOutputManager() const { return mXdgOutputManager.data(); }
bool usingInputContextFromCompositor() const { return mUsingInputContextFromCompositor; }
struct RegistryGlobal {
uint32_t id;
@ -181,6 +193,9 @@ public:
void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice);
void handleWindowDestroyed(QWaylandWindow *window);
wl_event_queue *createEventQueue();
void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1);
public slots:
void blockingReadEvents();
void flushRequests();
@ -206,6 +221,7 @@ private:
struct wl_display *mDisplay = nullptr;
QtWayland::wl_compositor mCompositor;
QScopedPointer<QWaylandShm> mShm;
QList<QWaylandScreen *> mWaitingScreens;
QList<QWaylandScreen *> mScreens;
QList<QWaylandInputDevice *> mInputDevices;
QList<Listener> mRegistryListeners;
@ -238,8 +254,17 @@ private:
struct wl_callback *mSyncCallback = nullptr;
static const wl_callback_listener syncCallbackListener;
bool mClientSideInputContextRequested = !QPlatformInputContextFactory::requested().isNull();
bool mUsingInputContextFromCompositor = false;
void registry_global(uint32_t id, const QString &interface, uint32_t version) override;
void registry_global_remove(uint32_t id) override;
#if QT_CONFIG(xkbcommon)
QXkbCommon::ScopedXKBContext mXkbContext;
#endif
friend class QWaylandIntegration;
};
}

View File

@ -50,7 +50,6 @@
#include "qwaylandinputdevice_p.h"
#include "qwaylandinputmethodeventbuilder_p.h"
#include "qwaylandwindow_p.h"
#include "qwaylandxkb_p.h"
QT_BEGIN_NAMESPACE
@ -315,6 +314,7 @@ void QWaylandTextInput::zwp_text_input_v2_delete_surrounding_text(uint32_t befor
void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, uint32_t state, uint32_t modifiers)
{
#if QT_CONFIG(xkbcommon)
if (m_resetCallback) {
qCDebug(qLcQpaInputMethods()) << "discard keysym: reset not confirmed";
return;
@ -325,13 +325,18 @@ void QWaylandTextInput::zwp_text_input_v2_keysym(uint32_t time, uint32_t sym, ui
Qt::KeyboardModifiers qtModifiers = modifiersToQtModifiers(modifiers);
QEvent::Type type = QWaylandXkb::toQtEventType(state);
QString text;
int qtkey;
std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, qtModifiers);
QEvent::Type type = state == WL_KEYBOARD_KEY_STATE_PRESSED ? QEvent::KeyPress : QEvent::KeyRelease;
QString text = QXkbCommon::lookupStringNoKeysymTransformations(sym);
int qtkey = QXkbCommon::keysymToQtKey(sym, qtModifiers);
QWindowSystemInterface::handleKeyEvent(QGuiApplication::focusWindow(),
time, type, qtkey, qtModifiers, text);
#else
Q_UNUSED(time);
Q_UNUSED(sym);
Q_UNUSED(state);
Q_UNUSED(modifiers);
#endif
}
void QWaylandTextInput::zwp_text_input_v2_language(const QString &language)

View File

@ -52,7 +52,6 @@
#include "qwaylandcursor_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "../shared/qwaylandxkb_p.h"
#include "qwaylandinputcontext_p.h"
#include <QtGui/private/qpixmap_raster_p.h>
@ -71,10 +70,6 @@
#include <QtGui/QGuiApplication>
#if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon-compose.h>
#endif
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -84,85 +79,51 @@ Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
connect(&mRepeatTimer, SIGNAL(timeout()), this, SLOT(repeatKey()));
mRepeatTimer.callOnTimeout([&]() {
if (!focusWindow()) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first.
return;
}
mRepeatTimer.setInterval(mRepeatRate);
handleKey(mRepeatKey.time, QEvent::KeyRelease, mRepeatKey.key, mRepeatKey.modifiers,
mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers,
mRepeatKey.text, true);
handleKey(mRepeatKey.time, QEvent::KeyPress, mRepeatKey.key, mRepeatKey.modifiers,
mRepeatKey.code, mRepeatKey.nativeVirtualKey, mRepeatKey.nativeModifiers,
mRepeatKey.text, true);
});
}
#if QT_CONFIG(xkbcommon)
bool QWaylandInputDevice::Keyboard::createDefaultKeyMap()
bool QWaylandInputDevice::Keyboard::createDefaultKeymap()
{
if (mXkbContext && mXkbMap && mXkbState) {
return true;
}
struct xkb_context *ctx = mParent->mQDisplay->xkbContext();
if (!ctx)
return false;
xkb_rule_names names;
names.rules = strdup("evdev");
names.model = strdup("pc105");
names.layout = strdup("us");
names.variant = strdup("");
names.options = strdup("");
struct xkb_rule_names names;
names.rules = "evdev";
names.model = "pc105";
names.layout = "us";
names.variant = "";
names.options = "";
mXkbContext = xkb_context_new(xkb_context_flags(0));
if (mXkbContext) {
mXkbMap = xkb_map_new_from_names(mXkbContext, &names, xkb_map_compile_flags(0));
if (mXkbMap) {
mXkbState = xkb_state_new(mXkbMap);
}
}
mXkbKeymap.reset(xkb_keymap_new_from_names(ctx, &names, XKB_KEYMAP_COMPILE_NO_FLAGS));
if (mXkbKeymap)
mXkbState.reset(xkb_state_new(mXkbKeymap.get()));
if (!mXkbContext || !mXkbMap || !mXkbState) {
qWarning() << "xkb_map_new_from_names failed, no key input";
if (!mXkbKeymap || !mXkbState) {
qCWarning(lcQpaWayland, "failed to create default keymap");
return false;
}
createComposeState();
return true;
}
void QWaylandInputDevice::Keyboard::releaseKeyMap()
{
if (mXkbState)
xkb_state_unref(mXkbState);
if (mXkbMap)
xkb_map_unref(mXkbMap);
if (mXkbContext)
xkb_context_unref(mXkbContext);
}
void QWaylandInputDevice::Keyboard::createComposeState()
{
static const char *locale = nullptr;
if (!locale) {
locale = getenv("LC_ALL");
if (!locale)
locale = getenv("LC_CTYPE");
if (!locale)
locale = getenv("LANG");
if (!locale)
locale = "C";
}
mXkbComposeTable = xkb_compose_table_new_from_locale(mXkbContext, locale, XKB_COMPOSE_COMPILE_NO_FLAGS);
if (mXkbComposeTable)
mXkbComposeState = xkb_compose_state_new(mXkbComposeTable, XKB_COMPOSE_STATE_NO_FLAGS);
}
void QWaylandInputDevice::Keyboard::releaseComposeState()
{
if (mXkbComposeState)
xkb_compose_state_unref(mXkbComposeState);
if (mXkbComposeTable)
xkb_compose_table_unref(mXkbComposeTable);
mXkbComposeState = nullptr;
mXkbComposeTable = nullptr;
}
#endif
QWaylandInputDevice::Keyboard::~Keyboard()
{
#if QT_CONFIG(xkbcommon)
releaseComposeState();
releaseKeyMap();
#endif
if (mFocus)
QWindowSystemInterface::handleWindowActivated(nullptr);
if (mParent->mVersion >= 3)
@ -402,9 +363,9 @@ QWaylandInputDevice::QWaylandInputDevice(QWaylandDisplay *display, int version,
}
#endif
if (mQDisplay->textInputManager()) {
mTextInput = new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat()));
}
if (mQDisplay->textInputManager())
mTextInput.reset(new QWaylandTextInput(mQDisplay, mQDisplay->textInputManager()->get_text_input(wl_seat())));
}
QWaylandInputDevice::~QWaylandInputDevice()
@ -487,12 +448,12 @@ QWaylandDataDevice *QWaylandInputDevice::dataDevice() const
void QWaylandInputDevice::setTextInput(QWaylandTextInput *textInput)
{
mTextInput = textInput;
mTextInput.reset(textInput);
}
QWaylandTextInput *QWaylandInputDevice::textInput() const
{
return mTextInput;
return mTextInput.data();
}
void QWaylandInputDevice::removeMouseButtonFromState(Qt::MouseButton button)
@ -521,6 +482,17 @@ QPointF QWaylandInputDevice::pointerSurfacePosition() const
return mPointer ? mPointer->mSurfacePos : QPointF();
}
QList<int> QWaylandInputDevice::possibleKeys(const QKeyEvent *event) const
{
#if QT_CONFIG(xkbcommon)
if (mKeyboard && mKeyboard->mXkbState)
return QXkbCommon::possibleKeys(mKeyboard->mXkbState.get(), event);
#else
Q_UNUSED(event);
#endif
return {};
}
Qt::KeyboardModifiers QWaylandInputDevice::modifiers() const
{
if (!mKeyboard)
@ -537,7 +509,7 @@ Qt::KeyboardModifiers QWaylandInputDevice::Keyboard::modifiers() const
if (!mXkbState)
return ret;
ret = QWaylandXkb::modifiers(mXkbState);
ret = QXkbCommon::modifiers(mXkbState.get());
#endif
return ret;
@ -1045,8 +1017,10 @@ bool QWaylandInputDevice::Pointer::isDefinitelyTerminated(QtWayland::wl_pointer:
void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd, uint32_t size)
{
mKeymapFormat = format;
#if QT_CONFIG(xkbcommon)
if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
qCWarning(lcQpaWayland) << "unknown keymap format:" << format;
close(fd);
return;
}
@ -1057,21 +1031,19 @@ void QWaylandInputDevice::Keyboard::keyboard_keymap(uint32_t format, int32_t fd,
return;
}
// Release the old keymap resources in the case they were already created in
// the key event or when the compositor issues a new map
releaseComposeState();
releaseKeyMap();
mXkbKeymap.reset(xkb_keymap_new_from_string(mParent->mQDisplay->xkbContext(), map_str,
XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS));
QXkbCommon::verifyHasLatinLayout(mXkbKeymap.get());
mXkbContext = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
mXkbMap = xkb_map_new_from_string(mXkbContext, map_str, XKB_KEYMAP_FORMAT_TEXT_V1, XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_str, size);
close(fd);
mXkbState = xkb_state_new(mXkbMap);
createComposeState();
if (mXkbKeymap)
mXkbState.reset(xkb_state_new(mXkbKeymap.get()));
else
mXkbState.reset(nullptr);
#else
Q_UNUSED(format);
Q_UNUSED(fd);
Q_UNUSED(size);
#endif
@ -1118,28 +1090,34 @@ void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surf
handleFocusLost();
}
static void sendKey(QWindow *tlw, ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
const QString& text = QString(), bool autorep = false, ushort count = 1)
void QWaylandInputDevice::Keyboard::handleKey(ulong timestamp, QEvent::Type type, int key,
Qt::KeyboardModifiers modifiers, quint32 nativeScanCode,
quint32 nativeVirtualKey, quint32 nativeModifiers,
const QString &text, bool autorepeat, ushort count)
{
QPlatformInputContext *inputContext = QGuiApplicationPrivate::platformIntegration()->inputContext();
bool filtered = false;
if (inputContext) {
QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
text, autorep, count);
if (inputContext && !mParent->mQDisplay->usingInputContextFromCompositor()) {
QKeyEvent event(type, key, modifiers, nativeScanCode, nativeVirtualKey,
nativeModifiers, text, autorepeat, count);
event.setTimestamp(timestamp);
filtered = inputContext->filterEvent(&event);
}
if (!filtered) {
QWindowSystemInterface::handleExtendedKeyEvent(tlw, timestamp, type, key, modifiers,
nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
QWindowSystemInterface::handleExtendedKeyEvent(focusWindow()->window(), timestamp, type, key, modifiers,
nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count);
}
}
void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state)
{
if (mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 && mKeymapFormat != WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
qCWarning(lcQpaWayland) << Q_FUNC_INFO << "unknown keymap format:" << mKeymapFormat;
return;
}
auto *window = focusWindow();
if (!window) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
@ -1147,102 +1125,52 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time,
return;
}
uint32_t code = key + 8;
bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
QString text;
int qtkey = key + 8; // qt-compositor substracts 8 for some reason
mParent->mSerial = serial;
const bool isDown = state != WL_KEYBOARD_KEY_STATE_RELEASED;
if (isDown)
mParent->mQDisplay->setLastInputDevice(mParent, serial, window);
if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) {
#if QT_CONFIG(xkbcommon)
if (!createDefaultKeyMap()) {
return;
}
if ((!mXkbKeymap || !mXkbState) && !createDefaultKeymap())
return;
QString composedText;
xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState, code);
if (mXkbComposeState) {
if (isDown)
xkb_compose_state_feed(mXkbComposeState, sym);
xkb_compose_status status = xkb_compose_state_get_status(mXkbComposeState);
auto code = key + 8; // map to wl_keyboard::keymap_format::keymap_format_xkb_v1
switch (status) {
case XKB_COMPOSE_COMPOSED: {
int size = xkb_compose_state_get_utf8(mXkbComposeState, nullptr, 0);
QVarLengthArray<char, 32> buffer(size + 1);
xkb_compose_state_get_utf8(mXkbComposeState, buffer.data(), buffer.size());
composedText = QString::fromUtf8(buffer.constData());
sym = xkb_compose_state_get_one_sym(mXkbComposeState);
xkb_compose_state_reset(mXkbComposeState);
} break;
case XKB_COMPOSE_COMPOSING:
case XKB_COMPOSE_CANCELLED:
return;
case XKB_COMPOSE_NOTHING:
break;
xkb_keysym_t sym = xkb_state_key_get_one_sym(mXkbState.get(), code);
Qt::KeyboardModifiers modifiers = mParent->modifiers();
int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, mXkbState.get(), code);
QString text = QXkbCommon::lookupString(mXkbState.get(), code);
QEvent::Type type = isDown ? QEvent::KeyPress : QEvent::KeyRelease;
handleKey(time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED && xkb_keymap_key_repeats(mXkbKeymap.get(), code)) {
mRepeatKey.key = qtkey;
mRepeatKey.code = code;
mRepeatKey.time = time;
mRepeatKey.text = text;
mRepeatKey.modifiers = modifiers;
mRepeatKey.nativeModifiers = mNativeModifiers;
mRepeatKey.nativeVirtualKey = sym;
mRepeatTimer.setInterval(mRepeatDelay);
mRepeatTimer.start();
} else if (mRepeatKey.code == code) {
mRepeatTimer.stop();
}
}
Qt::KeyboardModifiers modifiers = mParent->modifiers();
std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers);
if (!composedText.isNull())
text = composedText;
sendKey(window->window(), time, type, qtkey, modifiers, code, sym, mNativeModifiers, text);
#else
// Generic fallback for single hard keys: Assume 'key' is a Qt key code.
sendKey(window->window(), time, type, qtkey, Qt::NoModifier, code, 0, 0);
Q_UNUSED(time);
Q_UNUSED(key);
qCWarning(lcQpaWayland, "xkbcommon not available on this build, not performing key mapping");
return;
#endif
if (state == WL_KEYBOARD_KEY_STATE_PRESSED
#if QT_CONFIG(xkbcommon)
&& xkb_keymap_key_repeats(mXkbMap, code)
#endif
) {
mRepeatKey = qtkey;
mRepeatCode = code;
mRepeatTime = time;
mRepeatText = text;
#if QT_CONFIG(xkbcommon)
mRepeatSym = sym;
#endif
mRepeatTimer.setInterval(mRepeatDelay);
mRepeatTimer.start();
} else if (mRepeatCode == code) {
mRepeatTimer.stop();
}
}
void QWaylandInputDevice::Keyboard::repeatKey()
{
auto *window = focusWindow();
if (!window) {
// We destroyed the keyboard focus surface, but the server didn't get the message yet...
// or the server didn't send an enter event first.
} else if (mKeymapFormat == WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP) {
// raw scan code
return;
}
mRepeatTimer.setInterval(mRepeatRate);
sendKey(window->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon)
mRepeatSym, mNativeModifiers,
#else
0, 0,
#endif
mRepeatText, true);
sendKey(window->window(), mRepeatTime, QEvent::KeyPress, mRepeatKey, modifiers(), mRepeatCode,
#if QT_CONFIG(xkbcommon)
mRepeatSym, mNativeModifiers,
#else
0, 0,
#endif
mRepeatText, true);
}
void QWaylandInputDevice::Keyboard::handleFocusDestroyed()
@ -1275,12 +1203,11 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial,
Q_UNUSED(serial);
#if QT_CONFIG(xkbcommon)
if (mXkbState)
xkb_state_update_mask(mXkbState,
xkb_state_update_mask(mXkbState.get(),
mods_depressed, mods_latched, mods_locked,
0, 0, group);
mNativeModifiers = mods_depressed | mods_latched | mods_locked;
#else
Q_UNUSED(serial);
Q_UNUSED(mods_depressed);
Q_UNUSED(mods_latched);
Q_UNUSED(mods_locked);

View File

@ -54,6 +54,7 @@
#include <QtWaylandClient/private/qtwaylandclientglobal_p.h>
#include <QtWaylandClient/private/qwaylandwindow_p.h>
#include <QtCore/QScopedPointer>
#include <QSocketNotifier>
#include <QObject>
#include <QTimer>
@ -64,8 +65,7 @@
#include <QtWaylandClient/private/qwayland-wayland.h>
#if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif
#include <QtCore/QDebug>
@ -76,11 +76,6 @@
struct wl_cursor_image;
#endif
#if QT_CONFIG(xkbcommon)
struct xkb_compose_state;
struct xkb_compose_table;
#endif
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -130,6 +125,8 @@ public:
QWaylandWindow *keyboardFocus() const;
QWaylandWindow *touchFocus() const;
QList<int> possibleKeys(const QKeyEvent *event) const;
QPointF pointerSurfacePosition() const;
Qt::KeyboardModifiers modifiers() const;
@ -166,7 +163,7 @@ private:
Pointer *mPointer = nullptr;
Touch *mTouch = nullptr;
QWaylandTextInput *mTextInput = nullptr;
QScopedPointer<QWaylandTextInput> mTextInput;
uint32_t mTime = 0;
uint32_t mSerial = 0;
@ -215,41 +212,44 @@ public:
QWaylandInputDevice *mParent = nullptr;
::wl_surface *mFocus = nullptr;
#if QT_CONFIG(xkbcommon)
xkb_context *mXkbContext = nullptr;
xkb_keymap *mXkbMap = nullptr;
xkb_state *mXkbState = nullptr;
xkb_compose_table *mXkbComposeTable = nullptr;
xkb_compose_state *mXkbComposeState = nullptr;
#endif
uint32_t mNativeModifiers = 0;
int mRepeatKey;
uint32_t mRepeatCode;
uint32_t mRepeatTime;
struct repeatKey {
int key;
uint32_t code;
uint32_t time;
QString text;
Qt::KeyboardModifiers modifiers;
uint32_t nativeVirtualKey;
uint32_t nativeModifiers;
} mRepeatKey;
QTimer mRepeatTimer;
int mRepeatRate = 25;
int mRepeatDelay = 400;
QString mRepeatText;
#if QT_CONFIG(xkbcommon)
xkb_keysym_t mRepeatSym;
#endif
QTimer mRepeatTimer;
uint32_t mKeymapFormat = WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1;
Qt::KeyboardModifiers modifiers() const;
private slots:
void repeatKey();
void handleFocusDestroyed();
void handleFocusLost();
private:
#if QT_CONFIG(xkbcommon)
bool createDefaultKeyMap();
void releaseKeyMap();
void createComposeState();
void releaseComposeState();
bool createDefaultKeymap();
#endif
void handleKey(ulong timestamp, QEvent::Type type, int key, Qt::KeyboardModifiers modifiers,
quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers,
const QString &text, bool autorepeat = false, ushort count = 1);
#if QT_CONFIG(xkbcommon)
QXkbCommon::ScopedXKBKeymap mXkbKeymap;
QXkbCommon::ScopedXKBState mXkbState;
#endif
friend class QWaylandInputDevice;
};
class Q_WAYLAND_CLIENT_EXPORT QWaylandInputDevice::Pointer : public QObject, public QtWayland::wl_pointer

View File

@ -90,6 +90,10 @@
#include <QtLinuxAccessibilitySupport/private/bridge_p.h>
#endif
#if QT_CONFIG(xkbcommon)
#include <QtXkbCommonSupport/private/qxkbcommon_p.h>
#endif
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -146,20 +150,8 @@ QWaylandIntegration::QWaylandIntegration()
#if QT_CONFIG(draganddrop)
mDrag.reset(new QWaylandDrag(mDisplay.data()));
#endif
QString icStr = QPlatformInputContextFactory::requested();
if (!icStr.isNull()) {
mInputContext.reset(QPlatformInputContextFactory::create(icStr));
} else {
//try to use the input context using the wl_text_input interface
QPlatformInputContext *ctx = new QWaylandInputContext(mDisplay.data());
mInputContext.reset(ctx);
//use the traditional way for on screen keyboards for now
if (!mInputContext.data()->isValid()) {
ctx = QPlatformInputContextFactory::create();
mInputContext.reset(ctx);
}
}
reconfigureInputContext();
}
QWaylandIntegration::~QWaylandIntegration()
@ -301,6 +293,13 @@ QWaylandDisplay *QWaylandIntegration::display() const
return mDisplay.data();
}
QList<int> QWaylandIntegration::possibleKeys(const QKeyEvent *event) const
{
if (auto *seat = mDisplay->currentInputDevice())
return seat->possibleKeys(event);
return {};
}
QStringList QWaylandIntegration::themeNames() const
{
return GenericWaylandTheme::themeNames();
@ -462,6 +461,42 @@ void QWaylandIntegration::initializeInputDeviceIntegration()
}
}
void QWaylandIntegration::reconfigureInputContext()
{
if (!mDisplay) {
// This function can be called from QWaylandDisplay::registry_global() when we
// are in process of constructing QWaylandDisplay. Configuring input context
// in that case is done by calling reconfigureInputContext() from QWaylandIntegration
// constructor, after QWaylandDisplay has been constructed.
return;
}
const QString &requested = QPlatformInputContextFactory::requested();
if (requested == QLatin1String("qtvirtualkeyboard"))
qCWarning(lcQpaWayland) << "qtvirtualkeyboard currently is not supported at client-side,"
" use QT_IM_MODULE=qtvirtualkeyboard at compositor-side.";
if (requested.isNull())
mInputContext.reset(new QWaylandInputContext(mDisplay.data()));
else
mInputContext.reset(QPlatformInputContextFactory::create(requested));
const QString defaultInputContext(QStringLiteral("compose"));
if ((!mInputContext || !mInputContext->isValid()) && requested != defaultInputContext)
mInputContext.reset(QPlatformInputContextFactory::create(defaultInputContext));
#if QT_CONFIG(xkbcommon)
QXkbCommon::setXkbContext(mInputContext.data(), mDisplay->xkbContext());
#endif
// Even if compositor-side input context handling has been requested, we fallback to
// client-side handling if compositor does not provide the text-input extension. This
// is why we need to check here which input context actually is being used.
mDisplay->mUsingInputContextFromCompositor = qobject_cast<QWaylandInputContext *>(mInputContext.data());
qCDebug(lcQpaWayland) << "using input method:" << inputContext()->metaObject()->className();
}
QWaylandShellIntegration *QWaylandIntegration::createShellIntegration(const QString &integrationName)
{
if (QWaylandShellIntegrationFactory::keys().contains(integrationName)) {

View File

@ -106,6 +106,8 @@ public:
QWaylandDisplay *display() const;
QList<int> possibleKeys(const QKeyEvent *event) const override;
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
@ -116,6 +118,8 @@ public:
virtual QWaylandServerBufferIntegration *serverBufferIntegration() const;
virtual QWaylandShellIntegration *shellIntegration() const;
void reconfigureInputContext();
private:
// NOTE: mDisplay *must* be destructed after mDrag and mClientBufferIntegration
// and mShellIntegration.

View File

@ -40,6 +40,7 @@
#include "qwaylandscreen_p.h"
#include "qwaylanddisplay_p.h"
#include "qwaylandintegration_p.h"
#include "qwaylandcursor_p.h"
#include "qwaylandwindow_p.h"
@ -60,6 +61,14 @@ QWaylandScreen::QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uin
{
if (auto *xdgOutputManager = waylandDisplay->xdgOutputManager())
initXdgOutput(xdgOutputManager);
if (version < WL_OUTPUT_DONE_SINCE_VERSION) {
qCWarning(lcQpaWayland) << "wl_output done event not supported by compositor,"
<< "QScreen may not work correctly";
mWaylandDisplay->forceRoundTrip(); // Give the compositor a chance to send geometry etc.
mOutputDone = true; // Fake the done event
maybeInitialize();
}
}
QWaylandScreen::~QWaylandScreen()
@ -68,6 +77,24 @@ QWaylandScreen::~QWaylandScreen()
zxdg_output_v1::destroy();
}
void QWaylandScreen::maybeInitialize()
{
Q_ASSERT(!mInitialized);
if (!mOutputDone)
return;
if (mWaylandDisplay->xdgOutputManager() && !mXdgOutputDone)
return;
mInitialized = true;
mWaylandDisplay->handleScreenInitialized(this);
updateOutputProperties();
if (zxdg_output_v1::isInitialized())
updateXdgOutputProperties();
}
void QWaylandScreen::initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager)
{
Q_ASSERT(xdgOutputManager);
@ -233,10 +260,15 @@ void QWaylandScreen::output_scale(int32_t factor)
void QWaylandScreen::output_done()
{
// the done event is sent after all the geometry and the mode events are sent,
// and the last mode event to be sent is the active one, so we can trust the
// values of mGeometry and mRefreshRate here
mOutputDone = true;
if (mInitialized)
updateOutputProperties();
else
maybeInitialize();
}
void QWaylandScreen::updateOutputProperties()
{
if (mTransform >= 0) {
bool isPortrait = mGeometry.height() > mGeometry.width();
switch (mTransform) {
@ -263,7 +295,9 @@ void QWaylandScreen::output_done()
QWindowSystemInterface::handleScreenOrientationChange(screen(), m_orientation);
mTransform = -1;
}
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), refreshRate());
if (!zxdg_output_v1::isInitialized())
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
}
@ -281,7 +315,11 @@ void QWaylandScreen::zxdg_output_v1_logical_size(int32_t width, int32_t height)
void QWaylandScreen::zxdg_output_v1_done()
{
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
mXdgOutputDone = true;
if (mInitialized)
updateXdgOutputProperties();
else
maybeInitialize();
}
void QWaylandScreen::zxdg_output_v1_name(const QString &name)
@ -289,6 +327,12 @@ void QWaylandScreen::zxdg_output_v1_name(const QString &name)
mOutputName = name;
}
void QWaylandScreen::updateXdgOutputProperties()
{
Q_ASSERT(zxdg_output_v1::isInitialized());
QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), geometry());
}
} // namespace QtWaylandClient
QT_END_NAMESPACE

View File

@ -71,6 +71,8 @@ public:
QWaylandScreen(QWaylandDisplay *waylandDisplay, int version, uint32_t id);
~QWaylandScreen() override;
void maybeInitialize();
void initXdgOutput(QtWayland::zxdg_output_manager_v1 *xdgOutputManager);
QWaylandDisplay *display() const;
@ -116,12 +118,14 @@ private:
int32_t transform) override;
void output_scale(int32_t factor) override;
void output_done() override;
void updateOutputProperties();
// XdgOutput
void zxdg_output_v1_logical_position(int32_t x, int32_t y) override;
void zxdg_output_v1_logical_size(int32_t width, int32_t height) override;
void zxdg_output_v1_done() override;
void zxdg_output_v1_name(const QString &name) override;
void updateXdgOutputProperties();
int m_outputId;
QWaylandDisplay *mWaylandDisplay = nullptr;
@ -137,6 +141,9 @@ private:
QSize mPhysicalSize;
QString mOutputName;
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
bool mOutputDone = false;
bool mXdgOutputDone = false;
bool mInitialized = false;
#if QT_CONFIG(cursor)
QScopedPointer<QWaylandCursor> mWaylandCursor;

View File

@ -51,6 +51,7 @@
#include "qwaylandnativeinterface_p.h"
#include "qwaylanddecorationfactory_p.h"
#include "qwaylandshmbackingstore_p.h"
#include "qwaylandshellintegration_p.h"
#include <QtCore/QFileInfo>
#include <QtCore/QPointer>
@ -62,6 +63,7 @@
#include <QtGui/private/qwindow_p.h>
#include <QtCore/QDebug>
#include <QtCore/QThread>
QT_BEGIN_NAMESPACE
@ -74,6 +76,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window)
: QPlatformWindow(window)
, mDisplay(waylandScreen()->display())
, mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{
static WId id = 1;
@ -127,8 +130,9 @@ void QWaylandWindow::initWindow()
}
} else if (shouldCreateShellSurface()) {
Q_ASSERT(!mShellSurface);
Q_ASSERT(mDisplay->shellIntegration());
mShellSurface = mDisplay->createShellSurface(this);
mShellSurface = mDisplay->shellIntegration()->createShellSurface(this);
if (mShellSurface) {
// Set initial surface title
setWindowTitle(window()->title());
@ -204,6 +208,9 @@ void QWaylandWindow::initializeWlSurface()
bool QWaylandWindow::shouldCreateShellSurface() const
{
if (!mDisplay->shellIntegration())
return false;
if (shouldCreateSubSurface())
return false;
@ -339,7 +346,7 @@ void QWaylandWindow::setGeometry(const QRect &rect)
sendExposeEvent(exposeGeometry);
if (mShellSurface)
mShellSurface->setWindowGeometry(windowGeometry());
mShellSurface->setWindowGeometry(windowContentGeometry());
}
void QWaylandWindow::resizeFromApplyConfigure(const QSize &sizeWithMargins, const QPoint &offset)
@ -357,6 +364,8 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
{
if (!(mShellSurface && mShellSurface->handleExpose(rect)))
QWindowSystemInterface::handleExposeEvent(window(), rect);
else
qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
mLastExposeGeometry = rect;
}
@ -494,15 +503,8 @@ void QWaylandWindow::applyConfigure()
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
}
if (buffer) {
mFrameCallback = mSurface->frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameSync = true;
handleUpdate();
buffer->setBusy();
mSurface->attach(buffer->buffer(), x, y);
@ -566,32 +568,61 @@ void QWaylandWindow::commit()
}
const wl_callback_listener QWaylandWindow::callbackListener = {
QWaylandWindow::frameCallback
[](void *data, wl_callback *callback, uint32_t time) {
Q_UNUSED(callback);
Q_UNUSED(time);
auto *window = static_cast<QWaylandWindow*>(data);
if (window->thread() != QThread::currentThread())
QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection);
else
window->handleFrameCallback();
}
};
void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uint32_t time)
void QWaylandWindow::handleFrameCallback()
{
Q_UNUSED(time);
Q_UNUSED(callback);
QWaylandWindow *self = static_cast<QWaylandWindow*>(data);
bool wasExposed = isExposed();
self->mWaitingForFrameSync = false;
if (self->mUpdateRequested) {
self->mUpdateRequested = false;
self->deliverUpdateRequest();
if (mFrameCallbackTimerId != -1) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
}
mWaitingForFrameCallback = false;
mFrameCallbackTimedOut = false;
if (!wasExposed && isExposed())
sendExposeEvent(QRect(QPoint(), geometry().size()));
if (wasExposed && hasPendingUpdateRequest())
deliverUpdateRequest();
}
QMutex QWaylandWindow::mFrameSyncMutex;
void QWaylandWindow::waitForFrameSync()
bool QWaylandWindow::waitForFrameSync(int timeout)
{
QMutexLocker locker(&mFrameSyncMutex);
if (!mWaitingForFrameSync)
return;
mDisplay->flushRequests();
while (mWaitingForFrameSync)
mDisplay->blockingReadEvents();
if (!mWaitingForFrameCallback)
return true;
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
if (mWaitingForFrameCallback) {
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true;
mWaitingForUpdate = false;
sendExposeEvent(QRect());
}
// Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
return !mWaitingForFrameCallback;
}
QMargins QWaylandWindow::frameMargins() const
@ -613,7 +644,7 @@ QSize QWaylandWindow::surfaceSize() const
* Window geometry as defined by the xdg-shell spec (in wl_surface coordinates)
* topLeft is where the shadow stops and the decorations border start.
*/
QRect QWaylandWindow::windowGeometry() const
QRect QWaylandWindow::windowContentGeometry() const
{
return QRect(QPoint(), surfaceSize());
}
@ -833,7 +864,7 @@ void QWaylandWindow::handleMouse(QWaylandInputDevice *inputDevice, const QWaylan
#if QT_CONFIG(cursor)
if (e.type == QWaylandPointerEvent::Enter) {
QRect contentGeometry = windowGeometry().marginsRemoved(frameMargins());
QRect contentGeometry = windowContentGeometry().marginsRemoved(frameMargins());
if (contentGeometry.contains(e.local.toPoint()))
restoreMouseCursor(inputDevice);
}
@ -944,9 +975,19 @@ void QWaylandWindow::requestActivateWindow()
bool QWaylandWindow::isExposed() const
{
if (!window()->isVisible())
return false;
if (mFrameCallbackTimedOut)
return false;
if (mShellSurface)
return window()->isVisible() && mShellSurface->isExposed();
return QPlatformWindow::isExposed();
return mShellSurface->isExposed();
if (mSubSurfaceWindow)
return mSubSurfaceWindow->parent()->isExposed();
return !(shouldCreateShellSurface() || shouldCreateSubSurface());
}
bool QWaylandWindow::isActive() const
@ -1015,12 +1056,107 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa
return m_properties.value(name, defaultValue);
}
void QWaylandWindow::timerEvent(QTimerEvent *event)
{
if (event->timerId() == mFallbackUpdateTimerId) {
killTimer(mFallbackUpdateTimerId);
mFallbackUpdateTimerId = -1;
qCDebug(lcWaylandBackingstore) << "mFallbackUpdateTimer timed out";
if (!isExposed()) {
qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed,"
<< "not delivering update request.";
return;
}
if (mWaitingForUpdate && hasPendingUpdateRequest() && !mWaitingForFrameCallback) {
qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer,"
<< "may not be in sync with display";
deliverUpdateRequest();
}
}
if (event->timerId() == mFrameCallbackTimerId) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true;
mWaitingForUpdate = false;
sendExposeEvent(QRect());
}
}
void QWaylandWindow::requestUpdate()
{
if (!mWaitingForFrameSync)
QPlatformWindow::requestUpdate();
else
mUpdateRequested = true;
Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
// If we have a frame callback all is good and will be taken care of there
if (mWaitingForFrameCallback)
return;
// If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
if (mWaitingForUpdate) {
// Ideally, we should just have returned here, but we're not guaranteed that the client
// will actually update, so start this timer to deliver another request update after a while
// *IF* the client doesn't update.
int fallbackTimeout = 100;
mFallbackUpdateTimerId = startTimer(fallbackTimeout);
return;
}
// Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
// so use invokeMethod to delay the delivery a bit.
QMetaObject::invokeMethod(this, [this] {
// Things might have changed in the meantime
if (hasPendingUpdateRequest() && !mWaitingForUpdate && !mWaitingForFrameCallback)
deliverUpdateRequest();
}, Qt::QueuedConnection);
}
// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
// with eglSwapBuffers) to know when it's time to commit the next one.
// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
void QWaylandWindow::handleUpdate()
{
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
}
if (mFallbackUpdateTimerId != -1) {
// Ideally, we would stop the fallback timer here, but since we're on another thread,
// it's not allowed. Instead we set mFallbackUpdateTimer to -1 here, so we'll just
// ignore it if it times out before it's cleaned up by the invokeMethod call.
int id = mFallbackUpdateTimerId;
mFallbackUpdateTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
mFrameCallback = mSurface->frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameCallback = true;
mWaitingForUpdate = false;
// Stop current frame timer if any, can't use killTimer directly, see comment above.
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
// Start a timer for handling the case when the compositor stops sending frame callbacks.
QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly
if (mWaitingForFrameCallback)
mFrameCallbackTimerId = startTimer(100);
}, Qt::QueuedConnection);
}
void QWaylandWindow::deliverUpdateRequest()
{
mWaitingForUpdate = true;
QPlatformWindow::deliverUpdateRequest();
}
void QWaylandWindow::addAttachOffset(const QPoint point)

View File

@ -121,11 +121,11 @@ public:
void commit();
void waitForFrameSync();
bool waitForFrameSync(int timeout);
QMargins frameMargins() const override;
QSize surfaceSize() const;
QRect windowGeometry() const;
QRect windowContentGeometry() const;
QWaylandSurface *waylandSurface() const { return mSurface.data(); }
::wl_surface *wlSurface();
@ -194,7 +194,10 @@ public:
bool startSystemMove(const QPoint &pos) override;
void timerEvent(QTimerEvent *event) override;
void requestUpdate() override;
void handleUpdate();
void deliverUpdateRequest() override;
public slots:
void applyConfigure();
@ -214,10 +217,17 @@ protected:
Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
WId mWindowId;
bool mWaitingForFrameSync = false;
bool mWaitingForFrameCallback = false;
bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback
struct ::wl_callback *mFrameCallback = nullptr;
struct ::wl_event_queue *mFrameQueue = nullptr;
QWaitCondition mFrameSyncWait;
// True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
bool mWaitingForUpdate = false;
int mFallbackUpdateTimerId = -1; // Started when waiting for app to commit
QMutex mResizeLock;
bool mWaitingToApplyConfigure = false;
bool mCanResize = true;
@ -254,11 +264,10 @@ private:
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreensChanged();
bool mUpdateRequested = false;
QRect mLastExposeGeometry;
static const wl_callback_listener callbackListener;
static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time);
void handleFrameCallback();
static QMutex mFrameSyncMutex;
static QWaylandWindow *mMouseGrab;

View File

@ -1,395 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Jolla Ltd
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwaylandxkb_p.h"
#include <QKeyEvent>
#include <QString>
#if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon-keysyms.h>
#endif
QT_BEGIN_NAMESPACE
#if QT_CONFIG(xkbcommon)
static const uint32_t KeyTbl[] = {
XKB_KEY_Escape, Qt::Key_Escape,
XKB_KEY_Tab, Qt::Key_Tab,
XKB_KEY_ISO_Left_Tab, Qt::Key_Backtab,
XKB_KEY_BackSpace, Qt::Key_Backspace,
XKB_KEY_Return, Qt::Key_Return,
XKB_KEY_Insert, Qt::Key_Insert,
XKB_KEY_Delete, Qt::Key_Delete,
XKB_KEY_Clear, Qt::Key_Delete,
XKB_KEY_Pause, Qt::Key_Pause,
XKB_KEY_Print, Qt::Key_Print,
XKB_KEY_Home, Qt::Key_Home,
XKB_KEY_End, Qt::Key_End,
XKB_KEY_Left, Qt::Key_Left,
XKB_KEY_Up, Qt::Key_Up,
XKB_KEY_Right, Qt::Key_Right,
XKB_KEY_Down, Qt::Key_Down,
XKB_KEY_Prior, Qt::Key_PageUp,
XKB_KEY_Next, Qt::Key_PageDown,
XKB_KEY_Shift_L, Qt::Key_Shift,
XKB_KEY_Shift_R, Qt::Key_Shift,
XKB_KEY_Shift_Lock, Qt::Key_Shift,
XKB_KEY_Control_L, Qt::Key_Control,
XKB_KEY_Control_R, Qt::Key_Control,
XKB_KEY_Meta_L, Qt::Key_Meta,
XKB_KEY_Meta_R, Qt::Key_Meta,
XKB_KEY_Alt_L, Qt::Key_Alt,
XKB_KEY_Alt_R, Qt::Key_Alt,
XKB_KEY_Caps_Lock, Qt::Key_CapsLock,
XKB_KEY_Num_Lock, Qt::Key_NumLock,
XKB_KEY_Scroll_Lock, Qt::Key_ScrollLock,
XKB_KEY_Super_L, Qt::Key_Super_L,
XKB_KEY_Super_R, Qt::Key_Super_R,
XKB_KEY_Menu, Qt::Key_Menu,
XKB_KEY_Hyper_L, Qt::Key_Hyper_L,
XKB_KEY_Hyper_R, Qt::Key_Hyper_R,
XKB_KEY_Help, Qt::Key_Help,
XKB_KEY_KP_Space, Qt::Key_Space,
XKB_KEY_KP_Tab, Qt::Key_Tab,
XKB_KEY_KP_Enter, Qt::Key_Enter,
XKB_KEY_KP_Home, Qt::Key_Home,
XKB_KEY_KP_Left, Qt::Key_Left,
XKB_KEY_KP_Up, Qt::Key_Up,
XKB_KEY_KP_Right, Qt::Key_Right,
XKB_KEY_KP_Down, Qt::Key_Down,
XKB_KEY_KP_Prior, Qt::Key_PageUp,
XKB_KEY_KP_Next, Qt::Key_PageDown,
XKB_KEY_KP_End, Qt::Key_End,
XKB_KEY_KP_Begin, Qt::Key_Clear,
XKB_KEY_KP_Insert, Qt::Key_Insert,
XKB_KEY_KP_Delete, Qt::Key_Delete,
XKB_KEY_KP_Equal, Qt::Key_Equal,
XKB_KEY_KP_Multiply, Qt::Key_Asterisk,
XKB_KEY_KP_Add, Qt::Key_Plus,
XKB_KEY_KP_Separator, Qt::Key_Comma,
XKB_KEY_KP_Subtract, Qt::Key_Minus,
XKB_KEY_KP_Decimal, Qt::Key_Period,
XKB_KEY_KP_Divide, Qt::Key_Slash,
XKB_KEY_ISO_Level3_Shift, Qt::Key_AltGr,
XKB_KEY_Multi_key, Qt::Key_Multi_key,
XKB_KEY_Codeinput, Qt::Key_Codeinput,
XKB_KEY_SingleCandidate, Qt::Key_SingleCandidate,
XKB_KEY_MultipleCandidate, Qt::Key_MultipleCandidate,
XKB_KEY_PreviousCandidate, Qt::Key_PreviousCandidate,
XKB_KEY_Mode_switch, Qt::Key_Mode_switch,
XKB_KEY_script_switch, Qt::Key_Mode_switch,
XKB_KEY_XF86Back, Qt::Key_Back,
XKB_KEY_XF86Forward, Qt::Key_Forward,
XKB_KEY_XF86Stop, Qt::Key_Stop,
XKB_KEY_XF86Refresh, Qt::Key_Refresh,
XKB_KEY_XF86Favorites, Qt::Key_Favorites,
XKB_KEY_XF86AudioMedia, Qt::Key_LaunchMedia,
XKB_KEY_XF86OpenURL, Qt::Key_OpenUrl,
XKB_KEY_XF86HomePage, Qt::Key_HomePage,
XKB_KEY_XF86Search, Qt::Key_Search,
XKB_KEY_XF86AudioLowerVolume, Qt::Key_VolumeDown,
XKB_KEY_XF86AudioMute, Qt::Key_VolumeMute,
XKB_KEY_XF86AudioRaiseVolume, Qt::Key_VolumeUp,
XKB_KEY_XF86AudioPlay, Qt::Key_MediaTogglePlayPause,
XKB_KEY_XF86AudioStop, Qt::Key_MediaStop,
XKB_KEY_XF86AudioPrev, Qt::Key_MediaPrevious,
XKB_KEY_XF86AudioNext, Qt::Key_MediaNext,
XKB_KEY_XF86AudioRecord, Qt::Key_MediaRecord,
XKB_KEY_XF86AudioPause, Qt::Key_MediaPause,
XKB_KEY_XF86Mail, Qt::Key_LaunchMail,
XKB_KEY_XF86Calculator, Qt::Key_Calculator,
XKB_KEY_XF86Memo, Qt::Key_Memo,
XKB_KEY_XF86ToDoList, Qt::Key_ToDoList,
XKB_KEY_XF86Calendar, Qt::Key_Calendar,
XKB_KEY_XF86PowerDown, Qt::Key_PowerDown,
XKB_KEY_XF86ContrastAdjust, Qt::Key_ContrastAdjust,
XKB_KEY_XF86Standby, Qt::Key_Standby,
XKB_KEY_XF86MonBrightnessUp, Qt::Key_MonBrightnessUp,
XKB_KEY_XF86MonBrightnessDown, Qt::Key_MonBrightnessDown,
XKB_KEY_XF86KbdLightOnOff, Qt::Key_KeyboardLightOnOff,
XKB_KEY_XF86KbdBrightnessUp, Qt::Key_KeyboardBrightnessUp,
XKB_KEY_XF86KbdBrightnessDown, Qt::Key_KeyboardBrightnessDown,
XKB_KEY_XF86PowerOff, Qt::Key_PowerOff,
XKB_KEY_XF86WakeUp, Qt::Key_WakeUp,
XKB_KEY_XF86Eject, Qt::Key_Eject,
XKB_KEY_XF86ScreenSaver, Qt::Key_ScreenSaver,
XKB_KEY_XF86WWW, Qt::Key_WWW,
XKB_KEY_XF86Sleep, Qt::Key_Sleep,
XKB_KEY_XF86LightBulb, Qt::Key_LightBulb,
XKB_KEY_XF86Shop, Qt::Key_Shop,
XKB_KEY_XF86History, Qt::Key_History,
XKB_KEY_XF86AddFavorite, Qt::Key_AddFavorite,
XKB_KEY_XF86HotLinks, Qt::Key_HotLinks,
XKB_KEY_XF86BrightnessAdjust, Qt::Key_BrightnessAdjust,
XKB_KEY_XF86Finance, Qt::Key_Finance,
XKB_KEY_XF86Community, Qt::Key_Community,
XKB_KEY_XF86AudioRewind, Qt::Key_AudioRewind,
XKB_KEY_XF86BackForward, Qt::Key_BackForward,
XKB_KEY_XF86ApplicationLeft, Qt::Key_ApplicationLeft,
XKB_KEY_XF86ApplicationRight, Qt::Key_ApplicationRight,
XKB_KEY_XF86Book, Qt::Key_Book,
XKB_KEY_XF86CD, Qt::Key_CD,
XKB_KEY_XF86Calculater, Qt::Key_Calculator,
XKB_KEY_XF86Clear, Qt::Key_Clear,
XKB_KEY_XF86ClearGrab, Qt::Key_ClearGrab,
XKB_KEY_XF86Close, Qt::Key_Close,
XKB_KEY_XF86Copy, Qt::Key_Copy,
XKB_KEY_XF86Cut, Qt::Key_Cut,
XKB_KEY_XF86Display, Qt::Key_Display,
XKB_KEY_XF86DOS, Qt::Key_DOS,
XKB_KEY_XF86Documents, Qt::Key_Documents,
XKB_KEY_XF86Excel, Qt::Key_Excel,
XKB_KEY_XF86Explorer, Qt::Key_Explorer,
XKB_KEY_XF86Game, Qt::Key_Game,
XKB_KEY_XF86Go, Qt::Key_Go,
XKB_KEY_XF86iTouch, Qt::Key_iTouch,
XKB_KEY_XF86LogOff, Qt::Key_LogOff,
XKB_KEY_XF86Market, Qt::Key_Market,
XKB_KEY_XF86Meeting, Qt::Key_Meeting,
XKB_KEY_XF86MenuKB, Qt::Key_MenuKB,
XKB_KEY_XF86MenuPB, Qt::Key_MenuPB,
XKB_KEY_XF86MySites, Qt::Key_MySites,
XKB_KEY_XF86New, Qt::Key_New,
XKB_KEY_XF86News, Qt::Key_News,
XKB_KEY_XF86OfficeHome, Qt::Key_OfficeHome,
XKB_KEY_XF86Open, Qt::Key_Open,
XKB_KEY_XF86Option, Qt::Key_Option,
XKB_KEY_XF86Paste, Qt::Key_Paste,
XKB_KEY_XF86Phone, Qt::Key_Phone,
XKB_KEY_XF86Reply, Qt::Key_Reply,
XKB_KEY_XF86Reload, Qt::Key_Reload,
XKB_KEY_XF86RotateWindows, Qt::Key_RotateWindows,
XKB_KEY_XF86RotationPB, Qt::Key_RotationPB,
XKB_KEY_XF86RotationKB, Qt::Key_RotationKB,
XKB_KEY_XF86Save, Qt::Key_Save,
XKB_KEY_XF86Send, Qt::Key_Send,
XKB_KEY_XF86Spell, Qt::Key_Spell,
XKB_KEY_XF86SplitScreen, Qt::Key_SplitScreen,
XKB_KEY_XF86Support, Qt::Key_Support,
XKB_KEY_XF86TaskPane, Qt::Key_TaskPane,
XKB_KEY_XF86Terminal, Qt::Key_Terminal,
XKB_KEY_XF86Tools, Qt::Key_Tools,
XKB_KEY_XF86Travel, Qt::Key_Travel,
XKB_KEY_XF86Video, Qt::Key_Video,
XKB_KEY_XF86Word, Qt::Key_Word,
XKB_KEY_XF86Xfer, Qt::Key_Xfer,
XKB_KEY_XF86ZoomIn, Qt::Key_ZoomIn,
XKB_KEY_XF86ZoomOut, Qt::Key_ZoomOut,
XKB_KEY_XF86Away, Qt::Key_Away,
XKB_KEY_XF86Messenger, Qt::Key_Messenger,
XKB_KEY_XF86WebCam, Qt::Key_WebCam,
XKB_KEY_XF86MailForward, Qt::Key_MailForward,
XKB_KEY_XF86Pictures, Qt::Key_Pictures,
XKB_KEY_XF86Music, Qt::Key_Music,
XKB_KEY_XF86Battery, Qt::Key_Battery,
XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
XKB_KEY_XF86WLAN, Qt::Key_WLAN,
XKB_KEY_XF86UWB, Qt::Key_UWB,
XKB_KEY_XF86AudioForward, Qt::Key_AudioForward,
XKB_KEY_XF86AudioRepeat, Qt::Key_AudioRepeat,
XKB_KEY_XF86AudioRandomPlay, Qt::Key_AudioRandomPlay,
XKB_KEY_XF86Subtitle, Qt::Key_Subtitle,
XKB_KEY_XF86AudioCycleTrack, Qt::Key_AudioCycleTrack,
XKB_KEY_XF86Time, Qt::Key_Time,
XKB_KEY_XF86Select, Qt::Key_Select,
XKB_KEY_XF86View, Qt::Key_View,
XKB_KEY_XF86TopMenu, Qt::Key_TopMenu,
XKB_KEY_XF86Red, Qt::Key_Red,
XKB_KEY_XF86Green, Qt::Key_Green,
XKB_KEY_XF86Yellow, Qt::Key_Yellow,
XKB_KEY_XF86Blue, Qt::Key_Blue,
XKB_KEY_XF86Bluetooth, Qt::Key_Bluetooth,
XKB_KEY_XF86Suspend, Qt::Key_Suspend,
XKB_KEY_XF86Hibernate, Qt::Key_Hibernate,
XKB_KEY_XF86TouchpadToggle, Qt::Key_TouchpadToggle,
XKB_KEY_XF86TouchpadOn, Qt::Key_TouchpadOn,
XKB_KEY_XF86TouchpadOff, Qt::Key_TouchpadOff,
XKB_KEY_XF86AudioMicMute, Qt::Key_MicMute,
XKB_KEY_XF86Launch0, Qt::Key_Launch0,
XKB_KEY_XF86Launch1, Qt::Key_Launch1,
XKB_KEY_XF86Launch2, Qt::Key_Launch2,
XKB_KEY_XF86Launch3, Qt::Key_Launch3,
XKB_KEY_XF86Launch4, Qt::Key_Launch4,
XKB_KEY_XF86Launch5, Qt::Key_Launch5,
XKB_KEY_XF86Launch6, Qt::Key_Launch6,
XKB_KEY_XF86Launch7, Qt::Key_Launch7,
XKB_KEY_XF86Launch8, Qt::Key_Launch8,
XKB_KEY_XF86Launch9, Qt::Key_Launch9,
XKB_KEY_XF86LaunchA, Qt::Key_LaunchA,
XKB_KEY_XF86LaunchB, Qt::Key_LaunchB,
XKB_KEY_XF86LaunchC, Qt::Key_LaunchC,
XKB_KEY_XF86LaunchD, Qt::Key_LaunchD,
XKB_KEY_XF86LaunchE, Qt::Key_LaunchE,
XKB_KEY_XF86LaunchF, Qt::Key_LaunchF,
0, 0
};
static int lookupKeysym(xkb_keysym_t key)
{
int code = 0;
int i = 0;
while (KeyTbl[i]) {
if (key == KeyTbl[i]) {
code = (int)KeyTbl[i+1];
break;
}
i += 2;
}
return code;
}
static xkb_keysym_t toKeysymFromTable(uint32_t key)
{
for (int i = 0; KeyTbl[i]; i += 2) {
if (key == KeyTbl[i + 1])
return KeyTbl[i];
}
return 0;
}
#endif
std::pair<int, QString> QWaylandXkb::keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers)
{
#if QT_CONFIG(xkbcommon)
QString text;
uint utf32 = xkb_keysym_to_utf32(keysym);
if (utf32)
text = QString::fromUcs4(&utf32, 1);
int code = 0;
if (keysym >= XKB_KEY_F1 && keysym <= XKB_KEY_F35) {
code = Qt::Key_F1 + (int(keysym) - XKB_KEY_F1);
} else if (keysym >= XKB_KEY_KP_Space && keysym <= XKB_KEY_KP_9) {
if (keysym >= XKB_KEY_KP_0) {
// numeric keypad keys
code = Qt::Key_0 + ((int)keysym - XKB_KEY_KP_0);
} else {
code = lookupKeysym(keysym);
}
modifiers |= Qt::KeypadModifier;
} else if (text.length() == 1 && text.unicode()->unicode() > 0x1f
&& text.unicode()->unicode() != 0x7f
&& !(keysym >= XKB_KEY_dead_grave && keysym <= XKB_KEY_dead_currency)) {
code = text.unicode()->toUpper().unicode();
} else {
// any other keys
code = lookupKeysym(keysym);
}
// Map control + letter to proper text
if (utf32 >= 'A' && utf32 <= '~' && (modifiers & Qt::ControlModifier)) {
utf32 &= ~0x60;
text = QString::fromUcs4(&utf32, 1);
}
return { code, text };
#else
Q_UNUSED(modifiers)
return { keysym, "" };
#endif
}
Qt::KeyboardModifiers QWaylandXkb::modifiers(struct xkb_state *state)
{
#if QT_CONFIG(xkbcommon)
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
xkb_state_component cstate = static_cast<xkb_state_component>(XKB_STATE_DEPRESSED | XKB_STATE_LATCHED | XKB_STATE_LOCKED);
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_SHIFT, cstate))
modifiers |= Qt::ShiftModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_CTRL, cstate))
modifiers |= Qt::ControlModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_ALT, cstate))
modifiers |= Qt::AltModifier;
if (xkb_state_mod_name_is_active(state, XKB_MOD_NAME_LOGO, cstate))
modifiers |= Qt::MetaModifier;
return modifiers;
#else
Q_UNUSED(state)
return Qt::NoModifier;
#endif
}
QEvent::Type QWaylandXkb::toQtEventType(uint32_t state)
{
return state != 0 ? QEvent::KeyPress : QEvent::KeyRelease;
}
QVector<xkb_keysym_t> QWaylandXkb::toKeysym(QKeyEvent *event)
{
#if QT_CONFIG(xkbcommon)
QVector<xkb_keysym_t> keysyms;
if (event->key() >= Qt::Key_F1 && event->key() <= Qt::Key_F35) {
keysyms.append(XKB_KEY_F1 + (event->key() - Qt::Key_F1));
} else if (event->modifiers() & Qt::KeypadModifier) {
if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9)
keysyms.append(XKB_KEY_KP_0 + (event->key() - Qt::Key_0));
else
keysyms.append(toKeysymFromTable(event->key()));
} else if (!event->text().isEmpty()) {
// From libxkbcommon keysym-utf.c:
// "We allow to represent any UCS character in the range U-00000000 to
// U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff."
foreach (uint utf32, event->text().toUcs4()) {
keysyms.append(utf32 | 0x01000000);
}
} else {
keysyms.append(toKeysymFromTable(event->key()));
}
return keysyms;
#else
return QVector<xkb_keysym_t>() << event->nativeScanCode();
#endif
}
QT_END_NAMESPACE

View File

@ -1,73 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Jolla Ltd
** Contact: http://www.qt-project.org/legal
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QWAYLANDXKB_H
#define QWAYLANDXKB_H
#include <QtGui/private/qtguiglobal_p.h>
#include <Qt>
#include <QEvent>
#if QT_CONFIG(xkbcommon)
#include <xkbcommon/xkbcommon.h>
#else
typedef quint32 xkb_keysym_t;
struct xkb_state;
#endif
#include <utility>
QT_BEGIN_NAMESPACE
class QKeyEvent;
class QWaylandXkb
{
public:
static std::pair<int, QString> keysymToQtKey(xkb_keysym_t keysym, Qt::KeyboardModifiers &modifiers);
static Qt::KeyboardModifiers modifiers(struct xkb_state *state);
static QEvent::Type toQtEventType(uint32_t state);
static QVector<xkb_keysym_t> toKeysym(QKeyEvent *event);
};
QT_END_NAMESPACE
#endif

View File

@ -13,3 +13,5 @@ SUBDIRS += \
xdgoutput \
xdgshell \
xdgshellv6
qtConfig(im): SUBDIRS += inputcontext

View File

@ -0,0 +1,6 @@
include (../shared/shared.pri)
QT += waylandcompositor
TARGET = tst_inputcontext
SOURCES += tst_inputcontext.cpp

View File

@ -0,0 +1,184 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "mockcompositor.h"
#include "textinput.h"
#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/qpa/qplatforminputcontext.h>
#include <QtGui/qpa/qplatformintegration.h>
#include <QtGui/qpa/qplatforminputcontextfactory_p.h>
#include <QtTest/QtTest>
using namespace MockCompositor;
class tst_inputcontext : public QObject, private DefaultCompositor
{
Q_OBJECT
private slots:
void initTestCase();
void selectingInputContext_data();
void selectingInputContext();
void inputContextReconfigurationWhenTogglingTextInputExtension();
private:
QByteArray inputContextName() const;
void ensureTextInputPresentOnCompositor();
void ensureTextInputNotPresentOnCompositor();
QByteArray mComposeModule = QByteArray("QComposeInputContext"); // default input context
QByteArray mIbusModule = QByteArray("QIBusPlatformInputContext");
QByteArray mWaylandModule = QByteArray("QtWaylandClient::QWaylandInputContext");
TextInputManager *mTextInputManager = nullptr;
};
void tst_inputcontext::initTestCase()
{
// Verify that plugins are present and valid
QPlatformInputContext *context = QPlatformInputContextFactory::create(QStringLiteral("compose"));
QVERIFY(context && context->isValid());
context = QPlatformInputContextFactory::create(QStringLiteral("ibus"));
// The ibus plugin depends on properly configured system services, if plugin is not valid
// verify that wayland qpa plugin properly fallbacks to default input context.
if (!context || !context->isValid())
mIbusModule = mComposeModule;
}
QByteArray tst_inputcontext::inputContextName() const
{
QPlatformIntegration *platformIntegration = QGuiApplicationPrivate::platformIntegration();
if (platformIntegration->inputContext())
return platformIntegration->inputContext()->metaObject()->className();
return QByteArray("");
}
void tst_inputcontext::ensureTextInputPresentOnCompositor()
{
exec([&] {
QVector<TextInputManager *> extensions = getAll<TextInputManager>();
if (extensions.length() > 1)
QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned");
if (extensions.length() == 0)
add<TextInputManager>();
});
}
void tst_inputcontext::ensureTextInputNotPresentOnCompositor()
{
exec([&] {
QVector<TextInputManager *> extensions = getAll<TextInputManager>();
if (extensions.length() > 1)
QFAIL("TextInputManager is a singleton, hence there should not be more then one object returned");
if (extensions.length() == 1)
remove(extensions.first());
});
}
void tst_inputcontext::selectingInputContext_data()
{
QTest::addColumn<QByteArray>("requestedModule");
QTest::addColumn<QByteArray>("expectedModule");
// Test compositor without Text Input extension
QTest::newRow("ibus") << QByteArray("ibus") << mIbusModule;
QTest::newRow("compose") << QByteArray("compose") << mComposeModule;
QTest::newRow("empty") << QByteArray("") << mComposeModule;
QTest::newRow("null") << QByteArray() << mComposeModule;
QTest::newRow("fake") << QByteArray("fake") << mComposeModule;
// Test compositor with Text Input extension
QTest::newRow("ibus:text-input") << QByteArray("ibus") << mIbusModule;
QTest::newRow("compose:text-input") << QByteArray("compose") << mComposeModule;
QTest::newRow("empty:text-input") << QByteArray("") << mComposeModule;
QTest::newRow("null:text-input") << QByteArray() << mWaylandModule;
QTest::newRow("fake:text-input") << QByteArray("fake") << mComposeModule;
}
void tst_inputcontext::selectingInputContext()
{
QFETCH(QByteArray, requestedModule);
QFETCH(QByteArray, expectedModule);
if (requestedModule.isNull())
qunsetenv("QT_IM_MODULE");
else
qputenv("QT_IM_MODULE", requestedModule);
const bool withTextInputAtCompositorSide = QByteArray(QTest::currentDataTag()).endsWith(":text-input");
if (withTextInputAtCompositorSide)
ensureTextInputPresentOnCompositor();
else
ensureTextInputNotPresentOnCompositor();
int argc = 0;
QGuiApplication app(argc, nullptr); // loads the platform plugin
QCOMPARE(inputContextName(), expectedModule);
}
void tst_inputcontext::inputContextReconfigurationWhenTogglingTextInputExtension()
{
qunsetenv("QT_IM_MODULE");
ensureTextInputPresentOnCompositor();
int argc = 0;
QGuiApplication app(argc, nullptr); // loads the platform plugin
QCOMPARE(inputContextName(), mWaylandModule);
// remove text input extension after the platform plugin has been loaded
ensureTextInputNotPresentOnCompositor();
// QTRY_* because we need to spin the event loop for wayland QPA plugin
// to handle registry_global_remove()
QTRY_COMPARE(inputContextName(), mComposeModule);
// add text input extension after the platform plugin has been loaded
ensureTextInputPresentOnCompositor();
// QTRY_* because we need to spin the event loop for wayland QPA plugin
// to handle registry_global()
QTRY_COMPARE(inputContextName(), mWaylandModule);
}
int main(int argc, char *argv[])
{
qputenv("XDG_RUNTIME_DIR", ".");
qputenv("QT_QPA_PLATFORM", "wayland");
tst_inputcontext tc;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
}
#include "tst_inputcontext.moc"

View File

@ -215,10 +215,7 @@ void tst_output::screenOrder()
QTRY_COMPARE(QGuiApplication::screens().size(), 3);
const auto screens = QGuiApplication::screens();
QEXPECT_FAIL(nullptr, "TODO: fix screen order", Continue);
QCOMPARE(screens[1]->model(), "Screen 1");
QEXPECT_FAIL(nullptr, "TODO: fix screen order", Continue);
QCOMPARE(screens[2]->model(), "Screen 2");
exec([=] {

View File

@ -4,7 +4,8 @@ QMAKE_USE += wayland-server
WAYLANDSERVERSOURCES += \
$$PWD/../../../../src/3rdparty/protocol/wayland.xml \
$$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml
$$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml \
$$PWD/../../../../src/3rdparty/protocol/text-input-unstable-v2.xml
INCLUDEPATH += ../shared
@ -13,11 +14,13 @@ HEADERS += \
$$PWD/coreprotocol.h \
$$PWD/datadevice.h \
$$PWD/mockcompositor.h \
$$PWD/xdgshell.h
$$PWD/xdgshell.h \
$$PWD/textinput.h
SOURCES += \
$$PWD/corecompositor.cpp \
$$PWD/coreprotocol.cpp \
$$PWD/datadevice.cpp \
$$PWD/mockcompositor.cpp \
$$PWD/xdgshell.cpp
$$PWD/xdgshell.cpp \
$$PWD/textinput.cpp

View File

@ -0,0 +1,45 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "textinput.h"
namespace MockCompositor {
TextInputManager::TextInputManager(CoreCompositor *compositor)
{
init(compositor->m_display, 1);
}
void TextInputManager::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seatResource)
{
Q_UNUSED(resource);
Q_UNUSED(id);
Q_UNUSED(seatResource);
}
} // namespace MockCompositor

View File

@ -0,0 +1,51 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MOCKCOMPOSITOR_TEXTINPUT_H
#define MOCKCOMPOSITOR_TEXTINPUT_H
#include "coreprotocol.h"
#include <qwayland-server-text-input-unstable-v2.h>
#include <QtGui/qpa/qplatformnativeinterface.h>
namespace MockCompositor {
class TextInputManager : public Global, public QtWaylandServer::zwp_text_input_manager_v2
{
Q_OBJECT
public:
TextInputManager(CoreCompositor *compositor);
protected:
void zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override;
};
} // namespace MockCompositor
#endif // MOCKCOMPOSITOR_TEXTINPUT_H

View File

@ -65,7 +65,7 @@ void tst_xdgshell::showMinimized()
// Make sure the window on the compositor side is/was created here, and not after the test
// finishes, as that may mess up for later tests.
QCOMPOSITOR_TRY_VERIFY(surface());
QCOMPOSITOR_TRY_VERIFY(xdgSurface());
QVERIFY(!window.isExposed());
}
@ -426,9 +426,16 @@ void tst_xdgshell::switchPopups()
void tst_xdgshell::pongs()
{
QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
// Create and show a window to trigger shell integration initialzation,
// otherwise we don't have anything to send ping events to.
QRasterWindow window;
window.resize(200, 200);
window.show();
// Verify that the client has bound to the global
QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1);
QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong);
const uint serial = exec([=] { return nextSerial(); });
exec([=] {
auto *base = get<XdgWmBase>();