diff options
Diffstat (limited to 'src/compositor')
55 files changed, 1590 insertions, 343 deletions
diff --git a/src/compositor/compositor.pro b/src/compositor/compositor.pro index 47be591d7..b887cf281 100644 --- a/src/compositor/compositor.pro +++ b/src/compositor/compositor.pro @@ -3,6 +3,10 @@ MODULE = waylandcompositor QT = core gui-private +qtConfig(xkbcommon) { + QT_FOR_PRIVATE += xkbcommon_support-private +} + qtHaveModule(quick): QT += quick CONFIG -= precompile_header diff --git a/src/compositor/compositor_api/qwaylandclient.cpp b/src/compositor/compositor_api/qwaylandclient.cpp index 7f0b225b2..d26dfc6d5 100644 --- a/src/compositor/compositor_api/qwaylandclient.cpp +++ b/src/compositor/compositor_api/qwaylandclient.cpp @@ -44,7 +44,7 @@ #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> -#include <wayland-server.h> +#include <wayland-server-core.h> #include <wayland-util.h> QT_BEGIN_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandcompositor.cpp b/src/compositor/compositor_api/qwaylandcompositor.cpp index a0d69c52e..8ff9e0905 100644 --- a/src/compositor/compositor_api/qwaylandcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandcompositor.cpp @@ -72,7 +72,6 @@ #include "extensions/qwaylandqtwindowmanager.h" -#include "qwaylandxkb_p.h" #include "qwaylandsharedmemoryformathelper_p.h" #include <QtCore/QCoreApplication> @@ -129,12 +128,12 @@ public: bool isDown = ke->keyType == QEvent::KeyPress; #if QT_CONFIG(xkbcommon) - QString text; - Qt::KeyboardModifiers modifiers = QWaylandXkb::modifiers(keyb->xkbState()); + xkb_state *xkbState = keyb->xkbState(); + Qt::KeyboardModifiers modifiers = QXkbCommon::modifiers(xkbState); - const xkb_keysym_t sym = xkb_state_key_get_one_sym(keyb->xkbState(), code); - int qtkey; - std::tie(qtkey, text) = QWaylandXkb::keysymToQtKey(sym, modifiers); + const xkb_keysym_t sym = xkb_state_key_get_one_sym(xkbState, code); + int qtkey = QXkbCommon::keysymToQtKey(sym, modifiers, xkbState, code); + QString text = QXkbCommon::lookupString(xkbState, code); ke->key = qtkey; ke->modifiers = modifiers; @@ -162,12 +161,24 @@ QWaylandCompositorPrivate::QWaylandCompositorPrivate(QWaylandCompositor *composi { if (QGuiApplication::platformNativeInterface()) display = static_cast<wl_display*>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("server_wl_display")); - if (!display) + + if (!display) { display = wl_display_create(); + ownsDisplay = true; + } + eventHandler.reset(new QtWayland::WindowSystemEventHandler(compositor)); timer.start(); QWindowSystemInterfacePrivate::installWindowSystemEventHandler(eventHandler.data()); + +#if QT_CONFIG(xkbcommon) + mXkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS)); + if (!mXkbContext) { + qWarning("Failed to create a XKB context: keymap will not be supported"); + return; + } +#endif } void QWaylandCompositorPrivate::init() @@ -246,7 +257,8 @@ QWaylandCompositorPrivate::~QWaylandCompositorPrivate() // Some client buffer integrations need to clean up before the destroying the wl_display client_buffer_integration.reset(); - wl_display_destroy(display); + if (ownsDisplay) + wl_display_destroy(display); } void QWaylandCompositorPrivate::preInit() @@ -586,7 +598,7 @@ QByteArray QWaylandCompositor::socketName() const * \qmlmethod QtWaylandCompositor::WaylandCompositor::addSocketDescriptor(fd) * \since 5.12 * - * Listen for client connections on a file descriptor referring to a + * Listen for client connections on a file descriptor, \a fd, referring to a * server socket already bound and listening. * * Does not take ownership of the file descriptor; it must be closed @@ -598,7 +610,7 @@ QByteArray QWaylandCompositor::socketName() const */ /*! - * Listen for client connections on a file descriptor referring to a + * Listen for client connections on a file descriptor, \a fd, referring to a * server socket already bound and listening. * * Does not take ownership of the file descriptor; it must be closed diff --git a/src/compositor/compositor_api/qwaylandcompositor_p.h b/src/compositor/compositor_api/qwaylandcompositor_p.h index cdf4be6b4..2c9624216 100644 --- a/src/compositor/compositor_api/qwaylandcompositor_p.h +++ b/src/compositor/compositor_api/qwaylandcompositor_p.h @@ -60,6 +60,10 @@ #include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE namespace QtWayland { @@ -81,6 +85,10 @@ public: QWaylandCompositorPrivate(QWaylandCompositor *compositor); ~QWaylandCompositorPrivate() override; +#if QT_CONFIG(xkbcommon) + struct xkb_context *xkbContext() const { return mXkbContext.get(); } +#endif + void preInit(); void init(); @@ -137,6 +145,7 @@ protected: QList<int> externally_added_socket_fds; #endif struct wl_display *display = nullptr; + bool ownsDisplay = false; QList<QWaylandSeat *> seats; QList<QWaylandOutput *> outputs; @@ -168,6 +177,10 @@ protected: bool initialized = false; QList<QPointer<QObject> > polish_objects; +#if QT_CONFIG(xkbcommon) + QXkbCommon::ScopedXKBContext mXkbContext; +#endif + Q_DECLARE_PUBLIC(QWaylandCompositor) Q_DISABLE_COPY(QWaylandCompositorPrivate) }; diff --git a/src/compositor/compositor_api/qwaylanddestroylistener_p.h b/src/compositor/compositor_api/qwaylanddestroylistener_p.h index 7c6001c36..0bbeb69c0 100644 --- a/src/compositor/compositor_api/qwaylanddestroylistener_p.h +++ b/src/compositor/compositor_api/qwaylanddestroylistener_p.h @@ -55,7 +55,7 @@ #include <QtCore/private/qobject_p.h> -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandkeyboard.cpp b/src/compositor/compositor_api/qwaylandkeyboard.cpp index 68d855a66..2302c0b6a 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard.cpp +++ b/src/compositor/compositor_api/qwaylandkeyboard.cpp @@ -54,7 +54,6 @@ #if QT_CONFIG(xkbcommon) #include <sys/mman.h> #include <sys/types.h> -#include <qwaylandxkb_p.h> #endif QT_BEGIN_NAMESPACE @@ -67,12 +66,11 @@ QWaylandKeyboardPrivate::QWaylandKeyboardPrivate(QWaylandSeat *seat) QWaylandKeyboardPrivate::~QWaylandKeyboardPrivate() { #if QT_CONFIG(xkbcommon) - if (xkb_context) { + if (xkbContext()) { if (keymap_area) munmap(keymap_area, keymap_size); - close(keymap_fd); - xkb_context_unref(xkb_context); - xkb_state_unref(xkb_state); + if (keymap_fd >= 0) + close(keymap_fd); } #endif } @@ -137,14 +135,14 @@ void QWaylandKeyboardPrivate::keyboard_bind_resource(wl_keyboard::Resource *reso send_repeat_info(resource->handle, repeatRate, repeatDelay); #if QT_CONFIG(xkbcommon) - if (xkb_context) { + if (xkbContext()) { send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); } else #endif { int null_fd = open("/dev/null", O_RDONLY); - send_keymap(resource->handle, 0 /* WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP */, + send_keymap(resource->handle, WL_KEYBOARD_KEYMAP_FORMAT_NO_KEYMAP, null_fd, 0); close(null_fd); } @@ -164,11 +162,8 @@ void QWaylandKeyboardPrivate::keyboard_release(wl_keyboard::Resource *resource) void QWaylandKeyboardPrivate::keyEvent(uint code, uint32_t state) { -#if QT_CONFIG(xkbcommon) - uint key = toWaylandXkbV1Key(code); -#else - uint key = code; -#endif + uint key = toWaylandKey(code); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { keys << key; } else { @@ -180,30 +175,18 @@ void QWaylandKeyboardPrivate::sendKeyEvent(uint code, uint32_t state) { uint32_t time = compositor()->currentTimeMsecs(); uint32_t serial = compositor()->nextSerial(); -#if QT_CONFIG(xkbcommon) - uint key = toWaylandXkbV1Key(code); -#else - uint key = code; -#endif + uint key = toWaylandKey(code); if (focusResource) send_key(focusResource->handle, serial, time, key, state); } -void QWaylandKeyboardPrivate::modifiers(uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group) -{ - if (focusResource) { - send_modifiers(focusResource->handle, serial, mods_depressed, mods_latched, mods_locked, group); - } -} - #if QT_CONFIG(xkbcommon) void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() { if (!scanCodesByQtKey.isEmpty() || !xkbState()) return; - if (xkb_keymap *keymap = xkb_state_get_keymap(xkb_state)) { + if (xkb_keymap *keymap = xkb_state_get_keymap(xkbState())) { xkb_keymap_key_for_each(keymap, [](xkb_keymap *keymap, xkb_keycode_t keycode, void *d){ auto *scanCodesByQtKey = static_cast<QMap<ScanCodeKey, uint>*>(d); uint numLayouts = xkb_keymap_num_layouts_for_key(keymap, keycode); @@ -214,7 +197,7 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() continue; Qt::KeyboardModifiers mods = {}; - int qtKey = QWaylandXkb::keysymToQtKey(syms[0], mods).first; + int qtKey = QXkbCommon::keysymToQtKey(syms[0], mods); if (qtKey != 0) scanCodesByQtKey->insert({layout, qtKey}, keycode); } @@ -226,15 +209,15 @@ void QWaylandKeyboardPrivate::maybeUpdateXkbScanCodeTable() void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state) { #if QT_CONFIG(xkbcommon) - if (!xkb_context) + if (!xkbContext()) return; - xkb_state_update_key(xkb_state, code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); + xkb_state_update_key(xkbState(), code, state == WL_KEYBOARD_KEY_STATE_PRESSED ? XKB_KEY_DOWN : XKB_KEY_UP); - uint32_t modsDepressed = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_DEPRESSED); - uint32_t modsLatched = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LATCHED); - uint32_t modsLocked = xkb_state_serialize_mods(xkb_state, (xkb_state_component)XKB_STATE_LOCKED); - uint32_t group = xkb_state_serialize_group(xkb_state, (xkb_state_component)XKB_STATE_EFFECTIVE); + uint32_t modsDepressed = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_DEPRESSED); + uint32_t modsLatched = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LATCHED); + uint32_t modsLocked = xkb_state_serialize_mods(xkbState(), XKB_STATE_MODS_LOCKED); + uint32_t group = xkb_state_serialize_layout(xkbState(), XKB_STATE_LAYOUT_EFFECTIVE); if (this->modsDepressed == modsDepressed && this->modsLatched == modsLatched @@ -247,7 +230,10 @@ void QWaylandKeyboardPrivate::updateModifierState(uint code, uint32_t state) this->modsLocked = modsLocked; this->group = group; - modifiers(compositor()->nextSerial(), modsDepressed, modsLatched, modsLocked, group); + if (focusResource) { + send_modifiers(focusResource->handle, compositor()->nextSerial(), modsDepressed, + modsLatched, modsLocked, group); + } #else Q_UNUSED(code); Q_UNUSED(state); @@ -266,7 +252,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() pendingKeymap = false; #if QT_CONFIG(xkbcommon) - if (!xkb_context) + if (!xkbContext()) return; createXKBKeymap(); @@ -274,7 +260,7 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() send_keymap(res->handle, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymap_fd, keymap_size); } - xkb_state_update_mask(xkb_state, 0, modsLatched, modsLocked, 0, 0, 0); + xkb_state_update_mask(xkbState(), 0, modsLatched, modsLocked, 0, 0, 0); if (focusResource) send_modifiers(focusResource->handle, compositor()->nextSerial(), @@ -285,6 +271,24 @@ void QWaylandKeyboardPrivate::maybeUpdateKeymap() #endif } +uint QWaylandKeyboardPrivate::toWaylandKey(const uint nativeScanCode) +{ +#if QT_CONFIG(xkbcommon) + // In all current XKB keymaps there's a constant offset of 8 (for historical + // reasons) from hardware/evdev scancodes to XKB keycodes. On X11, we pass + // XKB keycodes (as sent by X server) via QKeyEvent::nativeScanCode. eglfs+evdev + // adds 8 for consistency, see qtbase/05c07c7636012ebb4131ca099ca4ea093af76410. + // eglfs+libinput also adds 8, for the same reason. Wayland protocol uses + // hardware/evdev scancodes, thus we need to minus 8 before sending the event + // out. + const uint offset = 8; + Q_ASSERT(nativeScanCode >= offset); + return nativeScanCode - offset; +#else + return nativeScanCode; +#endif +} + #if QT_CONFIG(xkbcommon) static int createAnonymousFile(size_t size) { @@ -316,18 +320,6 @@ static int createAnonymousFile(size_t size) return fd; } -void QWaylandKeyboardPrivate::initXKB() -{ - xkb_context = xkb_context_new(static_cast<xkb_context_flags>(0)); - if (!xkb_context) { - qWarning("Failed to create a XKB context: keymap will not be supported"); - return; - } - - createXKBKeymap(); -} - - void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap) { char *keymap_str = xkb_keymap_get_as_string(keymap, XKB_KEYMAP_FORMAT_TEXT_V1); @@ -356,46 +348,41 @@ void QWaylandKeyboardPrivate::createXKBState(xkb_keymap *keymap) strcpy(keymap_area, keymap_str); free(keymap_str); - if (xkb_state) - xkb_state_unref(xkb_state); - xkb_state = xkb_state_new(keymap); -} - -uint QWaylandKeyboardPrivate::toWaylandXkbV1Key(const uint nativeScanCode) -{ - const uint offset = 8; - Q_ASSERT(nativeScanCode >= offset); - return nativeScanCode - offset; + mXkbState.reset(xkb_state_new(keymap)); + if (!mXkbState) + qWarning("Failed to create XKB state"); } void QWaylandKeyboardPrivate::createXKBKeymap() { - if (!xkb_context) + if (!xkbContext()) return; - auto keymap = seat->keymap(); - struct xkb_rule_names rule_names = { strdup(qPrintable(keymap->rules())), - strdup(qPrintable(keymap->model())), - strdup(qPrintable(keymap->layout())), - strdup(qPrintable(keymap->variant())), - strdup(qPrintable(keymap->options())) }; - struct xkb_keymap *xkbKeymap = xkb_keymap_new_from_names(xkb_context, &rule_names, static_cast<xkb_keymap_compile_flags>(0)); - + QWaylandKeymap *keymap = seat->keymap(); + QByteArray rules = keymap->rules().toLocal8Bit(); + QByteArray model = keymap->model().toLocal8Bit(); + QByteArray layout = keymap->layout().toLocal8Bit(); + QByteArray variant = keymap->variant().toLocal8Bit(); + QByteArray options = keymap->options().toLocal8Bit(); + + struct xkb_rule_names rule_names = { + rules.constData(), + model.constData(), + layout.constData(), + variant.constData(), + options.constData() + }; + + QXkbCommon::ScopedXKBKeymap xkbKeymap(xkb_keymap_new_from_names(xkbContext(), &rule_names, + XKB_KEYMAP_COMPILE_NO_FLAGS)); if (xkbKeymap) { scanCodesByQtKey.clear(); - createXKBState(xkbKeymap); - xkb_keymap_unref(xkbKeymap); + createXKBState(xkbKeymap.get()); } else { qWarning("Failed to load the '%s' XKB keymap.", qPrintable(keymap->layout())); } - - free((char *)rule_names.rules); - free((char *)rule_names.model); - free((char *)rule_names.layout); - free((char *)rule_names.variant); - free((char *)rule_names.options); } -#endif +#endif // QT_CONFIG(xkbcommon) void QWaylandKeyboardPrivate::sendRepeatInfo() { @@ -430,7 +417,7 @@ QWaylandKeyboard::QWaylandKeyboard(QWaylandSeat *seat, QObject *parent) connect(keymap, &QWaylandKeymap::rulesChanged, this, &QWaylandKeyboard::updateKeymap); connect(keymap, &QWaylandKeymap::modelChanged, this, &QWaylandKeyboard::updateKeymap); #if QT_CONFIG(xkbcommon) - d->initXKB(); + d->createXKBKeymap(); #endif } @@ -486,7 +473,7 @@ QWaylandClient *QWaylandKeyboard::focusClient() const /*! * Sends the current key modifiers to \a client with the given \a serial. */ -void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint serial) +void QWaylandKeyboard::sendKeyModifiers(QWaylandClient *client, uint32_t serial) { Q_D(QWaylandKeyboard); QtWaylandServer::wl_keyboard::Resource *resource = d->resourceMap().value(client->client()); diff --git a/src/compositor/compositor_api/qwaylandkeyboard_p.h b/src/compositor/compositor_api/qwaylandkeyboard_p.h index 87e89e85e..4dfe0035e 100644 --- a/src/compositor/compositor_api/qwaylandkeyboard_p.h +++ b/src/compositor/compositor_api/qwaylandkeyboard_p.h @@ -51,6 +51,7 @@ // // We mean it. // +#include <QtWaylandCompositor/private/qwaylandcompositor_p.h> #include <QtWaylandCompositor/private/qtwaylandcompositorglobal_p.h> #include <QtWaylandCompositor/qwaylandseat.h> @@ -64,6 +65,7 @@ #if QT_CONFIG(xkbcommon) #include <xkbcommon/xkbcommon.h> +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> #endif @@ -83,11 +85,12 @@ public: QWaylandCompositor *compositor() const { return seat->compositor(); } void focused(QWaylandSurface* surface); - void modifiers(uint32_t serial, uint32_t mods_depressed, - uint32_t mods_latched, uint32_t mods_locked, uint32_t group); #if QT_CONFIG(xkbcommon) - struct xkb_state *xkbState() const { return xkb_state; } + struct xkb_state *xkbState() const { return mXkbState.get(); } + struct xkb_context *xkbContext() const { + return QWaylandCompositorPrivate::get(seat->compositor())->xkbContext(); + } uint32_t xkbModsMask() const { return modsDepressed | modsLatched | modsLocked; } void maybeUpdateXkbScanCodeTable(); #endif @@ -107,11 +110,10 @@ protected: private: #if QT_CONFIG(xkbcommon) - void initXKB(); void createXKBKeymap(); void createXKBState(xkb_keymap *keymap); #endif - static uint toWaylandXkbV1Key(const uint nativeScanCode); + static uint toWaylandKey(const uint nativeScanCode); void sendRepeatInfo(); @@ -134,8 +136,7 @@ private: char *keymap_area = nullptr; using ScanCodeKey = std::pair<uint,int>; // group/layout and QtKey QMap<ScanCodeKey, uint> scanCodesByQtKey; - struct xkb_context *xkb_context = nullptr; - struct xkb_state *xkb_state = nullptr; + QXkbCommon::ScopedXKBState mXkbState; #endif quint32 repeatRate = 40; diff --git a/src/compositor/compositor_api/qwaylandoutput.cpp b/src/compositor/compositor_api/qwaylandoutput.cpp index 1f34f4eac..006edbe6a 100644 --- a/src/compositor/compositor_api/qwaylandoutput.cpp +++ b/src/compositor/compositor_api/qwaylandoutput.cpp @@ -47,6 +47,7 @@ #include <QtWaylandCompositor/private/qwaylandsurface_p.h> #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> #include <QtWaylandCompositor/private/qwaylandview_p.h> +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtCore/QCoreApplication> #include <QtCore/QtMath> @@ -350,8 +351,8 @@ void QWaylandOutput::initialize() */ QWaylandOutput *QWaylandOutput::fromResource(wl_resource *resource) { - if (auto *r = QWaylandOutputPrivate::Resource::fromResource(resource)) - return static_cast<QWaylandOutputPrivate *>(r->output_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandOutputPrivate *>(resource)) + return p->q_func(); return nullptr; } diff --git a/src/compositor/compositor_api/qwaylandpointer.cpp b/src/compositor/compositor_api/qwaylandpointer.cpp index 77e736a58..96263e0c2 100644 --- a/src/compositor/compositor_api/qwaylandpointer.cpp +++ b/src/compositor/compositor_api/qwaylandpointer.cpp @@ -239,7 +239,7 @@ uint QWaylandPointer::sendMouseReleaseEvent(Qt::MouseButton button) /*! * Sets the current mouse focus to \a view and sends a mouse move event to it with the - * local position \a localPos and output space position \a outputSpacePos. + * local position \a localPos in surface coordinates and output space position \a outputSpacePos. */ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &localPos, const QPointF &outputSpacePos) { @@ -253,7 +253,7 @@ void QWaylandPointer::sendMouseMoveEvent(QWaylandView *view, const QPointF &loca if (view) { // We adjust if the mouse position is on the edge // to work around Qt's event propagation - QSizeF size(view->surface()->size()); + QSizeF size(view->surface()->destinationSize()); if (d->localPosition.x() == size.width()) d->localPosition.rx() -= 0.01; if (d->localPosition.y() == size.height()) @@ -294,7 +294,7 @@ QWaylandView *QWaylandPointer::mouseFocus() const } /*! - * Returns the current local position of the QWaylandPointer. + * Returns the current local position of the QWaylandPointer in surface coordinates. */ QPointF QWaylandPointer::currentLocalPosition() const { diff --git a/src/compositor/compositor_api/qwaylandquickcompositor.cpp b/src/compositor/compositor_api/qwaylandquickcompositor.cpp index 8e8a903e3..14b592efb 100644 --- a/src/compositor/compositor_api/qwaylandquickcompositor.cpp +++ b/src/compositor/compositor_api/qwaylandquickcompositor.cpp @@ -53,6 +53,7 @@ #include "qwaylandquickitem.h" #include "qwaylandoutput.h" #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> +#include <QtWaylandCompositor/QWaylandViewporter> #include "qwaylandsurfacegrabber.h" QT_BEGIN_NAMESPACE @@ -60,8 +61,9 @@ QT_BEGIN_NAMESPACE class QWaylandQuickCompositorPrivate : public QWaylandCompositorPrivate { public: - QWaylandQuickCompositorPrivate(QWaylandCompositor *compositor) + explicit QWaylandQuickCompositorPrivate(QWaylandCompositor *compositor) : QWaylandCompositorPrivate(compositor) + , m_viewporter(new QWaylandViewporter(compositor)) { } protected: @@ -69,6 +71,8 @@ protected: { return new QWaylandQuickSurface(); } +private: + QScopedPointer<QWaylandViewporter> m_viewporter; }; QWaylandQuickCompositor::QWaylandQuickCompositor(QObject *parent) @@ -86,15 +90,15 @@ QWaylandQuickCompositor::QWaylandQuickCompositor(QObject *parent) * For instance, the following code would allow the clients to request \c wl_shell * surfaces in the compositor using the \c wl_shell interface. * - * \code - * import QtWayland.Compositor 1.0 + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * WlShell { * // ... * } * } - * \endcode + * \endqml */ void QWaylandQuickCompositor::create() diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index 2b24c13b7..7b0d5c5d5 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.cpp +++ b/src/compositor/compositor_api/qwaylandquickitem.cpp @@ -64,7 +64,7 @@ #include <QtCore/QMutexLocker> #include <QtCore/QMutex> -#include <wayland-server.h> +#include <wayland-server-core.h> #include <QThread> #ifndef GL_TEXTURE_EXTERNAL_OES @@ -295,7 +295,8 @@ public: } auto texture = buffer.toOpenGLTexture(); - m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId() , QSize(surfaceItem->width(), surfaceItem->height()), opt); + auto size = surface->bufferSize(); + m_sgTex = surfaceItem->window()->createTextureFromId(texture->textureId(), size, opt); } } emit textureChanged(); @@ -414,7 +415,11 @@ QWaylandSurface *QWaylandQuickItem::surface() const void QWaylandQuickItem::setSurface(QWaylandSurface *surface) { Q_D(QWaylandQuickItem); + QWaylandCompositor *oldComp = d->view->surface() ? d->view->surface()->compositor() : nullptr; d->view->setSurface(surface); + QWaylandCompositor *newComp = d->view->surface() ? d->view->surface()->compositor() : nullptr; + if (oldComp != newComp) + emit compositorChanged(); update(); } @@ -821,7 +826,15 @@ void QWaylandQuickItem::setOutput(QWaylandOutput *output) } /*! - * \property QWaylandQuickItem::isBufferLocked + * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::bufferLocked + * + * This property holds whether the item's buffer is currently locked. As long as + * the buffer is locked, it will not be released and returned to the client. + * + * The default is false. + */ +/*! + * \property QWaylandQuickItem::bufferLocked * * This property holds whether the item's buffer is currently locked. As long as * the buffer is locked, it will not be released and returned to the client. @@ -886,7 +899,7 @@ void QWaylandQuickItem::handleSurfaceChanged() if (d->oldSurface) { disconnect(d->oldSurface.data(), &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged); disconnect(d->oldSurface.data(), &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged); - disconnect(d->oldSurface.data(), &QWaylandSurface::sizeChanged, this, &QWaylandQuickItem::updateSize); + disconnect(d->oldSurface.data(), &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize); disconnect(d->oldSurface.data(), &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize); disconnect(d->oldSurface.data(), &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer); disconnect(d->oldSurface.data(), &QWaylandSurface::redraw, this, &QQuickItem::update); @@ -903,7 +916,7 @@ void QWaylandQuickItem::handleSurfaceChanged() if (QWaylandSurface *newSurface = d->view->surface()) { connect(newSurface, &QWaylandSurface::hasContentChanged, this, &QWaylandQuickItem::surfaceMappedChanged); connect(newSurface, &QWaylandSurface::parentChanged, this, &QWaylandQuickItem::parentChanged); - connect(newSurface, &QWaylandSurface::sizeChanged, this, &QWaylandQuickItem::updateSize); + connect(newSurface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandQuickItem::updateSize); connect(newSurface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandQuickItem::updateSize); connect(newSurface, &QWaylandSurface::configure, this, &QWaylandQuickItem::updateBuffer); connect(newSurface, &QWaylandSurface::redraw, this, &QQuickItem::update); @@ -992,7 +1005,7 @@ void QWaylandQuickItem::updateSize() QSize size(0, 0); if (surface()) - size = surface()->size() * (d->scaleFactor() / surface()->bufferScale()); + size = surface()->destinationSize() * d->scaleFactor(); setImplicitSize(size.width(), size.height()); if (d->sizeFollowsSurface) @@ -1054,6 +1067,14 @@ bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) } /*! + * \qmlmethod point WaylandQuickItem::mapToSurface(point point) + * + * Maps the given \a point in this item's coordinate system to the equivalent + * point within the Wayland surface's coordinate system, and returns the mapped + * coordinate. + */ + +/*! * Maps the given \a point in this item's coordinate system to the equivalent * point within the Wayland surface's coordinate system, and returns the mapped * coordinate. @@ -1061,16 +1082,42 @@ bool QWaylandQuickItem::inputRegionContains(const QPointF &localPosition) QPointF QWaylandQuickItem::mapToSurface(const QPointF &point) const { Q_D(const QWaylandQuickItem); - if (!surface() || surface()->size().isEmpty()) + if (!surface() || surface()->destinationSize().isEmpty()) return point / d->scaleFactor(); - qreal xScale = width() / surface()->size().width() * surface()->bufferScale(); - qreal yScale = height() / surface()->size().height() * surface()->bufferScale(); + qreal xScale = width() / surface()->destinationSize().width(); + qreal yScale = height() / surface()->destinationSize().height(); return QPointF(point.x() / xScale, point.y() / yScale); } /*! + * \qmlmethod point WaylandQuickItem::mapFromSurface(point point) + * \since 5.13 + * + * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent + * point within this item's coordinate system, and returns the mapped coordinate. + */ + +/*! + * Maps the given \a point in the Wayland surfaces's coordinate system to the equivalent + * point within this item's coordinate system, and returns the mapped coordinate. + * + * \since 5.13 + */ +QPointF QWaylandQuickItem::mapFromSurface(const QPointF &point) const +{ + Q_D(const QWaylandQuickItem); + if (!surface() || surface()->destinationSize().isEmpty()) + return point * d->scaleFactor(); + + qreal xScale = width() / surface()->destinationSize().width(); + qreal yScale = height() / surface()->destinationSize().height(); + + return QPointF(point.x() * xScale, point.y() * yScale); +} + +/*! * \qmlproperty bool QtWaylandCompositor::WaylandQuickItem::sizeFollowsSurface * * This property specifies whether the size of the item should always match @@ -1245,7 +1292,7 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries) * If an animation is started, bufferLocked should be set to ensure the item keeps its content * until the animation finishes * - * \sa isBufferLocked + * \sa bufferLocked */ /*! @@ -1258,7 +1305,7 @@ void QWaylandQuickItem::updateInputMethod(Qt::InputMethodQueries queries) * If an animation is started, bufferLocked should be set to ensure the item keeps its content * until the animation finishes * - * \sa QWaylandQuickkItem::bufferLocked + * \sa QWaylandQuickItem::bufferLocked */ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) @@ -1301,6 +1348,10 @@ QSGNode *QWaylandQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDat d->provider->setSmooth(smooth()); node->setRect(rect); + qreal scale = surface()->bufferScale(); + QRectF source = surface()->sourceGeometry(); + node->setSourceRect(QRectF(source.topLeft() * scale, source.size() * scale)); + return node; } else { Q_ASSERT(!d->provider); diff --git a/src/compositor/compositor_api/qwaylandquickitem.h b/src/compositor/compositor_api/qwaylandquickitem.h index 23708353e..2aa8e1b73 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.h +++ b/src/compositor/compositor_api/qwaylandquickitem.h @@ -61,7 +61,7 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandQuickItem : public QQuickItem { Q_OBJECT Q_DECLARE_PRIVATE(QWaylandQuickItem) - Q_PROPERTY(QWaylandCompositor *compositor READ compositor) + Q_PROPERTY(QWaylandCompositor *compositor READ compositor NOTIFY compositorChanged) Q_PROPERTY(QWaylandSurface *surface READ surface WRITE setSurface NOTIFY surfaceChanged) Q_PROPERTY(bool paintEnabled READ paintEnabled WRITE setPaintEnabled) Q_PROPERTY(bool touchEventsEnabled READ touchEventsEnabled WRITE setTouchEventsEnabled NOTIFY touchEventsEnabledChanged) @@ -102,6 +102,7 @@ public: bool inputRegionContains(const QPointF &localPosition) const; bool inputRegionContains(const QPointF &localPosition); Q_INVOKABLE QPointF mapToSurface(const QPointF &point) const; + Q_REVISION(13) Q_INVOKABLE QPointF mapFromSurface(const QPointF &point) const; bool sizeFollowsSurface() const; void setSizeFollowsSurface(bool sizeFollowsSurface); @@ -176,6 +177,7 @@ private Q_SLOTS: Q_SIGNALS: void surfaceChanged(); + void compositorChanged(); void touchEventsEnabledChanged(); void originChanged(); void surfaceDestroyed(); diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp index a7b01ef03..bc5e1d8b2 100644 --- a/src/compositor/compositor_api/qwaylandseat.cpp +++ b/src/compositor/compositor_api/qwaylandseat.cpp @@ -54,6 +54,7 @@ #if QT_CONFIG(wayland_datadevice) #include <QtWaylandCompositor/private/qwldatadevice_p.h> #endif +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include "extensions/qwlqtkey_p.h" #include "extensions/qwaylandtextinput.h" @@ -176,7 +177,7 @@ void QWaylandSeatPrivate::seat_get_touch(wl_seat::Resource *resource, uint32_t i */ /*! - * Constructs a QWaylandSeat for the given \a compositor and with the given \a capabilityFlags. + * Constructs a QWaylandSeat for the given \a compositor and \a capabilityFlags. */ QWaylandSeat::QWaylandSeat(QWaylandCompositor *compositor, CapabilityFlags capabilityFlags) : QWaylandObject(*new QWaylandSeatPrivate(this)) @@ -195,6 +196,14 @@ QWaylandSeat::~QWaylandSeat() { } +/*! + * Initializes parts of the seat corresponding to the capabilities set in the constructor, or + * through setCapabilities(). + * + * \note Normally, this function is called automatically after the seat and compositor have been + * created, so calling it manually is usually unnecessary. + */ + void QWaylandSeat::initialize() { Q_D(QWaylandSeat); @@ -210,6 +219,11 @@ void QWaylandSeat::initialize() d->isInitialized = true; } +/*! + * Returns true if the QWaylandSeat is initialized; false otherwise. + * + * The value \c true indicates that it's now possible for clients to start using the seat. + */ bool QWaylandSeat::isInitialized() const { Q_D(const QWaylandSeat); @@ -534,6 +548,11 @@ bool QWaylandSeat::setKeyboardFocus(QWaylandSurface *surface) return true; } + +/*! + * Returns the keymap object for this QWaylandSeat. + */ + QWaylandKeymap *QWaylandSeat::keymap() { Q_D(const QWaylandSeat); @@ -631,8 +650,8 @@ bool QWaylandSeat::isOwner(QInputEvent *inputEvent) const */ QWaylandSeat *QWaylandSeat::fromSeatResource(struct ::wl_resource *resource) { - if (auto *r = QWaylandSeatPrivate::Resource::fromResource(resource)) - return static_cast<QWaylandSeatPrivate *>(r->seat_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandSeatPrivate *>(resource)) + return p->q_func(); return nullptr; } @@ -653,4 +672,65 @@ void QWaylandSeat::handleMouseFocusDestroyed() emit mouseFocusChanged(d->mouseFocus, oldFocus); } + +/*! \qmlsignal void QtWaylandCompositor::QWaylandSeat::keyboardFocusChanged(QWaylandSurface newFocus, QWaylandSurface oldFocus) + * + * This signal is emitted when setKeyboardFocus() is called or when a WaylandQuickItem has focus + * and the user starts pressing keys. + * + * \a newFocus has the surface that received keyboard focus; or \c nullptr if no surface has + * focus. + * \a oldFocus has the surface that lost keyboard focus; or \c nullptr if no surface had focus. + */ + +/*! + * \fn void QWaylandSeat::keyboardFocusChanged(QWaylandSurface *newFocus, QWaylandSurface *oldFocus) + * + * This signal is emitted when setKeyboardFocus() is called. + * + * \a newFocus has the surface that received keyboard focus; or \c nullptr if no surface has + * focus. + * \a oldFocus has the surface that lost keyboard focus; or \c nullptr if no surface had focus. + */ + +/*! \qmlsignal void QtWaylandCompositor::QWaylandSeat::cursorSurfaceRequest(QWaylandSurface surface, int hotspotX, int hotspotY) + * + * This signal is emitted when the client has requested for a specific \a surface to be the mouse + * cursor. For example, when the user hovers over a particular surface, and you want the cursor + * to change into a resize arrow. + * + * Both \a hotspotX and \a hotspotY are offsets from the top-left of a pointer surface, where a + * click should happen. For example, if the requested cursor surface is an arrow, the parameters + * indicate where the arrow's tip is, on that surface. + */ + + +/*! + * \fn void QWaylandSeat::cursorSurfaceRequest(QWaylandSurface *surface, int hotspotX, int hotspotY) + * + * This signal is emitted when the client has requested for a specific \a surface to be the mouse + * cursor. For example, when the user hovers over a particular surface, and you want the cursor + * to change into a resize arrow. + */ + +/*! + * \property QWaylandSeat::drag + * + * This property holds the drag and drop operations and sends signals when they start and end. + * The property stores details like what image should be under the mouse cursor when the user + * drags it. + */ + +/*! + * \property QWaylandSeat::keymap + * This property holds the keymap object. + * + * A keymap provides a way to translate actual key scan codes into a meaningful value. + * For example, if you use a keymap with a Norwegian layout, the key to the right of + * the letter L produces an Ø. + * + * Keymaps can also be used to customize key functions, such as to specify whether + * Control and CAPS lock should be swapped, and so on. + */ + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp index f457c372c..c79787e6e 100644 --- a/src/compositor/compositor_api/qwaylandsurface.cpp +++ b/src/compositor/compositor_api/qwaylandsurface.cpp @@ -59,6 +59,7 @@ #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> #include <QtWaylandCompositor/private/qwaylandview_p.h> #include <QtWaylandCompositor/private/qwaylandseat_p.h> +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtCore/private/qobject_p.h> @@ -234,21 +235,29 @@ void QWaylandSurfacePrivate::surface_commit(Resource *) // Needed in order to know whether we want to emit signals later QSize oldBufferSize = bufferSize; + QRectF oldSourceGeometry = sourceGeometry; + QSize oldDestinationSize = destinationSize; bool oldHasContent = hasContent; int oldBufferScale = bufferScale; // Update all internal state if (pending.buffer.hasBuffer() || pending.newlyAttached) bufferRef = pending.buffer; + bufferScale = pending.bufferScale; bufferSize = bufferRef.size(); - damage = pending.damage.intersected(QRect(QPoint(), bufferSize)); + QSize surfaceSize = bufferSize / bufferScale; + sourceGeometry = !pending.sourceGeometry.isValid() ? QRect(QPoint(), surfaceSize) : pending.sourceGeometry; + destinationSize = pending.destinationSize.isEmpty() ? sourceGeometry.size().toSize() : pending.destinationSize; + damage = pending.damage.intersected(QRect(QPoint(), destinationSize)); hasContent = bufferRef.hasContent(); - bufferScale = pending.bufferScale; frameCallbacks << pendingFrameCallbacks; - inputRegion = pending.inputRegion.intersected(QRect(QPoint(), bufferSize)); - opaqueRegion = pending.opaqueRegion.intersected(QRect(QPoint(), bufferSize)); + inputRegion = pending.inputRegion.intersected(QRect(QPoint(), destinationSize)); + opaqueRegion = pending.opaqueRegion.intersected(QRect(QPoint(), destinationSize)); QPoint offsetForNextFrame = pending.offset; + if (viewport) + viewport->checkCommittedState(); + // Clear per-commit state pending.buffer = QWaylandBufferRef(); pending.offset = QPoint(); @@ -268,12 +277,22 @@ void QWaylandSurfacePrivate::surface_commit(Resource *) emit q->damaged(damage); - if (oldBufferSize != bufferSize) + if (oldBufferSize != bufferSize) { + emit q->bufferSizeChanged(); +#if QT_DEPRECATED_SINCE(5, 13) emit q->sizeChanged(); +#endif + } if (oldBufferScale != bufferScale) emit q->bufferScaleChanged(); + if (oldDestinationSize != destinationSize) + emit q->destinationSizeChanged(); + + if (oldSourceGeometry != sourceGeometry) + emit q->sourceGeometryChanged(); + if (oldHasContent != hasContent) emit q->hasContentChanged(); @@ -431,9 +450,7 @@ QWaylandClient *QWaylandSurface::client() const } /*! - * \property QWaylandSurface::waylandClient - * - * This property holds the \c wl_client using this QWaylandSurface. + * Holds the \c wl_client using this QWaylandSurface. */ ::wl_client *QWaylandSurface::waylandClient() const { @@ -461,21 +478,109 @@ bool QWaylandSurface::hasContent() const } /*! + * \qmlproperty rect QtWaylandCompositor::WaylandSurface::sourceGeometry + * \since 5.13 + * + * This property describes the portion of the attached Wayland buffer that should + * be drawn on the screen. The coordinates are from the corner of the buffer and are + * scaled by \l bufferScale. + * + * \sa bufferScale + * \sa bufferSize + * \sa destinationSize + */ + +/*! + * \property QWaylandSurface::sourceGeometry + * \since 5.13 + * + * This property describes the portion of the attached QWaylandBuffer that should + * be drawn on the screen. The coordinates are from the corner of the buffer and are + * scaled by \l bufferScale. + * + * \sa bufferScale + * \sa bufferSize + * \sa destinationSize + */ +QRectF QWaylandSurface::sourceGeometry() const +{ + Q_D(const QWaylandSurface); + return d->sourceGeometry; +} + +/*! + * \qmlproperty size QtWaylandCompositor::WaylandSurface::destinationSize + * \since 5.13 + * + * This property holds the size of this WaylandSurface in surface coordinates. + * + * \sa bufferScale + * \sa bufferSize + */ + +/*! + * \property QWaylandSurface::destinationSize + * \since 5.13 + * + * This property holds the size of this WaylandSurface in surface coordinates. + * + * \sa bufferScale + * \sa bufferSize + */ +QSize QWaylandSurface::destinationSize() const +{ + Q_D(const QWaylandSurface); + return d->destinationSize; +} + +/*! + * \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferSize + * + * This property holds the size of the current buffer of this WaylandSurface in pixels, + * not in surface coordinates. + * + * For the size in surface coordinates, use \l destinationSize instead. + * + * \sa destinationSize + * \sa bufferScale + */ + +/*! + * \property QWaylandSurface::bufferSize + * + * This property holds the size of the current buffer of this QWaylandSurface in pixels, + * not in surface coordinates. + * + * For the size in surface coordinates, use \l destinationSize instead. + * + * \sa destinationSize + * \sa bufferScale + */ +QSize QWaylandSurface::bufferSize() const +{ + Q_D(const QWaylandSurface); + return d->bufferSize; +} + +#if QT_DEPRECATED_SINCE(5, 13) +/*! * \qmlproperty size QtWaylandCompositor::WaylandSurface::size + * \obsolete use bufferSize or destinationSize instead * - * This property holds the WaylandSurface's size in pixels. + * This property has been deprecated, use \l bufferSize or \l destinationSize instead. */ /*! * \property QWaylandSurface::size + * \obsolete use bufferSize or destinationSize instead * - * This property holds the QWaylandSurface's size in pixels. + * This property has been deprecated, use \l bufferSize or \l destinationSize instead. */ QSize QWaylandSurface::size() const { - Q_D(const QWaylandSurface); - return d->bufferSize; + return bufferSize(); } +#endif /*! * \qmlproperty size QtWaylandCompositor::WaylandSurface::bufferScale @@ -740,12 +845,12 @@ QList<QWaylandView *> QWaylandSurface::views() const } /*! - * Returns the QWaylandSurface corresponding to the Wayland resource \a res. + * Returns the QWaylandSurface corresponding to the Wayland resource \a resource. */ -QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *res) +QWaylandSurface *QWaylandSurface::fromResource(::wl_resource *resource) { - if (auto *r = QWaylandSurfacePrivate::Resource::fromResource(res)) - return static_cast<QWaylandSurfacePrivate *>(r->surface_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandSurfacePrivate *>(resource)) + return p->q_func(); return nullptr; } @@ -759,11 +864,12 @@ struct wl_resource *QWaylandSurface::resource() const } /*! - * Sets a \a role on the surface. A role defines how a surface will be mapped on screen, without a role - * a surface is supposed to be hidden. Only one role at all times can be set on a surface. Although + * Sets a \a role on the surface. A role defines how a surface will be mapped on screen; without a role + * a surface is supposed to be hidden. Only one role can be set on a surface, at all times. Although * setting the same role many times is allowed, attempting to change the role of a surface will trigger * a protocol error to the \a errorResource and send an \a errorCode to the client. * + * Returns true if a role can be assigned; false otherwise. */ bool QWaylandSurface::setRole(QWaylandSurfaceRole *role, wl_resource *errorResource, uint32_t errorCode) { @@ -904,4 +1010,21 @@ void QWaylandSurfacePrivate::Subsurface::subsurface_set_desync(wl_subsurface::Re * This signal is emitted when a \a drag has started from this surface. */ +/*! + * \fn void damaged(const QRegion &rect) + * + * This signal is emitted when the client tells the compositor that a particular part of, or + * possibly the entire surface has been updated, so the compositor can redraw that part. + * + * While the compositor APIs take care of redrawing automatically, this function may be useful + * if you require a specific, custom behavior. + */ + +/*! + * \fn void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent) + * + * This signal is emitted when the client has requested that this surface should be a + * subsurface of \a newParent. + */ + QT_END_NAMESPACE diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h index a138b2af5..667f911c3 100644 --- a/src/compositor/compositor_api/qwaylandsurface.h +++ b/src/compositor/compositor_api/qwaylandsurface.h @@ -81,7 +81,12 @@ class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandSurface : public QWaylandObject Q_OBJECT Q_DECLARE_PRIVATE(QWaylandSurface) Q_PROPERTY(QWaylandClient *client READ client CONSTANT) - Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) + Q_PROPERTY(QRectF sourceGeometry READ sourceGeometry NOTIFY sourceGeometryChanged REVISION 13) + Q_PROPERTY(QSize destinationSize READ destinationSize NOTIFY destinationSizeChanged REVISION 13) + Q_PROPERTY(QSize bufferSize READ bufferSize NOTIFY bufferSizeChanged REVISION 13) +#if QT_DEPRECATED_SINCE(5, 13) + Q_PROPERTY(QSize size READ size NOTIFY sizeChanged) // Qt 6: Remove +#endif Q_PROPERTY(int bufferScale READ bufferScale NOTIFY bufferScaleChanged) Q_PROPERTY(Qt::ScreenOrientation contentOrientation READ contentOrientation NOTIFY contentOrientationChanged) Q_PROPERTY(QWaylandSurface::Origin origin READ origin NOTIFY originChanged) @@ -110,7 +115,12 @@ public: bool hasContent() const; - QSize size() const; + QRectF sourceGeometry() const; + QSize destinationSize() const; +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED QSize size() const; +#endif + QSize bufferSize() const; int bufferScale() const; Qt::ScreenOrientation contentOrientation() const; @@ -155,7 +165,12 @@ Q_SIGNALS: void damaged(const QRegion &rect); void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent); void childAdded(QWaylandSurface *child); - void sizeChanged(); + Q_REVISION(13) void sourceGeometryChanged(); + Q_REVISION(13) void destinationSizeChanged(); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED void sizeChanged(); +#endif + Q_REVISION(13) void bufferSizeChanged(); void bufferScaleChanged(); void offsetForNextFrame(const QPoint &offset); void contentOrientationChanged(); diff --git a/src/compositor/compositor_api/qwaylandsurface_p.h b/src/compositor/compositor_api/qwaylandsurface_p.h index df868de63..1637d8704 100644 --- a/src/compositor/compositor_api/qwaylandsurface_p.h +++ b/src/compositor/compositor_api/qwaylandsurface_p.h @@ -73,6 +73,7 @@ #include <wayland-util.h> #include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#include <QtWaylandCompositor/private/qwaylandviewporter_p.h> QT_BEGIN_NAMESPACE @@ -144,14 +145,17 @@ public: //member variables QRegion damage; QWaylandBufferRef bufferRef; QWaylandSurfaceRole *role = nullptr; + QWaylandViewporterPrivate::Viewport *viewport = nullptr; struct { QWaylandBufferRef buffer; QRegion damage; QPoint offset; - bool newlyAttached; + bool newlyAttached = false; QRegion inputRegion; - int bufferScale; + int bufferScale = 1; + QRectF sourceGeometry; + QSize destinationSize; QRegion opaqueRegion; } pending; @@ -166,6 +170,8 @@ public: //member variables QRegion inputRegion; QRegion opaqueRegion; + QRectF sourceGeometry; + QSize destinationSize; QSize bufferSize; int bufferScale = 1; bool isCursorSurface = false; diff --git a/src/compositor/configure.json b/src/compositor/configure.json index 9a6655241..aabd2472f 100644 --- a/src/compositor/configure.json +++ b/src/compositor/configure.json @@ -80,6 +80,12 @@ "type": "compile", "test": "dmabuf_server_buffer", "use": "egl" + }, + "dmabuf-client-buffer": { + "label": "Linux Client dma-buf Buffer Sharing", + "type": "compile", + "test": "dmabuf_client_buffer", + "use": "egl" } }, @@ -128,6 +134,11 @@ "condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-server-buffer", "output": [ "privateFeature" ] }, + "wayland-dmabuf-client-buffer": { + "label": "Linux dma-buf client buffer integration", + "condition": "features.wayland-server && features.opengl && features.egl && tests.dmabuf-client-buffer", + "output": [ "privateFeature" ] + }, "wayland-shm-emulation-server-buffer": { "label": "Shm emulation server buffer", "condition": "features.wayland-server && features.opengl", diff --git a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc index 6be6444cc..440a793cb 100644 --- a/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc +++ b/src/compositor/doc/src/qtwaylandcompositor-overview.qdoc @@ -28,65 +28,53 @@ /*! \page qtwaylandcompositor-index.html \title Qt Wayland Compositor - \brief An API to develop display servers supporting the Wayland protocol - - \l {https://wayland.freedesktop.org/} {Wayland} is a display server - protocol to help in creating multi-process systems, where multiple client - applications may render content on the same display, by going via - a compositor process. - - Compared to a system with a single-process design, a multi-process system - gives several benefits: - - \list - \li Easier resource management, through the regular operating system mechanisms. - \li Better security, as each application can run with its own permissions or - sandbox. - \li Clearer separation of application UI and compositor UI, so each - can be modified independently. - \endlist - - In a typical Wayland-based system, multiple client processes will render their - own contents to off-screen buffers. The information about these buffers will - then be passed to a display server process by using the Wayland protocol. - Finally, the display server process will composite and position the contents - on a physical display. - - Qt Wayland Compositor is a module that provides convenient and powerful - QML and C++ APIs for developing custom display servers based on this protocol. - The server displays content from client applications that support the Wayland - protocol. The design philosophy of Wayland is to keep the core protocol simple - and minimal, and to expand on this with use-case-specific extensions. Qt Wayland - Compositor supports many common extensions by default, and also has APIs that - enables the creation of new, custom extensions. - - In one typical use case, a display server written with Qt Wayland Compositor will - be a subsystem inside a larger application manager process. Qt Wayland Compositor - provides the APIs to communicate with clients and display their contents on screen, - using C++ for low-level access and the full set of Qt Quick effects, animations and - convenience when using the QML APIs. A typical application manager would, in addition - to this, implement features such as application life cycle, virtual keyboard input, - security and IPC. Qt provides APIs that can be used to develop the remaining parts - of an application manager in other modules. The \l {https://www.qt.io/qt-automotive-suite/} - {Qt Automotive Suite} provides a complete application manager which includes a - display server developed using Qt Wayland Compositor. + \brief An API to develop display servers that support the Wayland protocol. + + The Qt Wayland Compositor is a module that provides convenient and powerful + QML and C++ APIs for developing custom display servers based on the + \l {https://wayland.freedesktop.org/}{Wayland} protocol. The display server, + often called a compositor, displays content from client applications that + support the Wayland protocol. + + Wayland's design philosophy is to keep the core protocol simple and minimal. + Developers can then expand on this core protocol with use-case-specific + extensions. Qt Wayland Compositor supports many common extensions by default, + and also has APIs to enable the creation of new, custom extensions. + + Typically, a compositor written with Qt Wayland Compositor becomes a + subsystem inside a larger application manager process. Qt Wayland Compositor + provides the APIs to communicate with clients and display their content on + the screen. The QML APIs contain high-level APIs that easily integrate with + the rest of Qt, enabling convenient animations, effects, and UI through + Qt Quick. There are also C++ APIs available - if you need more low-level + access. + + An application manager would typically implement additional features such as + application life cycle, virtual keyboard input, security, and Inter-Process + Communication (IPC). Qt provides the APIs that can be used to develop the + remaining parts of an application manager in other modules. The + \l {https://www.qt.io/qt-automotive-suite/}{Qt Automotive Suite} provides + \l{Qt Application Manager}, which is a complete application manager that + includes a compositor developed using Qt Wayland Compositor. + + For more information on Wayland, see \l{Wayland and Qt}. \section1 Features of Qt Wayland Compositor - The Qt Wayland Compositor API includes features needed to create a display server. + The Qt Wayland Compositor includes features necessary to create a compositor: \list - \li A QML API that can be used to display and manipulate client content, fully - integrated with all the features in Qt Quick. - \li A C++ API for low-level access and control. - \li Support for common extensions, including XDG Shell and IVI Application. - \li APIs to easily expand support to include custom extensions. + \li A QML API to display and manipulate client content, fully integrated + with all the features in Qt Quick. + \li A C++ API for low-level access and control. + \li Support for common extensions, including XDG Shell and IVI Application. + \li APIs to easily expand the support for custom extensions. \endlist \section1 Environment Variables and Command-line Arguments - The Qt Wayland Compositor API recognizes some environment variables and - command-line arguments that can be used to customize its behavior. + The Qt Wayland Compositor recognizes the following environment variables and + command-line arguments: \list \li Environment variables: @@ -107,8 +95,17 @@ \section1 Examples - Take a look at the \l{Qt Wayland Compositor Examples} for a demonstration on - how the APIs can be used to write custom display servers. + Take a look at the \l{Qt Wayland Compositor Examples} to learn how these APIs + can be used to write custom compositors. + + \section1 API Reference + + The Qt Wayland Compositor can be used from C++ or QML: + + \list + \li \l{Qt Wayland Compositor QML Types} + \li \l{Qt Wayland Compositor C++ Classes} + \endlist \section1 Licenses and Attributions @@ -124,13 +121,4 @@ \generatelist{groupsbymodule attributions-qtwaylandcompositor} - \section1 API Reference - - The Qt Wayland Compositor API can be used from C++ or QML. - - \list - \li \l{Qt Wayland Compositor QML Types} - \li \l{Qt Wayland Compositor C++ Classes} - \endlist - */ diff --git a/src/compositor/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 5c708f891..361f93984 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -9,6 +9,8 @@ WAYLANDSERVERSOURCES += \ ../extensions/qt-key-unstable-v1.xml \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ + ../3rdparty/protocol/viewporter.xml \ + ../3rdparty/protocol/scaler.xml \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ ../3rdparty/protocol/xdg-shell.xml \ ../3rdparty/protocol/xdg-decoration-unstable-v1.xml \ @@ -27,6 +29,10 @@ HEADERS += \ extensions/qwaylandtextinputmanager_p.h \ extensions/qwaylandqtwindowmanager.h \ extensions/qwaylandqtwindowmanager_p.h \ + extensions/qwaylandviewporter.h \ + extensions/qwaylandviewporter_p.h \ + extensions/qwaylandwlscaler.h \ + extensions/qwaylandwlscaler_p.h \ extensions/qwaylandxdgshellv5.h \ extensions/qwaylandxdgshellv5_p.h \ extensions/qwaylandxdgshellv6.h \ @@ -49,6 +55,8 @@ SOURCES += \ extensions/qwaylandtextinput.cpp \ extensions/qwaylandtextinputmanager.cpp \ extensions/qwaylandqtwindowmanager.cpp \ + extensions/qwaylandviewporter.cpp \ + extensions/qwaylandwlscaler.cpp \ extensions/qwaylandxdgshellv5.cpp \ extensions/qwaylandxdgshellv6.cpp \ extensions/qwaylandxdgshell.cpp \ diff --git a/src/compositor/extensions/pregenerated/3rdparty/.gitignore b/src/compositor/extensions/pregenerated/3rdparty/.gitignore new file mode 100644 index 000000000..db253cfaf --- /dev/null +++ b/src/compositor/extensions/pregenerated/3rdparty/.gitignore @@ -0,0 +1,3 @@ +!qwayland-server-*.cpp +!qwayland-server-*.h +!wayland-*-protocol.cpp diff --git a/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h b/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h index 63817a5e3..8124860b9 100644 --- a/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h +++ b/src/compositor/extensions/pregenerated/3rdparty/qwayland-server-xdg-shell-unstable-v5_p.h @@ -71,6 +71,7 @@ namespace QtWaylandServer { virtual ~Resource() {} xdg_shell_v5 *xdg_shell_object; + xdg_shell_v5 *object() { return xdg_shell_object; } struct ::wl_resource *handle; struct ::wl_client *client() const { return wl_resource_get_client(handle); } @@ -191,6 +192,7 @@ namespace QtWaylandServer { virtual ~Resource() {} xdg_surface_v5 *xdg_surface_object; + xdg_surface_v5 *object() { return xdg_surface_object; } struct ::wl_resource *handle; struct ::wl_client *client() const { return wl_resource_get_client(handle); } @@ -364,6 +366,7 @@ namespace QtWaylandServer { virtual ~Resource() {} xdg_popup_v5 *xdg_popup_object; + xdg_popup_v5 *object() { return xdg_popup_object; } struct ::wl_resource *handle; struct ::wl_client *client() const { return wl_resource_get_client(handle); } diff --git a/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h b/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h index b979f048c..493fd52d4 100644 --- a/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h +++ b/src/compositor/extensions/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-server-protocol_p.h @@ -6,7 +6,7 @@ #include <stdint.h> #include <stddef.h> -#include "wayland-server.h" +#include "wayland-server-core.h" #ifdef __cplusplus extern "C" { diff --git a/src/compositor/extensions/qwaylandiviapplication.cpp b/src/compositor/extensions/qwaylandiviapplication.cpp index 57b1627b5..a2e9842db 100644 --- a/src/compositor/extensions/qwaylandiviapplication.cpp +++ b/src/compositor/extensions/qwaylandiviapplication.cpp @@ -62,8 +62,9 @@ QT_BEGIN_NAMESPACE * To provide the functionality of the shell extension in a compositor, create * an instance of the IviApplication component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.0 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * IviApplication { @@ -74,7 +75,7 @@ QT_BEGIN_NAMESPACE * } * } * } - * \endcode + * \endqml */ /*! @@ -131,6 +132,9 @@ const struct wl_interface *QWaylandIviApplication::interface() return QWaylandIviApplicationPrivate::interface(); } +/*! + * \internal + */ QByteArray QWaylandIviApplication::interfaceName() { return QWaylandIviApplicationPrivate::interfaceName(); @@ -140,16 +144,17 @@ QByteArray QWaylandIviApplication::interfaceName() * \qmlsignal void QtWaylandCompositor::IviApplication::iviSurfaceRequested(WaylandSurface surface, int iviId, WaylandResource resource) * * This signal is emitted when the client has requested an \c ivi_surface to be associated - * with \a surface, which is identified by \a id. The handler for this signal is - * expected to create the ivi surface and initialize it within the scope of the + * with \a surface, which is identified by \a iviId. The handler for this signal is + * expected to create the ivi surface for \a resource and initialize it within the scope of the * signal emission. If no ivi surface is created, a default one will be created instead. + * */ /*! * \fn void QWaylandIviApplication::iviSurfaceRequested(QWaylandSurface *surface, uint iviId, const QWaylandResource &resource) * * This signal is emitted when the client has requested an \c ivi_surface to be associated - * with \a surface, which is identified by \a id. The handler for this signal is + * with \a surface, which is identified by \a iviId. The handler for this signal is * expected to create the ivi surface and initialize it within the scope of the * signal emission. If no ivi surface is created, a default one will be created instead. */ diff --git a/src/compositor/extensions/qwaylandivisurface.cpp b/src/compositor/extensions/qwaylandivisurface.cpp index b6398f060..0ae488def 100644 --- a/src/compositor/extensions/qwaylandivisurface.cpp +++ b/src/compositor/extensions/qwaylandivisurface.cpp @@ -47,6 +47,8 @@ #include <QtWaylandCompositor/QWaylandResource> #include <QDebug> +#include <QtWaylandCompositor/private/qwaylandutils_p.h> + QT_BEGIN_NAMESPACE QWaylandSurfaceRole QWaylandIviSurfacePrivate::s_role("ivi_surface"); @@ -182,10 +184,9 @@ QWaylandSurfaceRole *QWaylandIviSurface::role() */ QWaylandIviSurface *QWaylandIviSurface::fromResource(wl_resource *resource) { - auto iviSurfaceResource = QWaylandIviSurfacePrivate::Resource::fromResource(resource); - if (!iviSurfaceResource) - return nullptr; - return static_cast<QWaylandIviSurfacePrivate *>(iviSurfaceResource->ivi_surface_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandIviSurfacePrivate *>(resource)) + return p->q_func(); + return nullptr; } /*! diff --git a/src/compositor/extensions/qwaylandshellsurface.cpp b/src/compositor/extensions/qwaylandshellsurface.cpp index 3cfe44895..cb6d03646 100644 --- a/src/compositor/extensions/qwaylandshellsurface.cpp +++ b/src/compositor/extensions/qwaylandshellsurface.cpp @@ -87,7 +87,7 @@ */ /*! - * \property QWaylandWlShellSurface::windowType + * \property QWaylandShellSurface::windowType * * This property holds the window type of the QWaylandShellSurface. */ diff --git a/src/compositor/extensions/qwaylandtextinput.cpp b/src/compositor/extensions/qwaylandtextinput.cpp index 6cf33d18d..f60a32a14 100644 --- a/src/compositor/extensions/qwaylandtextinput.cpp +++ b/src/compositor/extensions/qwaylandtextinput.cpp @@ -45,12 +45,15 @@ #include "qwaylandsurface.h" #include "qwaylandview.h" -#include "qwaylandxkb_p.h" #include "qwaylandinputmethodeventbuilder_p.h" #include <QGuiApplication> #include <QInputMethodEvent> +#if QT_CONFIG(xkbcommon) +#include <QtXkbCommonSupport/private/qxkbcommon_p.h> +#endif + QT_BEGIN_NAMESPACE QWaylandTextInputClientState::QWaylandTextInputClientState() @@ -203,11 +206,15 @@ void QWaylandTextInputPrivate::sendKeyEvent(QKeyEvent *event) // TODO add support for modifiers - foreach (xkb_keysym_t keysym, QWaylandXkb::toKeysym(event)) { +#if QT_CONFIG(xkbcommon) + for (xkb_keysym_t keysym : QXkbCommon::toKeysym(event)) { send_keysym(focusResource->handle, event->timestamp(), keysym, event->type() == QEvent::KeyPress ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED, 0); } +#else + Q_UNUSED(event); +#endif } void QWaylandTextInputPrivate::sendInputPanelState() diff --git a/src/compositor/extensions/qwaylandviewporter.cpp b/src/compositor/extensions/qwaylandviewporter.cpp new file mode 100644 index 000000000..3856c135d --- /dev/null +++ b/src/compositor/extensions/qwaylandviewporter.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandviewporter_p.h" + +#include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandCompositor> + +#include <QtWaylandCompositor/private/qwaylandsurface_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QWaylandViewporter + \inmodule QtWaylandCompositor + \since 5.13 + \brief Provides an extension for surface resizing and cropping. + + The QWaylandViewporter extension provides a way for clients to resize and crop surface + contents. + + QWaylandViewporter corresponds to the Wayland interface, \c wp_viewporter. +*/ + +/*! + Constructs a QWaylandViewporter object. +*/ +QWaylandViewporter::QWaylandViewporter() + : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(*new QWaylandViewporterPrivate) +{ +} + +/*! + * Constructs a QWaylandViewporter object for the provided \a compositor. + */ +QWaylandViewporter::QWaylandViewporter(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate<QWaylandViewporter>(compositor, *new QWaylandViewporterPrivate()) +{ +} + +/*! + Initializes the extension. +*/ +void QWaylandViewporter::initialize() +{ + Q_D(QWaylandViewporter); + + QWaylandCompositorExtensionTemplate::initialize(); + auto *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + if (!compositor) { + qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandViewporter"; + return; + } + d->init(compositor->display(), 1); +} + +/*! + Returns the Wayland interface for the QWaylandViewporter. +*/ +const wl_interface *QWaylandViewporter::interface() +{ + return QWaylandViewporterPrivate::interface(); +} + +void QWaylandViewporterPrivate::wp_viewporter_destroy(Resource *resource) +{ + // Viewport objects are allowed ot outlive the viewporter + wl_resource_destroy(resource->handle); +} + +void QWaylandViewporterPrivate::wp_viewporter_get_viewport(Resource *resource, uint id, wl_resource *surfaceResource) +{ + auto *surface = QWaylandSurface::fromResource(surfaceResource); + if (!surface) { + qWarning() << "Couldn't find surface for viewporter"; + return; + } + + auto *surfacePrivate = QWaylandSurfacePrivate::get(surface); + if (surfacePrivate->viewport) { + wl_resource_post_error(resource->handle, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, + "viewport already exists for surface"); + return; + } + + surfacePrivate->viewport = new Viewport(surface, resource->client(), id); +} + +QWaylandViewporterPrivate::Viewport::Viewport(QWaylandSurface *surface, wl_client *client, int id) + : QtWaylandServer::wp_viewport(client, id, /*version*/ 1) + , m_surface(surface) +{ + Q_ASSERT(surface); +} + +QWaylandViewporterPrivate::Viewport::~Viewport() +{ + if (m_surface) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + Q_ASSERT(surfacePrivate->viewport == this); + surfacePrivate->viewport = nullptr; + } +} + +// This function has to be called immediately after a surface is committed, before no +// other client events have been dispatched, or we may incorrectly error out on an +// incomplete pending state. See comment below. +void QWaylandViewporterPrivate::Viewport::checkCommittedState() +{ + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + + // We can't use the current state for destination/source when checking, + // as that has fallbacks to the buffer size so we can't distinguish + // between the set/unset case. We use the pending state because no other + // requests has modified it yet. + QSize destination = surfacePrivate->pending.destinationSize; + QRectF source = surfacePrivate->pending.sourceGeometry; + + if (!destination.isValid() && source.size() != source.size().toSize()) { + wl_resource_post_error(resource()->handle, error_bad_size, + "non-integer size (%fx%f) with unset destination", + source.width(), source.height()); + return; + } + + QRectF max = QRectF(QPointF(), m_surface->bufferSize() / m_surface->bufferScale()); + // We can't use QRectF.contains, because that would return false for values on the border + if (max.united(source) != max) { + wl_resource_post_error(resource()->handle, error_out_of_buffer, + "source %f,%f, %fx%f extends outside attached buffer %fx%f", + source.x(), source.y(), source.width(), source.height(), + max.width(), max.height()); + return; + } +} + + +void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void QWaylandViewporterPrivate::Viewport::wp_viewport_destroy(Resource *resource) +{ + if (m_surface) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.destinationSize = QSize(); + surfacePrivate->pending.sourceGeometry = QRectF(); + } + wl_resource_destroy(resource->handle); +} + +void QWaylandViewporterPrivate::Viewport::wp_viewport_set_source(QtWaylandServer::wp_viewport::Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + Q_UNUSED(resource); + + if (!m_surface) { + wl_resource_post_error(resource->handle, error_no_surface, + "set_source requested for destroyed surface"); + return; + } + + QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); + QSizeF size(wl_fixed_to_double(width), wl_fixed_to_double(height)); + QRectF sourceGeometry(position, size); + + if (sourceGeometry == QRectF(-1, -1, -1, -1)) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.sourceGeometry = QRectF(); + return; + } + + if (position.x() < 0 || position.y() < 0) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative position in set_source"); + return; + } + + if (!size.isValid()) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative size in set_source"); + return; + } + + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.sourceGeometry = sourceGeometry; +} + +void QWaylandViewporterPrivate::Viewport::wp_viewport_set_destination(QtWaylandServer::wp_viewport::Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + if (!m_surface) { + wl_resource_post_error(resource->handle, error_no_surface, + "set_destination requested for destroyed surface"); + return; + } + + QSize destinationSize(width, height); + if (!destinationSize.isValid() && destinationSize != QSize(-1, -1)) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative size in set_destination"); + return; + } + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.destinationSize = destinationSize; +} + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandviewporter.h b/src/compositor/extensions/qwaylandviewporter.h new file mode 100644 index 000000000..811c74145 --- /dev/null +++ b/src/compositor/extensions/qwaylandviewporter.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDVIEWPORTER_H +#define QWAYLANDVIEWPORTER_H + +#include <QtWaylandCompositor/QWaylandCompositorExtension> + +QT_BEGIN_NAMESPACE + +class QWaylandViewporterPrivate; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandViewporter + : public QWaylandCompositorExtensionTemplate<QWaylandViewporter> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandViewporter) + +public: + explicit QWaylandViewporter(); + explicit QWaylandViewporter(QWaylandCompositor *compositor); + + void initialize() override; + + static const struct wl_interface *interface(); +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDVIEWPORTER_H diff --git a/src/compositor/extensions/qwaylandviewporter_p.h b/src/compositor/extensions/qwaylandviewporter_p.h new file mode 100644 index 000000000..d22da6990 --- /dev/null +++ b/src/compositor/extensions/qwaylandviewporter_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDVIEWPORTER_P_H +#define QWAYLANDVIEWPORTER_P_H + +#include "qwaylandviewporter.h" + +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-viewporter.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QWaylandSurface; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandViewporterPrivate + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::wp_viewporter +{ + Q_DECLARE_PUBLIC(QWaylandViewporter) +public: + explicit QWaylandViewporterPrivate() = default; + + class Q_WAYLAND_COMPOSITOR_EXPORT Viewport + : public QtWaylandServer::wp_viewport + { + public: + explicit Viewport(QWaylandSurface *surface, wl_client *client, int id); + ~Viewport() override; + void checkCommittedState(); + + protected: + void wp_viewport_destroy_resource(Resource *resource) override; + void wp_viewport_destroy(Resource *resource) override; + void wp_viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override; + void wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) override; + + private: + QPointer<QWaylandSurface> m_surface = nullptr; + }; + +protected: + void wp_viewporter_destroy(Resource *resource) override; + void wp_viewporter_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) override; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDVIEWPORTER_P_H diff --git a/src/compositor/extensions/qwaylandwlscaler.cpp b/src/compositor/extensions/qwaylandwlscaler.cpp new file mode 100644 index 000000000..5c8e4b270 --- /dev/null +++ b/src/compositor/extensions/qwaylandwlscaler.cpp @@ -0,0 +1,274 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandwlscaler_p.h" + +#include <QtWaylandCompositor/QWaylandSurface> +#include <QtWaylandCompositor/QWaylandCompositor> + +#include <QtWaylandCompositor/private/qwaylandsurface_p.h> + +QT_BEGIN_NAMESPACE + +#if QT_DEPRECATED_SINCE(5, 13) +/*! + \qmltype WlScaler + \inqmlmodule QtWayland.Compositor + \since 5.13 + \brief Provides an extension for surface resizing and cropping. + + The WlScaler extension provides a way for clients to resize and crop surface contents. + + WlScaler corresponds to the Wayland interface, \c wl_scaler. + + \c wl_scaler is a non-standard and deprecated protocol that has largely been replaced by + \c wp_viewporter. I.e. This extensions is only useful for supporting legacy clients. + \c wp_viewporter support is enabled automatically for all Qml compositors. + + To provide the functionality of the extension in a compositor, create an instance of the + WlScaler component and add it to the list of extensions supported by the compositor: + + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 + + WaylandCompositor { + // ... + WlScaler {} + } + \endqml + + \deprecated +*/ + +/*! + \class QWaylandWlScaler + \inmodule QtWaylandCompositor + \since 5.13 + \brief Provides an extension for surface resizing and croping. + + The QWaylandWlScaler extension provides a way for clients to resize and crop surface + contents. + + QWaylandWlScaler corresponds to the Wayland interface, \c wl_scaler. + + \c wl_scaler is a non-standard and deprecated protocol that has largely been replaced by + \c wp_viewporter. I.e. This extensions is only useful for supporting legacy clients. + + \sa QWaylandViewporter + + \deprecated +*/ + +/*! + Constructs a QWaylandWlScaler object. +*/ +QWaylandWlScaler::QWaylandWlScaler() + : QWaylandCompositorExtensionTemplate<QWaylandWlScaler>(*new QWaylandWlScalerPrivate) +{ +} + +/*! + * Constructs a QWaylandWlScaler object for the provided \a compositor. + */ +QWaylandWlScaler::QWaylandWlScaler(QWaylandCompositor *compositor) + : QWaylandCompositorExtensionTemplate<QWaylandWlScaler>(compositor, *new QWaylandWlScalerPrivate()) +{ +} + +/*! + Initializes the extension. +*/ +void QWaylandWlScaler::initialize() +{ + Q_D(QWaylandWlScaler); + + QWaylandCompositorExtensionTemplate::initialize(); + auto *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); + if (!compositor) { + qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandWlScaler"; + return; + } + d->init(compositor->display(), 2); +} + +/*! + Returns the Wayland interface for the QWaylandWlScaler. +*/ +const wl_interface *QWaylandWlScaler::interface() +{ + return QWaylandWlScalerPrivate::interface(); +} + +void QWaylandWlScalerPrivate::scaler_destroy(Resource *resource) +{ + // Viewport objects are allowed ot outlive the scaler + wl_resource_destroy(resource->handle); +} + +void QWaylandWlScalerPrivate::scaler_get_viewport(Resource *resource, uint id, wl_resource *surfaceResource) +{ + auto *surface = QWaylandSurface::fromResource(surfaceResource); + if (!surface) { + qWarning() << "Couldn't find surface for viewporter"; + return; + } + + // Note: This will only protect us not creating scalers for surfaces with wp_viewport objects + auto *surfacePrivate = QWaylandSurfacePrivate::get(surface); + if (surfacePrivate->viewport) { + wl_resource_post_error(resource->handle, WL_SCALER_ERROR_VIEWPORT_EXISTS, + "viewport already exists for surface"); + return; + } + + // We can't set viewport here, since it's of the new type for wp_viewporter +// surfacePrivate->viewport = new Viewport(surface, resource->client(), id, resource->version()); + new Viewport(surface, resource->client(), id, resource->version()); +} + +QWaylandWlScalerPrivate::Viewport::Viewport(QWaylandSurface *surface, wl_client *client, int id, int version) + : QtWaylandServer::wl_viewport(client, id, version) + , m_surface(surface) +{ + Q_ASSERT(surface); +} + +//TODO: This isn't currently called +// This function has to be called immediately after a surface is committed, before no +// other client events have been dispatched, or we may incorrectly error out on an +// incomplete pending state. See comment below. +void QWaylandWlScalerPrivate::Viewport::checkCommittedState() +{ + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + + // We can't use the current state for destination/source when checking, + // as that has fallbacks to the buffer size so we can't distinguish + // between the set/unset case. We use the pending state because no other + // requests has modified it yet. + QSize destination = surfacePrivate->pending.destinationSize; + QRectF source = surfacePrivate->pending.sourceGeometry; + + if (!destination.isValid() && source.size() != source.size().toSize()) { + //TODO: Do rounding to nearest integer + } + + QRectF max = QRectF(QPointF(), m_surface->bufferSize() / m_surface->bufferScale()); + // We can't use QRectF.contains, because that would return false for values on the border + if (max.united(source) != max) { + //TODO: surface contents are no undefined, surface size is still valid though + qCDebug(qLcWaylandCompositor) << "Source set outside buffer bounds (client error)"; + } +} + + +void QWaylandWlScalerPrivate::Viewport::viewport_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void QWaylandWlScalerPrivate::Viewport::viewport_destroy(Resource *resource) +{ + if (m_surface) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.destinationSize = QSize(); + surfacePrivate->pending.sourceGeometry = QRectF(); + } + wl_resource_destroy(resource->handle); +} + +void QWaylandWlScalerPrivate::Viewport::viewport_set(QtWaylandServer::wl_viewport::Resource *resource, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height, int32_t dst_width, int32_t dst_height) +{ + viewport_set_source(resource, src_x, src_y, src_width, src_height); + viewport_set_destination(resource, dst_width, dst_height); +} + +void QWaylandWlScalerPrivate::Viewport::viewport_set_source(QtWaylandServer::wl_viewport::Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + Q_UNUSED(resource); + + if (!m_surface) { + qCDebug(qLcWaylandCompositor) << "set_source requested for destroyed surface"; + return; + } + + QPointF position(wl_fixed_to_double(x), wl_fixed_to_double(y)); + QSizeF size(wl_fixed_to_double(width), wl_fixed_to_double(height)); + QRectF sourceGeometry(position, size); + + if (sourceGeometry == QRectF(-1, -1, -1, -1)) { + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.sourceGeometry = QRectF(); + return; + } + + if (position.x() < 0 || position.y() < 0) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative position in set_source"); + return; + } + + if (!size.isValid()) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative size in set_source"); + return; + } + + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.sourceGeometry = sourceGeometry; +} + +void QWaylandWlScalerPrivate::Viewport::viewport_set_destination(QtWaylandServer::wl_viewport::Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + + if (!m_surface) { + qCDebug(qLcWaylandCompositor) << "set_destination requested for destroyed surface"; + return; + } + + QSize destinationSize(width, height); + if (!destinationSize.isValid() && destinationSize != QSize(-1, -1)) { + wl_resource_post_error(resource->handle, error_bad_value, + "negative size in set_destination"); + return; + } + auto *surfacePrivate = QWaylandSurfacePrivate::get(m_surface); + surfacePrivate->pending.destinationSize = destinationSize; +} +#endif // QT_DEPRECATED_SINCE + +QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandwlscaler.h b/src/compositor/extensions/qwaylandwlscaler.h new file mode 100644 index 000000000..4ecdf3968 --- /dev/null +++ b/src/compositor/extensions/qwaylandwlscaler.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDWLSCALER_H +#define QWAYLANDWLSCALER_H + +#include <QtWaylandCompositor/QWaylandCompositorExtension> + +QT_BEGIN_NAMESPACE + +#if QT_DEPRECATED_SINCE(5, 13) +class QWaylandWlScalerPrivate; + +// TODO: We should have used the QT_DEPRECATED macro here, but for some reason +// header file generation stops working when that's added. +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandWlScaler + : public QWaylandCompositorExtensionTemplate<QWaylandWlScaler> +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QWaylandWlScaler) + +public: + explicit QWaylandWlScaler(); + explicit QWaylandWlScaler(QWaylandCompositor *compositor); + + void initialize() override; + + static const struct wl_interface *interface(); +}; +#endif + +QT_END_NAMESPACE + +#endif // QWAYLANDWLSCALER_H diff --git a/src/compositor/extensions/qwaylandwlscaler_p.h b/src/compositor/extensions/qwaylandwlscaler_p.h new file mode 100644 index 000000000..d3c2edd76 --- /dev/null +++ b/src/compositor/extensions/qwaylandwlscaler_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDWLSCALER_P_H +#define QWAYLANDWLSCALER_P_H + +#include "qwaylandwlscaler.h" + +#include <QtWaylandCompositor/private/qwaylandcompositorextension_p.h> +#include <QtWaylandCompositor/private/qwayland-server-scaler.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QWaylandSurface; + +class Q_WAYLAND_COMPOSITOR_EXPORT QWaylandWlScalerPrivate + : public QWaylandCompositorExtensionPrivate + , public QtWaylandServer::wl_scaler +{ + Q_DECLARE_PUBLIC(QWaylandWlScaler) +public: + explicit QWaylandWlScalerPrivate() = default; + +protected: + void scaler_destroy(Resource *resource) override; + void scaler_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) override; + +private: + class Viewport : public QtWaylandServer::wl_viewport + { + public: + explicit Viewport(QWaylandSurface *surface, wl_client *client, int id, int version); + void checkCommittedState(); + + protected: + void viewport_destroy_resource(Resource *resource) override; + void viewport_destroy(Resource *resource) override; + void viewport_set(Resource *resource, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height, int32_t dst_width, int32_t dst_height) override; + void viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) override; + void viewport_set_destination(Resource *resource, int32_t width, int32_t height) override; + + private: + QPointer<QWaylandSurface> m_surface = nullptr; + }; +}; + +QT_END_NAMESPACE + +#endif // QWAYLANDWLSCALER_P_H diff --git a/src/compositor/extensions/qwaylandwlshell.cpp b/src/compositor/extensions/qwaylandwlshell.cpp index d932a06c9..9871a8a5a 100644 --- a/src/compositor/extensions/qwaylandwlshell.cpp +++ b/src/compositor/extensions/qwaylandwlshell.cpp @@ -44,6 +44,7 @@ #ifdef QT_WAYLAND_COMPOSITOR_QUICK #include "qwaylandwlshellintegration_p.h" #endif +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtWaylandCompositor/QWaylandCompositor> #include <QtWaylandCompositor/QWaylandView> @@ -266,15 +267,16 @@ void QWaylandWlShellSurfacePrivate::shell_surface_set_class(Resource *resource, * To provide the functionality of the shell extension in a compositor, create * an instance of the WlShell component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.0 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * WlShell { * // ... * } * } - * \endcode + * \endqml */ /*! @@ -699,9 +701,8 @@ void QWaylandWlShellSurface::ping() */ QWaylandWlShellSurface *QWaylandWlShellSurface::fromResource(wl_resource *resource) { - QWaylandWlShellSurfacePrivate::Resource *res = QWaylandWlShellSurfacePrivate::Resource::fromResource(resource); - if (res) - return static_cast<QWaylandWlShellSurfacePrivate *>(res->shell_surface_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandWlShellSurfacePrivate *>(resource)) + return p->q_func(); return nullptr; } diff --git a/src/compositor/extensions/qwaylandwlshell_p.h b/src/compositor/extensions/qwaylandwlshell_p.h index b2beca169..dbf6e794f 100644 --- a/src/compositor/extensions/qwaylandwlshell_p.h +++ b/src/compositor/extensions/qwaylandwlshell_p.h @@ -47,7 +47,7 @@ #include <QtWaylandCompositor/QWaylandWlShellSurface> #include <QtWaylandCompositor/QWaylandSeat> -#include <wayland-server.h> +#include <wayland-server-core.h> #include <QHash> #include <QPoint> #include <QSet> diff --git a/src/compositor/extensions/qwaylandwlshellintegration.cpp b/src/compositor/extensions/qwaylandwlshellintegration.cpp index 896b1587d..99a2e7655 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration.cpp +++ b/src/compositor/extensions/qwaylandwlshellintegration.cpp @@ -84,8 +84,7 @@ void WlShellIntegration::handleStartResize(QWaylandSeat *seat, QWaylandWlShellSu grabberState = GrabberState::Resize; resizeState.seat = seat; resizeState.resizeEdges = edges; - float scaleFactor = m_item->view()->output()->scaleFactor(); - resizeState.initialSize = m_shellSurface->surface()->size() / scaleFactor; + resizeState.initialSize = m_shellSurface->surface()->destinationSize(); resizeState.initialized = false; } @@ -217,9 +216,7 @@ void WlShellIntegration::handleSetPopup(QWaylandSeat *seat, QWaylandSurface *par t.clear(&t); m_item->setRotation(0); m_item->setScale(1.0); - auto scaleFactor = m_item->output()->scaleFactor() / devicePixelRatio(); - m_item->setX(relativeToParent.x() * scaleFactor); - m_item->setY(relativeToParent.y() * scaleFactor); + m_item->setPosition(m_item->mapFromSurface(relativeToParent)); m_item->setParentItem(parentItem); } @@ -267,7 +264,7 @@ void WlShellIntegration::handleShellSurfaceDestroyed() void WlShellIntegration::handleSurfaceHasContentChanged() { - if (m_shellSurface && m_shellSurface->surface()->size().isEmpty() + if (m_shellSurface && m_shellSurface->surface()->destinationSize().isEmpty() && m_shellSurface->windowType() == Qt::WindowType::Popup) { handlePopupClosed(); } @@ -287,9 +284,8 @@ void WlShellIntegration::adjustOffsetForNextFrame(const QPointF &offset) if (!m_item->view()->isPrimary()) return; - float scaleFactor = m_item->view()->output()->scaleFactor(); QQuickItem *moveItem = m_item->moveItem(); - moveItem->setPosition(moveItem->position() + offset * scaleFactor / devicePixelRatio()); + moveItem->setPosition(moveItem->position() + m_item->mapFromSurface(offset)); } bool WlShellIntegration::mouseMoveEvent(QMouseEvent *event) diff --git a/src/compositor/extensions/qwaylandwlshellintegration_p.h b/src/compositor/extensions/qwaylandwlshellintegration_p.h index ff236e636..8af54dfc4 100644 --- a/src/compositor/extensions/qwaylandwlshellintegration_p.h +++ b/src/compositor/extensions/qwaylandwlshellintegration_p.h @@ -100,14 +100,14 @@ private: struct { QWaylandSeat *seat = nullptr; QPointF initialOffset; - bool initialized; + bool initialized = false; } moveState; struct { QWaylandSeat *seat = nullptr; QWaylandWlShellSurface::ResizeEdge resizeEdges; QSizeF initialSize; QPointF initialMousePos; - bool initialized; + bool initialized = false; } resizeState; bool isPopup = false; diff --git a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp index 1abd5e3fc..2d283ddf9 100644 --- a/src/compositor/extensions/qwaylandxdgdecorationv1.cpp +++ b/src/compositor/extensions/qwaylandxdgdecorationv1.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE \qmltype XdgDecorationManagerV1 \inqmlmodule QtWayland.Compositor \since 5.12 - \brief Provides an extension for negotiation of server-side and client-side window decorations + \brief Provides an extension for negotiation of server-side and client-side window decorations. The XdgDecorationManagerV1 extension provides a way for a compositor to announce support for server-side window decorations, and for xdg-shell clients to communicate whether they prefer @@ -59,8 +59,8 @@ QT_BEGIN_NAMESPACE To provide the functionality of the extension in a compositor, create an instance of the XdgDecorationManagerV1 component and add it to the list of extensions supported by the compositor: - \code - import QtWayland.Compositor 1.3 + \qml \QtMinorVersion + import QtWayland.Compositor 1.\1 WaylandCompositor { // Xdg decoration manager assumes xdg-shell is being used @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE preferredMode: XdgToplevel.ServerSideDecoration } } - \endcode + \endqml \sa XdgToplevel::decorationMode */ @@ -82,7 +82,7 @@ QT_BEGIN_NAMESPACE \class QWaylandXdgDecorationManagerV1 \inmodule QtWaylandCompositor \since 5.12 - \brief Provides an extension for negotiation of server-side and client-side window decorations + \brief Provides an extension for negotiation of server-side and client-side window decorations. The QWaylandXdgDecorationManagerV1 extension provides a way for a compositor to announce support for server-side window decorations, and for xdg-shell clients to communicate whether they prefer diff --git a/src/compositor/extensions/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp index bd332287e..eb7b958b4 100644 --- a/src/compositor/extensions/qwaylandxdgshell.cpp +++ b/src/compositor/extensions/qwaylandxdgshell.cpp @@ -40,6 +40,7 @@ #ifdef QT_WAYLAND_COMPOSITOR_QUICK #include "qwaylandxdgshellintegration_p.h" #endif +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtWaylandCompositor/QWaylandCompositor> #include <QtWaylandCompositor/QWaylandSeat> @@ -153,15 +154,16 @@ void QWaylandXdgShellPrivate::xdg_wm_base_pong(Resource *resource, uint32_t seri * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShell component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.3 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShell { * // ... * } * } - * \endcode + * \endqml */ /*! @@ -311,7 +313,7 @@ QRect QWaylandXdgSurfacePrivate::calculateFallbackWindowGeometry() const { // TODO: The unset window geometry should include subsurfaces as well, so this solution // won't work too well on those kinds of clients. - return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale()); + return QRect(QPoint(), m_surface->destinationSize()); } void QWaylandXdgSurfacePrivate::updateFallbackWindowGeometry() @@ -385,6 +387,7 @@ void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surfa "xdg_surface.get_popup without positioner"); return; } + if (!positioner->m_data.isComplete()) { QWaylandXdgPositionerData p = positioner->m_data; wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER, @@ -393,6 +396,17 @@ void QWaylandXdgSurfacePrivate::xdg_surface_get_popup(QtWaylandServer::xdg_surfa return; } + QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size()); + if (!anchorBounds.contains(positioner->m_data.anchorRect)) { + // TODO: this is a protocol error and should ideally be handled like this: + //wl_resource_post_error(resource->handle, XDG_WM_BASE_ERROR_INVALID_POSITIONER, + // "xdg_positioner anchor rect extends beyound its parent's window geometry"); + //return; + // However, our own clients currently do this, so we'll settle for a gentle warning instead. + qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: xdg_positioner anchor" + << "rect extends beyond its parent's window geometry"; + } + if (!m_surface->setRole(QWaylandXdgPopup::role(), resource->handle, XDG_WM_BASE_ERROR_ROLE)) return; @@ -510,7 +524,7 @@ void QWaylandXdgSurface::initialize(QWaylandXdgShell *xdgShell, QWaylandSurface d->init(resource.resource()); setExtensionContainer(surface); d->m_windowGeometry = d->calculateFallbackWindowGeometry(); - connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged); + connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurface::handleSurfaceSizeChanged); connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurface::handleBufferScaleChanged); emit shellChanged(); emit surfaceChanged(); @@ -674,10 +688,9 @@ QByteArray QWaylandXdgSurface::interfaceName() */ QWaylandXdgSurface *QWaylandXdgSurface::fromResource(wl_resource *resource) { - auto xsResource = QWaylandXdgSurfacePrivate::Resource::fromResource(resource); - if (!xsResource) - return nullptr; - return static_cast<QWaylandXdgSurfacePrivate *>(xsResource->xdg_surface_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandXdgSurfacePrivate *>(resource)) + return p->q_func(); + return nullptr; } #ifdef QT_WAYLAND_COMPOSITOR_QUICK @@ -1182,8 +1195,8 @@ QWaylandSurfaceRole *QWaylandXdgToplevel::role() */ QWaylandXdgToplevel *QWaylandXdgToplevel::fromResource(wl_resource *resource) { - if (auto *r = QWaylandXdgToplevelPrivate::Resource::fromResource(resource)) - return static_cast<QWaylandXdgToplevelPrivate *>(r->xdg_toplevel_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandXdgToplevelPrivate *>(resource)) + return p->q_func(); return nullptr; } @@ -1869,16 +1882,13 @@ QWaylandXdgPopupPrivate::QWaylandXdgPopupPrivate(QWaylandXdgSurface *xdgSurface, QWaylandXdgPositioner *positioner, const QWaylandResource &resource) : m_xdgSurface(xdgSurface) , m_parentXdgSurface(parentXdgSurface) + , m_positionerData(positioner->m_data) { + Q_ASSERT(m_positionerData.isComplete()); init(resource.resource()); - m_positionerData = positioner->m_data; - - if (!m_positionerData.isComplete()) - qWarning() << "Trying to create xdg popup with incomplete positioner"; QWaylandXdgSurfacePrivate::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup); - //TODO: positioner rect may not extend parent's window geometry, enforce this? //TODO: Need an API for sending a different initial configure sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size)); } @@ -2064,9 +2074,7 @@ void QWaylandXdgPositioner::xdg_positioner_set_offset(QtWaylandServer::xdg_posit QWaylandXdgPositioner *QWaylandXdgPositioner::fromResource(wl_resource *resource) { - if (auto *r = Resource::fromResource(resource)) - return static_cast<QWaylandXdgPositioner *>(r->xdg_positioner_object); - return nullptr; + return QtWayland::fromResource<QWaylandXdgPositioner *>(resource); } Qt::Edges QWaylandXdgPositioner::convertToEdges(anchor anchor) diff --git a/src/compositor/extensions/qwaylandxdgshellintegration.cpp b/src/compositor/extensions/qwaylandxdgshellintegration.cpp index cc8faf6c7..3de52944b 100644 --- a/src/compositor/extensions/qwaylandxdgshellintegration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellintegration.cpp @@ -73,7 +73,7 @@ XdgToplevelIntegration::XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *it connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){ handlePopupCreated(item, popup); }); - connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged); + connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged); connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelIntegration::handleToplevelDestroyed); } @@ -130,7 +130,7 @@ void XdgToplevelIntegration::handleStartResize(QWaylandSeat *seat, Qt::Edges edg resizeState.resizeEdges = edges; resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); resizeState.initialPosition = m_item->moveItem()->position(); - resizeState.initialSurfaceSize = m_item->surface()->size(); + resizeState.initialSurfaceSize = m_item->surface()->destinationSize(); resizeState.initialized = false; } @@ -247,14 +247,14 @@ void XdgToplevelIntegration::handleActivatedChanged() void XdgToplevelIntegration::handleSurfaceSizeChanged() { if (grabberState == GrabberState::Resize) { - qreal x = resizeState.initialPosition.x(); - qreal y = resizeState.initialPosition.y(); + qreal dx = 0; + qreal dy = 0; if (resizeState.resizeEdges & Qt::TopEdge) - y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height(); - + dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height(); if (resizeState.resizeEdges & Qt::LeftEdge) - x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width(); - m_item->moveItem()->setPosition(QPointF(x, y)); + dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width(); + QPointF offset = m_item->mapFromSurface({dx, dy}); + m_item->moveItem()->setPosition(resizeState.initialPosition + offset); } } @@ -285,11 +285,11 @@ void XdgPopupIntegration::handleGeometryChanged() { if (m_item->view()->output()) { const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft(); - const QPoint position = m_popup->unconstrainedPosition() + windowOffset; + const QPoint surfacePosition = m_popup->unconstrainedPosition() + windowOffset; + const QPoint itemPosition = m_item->mapFromSurface(surfacePosition).toPoint(); //TODO: positioner size or other size...? - const float scaleFactor = m_item->view()->output()->scaleFactor(); //TODO check positioner constraints etc... sliding, flipping - m_item->moveItem()->setPosition(position * scaleFactor); + m_item->moveItem()->setPosition(itemPosition); } else { qWarning() << "XdgPopupIntegration popup item without output" << m_item; } diff --git a/src/compositor/extensions/qwaylandxdgshellv5.cpp b/src/compositor/extensions/qwaylandxdgshellv5.cpp index a6e88aabb..0628f55e7 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv5.cpp @@ -43,6 +43,7 @@ #ifdef QT_WAYLAND_COMPOSITOR_QUICK #include "qwaylandxdgshellv5integration_p.h" #endif +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtWaylandCompositor/QWaylandCompositor> #include <QtWaylandCompositor/QWaylandSurface> @@ -249,7 +250,7 @@ QRect QWaylandXdgSurfaceV5Private::calculateFallbackWindowGeometry() const { // TODO: The unset window geometry should include subsurfaces as well, so this solution // won't work too well on those kinds of clients. - return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale()); + return QRect(QPoint(), m_surface->destinationSize()); } void QWaylandXdgSurfaceV5Private::updateFallbackWindowGeometry() @@ -512,15 +513,17 @@ void QWaylandXdgPopupV5Private::xdg_popup_destroy(Resource *resource) * * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShellV5 component and add it as a child of the - * compositor: \code - * import QtWayland.Compositor 1.0 + * compositor: + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShellV5 { * // ... * } * } - * \endcode + * \endqml * * \deprecated */ @@ -837,7 +840,7 @@ void QWaylandXdgSurfaceV5::initialize(QWaylandXdgShellV5 *xdgShell, QWaylandSurf d->init(resource.resource()); setExtensionContainer(surface); d->m_windowGeometry = d->calculateFallbackWindowGeometry(); - connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurfaceV5::handleSurfaceSizeChanged); + connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurfaceV5::handleSurfaceSizeChanged); connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurfaceV5::handleBufferScaleChanged); emit shellChanged(); emit surfaceChanged(); @@ -1179,10 +1182,9 @@ QWaylandSurfaceRole *QWaylandXdgSurfaceV5::role() */ QWaylandXdgSurfaceV5 *QWaylandXdgSurfaceV5::fromResource(wl_resource *resource) { - auto xsResource = QWaylandXdgSurfaceV5Private::Resource::fromResource(resource); - if (!xsResource) - return nullptr; - return static_cast<QWaylandXdgSurfaceV5Private *>(xsResource->xdg_surface_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandXdgSurfaceV5Private *>(resource)) + return p->q_func(); + return nullptr; } QSize QWaylandXdgSurfaceV5::sizeForResize(const QSizeF &size, const QPointF &delta, @@ -1497,10 +1499,9 @@ QWaylandSurfaceRole *QWaylandXdgPopupV5::role() QWaylandXdgPopupV5 *QWaylandXdgPopupV5::fromResource(wl_resource *resource) { - auto popupResource = QWaylandXdgPopupV5Private::Resource::fromResource(resource); - if (!popupResource) - return nullptr; - return static_cast<QWaylandXdgPopupV5Private *>(popupResource->xdg_popup_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandXdgPopupV5Private *>(resource)) + return p->q_func(); + return nullptr; } void QWaylandXdgPopupV5::sendPopupDone() diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp index ea04a33d2..1d63632a3 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv5integration.cpp @@ -71,7 +71,7 @@ XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item connect(m_xdgSurface, &QWaylandXdgSurfaceV5::unsetMaximized, this, &XdgShellV5Integration::handleUnsetMaximized); connect(m_xdgSurface, &QWaylandXdgSurfaceV5::maximizedChanged, this, &XdgShellV5Integration::handleMaximizedChanged); connect(m_xdgSurface, &QWaylandXdgSurfaceV5::activatedChanged, this, &XdgShellV5Integration::handleActivatedChanged); - connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgShellV5Integration::handleSurfaceSizeChanged); + connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgShellV5Integration::handleSurfaceSizeChanged); connect(m_xdgSurface->shell(), &QWaylandXdgShellV5::xdgPopupCreated, this, [item](QWaylandXdgPopupV5 *popup){ handlePopupCreated(item, popup); }); @@ -139,7 +139,7 @@ void XdgShellV5Integration::handleStartResize(QWaylandSeat *seat, QWaylandXdgSur resizeState.resizeEdges = edges; resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); resizeState.initialPosition = m_item->moveItem()->position(); - resizeState.initialSurfaceSize = m_item->surface()->size(); + resizeState.initialSurfaceSize = m_item->surface()->destinationSize(); resizeState.initialized = false; } @@ -194,14 +194,14 @@ void XdgShellV5Integration::handleActivatedChanged() void XdgShellV5Integration::handleSurfaceSizeChanged() { if (grabberState == GrabberState::Resize) { - qreal x = resizeState.initialPosition.x(); - qreal y = resizeState.initialPosition.y(); + qreal dx = 0; + qreal dy = 0; if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::TopEdge) - y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height(); - + dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height(); if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::LeftEdge) - x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width(); - m_item->moveItem()->setPosition(QPointF(x, y)); + dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width(); + QPointF offset = m_item->mapFromSurface({dx, dy}); + m_item->moveItem()->setPosition(resizeState.initialPosition + offset); } } @@ -212,10 +212,12 @@ XdgPopupV5Integration::XdgPopupV5Integration(QWaylandQuickShellSurfaceItem *item , m_xdgShell(QWaylandXdgPopupV5Private::get(m_xdgPopup)->m_xdgShell) { item->setSurface(m_xdgPopup->surface()); - if (item->view()->output()) - item->moveItem()->setPosition(QPointF(m_xdgPopup->position() * item->view()->output()->scaleFactor())); - else + if (item->view()->output()) { + QPoint position = item->mapFromSurface(m_xdgPopup->position()).toPoint(); + item->moveItem()->setPosition(position); + } else { qWarning() << "XdgPopupV5Integration popup item without output" << item; + } QWaylandClient *client = m_xdgPopup->surface()->client(); auto shell = m_xdgShell; diff --git a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h index b2c16c6d9..5d0e1e142 100644 --- a/src/compositor/extensions/qwaylandxdgshellv5integration_p.h +++ b/src/compositor/extensions/qwaylandxdgshellv5integration_p.h @@ -91,7 +91,7 @@ private: struct { QWaylandSeat *seat = nullptr; QPointF initialOffset; - bool initialized; + bool initialized = false; } moveState; struct { @@ -101,7 +101,7 @@ private: QPointF initialMousePos; QPointF initialPosition; QSize initialSurfaceSize; - bool initialized; + bool initialized = false; } resizeState; struct { diff --git a/src/compositor/extensions/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp index 8338fe6e2..f971fe5b4 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6.cpp @@ -40,6 +40,7 @@ #ifdef QT_WAYLAND_COMPOSITOR_QUICK #include "qwaylandxdgshellv6integration_p.h" #endif +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtWaylandCompositor/QWaylandCompositor> #include <QtWaylandCompositor/QWaylandSeat> @@ -158,15 +159,16 @@ void QWaylandXdgShellV6Private::zxdg_shell_v6_pong(Resource *resource, uint32_t * To provide the functionality of the shell extension in a compositor, create * an instance of the XdgShellV6 component and add it to the list of extensions * supported by the compositor: - * \code - * import QtWayland.Compositor 1.1 + * + * \qml \QtMinorVersion + * import QtWayland.Compositor 1.\1 * * WaylandCompositor { * XdgShellV6 { * // ... * } * } - * \endcode + * \endqml */ /*! @@ -316,7 +318,7 @@ QRect QWaylandXdgSurfaceV6Private::calculateFallbackWindowGeometry() const { // TODO: The unset window geometry should include subsurfaces as well, so this solution // won't work too well on those kinds of clients. - return QRect(QPoint(0, 0), m_surface->size() / m_surface->bufferScale()); + return QRect(QPoint(), m_surface->destinationSize()); } void QWaylandXdgSurfaceV6Private::updateFallbackWindowGeometry() @@ -390,6 +392,7 @@ void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_popup(QtWaylandServer::zxd "zxdg_surface_v6.get_popup without positioner"); return; } + if (!positioner->m_data.isComplete()) { QWaylandXdgPositionerV6Data p = positioner->m_data; wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, @@ -398,6 +401,17 @@ void QWaylandXdgSurfaceV6Private::zxdg_surface_v6_get_popup(QtWaylandServer::zxd return; } + QRect anchorBounds(QPoint(0, 0), parent->windowGeometry().size()); + if (!anchorBounds.contains(positioner->m_data.anchorRect)) { + // TODO: this is a protocol error and should ideally be handled like this: + //wl_resource_post_error(resource->handle, ZXDG_SHELL_V6_ERROR_INVALID_POSITIONER, + // "zxdg_positioner_v6 anchor rect extends beyound its parent's window geometry"); + //return; + // However, our own clients currently do this, so we'll settle for a gentle warning instead. + qCWarning(qLcWaylandCompositor) << "Ignoring client protocol error: zxdg_positioner_v6 anchor" + << "rect extends beyond its parent's window geometry"; + } + if (!m_surface->setRole(QWaylandXdgPopupV6::role(), resource->handle, ZXDG_SHELL_V6_ERROR_ROLE)) return; @@ -515,7 +529,7 @@ void QWaylandXdgSurfaceV6::initialize(QWaylandXdgShellV6 *xdgShell, QWaylandSurf d->init(resource.resource()); setExtensionContainer(surface); d->m_windowGeometry = d->calculateFallbackWindowGeometry(); - connect(surface, &QWaylandSurface::sizeChanged, this, &QWaylandXdgSurfaceV6::handleSurfaceSizeChanged); + connect(surface, &QWaylandSurface::destinationSizeChanged, this, &QWaylandXdgSurfaceV6::handleSurfaceSizeChanged); connect(surface, &QWaylandSurface::bufferScaleChanged, this, &QWaylandXdgSurfaceV6::handleBufferScaleChanged); emit shellChanged(); emit surfaceChanged(); @@ -679,10 +693,9 @@ QByteArray QWaylandXdgSurfaceV6::interfaceName() */ QWaylandXdgSurfaceV6 *QWaylandXdgSurfaceV6::fromResource(wl_resource *resource) { - auto xsResource = QWaylandXdgSurfaceV6Private::Resource::fromResource(resource); - if (!xsResource) - return nullptr; - return static_cast<QWaylandXdgSurfaceV6Private *>(xsResource->zxdg_surface_v6_object)->q_func(); + if (auto p = QtWayland::fromResource<QWaylandXdgSurfaceV6Private *>(resource)) + return p->q_func(); + return nullptr; } #ifdef QT_WAYLAND_COMPOSITOR_QUICK @@ -1800,16 +1813,13 @@ QWaylandXdgPopupV6Private::QWaylandXdgPopupV6Private(QWaylandXdgSurfaceV6 *xdgSu QWaylandXdgPositionerV6 *positioner, const QWaylandResource &resource) : m_xdgSurface(xdgSurface) , m_parentXdgSurface(parentXdgSurface) + , m_positionerData(positioner->m_data) { + Q_ASSERT(m_positionerData.isComplete()); init(resource.resource()); - m_positionerData = positioner->m_data; - - if (!m_positionerData.isComplete()) - qWarning() << "Trying to create xdg popup with incomplete positioner"; QWaylandXdgSurfaceV6Private::get(m_xdgSurface)->setWindowType(Qt::WindowType::Popup); - //TODO: positioner rect may not extend parent's window geometry, enforce this? //TODO: Need an API for sending a different initial configure sendConfigure(QRect(m_positionerData.unconstrainedPosition(), m_positionerData.size)); } @@ -1995,9 +2005,7 @@ void QWaylandXdgPositionerV6::zxdg_positioner_v6_set_offset(QtWaylandServer::zxd QWaylandXdgPositionerV6 *QWaylandXdgPositionerV6::fromResource(wl_resource *resource) { - if (auto *r = Resource::fromResource(resource)) - return static_cast<QWaylandXdgPositionerV6 *>(r->zxdg_positioner_v6_object); - return nullptr; + return QtWayland::fromResource<QWaylandXdgPositionerV6 *>(resource); } QT_END_NAMESPACE diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp index 61a9092a3..66dbc6841 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6integration.cpp +++ b/src/compositor/extensions/qwaylandxdgshellv6integration.cpp @@ -73,7 +73,7 @@ XdgToplevelV6Integration::XdgToplevelV6Integration(QWaylandQuickShellSurfaceItem connect(m_xdgSurface->shell(), &QWaylandXdgShellV6::popupCreated, this, [item](QWaylandXdgPopupV6 *popup, QWaylandXdgSurfaceV6 *){ handlePopupCreated(item, popup); }); - connect(m_xdgSurface->surface(), &QWaylandSurface::sizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged); + connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgToplevelV6Integration::handleSurfaceSizeChanged); connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelV6Integration::handleToplevelDestroyed); } @@ -130,7 +130,7 @@ void XdgToplevelV6Integration::handleStartResize(QWaylandSeat *seat, Qt::Edges e resizeState.resizeEdges = edges; resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size(); resizeState.initialPosition = m_item->moveItem()->position(); - resizeState.initialSurfaceSize = m_item->surface()->size(); + resizeState.initialSurfaceSize = m_item->surface()->destinationSize(); resizeState.initialized = false; } @@ -247,14 +247,14 @@ void XdgToplevelV6Integration::handleActivatedChanged() void XdgToplevelV6Integration::handleSurfaceSizeChanged() { if (grabberState == GrabberState::Resize) { - qreal x = resizeState.initialPosition.x(); - qreal y = resizeState.initialPosition.y(); + qreal dx = 0; + qreal dy = 0; if (resizeState.resizeEdges & Qt::TopEdge) - y += resizeState.initialSurfaceSize.height() - m_item->surface()->size().height(); - + dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height(); if (resizeState.resizeEdges & Qt::LeftEdge) - x += resizeState.initialSurfaceSize.width() - m_item->surface()->size().width(); - m_item->moveItem()->setPosition(QPointF(x, y)); + dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width(); + QPointF offset = m_item->mapFromSurface({dx, dy}); + m_item->moveItem()->setPosition(resizeState.initialPosition + offset); } } @@ -285,11 +285,11 @@ void XdgPopupV6Integration::handleGeometryChanged() { if (m_item->view()->output()) { const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft(); - const QPoint position = m_popup->unconstrainedPosition() + windowOffset; + const QPoint surfacePosition = m_popup->unconstrainedPosition() + windowOffset; + const QPoint itemPosition = m_item->mapFromSurface(surfacePosition).toPoint(); //TODO: positioner size or other size...? - const float scaleFactor = m_item->view()->output()->scaleFactor(); //TODO check positioner constraints etc... sliding, flipping - m_item->moveItem()->setPosition(position * scaleFactor); + m_item->moveItem()->setPosition(itemPosition); } else { qWarning() << "XdgPopupV6Integration popup item without output" << m_item; } diff --git a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h index 5b56af89b..049b901c9 100644 --- a/src/compositor/extensions/qwaylandxdgshellv6integration_p.h +++ b/src/compositor/extensions/qwaylandxdgshellv6integration_p.h @@ -96,7 +96,7 @@ private: struct { QWaylandSeat *seat = nullptr; QPointF initialOffset; - bool initialized; + bool initialized = false; } moveState; struct { @@ -106,7 +106,7 @@ private: QPointF initialMousePos; QPointF initialPosition; QSize initialSurfaceSize; - bool initialized; + bool initialized = false; } resizeState; struct { diff --git a/src/compositor/global/global.pri b/src/compositor/global/global.pri index 29d4f4376..172f916bf 100644 --- a/src/compositor/global/global.pri +++ b/src/compositor/global/global.pri @@ -4,6 +4,7 @@ HEADERS += \ global/qtwaylandcompositorglobal.h \ global/qwaylandcompositorextension.h \ global/qwaylandcompositorextension_p.h \ + global/qwaylandutils_p.h \ global/qwaylandquickextension.h \ SOURCES += \ diff --git a/src/compositor/global/qwaylandcompositorextension.cpp b/src/compositor/global/qwaylandcompositorextension.cpp index e50df48bd..912985399 100644 --- a/src/compositor/global/qwaylandcompositorextension.cpp +++ b/src/compositor/global/qwaylandcompositorextension.cpp @@ -44,7 +44,7 @@ #include <QtCore/QCoreApplication> #include <QtCore/QDebug> -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE diff --git a/src/compositor/global/qwaylandutils_p.h b/src/compositor/global/qwaylandutils_p.h new file mode 100644 index 000000000..934e27617 --- /dev/null +++ b/src/compositor/global/qwaylandutils_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWaylandCompositor module 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 QWAYLANDUTILS_P_H +#define QWAYLANDUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +struct wl_resource; + +QT_BEGIN_NAMESPACE + +namespace QtWayland { + +template<typename return_type> +return_type fromResource(struct ::wl_resource *resource) { + if (auto *r = std::remove_pointer<return_type>::type::Resource::fromResource(resource)) + return static_cast<return_type>(r->object()); + return nullptr; +} + +} // namespace QtWayland + +QT_END_NAMESPACE + +#endif // QWAYLANDUTILS_P_H diff --git a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h index 13a69fce9..7b458fbc2 100644 --- a/src/compositor/hardware_integration/qwlclientbufferintegration_p.h +++ b/src/compositor/hardware_integration/qwlclientbufferintegration_p.h @@ -55,7 +55,7 @@ #include <QtWaylandCompositor/qwaylandsurface.h> #include <QtWaylandCompositor/qwaylandbufferref.h> #include <QtCore/QSize> -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE diff --git a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp index 7df9ead3c..cb1ee3da0 100644 --- a/src/compositor/wayland_wrapper/qwlclientbuffer.cpp +++ b/src/compositor/wayland_wrapper/qwlclientbuffer.cpp @@ -47,7 +47,7 @@ #include <QtCore/QDebug> -#include <wayland-server-protocol.h> +#include <QtWaylandCompositor/private/wayland-wayland-server-protocol.h> #include "qwaylandsharedmemoryformathelper_p.h" #include <QtWaylandCompositor/private/qwaylandcompositor_p.h> diff --git a/src/compositor/wayland_wrapper/qwlclientbuffer_p.h b/src/compositor/wayland_wrapper/qwlclientbuffer_p.h index ac8c1ed01..f31ef5d46 100644 --- a/src/compositor/wayland_wrapper/qwlclientbuffer_p.h +++ b/src/compositor/wayland_wrapper/qwlclientbuffer_p.h @@ -59,7 +59,7 @@ #include <QtWaylandCompositor/QWaylandSurface> #include <QtWaylandCompositor/QWaylandBufferRef> -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE @@ -110,7 +110,7 @@ protected: void ref(); void deref(); void sendRelease(); - void setDestroyed(); + virtual void setDestroyed(); struct ::wl_resource *m_buffer = nullptr; QRegion m_damage; diff --git a/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h b/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h index eca6d4b54..aed4e9185 100644 --- a/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h +++ b/src/compositor/wayland_wrapper/qwldatadevicemanager_p.h @@ -109,7 +109,7 @@ private: QMimeData m_retainedData; QSocketNotifier *m_retainedReadNotifier = nullptr; QList<QSocketNotifier *> m_obsoleteRetainedReadNotifiers; - int m_retainedReadIndex; + int m_retainedReadIndex = 0; QByteArray m_retainedReadBuf; bool m_compositorOwnsSelection = false; diff --git a/src/compositor/wayland_wrapper/qwldatasource.cpp b/src/compositor/wayland_wrapper/qwldatasource.cpp index baa47d6fc..f5f456790 100644 --- a/src/compositor/wayland_wrapper/qwldatasource.cpp +++ b/src/compositor/wayland_wrapper/qwldatasource.cpp @@ -41,6 +41,7 @@ #include "qwldataoffer_p.h" #include "qwldatadevice_p.h" #include "qwldatadevicemanager_p.h" +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <unistd.h> #include <QtWaylandCompositor/private/wayland-wayland-server-protocol.h> @@ -101,7 +102,7 @@ void DataSource::setDevice(DataDevice *device) DataSource *DataSource::fromResource(struct ::wl_resource *resource) { - return static_cast<DataSource *>(Resource::fromResource(resource)->data_source_object); + return QtWayland::fromResource<DataSource *>(resource); } void DataSource::data_source_offer(Resource *, const QString &mime_type) diff --git a/src/compositor/wayland_wrapper/qwlregion.cpp b/src/compositor/wayland_wrapper/qwlregion.cpp index 52c19e946..4383474cb 100644 --- a/src/compositor/wayland_wrapper/qwlregion.cpp +++ b/src/compositor/wayland_wrapper/qwlregion.cpp @@ -39,6 +39,8 @@ #include "qwlregion_p.h" +#include <QtWaylandCompositor/private/qwaylandutils_p.h> + QT_BEGIN_NAMESPACE namespace QtWayland { @@ -54,9 +56,7 @@ Region::~Region() Region *Region::fromResource(struct ::wl_resource *resource) { - if (auto *r = Resource::fromResource(resource)) - return static_cast<Region *>(r->region_object); - return nullptr; + return QtWayland::fromResource<Region *>(resource); } void Region::region_destroy_resource(Resource *) diff --git a/src/compositor/wayland_wrapper/wayland_wrapper.pri b/src/compositor/wayland_wrapper/wayland_wrapper.pri index 3041d7696..08dd38e82 100644 --- a/src/compositor/wayland_wrapper/wayland_wrapper.pri +++ b/src/compositor/wayland_wrapper/wayland_wrapper.pri @@ -1,18 +1,16 @@ CONFIG += wayland-scanner -WAYLANDSERVERSOURCES_SYSTEM += \ - ../3rdparty/protocol/wayland.xml \ +WAYLANDSERVERSOURCES += \ + ../3rdparty/protocol/wayland.xml HEADERS += \ wayland_wrapper/qwlbuffermanager_p.h \ wayland_wrapper/qwlclientbuffer_p.h \ - wayland_wrapper/qwlregion_p.h \ - ../shared/qwaylandxkb_p.h \ + wayland_wrapper/qwlregion_p.h SOURCES += \ wayland_wrapper/qwlbuffermanager.cpp \ wayland_wrapper/qwlclientbuffer.cpp \ - wayland_wrapper/qwlregion.cpp \ - ../shared/qwaylandxkb.cpp \ + wayland_wrapper/qwlregion.cpp qtConfig(wayland-datadevice) { HEADERS += \ |