diff options
165 files changed, 7252 insertions, 1051 deletions
diff --git a/.gitignore b/.gitignore index bb3819ca3..b1a806210 100644 --- a/.gitignore +++ b/.gitignore @@ -65,8 +65,11 @@ src/plugins/shellintegration/ivi-shell/qwayland-ivi-application.h src/plugins/shellintegration/ivi-shell/qwayland-ivi-application.cpp src/plugins/shellintegration/ivi-shell/qwayland-ivi-controller.h src/plugins/shellintegration/ivi-shell/qwayland-ivi-controller.cpp +src/plugins/shellintegration/fullscreen-shell-v1/qwayland-fullscreen-shell-unstable-v1.cpp +src/plugins/shellintegration/fullscreen-shell-v1/qwayland-fullscreen-shell-unstable-v1.h src/imports/compositor/compositor.qrc tests/auto/client/client/tst_client +tests/auto/client/fullscreenshellv1/tst_client_fullscreenshell1 tests/auto/compositor/compositor/tst_compositor tests/auto/compositor/compositor/qwayland-*.cpp tests/auto/compositor/compositor/qwayland-*.h diff --git a/.qmake.conf b/.qmake.conf index 097d8b948..f8cda0e7d 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,3 +1,3 @@ load(qt_build_config) -MODULE_VERSION = 5.12.0 +MODULE_VERSION = 5.13.0 diff --git a/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro b/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro new file mode 100644 index 000000000..28dcadcbf --- /dev/null +++ b/config.tests/dmabuf_client_buffer/dmabuf_client_buffer.pro @@ -0,0 +1 @@ +SOURCES += main.cpp diff --git a/config.tests/dmabuf_client_buffer/main.cpp b/config.tests/dmabuf_client_buffer/main.cpp new file mode 100644 index 000000000..71447dc8e --- /dev/null +++ b/config.tests/dmabuf_client_buffer/main.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Qt Compositor. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +//If libdrm is available, the following files should exist +#include "drm_mode.h" +#include "drm_fourcc.h" + +int main() +{ +// test if DMA BUF is supported +#ifndef EGL_LINUX_DMA_BUF_EXT +#error DMA BUF Extension not available +#endif + +// test if DMA BUF import modifier extension is supported +#ifndef EGL_EXT_image_dma_buf_import_modifiers +#error DMA BUF Import modifier extension not available +#endif + + return 0; +} diff --git a/config.tests/wayland/main.cpp b/config.tests/wayland/main.cpp index 9e0002db5..8cab379bf 100644 --- a/config.tests/wayland/main.cpp +++ b/config.tests/wayland/main.cpp @@ -53,15 +53,15 @@ int main() { #if WAYLAND_VERSION_MAJOR < 1 -# error Wayland 1.6.0 or higher required +# error Wayland 1.8.0 or higher required #endif #if WAYLAND_VERSION_MAJOR == 1 -# if WAYLAND_VERSION_MINOR < 6 -# error Wayland 1.6.0 or higher required +# if WAYLAND_VERSION_MINOR < 8 +# error Wayland 1.8.0 or higher required # endif -# if WAYLAND_VERSION_MINOR == 6 +# if WAYLAND_VERSION_MINOR == 8 # if WAYLAND_VERSION_MICRO < 0 -# error Wayland 1.6.0 or higher required +# error Wayland 1.8.0 or higher required # endif # endif #endif diff --git a/examples/wayland/minimal-cpp/compositor.h b/examples/wayland/minimal-cpp/compositor.h index f06421320..3c0c80e0e 100644 --- a/examples/wayland/minimal-cpp/compositor.h +++ b/examples/wayland/minimal-cpp/compositor.h @@ -71,11 +71,11 @@ public: QOpenGLTexture *getTexture(); int iviId() const { return m_iviId; } - QRect globalGeometry() const { return QRect(globalPosition(), surface()->size()); } + QRect globalGeometry() const { return QRect(globalPosition(), surface()->destinationSize()); } void setGlobalPosition(const QPoint &globalPos) { m_pos = globalPos; m_positionSet = true; } QPoint globalPosition() const { return m_pos; } QPoint mapToLocal(const QPoint &globalPos) const; - QSize size() const { return surface() ? surface()->size() : QSize(); } + QSize size() const { return surface() ? surface()->destinationSize() : QSize(); } void initPosition(const QSize &screenSize, const QSize &surfaceSize); diff --git a/examples/wayland/minimal-cpp/window.cpp b/examples/wayland/minimal-cpp/window.cpp index 673e15fd8..f345bd51f 100644 --- a/examples/wayland/minimal-cpp/window.cpp +++ b/examples/wayland/minimal-cpp/window.cpp @@ -95,7 +95,7 @@ void Window::paintGL() GLuint textureId = texture->textureId(); QWaylandSurface *surface = view->surface(); if (surface && surface->hasContent()) { - QSize s = surface->size(); + QSize s = surface->destinationSize(); view->initPosition(size(), s); QPointF pos = view->globalPosition(); QRectF surfaceGeometry(pos, s); diff --git a/examples/wayland/qwindow-compositor/compositor.cpp b/examples/wayland/qwindow-compositor/compositor.cpp index f25e67d87..199de22e2 100644 --- a/examples/wayland/qwindow-compositor/compositor.cpp +++ b/examples/wayland/qwindow-compositor/compositor.cpp @@ -79,7 +79,7 @@ QOpenGLTexture *View::getTexture() if (newContent) { m_texture = buf.toOpenGLTexture(); if (surface()) { - m_size = surface()->size(); + m_size = surface()->destinationSize(); m_origin = buf.origin() == QWaylandSurface::OriginTopLeft ? QOpenGLTextureBlitter::OriginTopLeft : QOpenGLTextureBlitter::OriginBottomLeft; @@ -96,7 +96,7 @@ QOpenGLTextureBlitter::Origin View::textureOrigin() const QSize View::size() const { - return surface() ? surface()->size() : m_size; + return surface() ? surface()->destinationSize() : m_size; } bool View::isCursor() const diff --git a/examples/wayland/qwindow-compositor/compositor.h b/examples/wayland/qwindow-compositor/compositor.h index 2a395a1ab..8f18dc53d 100644 --- a/examples/wayland/qwindow-compositor/compositor.h +++ b/examples/wayland/qwindow-compositor/compositor.h @@ -82,7 +82,7 @@ public: void setParentView(View *parent) { m_parentView = parent; } View *parentView() const { return m_parentView; } QPointF parentPosition() const { return m_parentView ? (m_parentView->position() + m_parentView->parentPosition()) : QPointF(); } - QSize windowSize() { return m_xdgSurface ? m_xdgSurface->windowGeometry().size() : surface() ? surface()->size() : m_size; } + QSize windowSize() { return m_xdgSurface ? m_xdgSurface->windowGeometry().size() : surface() ? surface()->destinationSize() : m_size; } QPoint offset() const { return m_offset; } qreal animationFactor() const {return m_animationFactor; } diff --git a/examples/wayland/qwindow-compositor/window.cpp b/examples/wayland/qwindow-compositor/window.cpp index 80aeec4c3..b8b8e52ec 100644 --- a/examples/wayland/qwindow-compositor/window.cpp +++ b/examples/wayland/qwindow-compositor/window.cpp @@ -180,7 +180,7 @@ void Window::startResize(int edge, bool anchored) m_grabState = ResizeGrab; m_resizeEdge = edge; m_resizeAnchored = anchored; - m_resizeAnchorPosition = getAnchorPosition(m_mouseView->position(), edge, m_mouseView->surface()->size()); + m_resizeAnchorPosition = getAnchorPosition(m_mouseView->position(), edge, m_mouseView->surface()->destinationSize()); } void Window::startDrag(View *dragIcon) diff --git a/features/wayland-scanner-client-wayland-protocol-include.prf b/features/wayland-scanner-client-wayland-protocol-include.prf new file mode 100644 index 000000000..9c5d2d1ed --- /dev/null +++ b/features/wayland-scanner-client-wayland-protocol-include.prf @@ -0,0 +1,53 @@ +# Special version of WAYLANDCLIENTSOURCES to be used with protocols that +# have requests that create objects with interfaces defined in the core +# wayland protocol. +# +# E.g. the xcomposite protocol has a request which creates a wl_buffer. With +# the regular wayland-scanner.prf compilation would fail because +# wl_buffer_interface is not defined. +# +# This version solves the problem by prepending +# #include <QtWaylandClient/private/wayland-wayland-client-protocol.h> +# to the wayland-scanner generated files. + +isEmpty(QMAKE_WAYLAND_SCANNER):error("QMAKE_WAYLAND_SCANNER not defined for this mkspec") + +!isEmpty(MODULE_INCNAME) { + WAYLAND_INCLUDE_DIR = $$MODULE_INCNAME/private +} + +wayland_client_header.name = wayland ${QMAKE_FILE_BASE} +wayland_client_header.input = WAYLANDCLIENTSOURCES +wayland_client_header.variable_out = HEADERS +wayland_client_header.output = wayland-${QMAKE_FILE_BASE}-client-protocol$${first(QMAKE_EXT_H)} +# XXX: Prepend the necessary include in the generated header +wayland_client_header.commands = echo \"$${LITERAL_HASH}include <QtWaylandClient/private/wayland-wayland-client-protocol.h>\" > ${QMAKE_FILE_OUT} && $$QMAKE_WAYLAND_SCANNER --include-core-only client-header < ${QMAKE_FILE_IN} >> ${QMAKE_FILE_OUT} +QMAKE_EXTRA_COMPILERS += wayland_client_header + +wayland_code.name = wayland ${QMAKE_FILE_BASE} +wayland_code.input = WAYLANDCLIENTSOURCES +wayland_code.variable_out = SOURCES +wayland_code.output = wayland-${QMAKE_FILE_BASE}-protocol.c +wayland_code.commands = $$QMAKE_WAYLAND_SCANNER --include-core-only code < ${QMAKE_FILE_IN} > ${QMAKE_FILE_OUT} +silent:wayland_code.commands = @echo Wayland code header ${QMAKE_FILE_IN} && $$wayland_code.commands +QMAKE_EXTRA_COMPILERS += wayland_code + +qtPrepareTool(QMAKE_QTWAYLANDSCANNER, qtwaylandscanner) + +qtwayland_client_header.name = qtwayland ${QMAKE_FILE_BASE} +qtwayland_client_header.input = WAYLANDCLIENTSOURCES +qtwayland_client_header.variable_out = HEADERS +qtwayland_client_header.depends += $$QMAKE_QTWAYLANDSCANNER_EXE wayland-${QMAKE_FILE_BASE}-client-protocol$${first(QMAKE_EXT_H)} +qtwayland_client_header.output = qwayland-${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)} +qtwayland_client_header.commands = $$QMAKE_QTWAYLANDSCANNER client-header ${QMAKE_FILE_IN} $$WAYLAND_INCLUDE_DIR > ${QMAKE_FILE_OUT} +silent:qtwayland_client_header.commands = @echo QtWayland client header ${QMAKE_FILE_IN} && $$qtwayland_client_header.commands +QMAKE_EXTRA_COMPILERS += qtwayland_client_header + +qtwayland_client_code.name = qtwayland ${QMAKE_FILE_BASE} +qtwayland_client_code.input = WAYLANDCLIENTSOURCES +qtwayland_client_code.variable_out = SOURCES +qtwayland_client_code.depends += $$QMAKE_QTWAYLANDSCANNER_EXE qwayland-${QMAKE_FILE_BASE}$${first(QMAKE_EXT_H)} +qtwayland_client_code.output = qwayland-${QMAKE_FILE_BASE}.cpp +qtwayland_client_code.commands = $$QMAKE_QTWAYLANDSCANNER client-code ${QMAKE_FILE_IN} $$WAYLAND_INCLUDE_DIR > ${QMAKE_FILE_OUT} +silent:qtwayland_client_code.commands = @echo QtWayland client code ${QMAKE_FILE_IN} && $$qtwayland_client_code.commands +QMAKE_EXTRA_COMPILERS += qtwayland_client_code diff --git a/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml b/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml new file mode 100644 index 000000000..1bca7b7c7 --- /dev/null +++ b/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml @@ -0,0 +1,245 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="fullscreen_shell_unstable_v1"> + + <copyright> + Copyright © 2016 Yong Bakos + Copyright © 2015 Jason Ekstrand + Copyright © 2015 Jonas Ã…dahl + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <interface name="zwp_fullscreen_shell_v1" version="1"> + <description summary="displays a single surface per output"> + Displays a single surface per output. + + This interface provides a mechanism for a single client to display + simple full-screen surfaces. While there technically may be multiple + clients bound to this interface, only one of those clients should be + shown at a time. + + To present a surface, the client uses either the present_surface or + present_surface_for_mode requests. Presenting a surface takes effect + on the next wl_surface.commit. See the individual requests for + details about scaling and mode switches. + + The client can have at most one surface per output at any time. + Requesting a surface to be presented on an output that already has a + surface replaces the previously presented surface. Presenting a null + surface removes its content and effectively disables the output. + Exactly what happens when an output is "disabled" is + compositor-specific. The same surface may be presented on multiple + outputs simultaneously. + + Once a surface is presented on an output, it stays on that output + until either the client removes it or the compositor destroys the + output. This way, the client can update the output's contents by + simply attaching a new buffer. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <request name="release" type="destructor"> + <description summary="release the wl_fullscreen_shell interface"> + Release the binding from the wl_fullscreen_shell interface. + + This destroys the server-side object and frees this binding. If + the client binds to wl_fullscreen_shell multiple times, it may wish + to free some of those bindings. + </description> + </request> + + <enum name="capability"> + <description summary="capabilities advertised by the compositor"> + Various capabilities that can be advertised by the compositor. They + are advertised one-at-a-time when the wl_fullscreen_shell interface is + bound. See the wl_fullscreen_shell.capability event for more details. + + ARBITRARY_MODES: + This is a hint to the client that indicates that the compositor is + capable of setting practically any mode on its outputs. If this + capability is provided, wl_fullscreen_shell.present_surface_for_mode + will almost never fail and clients should feel free to set whatever + mode they like. If the compositor does not advertise this, it may + still support some modes that are not advertised through wl_global.mode + but it is less likely. + + CURSOR_PLANE: + This is a hint to the client that indicates that the compositor can + handle a cursor surface from the client without actually compositing. + This may be because of a hardware cursor plane or some other mechanism. + If the compositor does not advertise this capability then setting + wl_pointer.cursor may degrade performance or be ignored entirely. If + CURSOR_PLANE is not advertised, it is recommended that the client draw + its own cursor and set wl_pointer.cursor(NULL). + </description> + <entry name="arbitrary_modes" value="1" summary="compositor is capable of almost any output mode"/> + <entry name="cursor_plane" value="2" summary="compositor has a separate cursor plane"/> + </enum> + + <event name="capability"> + <description summary="advertises a capability of the compositor"> + Advertises a single capability of the compositor. + + When the wl_fullscreen_shell interface is bound, this event is emitted + once for each capability advertised. Valid capabilities are given by + the wl_fullscreen_shell.capability enum. If clients want to take + advantage of any of these capabilities, they should use a + wl_display.sync request immediately after binding to ensure that they + receive all the capability events. + </description> + <arg name="capability" type="uint"/> + </event> + + <enum name="present_method"> + <description summary="different method to set the surface fullscreen"> + Hints to indicate to the compositor how to deal with a conflict + between the dimensions of the surface and the dimensions of the + output. The compositor is free to ignore this parameter. + </description> + <entry name="default" value="0" summary="no preference, apply default policy"/> + <entry name="center" value="1" summary="center the surface on the output"/> + <entry name="zoom" value="2" summary="scale the surface, preserving aspect ratio, to the largest size that will fit on the output" /> + <entry name="zoom_crop" value="3" summary="scale the surface, preserving aspect ratio, to fully fill the output cropping if needed" /> + <entry name="stretch" value="4" summary="scale the surface to the size of the output ignoring aspect ratio" /> + </enum> + + <request name="present_surface"> + <description summary="present surface for display"> + Present a surface on the given output. + + If the output is null, the compositor will present the surface on + whatever display (or displays) it thinks best. In particular, this + may replace any or all surfaces currently presented so it should + not be used in combination with placing surfaces on specific + outputs. + + The method parameter is a hint to the compositor for how the surface + is to be presented. In particular, it tells the compositor how to + handle a size mismatch between the presented surface and the + output. The compositor is free to ignore this parameter. + + The "zoom", "zoom_crop", and "stretch" methods imply a scaling + operation on the surface. This will override any kind of output + scaling, so the buffer_scale property of the surface is effectively + ignored. + </description> + <arg name="surface" type="object" interface="wl_surface" allow-null="true"/> + <arg name="method" type="uint"/> + <arg name="output" type="object" interface="wl_output" allow-null="true"/> + </request> + + <request name="present_surface_for_mode"> + <description summary="present surface for display at a particular mode"> + Presents a surface on the given output for a particular mode. + + If the current size of the output differs from that of the surface, + the compositor will attempt to change the size of the output to + match the surface. The result of the mode-switch operation will be + returned via the provided wl_fullscreen_shell_mode_feedback object. + + If the current output mode matches the one requested or if the + compositor successfully switches the mode to match the surface, + then the mode_successful event will be sent and the output will + contain the contents of the given surface. If the compositor + cannot match the output size to the surface size, the mode_failed + will be sent and the output will contain the contents of the + previously presented surface (if any). If another surface is + presented on the given output before either of these has a chance + to happen, the present_cancelled event will be sent. + + Due to race conditions and other issues unknown to the client, no + mode-switch operation is guaranteed to succeed. However, if the + mode is one advertised by wl_output.mode or if the compositor + advertises the ARBITRARY_MODES capability, then the client should + expect that the mode-switch operation will usually succeed. + + If the size of the presented surface changes, the resulting output + is undefined. The compositor may attempt to change the output mode + to compensate. However, there is no guarantee that a suitable mode + will be found and the client has no way to be notified of success + or failure. + + The framerate parameter specifies the desired framerate for the + output in mHz. The compositor is free to ignore this parameter. A + value of 0 indicates that the client has no preference. + + If the value of wl_output.scale differs from wl_surface.buffer_scale, + then the compositor may choose a mode that matches either the buffer + size or the surface size. In either case, the surface will fill the + output. + </description> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="output" type="object" interface="wl_output"/> + <arg name="framerate" type="int"/> + <arg name="feedback" type="new_id" interface="zwp_fullscreen_shell_mode_feedback_v1"/> + </request> + + <enum name="error"> + <description summary="wl_fullscreen_shell error values"> + These errors can be emitted in response to wl_fullscreen_shell requests. + </description> + <entry name="invalid_method" value="0" summary="present_method is not known"/> + </enum> + </interface> + + <interface name="zwp_fullscreen_shell_mode_feedback_v1" version="1"> + <event name="mode_successful"> + <description summary="mode switch succeeded"> + This event indicates that the attempted mode switch operation was + successful. A surface of the size requested in the mode switch + will fill the output without scaling. + + Upon receiving this event, the client should destroy the + wl_fullscreen_shell_mode_feedback object. + </description> + </event> + + <event name="mode_failed"> + <description summary="mode switch failed"> + This event indicates that the attempted mode switch operation + failed. This may be because the requested output mode is not + possible or it may mean that the compositor does not want to allow it. + + Upon receiving this event, the client should destroy the + wl_fullscreen_shell_mode_feedback object. + </description> + </event> + + <event name="present_cancelled"> + <description summary="mode switch cancelled"> + This event indicates that the attempted mode switch operation was + cancelled. Most likely this is because the client requested a + second mode switch before the first one completed. + + Upon receiving this event, the client should destroy the + wl_fullscreen_shell_mode_feedback object. + </description> + </event> + </interface> + +</protocol> diff --git a/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml b/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml new file mode 100644 index 000000000..154afe23e --- /dev/null +++ b/src/3rdparty/protocol/linux-dmabuf-unstable-v1.xml @@ -0,0 +1,348 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="linux_dmabuf_unstable_v1"> + + <copyright> + Copyright © 2014, 2015 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <interface name="zwp_linux_dmabuf_v1" version="3"> + <description summary="factory for creating dmabuf-based wl_buffers"> + Following the interfaces from: + https://www.khronos.org/registry/egl/extensions/EXT/EGL_EXT_image_dma_buf_import.txt + and the Linux DRM sub-system's AddFb2 ioctl. + + This interface offers ways to create generic dmabuf-based + wl_buffers. Immediately after a client binds to this interface, + the set of supported formats and format modifiers is sent with + 'format' and 'modifier' events. + + The following are required from clients: + + - Clients must ensure that either all data in the dma-buf is + coherent for all subsequent read access or that coherency is + correctly handled by the underlying kernel-side dma-buf + implementation. + + - Don't make any more attachments after sending the buffer to the + compositor. Making more attachments later increases the risk of + the compositor not being able to use (re-import) an existing + dmabuf-based wl_buffer. + + The underlying graphics stack must ensure the following: + + - The dmabuf file descriptors relayed to the server will stay valid + for the whole lifetime of the wl_buffer. This means the server may + at any time use those fds to import the dmabuf into any kernel + sub-system that might accept it. + + To create a wl_buffer from one or more dmabufs, a client creates a + zwp_linux_dmabuf_params_v1 object with a zwp_linux_dmabuf_v1.create_params + request. All planes required by the intended format are added with + the 'add' request. Finally, a 'create' or 'create_immed' request is + issued, which has the following outcome depending on the import success. + + The 'create' request, + - on success, triggers a 'created' event which provides the final + wl_buffer to the client. + - on failure, triggers a 'failed' event to convey that the server + cannot use the dmabufs received from the client. + + For the 'create_immed' request, + - on success, the server immediately imports the added dmabufs to + create a wl_buffer. No event is sent from the server in this case. + - on failure, the server can choose to either: + - terminate the client by raising a fatal error. + - mark the wl_buffer as failed, and send a 'failed' event to the + client. If the client uses a failed wl_buffer as an argument to any + request, the behaviour is compositor implementation-defined. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. + </description> + + <request name="destroy" type="destructor"> + <description summary="unbind the factory"> + Objects created through this interface, especially wl_buffers, will + remain valid. + </description> + </request> + + <request name="create_params"> + <description summary="create a temporary object for buffer parameters"> + This temporary object is used to collect multiple dmabuf handles into + a single batch to create a wl_buffer. It can only be used once and + should be destroyed after a 'created' or 'failed' event has been + received. + </description> + <arg name="params_id" type="new_id" interface="zwp_linux_buffer_params_v1" + summary="the new temporary"/> + </request> + + <event name="format"> + <description summary="supported buffer format"> + This event advertises one buffer format that the server supports. + All the supported formats are advertised once when the client + binds to this interface. A roundtrip after binding guarantees + that the client has received all supported formats. + + For the definition of the format codes, see the + zwp_linux_buffer_params_v1::create request. + + Warning: the 'format' event is likely to be deprecated and replaced + with the 'modifier' event introduced in zwp_linux_dmabuf_v1 + version 3, described below. Please refrain from using the information + received from this event. + </description> + <arg name="format" type="uint" summary="DRM_FORMAT code"/> + </event> + + <event name="modifier" since="3"> + <description summary="supported buffer format modifier"> + This event advertises the formats that the server supports, along with + the modifiers supported for each format. All the supported modifiers + for all the supported formats are advertised once when the client + binds to this interface. A roundtrip after binding guarantees that + the client has received all supported format-modifier pairs. + + For the definition of the format and modifier codes, see the + zwp_linux_buffer_params_v1::create request. + </description> + <arg name="format" type="uint" summary="DRM_FORMAT code"/> + <arg name="modifier_hi" type="uint" + summary="high 32 bits of layout modifier"/> + <arg name="modifier_lo" type="uint" + summary="low 32 bits of layout modifier"/> + </event> + </interface> + + <interface name="zwp_linux_buffer_params_v1" version="3"> + <description summary="parameters for creating a dmabuf-based wl_buffer"> + This temporary object is a collection of dmabufs and other + parameters that together form a single logical buffer. The temporary + object may eventually create one wl_buffer unless cancelled by + destroying it before requesting 'create'. + + Single-planar formats only require one dmabuf, however + multi-planar formats may require more than one dmabuf. For all + formats, an 'add' request must be called once per plane (even if the + underlying dmabuf fd is identical). + + You must use consecutive plane indices ('plane_idx' argument for 'add') + from zero to the number of planes used by the drm_fourcc format code. + All planes required by the format must be given exactly once, but can + be given in any order. Each plane index can be set only once. + </description> + + <enum name="error"> + <entry name="already_used" value="0" + summary="the dmabuf_batch object has already been used to create a wl_buffer"/> + <entry name="plane_idx" value="1" + summary="plane index out of bounds"/> + <entry name="plane_set" value="2" + summary="the plane index was already set"/> + <entry name="incomplete" value="3" + summary="missing or too many planes to create a buffer"/> + <entry name="invalid_format" value="4" + summary="format not supported"/> + <entry name="invalid_dimensions" value="5" + summary="invalid width or height"/> + <entry name="out_of_bounds" value="6" + summary="offset + stride * height goes out of dmabuf bounds"/> + <entry name="invalid_wl_buffer" value="7" + summary="invalid wl_buffer resulted from importing dmabufs via + the create_immed request on given buffer_params"/> + </enum> + + <request name="destroy" type="destructor"> + <description summary="delete this object, used or not"> + Cleans up the temporary data sent to the server for dmabuf-based + wl_buffer creation. + </description> + </request> + + <request name="add"> + <description summary="add a dmabuf to the temporary set"> + This request adds one dmabuf to the set in this + zwp_linux_buffer_params_v1. + + The 64-bit unsigned value combined from modifier_hi and modifier_lo + is the dmabuf layout modifier. DRM AddFB2 ioctl calls this the + fb modifier, which is defined in drm_mode.h of Linux UAPI. + This is an opaque token. Drivers use this token to express tiling, + compression, etc. driver-specific modifications to the base format + defined by the DRM fourcc code. + + This request raises the PLANE_IDX error if plane_idx is too large. + The error PLANE_SET is raised if attempting to set a plane that + was already set. + </description> + <arg name="fd" type="fd" summary="dmabuf fd"/> + <arg name="plane_idx" type="uint" summary="plane index"/> + <arg name="offset" type="uint" summary="offset in bytes"/> + <arg name="stride" type="uint" summary="stride in bytes"/> + <arg name="modifier_hi" type="uint" + summary="high 32 bits of layout modifier"/> + <arg name="modifier_lo" type="uint" + summary="low 32 bits of layout modifier"/> + </request> + + <enum name="flags"> + <entry name="y_invert" value="1" summary="contents are y-inverted"/> + <entry name="interlaced" value="2" summary="content is interlaced"/> + <entry name="bottom_first" value="4" summary="bottom field first"/> + </enum> + + <request name="create"> + <description summary="create a wl_buffer from the given dmabufs"> + This asks for creation of a wl_buffer from the added dmabuf + buffers. The wl_buffer is not created immediately but returned via + the 'created' event if the dmabuf sharing succeeds. The sharing + may fail at runtime for reasons a client cannot predict, in + which case the 'failed' event is triggered. + + The 'format' argument is a DRM_FORMAT code, as defined by the + libdrm's drm_fourcc.h. The Linux kernel's DRM sub-system is the + authoritative source on how the format codes should work. + + The 'flags' is a bitfield of the flags defined in enum "flags". + 'y_invert' means the that the image needs to be y-flipped. + + Flag 'interlaced' means that the frame in the buffer is not + progressive as usual, but interlaced. An interlaced buffer as + supported here must always contain both top and bottom fields. + The top field always begins on the first pixel row. The temporal + ordering between the two fields is top field first, unless + 'bottom_first' is specified. It is undefined whether 'bottom_first' + is ignored if 'interlaced' is not set. + + This protocol does not convey any information about field rate, + duration, or timing, other than the relative ordering between the + two fields in one buffer. A compositor may have to estimate the + intended field rate from the incoming buffer rate. It is undefined + whether the time of receiving wl_surface.commit with a new buffer + attached, applying the wl_surface state, wl_surface.frame callback + trigger, presentation, or any other point in the compositor cycle + is used to measure the frame or field times. There is no support + for detecting missed or late frames/fields/buffers either, and + there is no support whatsoever for cooperating with interlaced + compositor output. + + The composited image quality resulting from the use of interlaced + buffers is explicitly undefined. A compositor may use elaborate + hardware features or software to deinterlace and create progressive + output frames from a sequence of interlaced input buffers, or it + may produce substandard image quality. However, compositors that + cannot guarantee reasonable image quality in all cases are recommended + to just reject all interlaced buffers. + + Any argument errors, including non-positive width or height, + mismatch between the number of planes and the format, bad + format, bad offset or stride, may be indicated by fatal protocol + errors: INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, + OUT_OF_BOUNDS. + + Dmabuf import errors in the server that are not obvious client + bugs are returned via the 'failed' event as non-fatal. This + allows attempting dmabuf sharing and falling back in the client + if it fails. + + This request can be sent only once in the object's lifetime, after + which the only legal request is destroy. This object should be + destroyed after issuing a 'create' request. Attempting to use this + object after issuing 'create' raises ALREADY_USED protocol error. + + It is not mandatory to issue 'create'. If a client wants to + cancel the buffer creation, it can just destroy this object. + </description> + <arg name="width" type="int" summary="base plane width in pixels"/> + <arg name="height" type="int" summary="base plane height in pixels"/> + <arg name="format" type="uint" summary="DRM_FORMAT code"/> + <arg name="flags" type="uint" summary="see enum flags"/> + </request> + + <event name="created"> + <description summary="buffer creation succeeded"> + This event indicates that the attempted buffer creation was + successful. It provides the new wl_buffer referencing the dmabuf(s). + + Upon receiving this event, the client should destroy the + zlinux_dmabuf_params object. + </description> + <arg name="buffer" type="new_id" interface="wl_buffer" + summary="the newly created wl_buffer"/> + </event> + + <event name="failed"> + <description summary="buffer creation failed"> + This event indicates that the attempted buffer creation has + failed. It usually means that one of the dmabuf constraints + has not been fulfilled. + + Upon receiving this event, the client should destroy the + zlinux_buffer_params object. + </description> + </event> + + <request name="create_immed" since="2"> + <description summary="immediately create a wl_buffer from the given + dmabufs"> + This asks for immediate creation of a wl_buffer by importing the + added dmabufs. + + In case of import success, no event is sent from the server, and the + wl_buffer is ready to be used by the client. + + Upon import failure, either of the following may happen, as seen fit + by the implementation: + - the client is terminated with one of the following fatal protocol + errors: + - INCOMPLETE, INVALID_FORMAT, INVALID_DIMENSIONS, OUT_OF_BOUNDS, + in case of argument errors such as mismatch between the number + of planes and the format, bad format, non-positive width or + height, or bad offset or stride. + - INVALID_WL_BUFFER, in case the cause for failure is unknown or + plaform specific. + - the server creates an invalid wl_buffer, marks it as failed and + sends a 'failed' event to the client. The result of using this + invalid wl_buffer as an argument in any request by the client is + defined by the compositor implementation. + + This takes the same arguments as a 'create' request, and obeys the + same restrictions. + </description> + <arg name="buffer_id" type="new_id" interface="wl_buffer" + summary="id for the newly created wl_buffer"/> + <arg name="width" type="int" summary="base plane width in pixels"/> + <arg name="height" type="int" summary="base plane height in pixels"/> + <arg name="format" type="uint" summary="DRM_FORMAT code"/> + <arg name="flags" type="uint" summary="see enum flags"/> + </request> + + </interface> + +</protocol> diff --git a/src/3rdparty/protocol/qt_attribution.json b/src/3rdparty/protocol/qt_attribution.json index e5bf91e10..477293118 100644 --- a/src/3rdparty/protocol/qt_attribution.json +++ b/src/3rdparty/protocol/qt_attribution.json @@ -1,5 +1,24 @@ [ { + "Id": "wayland-fullscreen-protocol", + "Name": "Wayland Fullscreen Shell Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland platform plugin.", + "Files": "fullscreen-shell-unstable-v1.xml", + + "Description": "A Wayland shell for displaying a single surface per output", + "Homepage": "https://wayland.freedesktop.org", + "Version": "unstable v1", + "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "MIT_LICENSE.txt", + "Copyright": "Copyright © 2016 Yong Bakos +Copyright © 2015 Jason Ekstrand +Copyright © 2015 Jonas Ã…dahl" + }, + + { "Id": "wayland-protocol", "Name": "Wayland Protocol", "QDocModule": "qtwaylandcompositor", @@ -37,6 +56,23 @@ Copyright (c) 2013 BMW Car IT GmbH" }, { + "Id": "wayland-viewporter-protocol", + "Name": "Wayland Viewporter Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland Compositor API", + "Files": "viewporter.xml", + + "Description": "The Wayland viewporter extension allows a client to scale or crop a surface without modifying the buffer", + "Homepage": "https://wayland.freedesktop.org", + "Version": "1", + "DownloadLocation": "https://cgit.freedesktop.org/wayland/wayland-protocols/plain/stable/viewporter/viewporter.xml", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "MIT_LICENSE.txt", + "Copyright": "Copyright © 2013-2016 Collabora, Ltd." + }, + + { "Id": "wayland-xdg-decoration-protocol", "Name": "Wayland xdg-decoration Protocol", "QDocModule": "qtwaylandcompositor", @@ -103,5 +139,20 @@ Copyright © 2010-2013 Intel Corporation" "LicenseFile": "HPND_LICENSE.txt", "Copyright": "Copyright © 2012, 2013 Intel Corporation Copyright © 2015, 2016 Jan Arne Petersen" + }, + + { + "Id": "wayland-linux-dmabuf-unstable-v1", + "Name": "Wayland Linux Dmabuf Unstable V1 Protocol", + "QDocModule": "qtwaylandcompositor", + "QtUsage": "Used in the Qt Wayland Compositor", + "Files": "linux-dmabuf-unstable-v1.xml", + + "Description": "The linux dmabuf protocol is a way to create dmabuf-based wl_buffers", + "Homepage": "https://wayland.freedesktop.org", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "MIT_LICENSE.txt", + "Copyright": "Copyright © 2014, 2015 Collabora, Ltd." } ] diff --git a/src/3rdparty/protocol/viewporter.xml b/src/3rdparty/protocol/viewporter.xml new file mode 100644 index 000000000..c732d8c35 --- /dev/null +++ b/src/3rdparty/protocol/viewporter.xml @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="viewporter"> + + <copyright> + Copyright © 2013-2016 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + + <interface name="wp_viewporter" version="1"> + <description summary="surface cropping and scaling"> + The global interface exposing surface cropping and scaling + capabilities is used to instantiate an interface extension for a + wl_surface object. This extended interface will then allow + cropping and scaling the surface contents, effectively + disconnecting the direct relationship between the buffer and the + surface size. + </description> + + <request name="destroy" type="destructor"> + <description summary="unbind from the cropping and scaling interface"> + Informs the server that the client will not be using this + protocol object anymore. This does not affect any other objects, + wp_viewport objects included. + </description> + </request> + + <enum name="error"> + <entry name="viewport_exists" value="0" + summary="the surface already has a viewport object associated"/> + </enum> + + <request name="get_viewport"> + <description summary="extend surface interface for crop and scale"> + Instantiate an interface extension for the given wl_surface to + crop and scale its content. If the given wl_surface already has + a wp_viewport object associated, the viewport_exists + protocol error is raised. + </description> + <arg name="id" type="new_id" interface="wp_viewport" + summary="the new viewport interface id"/> + <arg name="surface" type="object" interface="wl_surface" + summary="the surface"/> + </request> + </interface> + + <interface name="wp_viewport" version="1"> + <description summary="crop and scale interface to a wl_surface"> + An additional interface to a wl_surface object, which allows the + client to specify the cropping and scaling of the surface + contents. + + This interface works with two concepts: the source rectangle (src_x, + src_y, src_width, src_height), and the destination size (dst_width, + dst_height). The contents of the source rectangle are scaled to the + destination size, and content outside the source rectangle is ignored. + This state is double-buffered, and is applied on the next + wl_surface.commit. + + The two parts of crop and scale state are independent: the source + rectangle, and the destination size. Initially both are unset, that + is, no scaling is applied. The whole of the current wl_buffer is + used as the source, and the surface size is as defined in + wl_surface.attach. + + If the destination size is set, it causes the surface size to become + dst_width, dst_height. The source (rectangle) is scaled to exactly + this size. This overrides whatever the attached wl_buffer size is, + unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface + has no content and therefore no size. Otherwise, the size is always + at least 1x1 in surface local coordinates. + + If the source rectangle is set, it defines what area of the wl_buffer is + taken as the source. If the source rectangle is set and the destination + size is not set, then src_width and src_height must be integers, and the + surface size becomes the source rectangle size. This results in cropping + without scaling. If src_width or src_height are not integers and + destination size is not set, the bad_size protocol error is raised when + the surface state is applied. + + The coordinate transformations from buffer pixel coordinates up to + the surface-local coordinates happen in the following order: + 1. buffer_transform (wl_surface.set_buffer_transform) + 2. buffer_scale (wl_surface.set_buffer_scale) + 3. crop and scale (wp_viewport.set*) + This means, that the source rectangle coordinates of crop and scale + are given in the coordinates after the buffer transform and scale, + i.e. in the coordinates that would be the surface-local coordinates + if the crop and scale was not applied. + + If src_x or src_y are negative, the bad_value protocol error is raised. + Otherwise, if the source rectangle is partially or completely outside of + the non-NULL wl_buffer, then the out_of_buffer protocol error is raised + when the surface state is applied. A NULL wl_buffer does not raise the + out_of_buffer error. + + The x, y arguments of wl_surface.attach are applied as normal to + the surface. They indicate how many pixels to remove from the + surface size from the left and the top. In other words, they are + still in the surface-local coordinate system, just like dst_width + and dst_height are. + + If the wl_surface associated with the wp_viewport is destroyed, + all wp_viewport requests except 'destroy' raise the protocol error + no_surface. + + If the wp_viewport object is destroyed, the crop and scale + state is removed from the wl_surface. The change will be applied + on the next wl_surface.commit. + </description> + + <request name="destroy" type="destructor"> + <description summary="remove scaling and cropping from the surface"> + The associated wl_surface's crop and scale state is removed. + The change is applied on the next wl_surface.commit. + </description> + </request> + + <enum name="error"> + <entry name="bad_value" value="0" + summary="negative or zero values in width or height"/> + <entry name="bad_size" value="1" + summary="destination size is not integer"/> + <entry name="out_of_buffer" value="2" + summary="source rectangle extends outside of the content area"/> + <entry name="no_surface" value="3" + summary="the wl_surface was destroyed"/> + </enum> + + <request name="set_source"> + <description summary="set the source rectangle for cropping"> + Set the source rectangle of the associated wl_surface. See + wp_viewport for the description, and relation to the wl_buffer + size. + + If all of x, y, width and height are -1.0, the source rectangle is + unset instead. Any other set of values where width or height are zero + or negative, or x or y are negative, raise the bad_value protocol + error. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + </description> + <arg name="x" type="fixed" summary="source rectangle x"/> + <arg name="y" type="fixed" summary="source rectangle y"/> + <arg name="width" type="fixed" summary="source rectangle width"/> + <arg name="height" type="fixed" summary="source rectangle height"/> + </request> + + <request name="set_destination"> + <description summary="set the surface size for scaling"> + Set the destination size of the associated wl_surface. See + wp_viewport for the description, and relation to the wl_buffer + size. + + If width is -1 and height is -1, the destination size is unset + instead. Any other pair of values for width and height that + contains zero or negative values raises the bad_value protocol + error. + + The crop and scale state is double-buffered state, and will be + applied on the next wl_surface.commit. + </description> + <arg name="width" type="int" summary="surface width"/> + <arg name="height" type="int" summary="surface height"/> + </request> + </interface> + +</protocol> diff --git a/src/client/client.pro b/src/client/client.pro index 30f32dd7e..38d0ac3e1 100644 --- a/src/client/client.pro +++ b/src/client/client.pro @@ -18,6 +18,9 @@ CONFIG += link_pkgconfig wayland-scanner qtConfig(xkbcommon): \ QMAKE_USE_PRIVATE += xkbcommon +qtHaveModule(linuxaccessibility_support_private): \ + QT += linuxaccessibility_support_private + QMAKE_USE += wayland-client INCLUDEPATH += $$PWD/../shared @@ -29,9 +32,7 @@ WAYLANDCLIENTSOURCES += \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ ../3rdparty/protocol/xdg-output-unstable-v1.xml \ - -WAYLANDCLIENTSOURCES_SYSTEM += \ - ../3rdparty/protocol/wayland.xml \ + ../3rdparty/protocol/wayland.xml SOURCES += qwaylandintegration.cpp \ qwaylandnativeinterface.cpp \ diff --git a/src/client/configure.json b/src/client/configure.json index 1f86a4936..b5146680e 100644 --- a/src/client/configure.json +++ b/src/client/configure.json @@ -87,6 +87,36 @@ "condition": "features.draganddrop || features.clipboard", "output": [ "privateFeature" ] }, + "wayland-client-fullscreen-shell-v1": { + "label": "fullscreen-shell-v1", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-ivi-shell": { + "label": "ivi-shell", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-wl-shell": { + "label": "wl-shell (deprecated)", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell": { + "label": "xdg-shell", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell-v5": { + "label": "xdg-shell unstable v5 (deprecated)", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, + "wayland-client-xdg-shell-v6": { + "label": "xdg-shell unstable v6", + "condition": "features.wayland-client", + "output": [ "privateFeature" ] + }, "wayland-egl": { "label": "EGL", "condition": "features.wayland-client && features.opengl && features.egl && libs.wayland-egl", @@ -151,6 +181,17 @@ "wayland-shm-emulation-server-buffer" ] }, + { + "section": "Qt Wayland Client Shell Integrations", + "condition": "features.wayland-client", + "entries": [ + "wayland-client-xdg-shell", + "wayland-client-xdg-shell-v5", + "wayland-client-xdg-shell-v6", + "wayland-client-ivi-shell", + "wayland-client-wl-shell" + ] + }, "wayland-client" ] } diff --git a/src/client/qwaylandabstractdecoration.cpp b/src/client/qwaylandabstractdecoration.cpp index 6a8f1d92d..3196af3b0 100644 --- a/src/client/qwaylandabstractdecoration.cpp +++ b/src/client/qwaylandabstractdecoration.cpp @@ -146,11 +146,11 @@ void QWaylandAbstractDecoration::setMouseButtons(Qt::MouseButtons mb) d->m_mouseButtons = mb; } -void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize resize, Qt::MouseButtons buttons) +void QWaylandAbstractDecoration::startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons) { Q_D(QWaylandAbstractDecoration); if (isLeftClicked(buttons) && d->m_wayland_window->shellSurface()) { - d->m_wayland_window->shellSurface()->resize(inputDevice, resize); + d->m_wayland_window->shellSurface()->resize(inputDevice, edges); inputDevice->removeMouseButtonFromState(Qt::LeftButton); } } diff --git a/src/client/qwaylandabstractdecoration_p.h b/src/client/qwaylandabstractdecoration_p.h index 84a6d4dd7..f5b1854dd 100644 --- a/src/client/qwaylandabstractdecoration_p.h +++ b/src/client/qwaylandabstractdecoration_p.h @@ -61,8 +61,6 @@ #include <QtGui/QImage> #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <wayland-client.h> - #include <QtCore/QDebug> QT_BEGIN_NAMESPACE @@ -105,7 +103,7 @@ protected: void setMouseButtons(Qt::MouseButtons mb); - void startResize(QWaylandInputDevice *inputDevice,enum wl_shell_surface_resize resize, Qt::MouseButtons buttons); + void startResize(QWaylandInputDevice *inputDevice, Qt::Edges edges, Qt::MouseButtons buttons); void startMove(QWaylandInputDevice *inputDevice, Qt::MouseButtons buttons); bool isLeftClicked(Qt::MouseButtons newMouseButtonState); diff --git a/src/client/qwaylandbuffer_p.h b/src/client/qwaylandbuffer_p.h index eea090f35..945f1279a 100644 --- a/src/client/qwaylandbuffer_p.h +++ b/src/client/qwaylandbuffer_p.h @@ -56,8 +56,7 @@ #include <QtCore/QSize> #include <QtCore/QRect> -#include <wayland-client.h> -#include <wayland-client-protocol.h> +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> QT_BEGIN_NAMESPACE diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index a2957e0dd..91c1d4115 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -236,8 +236,6 @@ void QWaylandDisplay::waitForScreens() void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uint32_t version) { - Q_UNUSED(version); - struct ::wl_registry *registry = object(); if (interface == QStringLiteral("wl_output")) { @@ -272,10 +270,13 @@ void QWaylandDisplay::registry_global(uint32_t id, const QString &interface, uin inputDevice->setTextInput(new QWaylandTextInput(this, mTextInputManager->get_text_input(inputDevice->wl_seat()))); } } else if (interface == QStringLiteral("qt_hardware_integration")) { - mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); - // make a roundtrip here since we need to receive the events sent by - // qt_hardware_integration before creating windows - forceRoundTrip(); + bool disableHardwareIntegration = qEnvironmentVariableIntValue("QT_WAYLAND_DISABLE_HW_INTEGRATION"); + if (!disableHardwareIntegration) { + mHardwareIntegration.reset(new QWaylandHardwareIntegration(registry, id)); + // make a roundtrip here since we need to receive the events sent by + // qt_hardware_integration before creating windows + forceRoundTrip(); + } } else if (interface == QLatin1String("zxdg_output_manager_v1")) { mXdgOutputManager.reset(new QtWayland::zxdg_output_manager_v1(registry, id, 1)); for (auto *screen : qAsConst(mScreens)) diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index 43ccc0a96..e9439d1be 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -59,8 +59,6 @@ #include <QtCore/QWaitCondition> #include <QtCore/QLoggingCategory> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qtwaylandclientglobal_p.h> #include <QtWaylandClient/private/qwaylandshm_p.h> diff --git a/src/client/qwaylandextendedsurface_p.h b/src/client/qwaylandextendedsurface_p.h index cd604f342..d71ac6be9 100644 --- a/src/client/qwaylandextendedsurface_p.h +++ b/src/client/qwaylandextendedsurface_p.h @@ -56,7 +56,6 @@ #include <QtWaylandClient/qtwaylandclientglobal.h> -#include <wayland-client.h> #include <QtWaylandClient/private/qwayland-surface-extension.h> QT_BEGIN_NAMESPACE diff --git a/src/client/qwaylandinputcontext_p.h b/src/client/qwaylandinputcontext_p.h index 93300e1f5..10132dfe1 100644 --- a/src/client/qwaylandinputcontext_p.h +++ b/src/client/qwaylandinputcontext_p.h @@ -62,6 +62,9 @@ #include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h> #include <qwaylandinputmethodeventbuilder_p.h> +struct wl_callback; +struct wl_callback_listener; + QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(qLcQpaInputMethods) diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index 90e138a3d..2dff4b6c3 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -238,7 +238,6 @@ void QWaylandInputDevice::seat_capabilities(uint32_t caps) if (caps & WL_SEAT_CAPABILITY_POINTER && !mPointer) { mPointer = createPointer(this); mPointer->init(get_pointer()); - pointerSurface = mQDisplay->createSurface(this); } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && mPointer) { delete mPointer; mPointer = nullptr; @@ -419,6 +418,9 @@ void QWaylandInputDevice::setCursor(struct wl_buffer *buffer, const QPoint &hotS return; } + if (!pointerSurface) + pointerSurface = mQDisplay->createSurface(this); + mPointer->set_cursor(mPointer->mEnterSerial, pointerSurface, hotSpot.x(), hotSpot.y()); wl_surface_attach(pointerSurface, buffer, 0, 0); @@ -787,7 +789,7 @@ void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, #if QT_CONFIG(xkbcommon) mRepeatSym = sym; #endif - mRepeatTimer.setInterval(400); + mRepeatTimer.setInterval(mRepeatDelay); mRepeatTimer.start(); } else if (mRepeatCode == code) { mRepeatTimer.stop(); @@ -802,7 +804,7 @@ void QWaylandInputDevice::Keyboard::repeatKey() return; } - mRepeatTimer.setInterval(25); + mRepeatTimer.setInterval(mRepeatRate); sendKey(mFocus->window(), mRepeatTime, QEvent::KeyRelease, mRepeatKey, modifiers(), mRepeatCode, #if QT_CONFIG(xkbcommon) mRepeatSym, mNativeModifiers, @@ -842,6 +844,12 @@ void QWaylandInputDevice::Keyboard::keyboard_modifiers(uint32_t serial, #endif } +void QWaylandInputDevice::Keyboard::keyboard_repeat_info(int32_t rate, int32_t delay) +{ + mRepeatRate = rate; + mRepeatDelay = delay; +} + void QWaylandInputDevice::Touch::touch_down(uint32_t serial, uint32_t time, struct wl_surface *surface, diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 7aa86539b..f27f329be 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -61,8 +61,6 @@ #include <qpa/qplatformscreen.h> #include <qpa/qwindowsysteminterface.h> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #if QT_CONFIG(xkbcommon) @@ -206,6 +204,7 @@ public: uint32_t mods_latched, uint32_t mods_locked, uint32_t group) override; + void keyboard_repeat_info(int32_t rate, int32_t delay) override; QWaylandInputDevice *mParent = nullptr; QPointer<QWaylandWindow> mFocus; @@ -221,6 +220,8 @@ public: int mRepeatKey; uint32_t mRepeatCode; uint32_t mRepeatTime; + int mRepeatRate = 25; + int mRepeatDelay = 400; QString mRepeatText; #if QT_CONFIG(xkbcommon) xkb_keysym_t mRepeatSym; diff --git a/src/client/qwaylandintegration.cpp b/src/client/qwaylandintegration.cpp index e935ef31f..1ffaf3c89 100644 --- a/src/client/qwaylandintegration.cpp +++ b/src/client/qwaylandintegration.cpp @@ -86,6 +86,10 @@ #include "qwaylandinputdeviceintegration_p.h" #include "qwaylandinputdeviceintegrationfactory_p.h" +#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE +#include <QtLinuxAccessibilitySupport/private/bridge_p.h> +#endif + QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -129,9 +133,6 @@ QWaylandIntegration::QWaylandIntegration() : mFontDb(new QGenericUnixFontDatabase()) #endif , mNativeInterface(new QWaylandNativeInterface(this)) -#if QT_CONFIG(accessibility) - , mAccessibility(new QPlatformAccessibility()) -#endif { initializeInputDeviceIntegration(); mDisplay.reset(new QWaylandDisplay(this)); @@ -277,6 +278,15 @@ QVariant QWaylandIntegration::styleHint(StyleHint hint) const #if QT_CONFIG(accessibility) QPlatformAccessibility *QWaylandIntegration::accessibility() const { + if (!mAccessibility) { +#ifndef QT_NO_ACCESSIBILITY_ATSPI_BRIDGE + Q_ASSERT_X(QCoreApplication::eventDispatcher(), "QXcbIntegration", + "Initializing accessibility without event-dispatcher!"); + mAccessibility.reset(new QSpiAccessibleBridge()); +#else + mAccessibility.reset(new QPlatformAccessibility()); +#endif + } return mAccessibility.data(); } #endif @@ -329,16 +339,15 @@ void QWaylandIntegration::initializeClientBufferIntegration() { mClientBufferIntegrationInitialized = true; - QString targetKey; - bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION"); - disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration(); - if (disableHardwareIntegration) { - QByteArray clientBufferIntegrationName = qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION"); - if (clientBufferIntegrationName.isEmpty()) - clientBufferIntegrationName = QByteArrayLiteral("wayland-egl"); - targetKey = QString::fromLocal8Bit(clientBufferIntegrationName); - } else { - targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); + QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_CLIENT_BUFFER_INTEGRATION")); + + if (targetKey.isEmpty()) { + if (mDisplay->hardwareIntegration() + && mDisplay->hardwareIntegration()->clientBufferIntegration() != QLatin1String("linux-dmabuf-unstable-v1")) { + targetKey = mDisplay->hardwareIntegration()->clientBufferIntegration(); + } else { + targetKey = QLatin1Literal("wayland-egl"); + } } if (targetKey.isEmpty()) { @@ -347,29 +356,28 @@ void QWaylandIntegration::initializeClientBufferIntegration() } QStringList keys = QWaylandClientBufferIntegrationFactory::keys(); - if (keys.contains(targetKey)) { + qCDebug(lcQpaWayland) << "Available client buffer integrations:" << keys; + + if (keys.contains(targetKey)) mClientBufferIntegration.reset(QWaylandClientBufferIntegrationFactory::create(targetKey, QStringList())); - } - if (mClientBufferIntegration) + + if (mClientBufferIntegration) { + qCDebug(lcQpaWayland) << "Initializing client buffer integration" << targetKey; mClientBufferIntegration->initialize(mDisplay.data()); - else - qWarning("Failed to load client buffer integration: %s\n", qPrintable(targetKey)); + } else { + qCWarning(lcQpaWayland) << "Failed to load client buffer integration:" << targetKey; + qCWarning(lcQpaWayland) << "Available client buffer integrations:" << keys; + } } void QWaylandIntegration::initializeServerBufferIntegration() { mServerBufferIntegrationInitialized = true; - QString targetKey; + QString targetKey = QString::fromLocal8Bit(qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION")); - bool disableHardwareIntegration = qEnvironmentVariableIsSet("QT_WAYLAND_DISABLE_HW_INTEGRATION"); - disableHardwareIntegration = disableHardwareIntegration || !mDisplay->hardwareIntegration(); - if (disableHardwareIntegration) { - QByteArray serverBufferIntegrationName = qgetenv("QT_WAYLAND_SERVER_BUFFER_INTEGRATION"); - targetKey = QString::fromLocal8Bit(serverBufferIntegrationName); - } else { + if (targetKey.isEmpty() && mDisplay->hardwareIntegration()) targetKey = mDisplay->hardwareIntegration()->serverBufferIntegration(); - } if (targetKey.isEmpty()) { qWarning("Failed to determine what server buffer integration to use"); @@ -377,13 +385,18 @@ void QWaylandIntegration::initializeServerBufferIntegration() } QStringList keys = QWaylandServerBufferIntegrationFactory::keys(); - if (keys.contains(targetKey)) { + qCDebug(lcQpaWayland) << "Available server buffer integrations:" << keys; + + if (keys.contains(targetKey)) mServerBufferIntegration.reset(QWaylandServerBufferIntegrationFactory::create(targetKey, QStringList())); - } - if (mServerBufferIntegration) + + if (mServerBufferIntegration) { + qCDebug(lcQpaWayland) << "Initializing server buffer integration" << targetKey; mServerBufferIntegration->initialize(mDisplay.data()); - else - qWarning("Failed to load server buffer integration %s\n", qPrintable(targetKey)); + } else { + qCWarning(lcQpaWayland) << "Failed to load server buffer integration: " << targetKey; + qCWarning(lcQpaWayland) << "Available server buffer integrations:" << keys; + } } void QWaylandIntegration::initializeShellIntegration() @@ -411,7 +424,7 @@ void QWaylandIntegration::initializeShellIntegration() Q_FOREACH (QString preferredShell, preferredShells) { mShellIntegration.reset(createShellIntegration(preferredShell)); if (mShellIntegration) { - qDebug("Using the '%s' shell integration", qPrintable(preferredShell)); + qCDebug(lcQpaWayland, "Using the '%s' shell integration", qPrintable(preferredShell)); break; } } diff --git a/src/client/qwaylandintegration_p.h b/src/client/qwaylandintegration_p.h index a5a3d7b69..944f635bb 100644 --- a/src/client/qwaylandintegration_p.h +++ b/src/client/qwaylandintegration_p.h @@ -145,7 +145,7 @@ private: QScopedPointer<QPlatformNativeInterface> mNativeInterface; QScopedPointer<QPlatformInputContext> mInputContext; #if QT_CONFIG(accessibility) - QScopedPointer<QPlatformAccessibility> mAccessibility; + mutable QScopedPointer<QPlatformAccessibility> mAccessibility; #endif bool mFailed = false; bool mClientBufferIntegrationInitialized = false; diff --git a/src/client/qwaylandshellsurface_p.h b/src/client/qwaylandshellsurface_p.h index 6bc3258c7..f683d9e01 100644 --- a/src/client/qwaylandshellsurface_p.h +++ b/src/client/qwaylandshellsurface_p.h @@ -54,8 +54,6 @@ #include <QtCore/QSize> #include <QObject> -#include <wayland-client.h> - #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/qtwaylandclientglobal.h> @@ -75,8 +73,7 @@ class Q_WAYLAND_CLIENT_EXPORT QWaylandShellSurface : public QObject public: explicit QWaylandShellSurface(QWaylandWindow *window); ~QWaylandShellSurface() override {} - virtual void resize(QWaylandInputDevice * /*inputDevice*/, enum wl_shell_surface_resize /*edges*/) - {} + virtual void resize(QWaylandInputDevice * /*inputDevice*/, Qt::Edges /*edges*/) {} virtual bool move(QWaylandInputDevice *) { return false; } virtual void setTitle(const QString & /*title*/) {} diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 3fe2ce80c..34044ec9b 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -49,8 +49,7 @@ #include <QtGui/QPainter> #include <QMutexLocker> -#include <wayland-client.h> -#include <wayland-client-protocol.h> +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> #include <unistd.h> #include <sys/mman.h> diff --git a/src/client/qwaylandsubsurface_p.h b/src/client/qwaylandsubsurface_p.h index e9a7cb20e..76da10b24 100644 --- a/src/client/qwaylandsubsurface_p.h +++ b/src/client/qwaylandsubsurface_p.h @@ -51,8 +51,6 @@ // We mean it. // -#include <wayland-client.h> - #include <QtCore/qglobal.h> #include <QtCore/qmutex.h> diff --git a/src/client/qwaylandwindow.cpp b/src/client/qwaylandwindow.cpp index 470ae0091..17576ecee 100644 --- a/src/client/qwaylandwindow.cpp +++ b/src/client/qwaylandwindow.cpp @@ -67,8 +67,6 @@ #include <QtCore/QDebug> -#include <wayland-client.h> - QT_BEGIN_NAMESPACE namespace QtWaylandClient { @@ -500,9 +498,11 @@ void QWaylandWindow::surface_enter(wl_output *output) auto addedScreen = QWaylandScreen::fromWlOutput(output); if (mScreens.contains(addedScreen)) { - qWarning() << "Unexpected wl_surface.enter received for output with id:" - << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) - << "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model(); + qWarning(lcQpaWayland) << "Ignoring unexpected wl_surface.enter received for output with id:" + << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) + << "screen name:" << addedScreen->name() + << "screen model:" << addedScreen->model() + << "This is most likely a bug in the compositor."; return; } @@ -519,9 +519,11 @@ void QWaylandWindow::surface_leave(wl_output *output) auto *removedScreen = QWaylandScreen::fromWlOutput(output); bool wasRemoved = mScreens.removeOne(removedScreen); if (!wasRemoved) { - qWarning() << "Unexpected wl_surface.leave received for output with id:" - << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) - << "screen name:" << removedScreen->name() << "screen model:" << removedScreen->model(); + qWarning(lcQpaWayland) << "Ignoring unexpected wl_surface.leave received for output with id:" + << wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output)) + << "screen name:" << removedScreen->name() + << "screen model:" << removedScreen->model() + << "This is most likely a bug in the compositor."; return; } diff --git a/src/client/qwaylandwindowmanagerintegration_p.h b/src/client/qwaylandwindowmanagerintegration_p.h index 1319abd91..31de6ddd3 100644 --- a/src/client/qwaylandwindowmanagerintegration_p.h +++ b/src/client/qwaylandwindowmanagerintegration_p.h @@ -54,7 +54,6 @@ #include <QtCore/QObject> #include <QtCore/QScopedPointer> -#include <wayland-client.h> #include <QtServiceSupport/private/qgenericunixservices_p.h> #include <QtWaylandClient/private/qwayland-qt-windowmanager.h> 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/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/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..426008a60 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) diff --git a/src/compositor/compositor_api/qwaylandquickitem.cpp b/src/compositor/compositor_api/qwaylandquickitem.cpp index 77a9247a9..f848a0169 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 @@ -886,7 +886,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 +903,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 +992,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) @@ -1061,16 +1061,32 @@ 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); } /*! + * 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. + */ +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 @@ -1301,6 +1317,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..6f47c29a4 100644 --- a/src/compositor/compositor_api/qwaylandquickitem.h +++ b/src/compositor/compositor_api/qwaylandquickitem.h @@ -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_INVOKABLE QPointF mapFromSurface(const QPointF &point) const; bool sizeFollowsSurface() const; void setSizeFollowsSurface(bool sizeFollowsSurface); diff --git a/src/compositor/compositor_api/qwaylandseat.cpp b/src/compositor/compositor_api/qwaylandseat.cpp index a7b01ef03..d135ad035 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" @@ -631,8 +632,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; } diff --git a/src/compositor/compositor_api/qwaylandsurface.cpp b/src/compositor/compositor_api/qwaylandsurface.cpp index 050ab5641..eef51283c 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(); @@ -461,21 +480,105 @@ bool QWaylandSurface::hasContent() const } /*! + * \qmlproperty rect QtWaylandCompositor::WaylandSurface::sourceGeometry + * + * 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 + * + * 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 + * + * This property holds the size of this WaylandSurface in surface coordinates. + * + * \sa bufferScale + * \sa bufferSize + */ + +/*! + * \property QWaylandSurface::destinationSize + * + * 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 @@ -742,10 +845,10 @@ QList<QWaylandView *> QWaylandSurface::views() const /*! * Returns the QWaylandSurface corresponding to the Wayland resource \a res. */ -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; } diff --git a/src/compositor/compositor_api/qwaylandsurface.h b/src/compositor/compositor_api/qwaylandsurface.h index a138b2af5..9bf842900 100644 --- a/src/compositor/compositor_api/qwaylandsurface.h +++ b/src/compositor/compositor_api/qwaylandsurface.h @@ -81,7 +81,11 @@ 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) + Q_PROPERTY(QSize destinationSize READ destinationSize NOTIFY destinationSizeChanged) +#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 +114,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 +164,12 @@ Q_SIGNALS: void damaged(const QRegion &rect); void parentChanged(QWaylandSurface *newParent, QWaylandSurface *oldParent); void childAdded(QWaylandSurface *child); - void sizeChanged(); + void sourceGeometryChanged(); + void destinationSizeChanged(); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED void sizeChanged(); +#endif + 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..85643623d 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,6 +145,7 @@ public: //member variables QRegion damage; QWaylandBufferRef bufferRef; QWaylandSurfaceRole *role = nullptr; + QWaylandViewporterPrivate::Viewport *viewport = nullptr; struct { QWaylandBufferRef buffer; @@ -152,6 +154,8 @@ public: //member variables bool newlyAttached; QRegion inputRegion; int bufferScale; + 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 3b3d33200..ec9327adf 100644 --- a/src/compositor/configure.json +++ b/src/compositor/configure.json @@ -79,6 +79,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" } }, @@ -127,6 +133,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/extensions/extensions.pri b/src/compositor/extensions/extensions.pri index 5c708f891..38fe79a2f 100644 --- a/src/compositor/extensions/extensions.pri +++ b/src/compositor/extensions/extensions.pri @@ -9,6 +9,7 @@ WAYLANDSERVERSOURCES += \ ../extensions/qt-key-unstable-v1.xml \ ../extensions/qt-windowmanager.xml \ ../3rdparty/protocol/text-input-unstable-v2.xml \ + ../3rdparty/protocol/viewporter.xml \ ../3rdparty/protocol/xdg-shell-unstable-v6.xml \ ../3rdparty/protocol/xdg-shell.xml \ ../3rdparty/protocol/xdg-decoration-unstable-v1.xml \ @@ -27,6 +28,8 @@ HEADERS += \ extensions/qwaylandtextinputmanager_p.h \ extensions/qwaylandqtwindowmanager.h \ extensions/qwaylandqtwindowmanager_p.h \ + extensions/qwaylandviewporter.h \ + extensions/qwaylandviewporter_p.h \ extensions/qwaylandxdgshellv5.h \ extensions/qwaylandxdgshellv5_p.h \ extensions/qwaylandxdgshellv6.h \ @@ -49,6 +52,7 @@ SOURCES += \ extensions/qwaylandtextinput.cpp \ extensions/qwaylandtextinputmanager.cpp \ extensions/qwaylandqtwindowmanager.cpp \ + extensions/qwaylandviewporter.cpp \ extensions/qwaylandxdgshellv5.cpp \ extensions/qwaylandxdgshellv6.cpp \ extensions/qwaylandxdgshell.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/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/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/qwaylandwlshell.cpp b/src/compositor/extensions/qwaylandwlshell.cpp index d932a06c9..3f6734632 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> @@ -699,9 +700,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 e8d568fce..f8a29bac8 100644 --- a/src/compositor/extensions/qwaylandwlshell_p.h +++ b/src/compositor/extensions/qwaylandwlshell_p.h @@ -46,7 +46,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/qwaylandxdgshell.cpp b/src/compositor/extensions/qwaylandxdgshell.cpp index bd332287e..6f0c83122 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> @@ -311,7 +312,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() @@ -510,7 +511,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 +675,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 +1182,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; } @@ -2064,9 +2064,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..9e157a8a3 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() @@ -837,7 +838,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 +1180,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 +1497,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/qwaylandxdgshellv6.cpp b/src/compositor/extensions/qwaylandxdgshellv6.cpp index 8338fe6e2..9aab9b2b8 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> @@ -316,7 +317,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() @@ -515,7 +516,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 +680,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 @@ -1995,9 +1995,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/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/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..b0c8371f4 100644 --- a/src/compositor/wayland_wrapper/wayland_wrapper.pri +++ b/src/compositor/wayland_wrapper/wayland_wrapper.pri @@ -1,6 +1,6 @@ CONFIG += wayland-scanner -WAYLANDSERVERSOURCES_SYSTEM += \ - ../3rdparty/protocol/wayland.xml \ +WAYLANDSERVERSOURCES += \ + ../3rdparty/protocol/wayland.xml HEADERS += \ wayland_wrapper/qwlbuffermanager_p.h \ diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h index 5e8a3bf46..9a614f58e 100644 --- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h +++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglintegration.h @@ -41,7 +41,7 @@ #define QWAYLANDBRCMEGLINTEGRATION_H #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> -#include <wayland-client.h> +#include <wayland-client-core.h> #include <EGL/egl.h> #include <EGL/eglext.h> diff --git a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp index 5cd52f676..31adf100b 100644 --- a/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp +++ b/src/hardwareintegration/client/brcm-egl/qwaylandbrcmeglwindow.cpp @@ -50,7 +50,6 @@ #include <EGL/eglext_brcm.h> -#include <wayland-client.h> #include "wayland-brcm-client-protocol.h" QT_BEGIN_NAMESPACE diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp b/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp index 4b3a635c7..3a34d2561 100644 --- a/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp +++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglclientbufferintegration.cpp @@ -42,7 +42,7 @@ #include "qwaylandeglwindow.h" #include "qwaylandglcontext.h" -#include <wayland-client.h> +#include <wayland-client-core.h> #include <QtCore/QDebug> #include <private/qeglconvenience_p.h> @@ -65,7 +65,7 @@ static const char *qwaylandegl_threadedgl_blacklist_vendor[] = { QWaylandEglClientBufferIntegration::QWaylandEglClientBufferIntegration() { - qDebug() << "Using Wayland-EGL"; + qCDebug(lcQpaWayland) << "Using Wayland-EGL"; } @@ -87,7 +87,7 @@ void QWaylandEglClientBufferIntegration::initialize(QWaylandDisplay *display) m_eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display->wl_display(), nullptr); } else { - qWarning("The EGL implementation does not support the Wayland platform"); + qCWarning(lcQpaWayland) << "The EGL implementation does not support the Wayland platform"; return; } } else { @@ -102,13 +102,13 @@ void QWaylandEglClientBufferIntegration::initialize(QWaylandDisplay *display) m_display = display; if (m_eglDisplay == EGL_NO_DISPLAY) { - qWarning("EGL not available"); + qCWarning(lcQpaWayland) << "EGL not available"; return; } EGLint major,minor; if (!eglInitialize(m_eglDisplay, &major, &minor)) { - qWarning("failed to initialize EGL display"); + qCWarning(lcQpaWayland) << "Failed to initialize EGL display" << hex << eglGetError(); m_eglDisplay = EGL_NO_DISPLAY; return; } diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h b/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h index 233ce78bd..e9998b832 100644 --- a/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h +++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglinclude.h @@ -41,7 +41,7 @@ #define QWAYLANDEGLINCLUDE_H #include <string.h> -#include <wayland-client.h> +#include <wayland-client-core.h> #include <wayland-egl.h> diff --git a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h index 88e893a74..9e6cb876c 100644 --- a/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h +++ b/src/hardwareintegration/client/wayland-egl/qwaylandeglwindow.h @@ -62,7 +62,7 @@ public: void ensureSize() override; void updateSurface(bool create); - virtual void setGeometry(const QRect &rect) override; + void setGeometry(const QRect &rect) override; QRect contentsRect() const; EGLSurface eglSurface() const; diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp index aa5367e02..104a4df91 100644 --- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp +++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.cpp @@ -118,7 +118,7 @@ const struct qt_xcomposite_listener QWaylandXCompositeEGLClientBufferIntegration QWaylandXCompositeEGLClientBufferIntegration::rootInformation }; -void QWaylandXCompositeEGLClientBufferIntegration::wlDisplayHandleGlobal(void *data, wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +void QWaylandXCompositeEGLClientBufferIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) { Q_UNUSED(version); if (interface == "qt_xcomposite") { diff --git a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h index ee55d6892..7037ee2d0 100644 --- a/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h +++ b/src/hardwareintegration/client/xcomposite-egl/qwaylandxcompositeeglclientbufferintegration.h @@ -41,7 +41,7 @@ #define QWAYLANDXCOMPOSITEEGLCLIENTBUFFERINTEGRATION_H #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> -#include <wayland-client.h> +#include <wayland-client-core.h> #include <QtCore/QTextStream> #include <QtCore/QDataStream> @@ -64,6 +64,7 @@ struct qt_xcomposite; struct qt_xcomposite_listener; +struct wl_registry; QT_BEGIN_NAMESPACE @@ -100,7 +101,7 @@ private: int mScreen; Window mRootWindow; - static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id, + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); static const struct ::qt_xcomposite_listener xcomposite_listener; diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp index 090cfb8a0..8be47fa2e 100644 --- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp +++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.cpp @@ -109,7 +109,7 @@ const struct qt_xcomposite_listener QWaylandXCompositeGLXIntegration::xcomposite QWaylandXCompositeGLXIntegration::rootInformation }; -void QWaylandXCompositeGLXIntegration::wlDisplayHandleGlobal(void *data, wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) +void QWaylandXCompositeGLXIntegration::wlDisplayHandleGlobal(void *data, ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version) { Q_UNUSED(version); if (interface == "qt_xcomposite") { diff --git a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h index 26f2bad6f..809690816 100644 --- a/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h +++ b/src/hardwareintegration/client/xcomposite-glx/qwaylandxcompositeglxintegration.h @@ -41,7 +41,7 @@ #define QWAYLANDXCOMPOSITEGLXINTEGRATION_H #include <QtWaylandClient/private/qwaylandclientbufferintegration_p.h> -#include <wayland-client.h> +#include <wayland-client-core.h> #include <QtCore/QTextStream> #include <QtCore/QDataStream> @@ -59,6 +59,7 @@ struct qt_xcomposite; struct qt_xcomposite_listener; +struct wl_registry; QT_BEGIN_NAMESPACE @@ -93,7 +94,7 @@ private: int mScreen = 0; Window mRootWindow = 0; - static void wlDisplayHandleGlobal(void *data, struct wl_registry *registry, uint32_t id, + static void wlDisplayHandleGlobal(void *data, struct ::wl_registry *registry, uint32_t id, const QString &interface, uint32_t version); static const struct qt_xcomposite_listener xcomposite_listener; diff --git a/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp b/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp index 5f1818cdf..33d9a6038 100644 --- a/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp +++ b/src/hardwareintegration/client/xcomposite_share/qwaylandxcompositebuffer.cpp @@ -39,7 +39,6 @@ #include "qwaylandxcompositebuffer.h" -#include <wayland-client.h> #include "wayland-xcomposite-client-protocol.h" QT_BEGIN_NAMESPACE diff --git a/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri b/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri index b18aa2d50..d2b129d03 100644 --- a/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri +++ b/src/hardwareintegration/client/xcomposite_share/xcomposite_share.pri @@ -1,7 +1,7 @@ INCLUDEPATH += $$PWD QMAKE_USE += xcomposite x11 -CONFIG += wayland-scanner +CONFIG += wayland-scanner-client-wayland-protocol-include WAYLANDCLIENTSOURCES += $$PWD/../../../extensions/xcomposite.xml HEADERS += \ diff --git a/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h b/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h index 3028fbed5..83545de2e 100644 --- a/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h +++ b/src/hardwareintegration/compositor/brcm-egl/brcmbuffer.h @@ -41,6 +41,7 @@ #define BRCMBUFFER_H #include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#include <QtWaylandCompositor/private/qwaylandutils_p.h> #include <QtCore/QSize> #include <QtCore/QVector> @@ -62,7 +63,7 @@ public: QSize size() { return m_size; } - static BrcmBuffer *fromResource(struct ::wl_resource *resource) { return static_cast<BrcmBuffer*>(Resource::fromResource(resource)->buffer_object); } + static BrcmBuffer *fromResource(struct ::wl_resource *resource) { return QtWayland::fromResource<BrcmBuffer *>(resource); } protected: void buffer_destroy_resource(Resource *resource) override; diff --git a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp index 5a42c00dc..af9059603 100644 --- a/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp +++ b/src/hardwareintegration/compositor/libhybris-egl-server/libhybriseglserverbufferintegration.cpp @@ -42,7 +42,7 @@ #include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLTexture> #include <hybris/eglplatformcommon/hybris_nativebufferext.h> -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE LibHybrisEglServerBuffer::LibHybrisEglServerBuffer(LibHybrisEglServerBufferIntegration *integration, const QImage &qimage, QtWayland::ServerBuffer::Format format) diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri new file mode 100644 index 000000000..77f6d9410 --- /dev/null +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri @@ -0,0 +1,16 @@ +INCLUDEPATH += $$PWD + +QMAKE_USE_PRIVATE += egl wayland-server wayland-egl + +CONFIG += wayland-scanner +WAYLANDSERVERSOURCES += $$PWD/../../../3rdparty/protocol/linux-dmabuf-unstable-v1.xml + +QT += egl_support-private + +SOURCES += \ + $$PWD/linuxdmabufclientbufferintegration.cpp \ + $$PWD/linuxdmabuf.cpp + +HEADERS += \ + $$PWD/linuxdmabufclientbufferintegration.h \ + $$PWD/linuxdmabuf.h diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp new file mode 100644 index 000000000..2ba0462c7 --- /dev/null +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.cpp @@ -0,0 +1,332 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "linuxdmabuf.h" +#include "linuxdmabufclientbufferintegration.h" + +#include <QtWaylandCompositor/QWaylandCompositor> + +#include <drm_fourcc.h> +#include <drm_mode.h> +#include <unistd.h> + +QT_BEGIN_NAMESPACE + +LinuxDmabuf::LinuxDmabuf(wl_display *display, LinuxDmabufClientBufferIntegration *clientBufferIntegration) + : zwp_linux_dmabuf_v1(display, 3 /*version*/) + , m_clientBufferIntegration(clientBufferIntegration) +{ +} + +void LinuxDmabuf::setSupportedModifiers(const QHash<uint32_t, QVector<uint64_t>> &modifiers) +{ + Q_ASSERT(resourceMap().isEmpty()); + m_modifiers = modifiers; +} + +void LinuxDmabuf::zwp_linux_dmabuf_v1_bind_resource(Resource *resource) +{ + for (auto it = m_modifiers.constBegin(); it != m_modifiers.constEnd(); ++it) { + auto format = it.key(); + auto modifiers = it.value(); + // send DRM_FORMAT_MOD_INVALID when no modifiers are supported for a format + if (modifiers.isEmpty()) + modifiers << DRM_FORMAT_MOD_INVALID; + for (const auto &modifier : qAsConst(modifiers)) { + if (resource->version() >= ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) { + const uint32_t modifier_lo = modifier & 0xFFFFFFFF; + const uint32_t modifier_hi = modifier >> 32; + send_modifier(resource->handle, format, modifier_hi, modifier_lo); + } else if (modifier == DRM_FORMAT_MOD_LINEAR || modifier == DRM_FORMAT_MOD_INVALID) { + send_format(resource->handle, format); + } + } + } +} + +void LinuxDmabuf::zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) +{ + wl_resource *r = wl_resource_create(resource->client(), &zwp_linux_buffer_params_v1_interface, + wl_resource_get_version(resource->handle), params_id); + new LinuxDmabufParams(m_clientBufferIntegration, r); // deleted by the client, or when it disconnects +} + +LinuxDmabufParams::LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource) + : zwp_linux_buffer_params_v1(resource) + , m_clientBufferIntegration(clientBufferIntegration) +{ +} + +LinuxDmabufParams::~LinuxDmabufParams() +{ + for (auto it = m_planes.begin(); it != m_planes.end(); ++it) { + if (it.value().fd != -1) + close(it.value().fd); + it.value().fd = -1; + } +} + +bool LinuxDmabufParams::handleCreateParams(Resource *resource, int width, int height, uint format, uint flags) +{ + if (m_used) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, + "Params already used"); + return false; + } + + if (width <= 0 || height <= 0) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_DIMENSIONS, + "Invalid dimensions in create request"); + return false; + } + + if (m_planes.isEmpty()) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "Cannot create a buffer with no planes"); + return false; + } + + // check for holes in plane sequence + auto planeIds = m_planes.keys(); + std::sort(planeIds.begin(), planeIds.end()); + for (int i = 0; i < planeIds.count(); ++i) { + if (uint(i) != planeIds[i]) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, + "No dmabuf parameters provided for plane %i", i); + return false; + } + } + + // check for overflows + for (auto it = m_planes.constBegin(); it != m_planes.constEnd(); ++it) { + const auto planeId = it.key(); + const auto plane = it.value(); + if (static_cast<int64_t>(plane.offset) + plane.stride > UINT32_MAX) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "Size overflow for plane %i", + planeId); + return false; + } + if (planeId == 0 && static_cast<int64_t>(plane.offset) + plane.stride * static_cast<int64_t>(height) > UINT32_MAX) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "Size overflow for plane %i", + planeId); + return false; + } + + // do not report an error as it might be caused by the kernel not supporting seeking on dmabuf + off_t size = lseek(plane.fd, 0, SEEK_END); + if (size == -1) { + qCDebug(qLcWaylandCompositorHardwareIntegration) << "Seeking is not supported"; + continue; + } + + if (static_cast<int64_t>(plane.offset) >= size) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "Invalid offset %i for plane %i", + plane.offset, planeId); + return false; + } + + if (static_cast<int64_t>(plane.offset) + static_cast<int64_t>(plane.stride) > size) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "Invalid stride %i for plane %i", + plane.stride, planeId); + return false; + } + + // only valid for first plane as other planes might be sub-sampled + if (planeId == 0 && plane.offset + static_cast<int64_t>(plane.stride) * height > size) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_OUT_OF_BOUNDS, + "Invalid buffer stride or height for plane %i", planeId); + return false; + } + } + + m_size = QSize(width, height); + m_drmFormat = format; + m_flags = flags; + m_used = true; + + return true; +} + +void LinuxDmabufParams::zwp_linux_buffer_params_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void LinuxDmabufParams::zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void LinuxDmabufParams::zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) +{ + const uint64_t modifiers = (static_cast<uint64_t>(modifier_hi) << 32) | modifier_lo; + if (plane_idx >= LinuxDmabufWlBuffer::MaxDmabufPlanes) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, + "Plane index %i is out of bounds", plane_idx); + } + + if (m_planes.contains(plane_idx)) { + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, + "Plane already set"); + } + + Plane plane; + plane.fd = fd; + plane.modifiers = modifiers; + plane.offset = offset; + plane.stride = stride; + m_planes.insert(plane_idx, plane); +} + +void LinuxDmabufParams::zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) +{ + if (!handleCreateParams(resource, width, height, format, flags)) + return; + + auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration); + buffer->m_size = m_size; + buffer->m_flags = m_flags; + buffer->m_drmFormat = m_drmFormat; + buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence + for (auto it = m_planes.begin(); it != m_planes.end(); ++it) { + buffer->m_planes[it.key()] = it.value(); + it.value().fd = -1; // ownership is moved + } + + if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) { + send_failed(resource->handle); + } else { + send_created(resource->handle, buffer->resource()->handle); + } +} + +void LinuxDmabufParams::zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) +{ + if (!handleCreateParams(resource, width, height, format, flags)) + return; + + auto *buffer = new LinuxDmabufWlBuffer(resource->client(), m_clientBufferIntegration, buffer_id); + buffer->m_size = m_size; + buffer->m_flags = m_flags; + buffer->m_drmFormat = m_drmFormat; + buffer->m_planesNumber = m_planes.size(); // it is checked before that planes are in consecutive sequence + for (auto it = m_planes.begin(); it != m_planes.end(); ++it) { + buffer->m_planes[it.key()] = it.value(); + it.value().fd = -1; // ownership is moved + } + + if (!m_clientBufferIntegration->importBuffer(buffer->resource()->handle, buffer)) { + // for the 'create_immed' request, the implementation can decide + // how to handle the failure by an unknown cause; we decide + // to raise a fatal error at the client + wl_resource_post_error(resource->handle, + ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, + "Import of the provided DMA buffer failed"); + } + // note: create signal shall not be sent for the 'create_immed' request +} + +LinuxDmabufWlBuffer::LinuxDmabufWlBuffer(::wl_client *client, LinuxDmabufClientBufferIntegration *clientBufferIntegration, uint id) + : wl_buffer(client, id, 1 /*version*/) + , m_clientBufferIntegration(clientBufferIntegration) +{ +} + +LinuxDmabufWlBuffer::~LinuxDmabufWlBuffer() +{ + m_clientBufferIntegration->removeBuffer(resource()->handle); + buffer_destroy(resource()); +} + +void LinuxDmabufWlBuffer::buffer_destroy(Resource *resource) +{ + Q_UNUSED(resource); + for (uint32_t i = 0; i < m_planesNumber; ++i) { + if (m_textures[i] != nullptr) { + m_clientBufferIntegration->deleteGLTextureWhenPossible(m_textures[i]); + m_textures[i] = nullptr; + } + if (m_eglImages[i] != EGL_NO_IMAGE_KHR) { + m_clientBufferIntegration->deleteImage(m_eglImages[i]); + m_eglImages[i] = EGL_NO_IMAGE_KHR; + } + if (m_planes[i].fd != -1) + close(m_planes[i].fd); + m_planes[i].fd = -1; + } + m_planesNumber = 0; +} + +void LinuxDmabufWlBuffer::initImage(uint32_t plane, EGLImageKHR image) +{ + Q_ASSERT(plane < m_planesNumber); + Q_ASSERT(m_eglImages.at(plane) == EGL_NO_IMAGE_KHR); + m_eglImages[plane] = image; +} + +void LinuxDmabufWlBuffer::initTexture(uint32_t plane, QOpenGLTexture *texture) +{ + Q_ASSERT(plane < m_planesNumber); + Q_ASSERT(m_textures.at(plane) == nullptr); + m_textures[plane] = texture; +} + +void LinuxDmabufWlBuffer::buffer_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h new file mode 100644 index 000000000..2abc2ce6b --- /dev/null +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabuf.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LINUXDMABUF_H +#define LINUXDMABUF_H + +#include "qwayland-server-linux-dmabuf-unstable-v1.h" + +#include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h> + +#include <QtCore/QObject> +#include <QtCore/QHash> +#include <QtCore/QSize> +#include <QtCore/QTextStream> +#include <QtGui/QOpenGLTexture> + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +// compatibility with libdrm <= 2.4.74 +#ifndef DRM_FORMAT_RESERVED +#define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) +#endif +#ifndef DRM_FORMAT_MOD_VENDOR_NONE +#define DRM_FORMAT_MOD_VENDOR_NONE 0 +#endif +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR fourcc_mod_code(NONE, 0) +#endif +#ifndef DRM_FORMAT_MOD_INVALID +#define DRM_FORMAT_MOD_INVALID fourcc_mod_code(NONE, DRM_FORMAT_RESERVED) +#endif + +QT_BEGIN_NAMESPACE + +class QWaylandCompositor; +class QWaylandResource; +class LinuxDmabufParams; +class LinuxDmabufClientBufferIntegration; + +struct Plane { + int fd = -1; + uint32_t offset = 0; + uint32_t stride = 0; + uint64_t modifiers = 0; +}; + +class LinuxDmabuf : public QtWaylandServer::zwp_linux_dmabuf_v1 +{ +public: + explicit LinuxDmabuf(wl_display *display, LinuxDmabufClientBufferIntegration *clientBufferIntegration); + + void setSupportedModifiers(const QHash<uint32_t, QVector<uint64_t>> &modifiers); + +protected: + void zwp_linux_dmabuf_v1_bind_resource(Resource *resource) override; + void zwp_linux_dmabuf_v1_create_params(Resource *resource, uint32_t params_id) override; + +private: + QHash<uint32_t, QVector<uint64_t>> m_modifiers; // key=DRM format, value=supported DRM modifiers for format + LinuxDmabufClientBufferIntegration *m_clientBufferIntegration; +}; + +class LinuxDmabufParams : public QtWaylandServer::zwp_linux_buffer_params_v1 +{ +public: + explicit LinuxDmabufParams(LinuxDmabufClientBufferIntegration *clientBufferIntegration, wl_resource *resource); + ~LinuxDmabufParams() override; + +private: + bool handleCreateParams(Resource *resource, int width, int height, uint format, uint flags); + uint m_drmFormat = 0; + uint m_flags = 0; + QSize m_size; + bool m_used = false; + QMap<uint, Plane> m_planes; + LinuxDmabufClientBufferIntegration *m_clientBufferIntegration; + +protected: + void zwp_linux_buffer_params_v1_destroy(Resource *resource) override; + void zwp_linux_buffer_params_v1_add(Resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t modifier_hi, uint32_t modifier_lo) override; + void zwp_linux_buffer_params_v1_create(Resource *resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) override; + void zwp_linux_buffer_params_v1_create_immed(Resource *resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) override; + void zwp_linux_buffer_params_v1_destroy_resource(Resource *resource) override; + + friend class LinuxDmabufClientBufferIntegrationPrivate; +}; + +class LinuxDmabufWlBuffer : public QtWaylandServer::wl_buffer +{ +public: + explicit LinuxDmabufWlBuffer(::wl_client *client, LinuxDmabufClientBufferIntegration *clientBufferIntegration, uint id = 0); + ~LinuxDmabufWlBuffer() override; + + void initImage(uint32_t plane, EGLImageKHR image); + void initTexture(uint32_t plane, QOpenGLTexture *texture); + inline QSize size() const { return m_size; } + inline uint32_t flags() const { return m_flags; } + inline uint32_t drmFormat() const { return m_drmFormat; } + inline Plane& plane(uint index) { return m_planes.at(index); } + inline uint32_t planesNumber() const { return m_planesNumber; } + inline EGLImageKHR image(uint32_t plane) { return m_eglImages.at(plane); } + inline QOpenGLTexture *texture(uint32_t plane) const { return m_textures.at(plane); } + void buffer_destroy_resource(Resource *resource) override; + + static const uint32_t MaxDmabufPlanes = 4; + +private: + QSize m_size; + uint32_t m_flags = 0; + uint32_t m_drmFormat = EGL_TEXTURE_RGBA; + std::array<Plane, MaxDmabufPlanes> m_planes; + uint32_t m_planesNumber = 1; + LinuxDmabufClientBufferIntegration *m_clientBufferIntegration = nullptr; + std::array<EGLImageKHR, MaxDmabufPlanes> m_eglImages = { {EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR, EGL_NO_IMAGE_KHR} }; + std::array<QOpenGLTexture *, MaxDmabufPlanes> m_textures = { {nullptr, nullptr, nullptr, nullptr} }; + void freeResources(); + void buffer_destroy(Resource *resource) override; + + friend class LinuxDmabufParams; +}; + +QT_END_NAMESPACE + +#endif // LINUXDMABUF_H diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp new file mode 100644 index 000000000..a85f24542 --- /dev/null +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.cpp @@ -0,0 +1,501 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "linuxdmabufclientbufferintegration.h" +#include "linuxdmabuf.h" + +#include <QtWaylandCompositor/QWaylandCompositor> +#include <QtWaylandCompositor/private/qwayland-server-wayland.h> +#include <qpa/qplatformnativeinterface.h> +#include <QtGui/QGuiApplication> +#include <QtGui/QOpenGLContext> +#include <QtGui/QOpenGLTexture> + +#include <EGL/egl.h> +#include <EGL/eglext.h> +#include <unistd.h> +#include <drm_fourcc.h> + +QT_BEGIN_NAMESPACE + +static QWaylandBufferRef::BufferFormatEgl formatFromDrmFormat(EGLint format) { + switch (format) { + case DRM_FORMAT_RGB332: + case DRM_FORMAT_BGR233: + case DRM_FORMAT_XRGB4444: + case DRM_FORMAT_XBGR4444: + case DRM_FORMAT_RGBX4444: + case DRM_FORMAT_BGRX4444: + case DRM_FORMAT_XRGB1555: + case DRM_FORMAT_XBGR1555: + case DRM_FORMAT_RGBX5551: + case DRM_FORMAT_BGRX5551: + case DRM_FORMAT_RGB565: + case DRM_FORMAT_BGR565: + case DRM_FORMAT_RGB888: + case DRM_FORMAT_BGR888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_XRGB2101010: + case DRM_FORMAT_XBGR2101010: + case DRM_FORMAT_RGBX1010102: + case DRM_FORMAT_BGRX1010102: + return QWaylandBufferRef::BufferFormatEgl_RGB; + case DRM_FORMAT_ARGB4444: + case DRM_FORMAT_ABGR4444: + case DRM_FORMAT_RGBA4444: + case DRM_FORMAT_BGRA4444: + case DRM_FORMAT_ARGB1555: + case DRM_FORMAT_ABGR1555: + case DRM_FORMAT_RGBA5551: + case DRM_FORMAT_BGRA5551: + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_ABGR8888: + case DRM_FORMAT_RGBA8888: + case DRM_FORMAT_BGRA8888: + case DRM_FORMAT_ARGB2101010: + case DRM_FORMAT_ABGR2101010: + case DRM_FORMAT_RGBA1010102: + case DRM_FORMAT_BGRA1010102: + return QWaylandBufferRef::BufferFormatEgl_RGBA; + case DRM_FORMAT_YUYV: + return QWaylandBufferRef::BufferFormatEgl_Y_XUXV; + default: + qCDebug(qLcWaylandCompositorHardwareIntegration) << "Buffer format" << hex << format << "not supported"; + return QWaylandBufferRef::BufferFormatEgl_Null; + } +} + +static QOpenGLTexture::TextureFormat openGLFormatFromBufferFormat(QWaylandBufferRef::BufferFormatEgl format) { + switch (format) { + case QWaylandBufferRef::BufferFormatEgl_RGB: + return QOpenGLTexture::RGBFormat; + case QWaylandBufferRef::BufferFormatEgl_RGBA: + return QOpenGLTexture::RGBAFormat; + default: + return QOpenGLTexture::NoFormat; + } +} + +bool LinuxDmabufClientBufferIntegration::initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer) +{ + bool success = true; + + // Resolving GL functions may need a context current, so do it only here. + if (!gl_egl_image_target_texture_2d) + gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + + if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported."; + success = false; + } + + for (uint32_t i = 0; i < dmabufBuffer->planesNumber(); ++i) { + QVarLengthArray<EGLint, 17> attribs; + switch (i) { + case 0: + attribs = { + EGL_WIDTH, dmabufBuffer->size().width(), + EGL_HEIGHT, dmabufBuffer->size().height(), + EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()), + EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(i).fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset), + EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride), + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff), + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32), + EGL_NONE + }; + break; + case 1: + attribs = { + EGL_WIDTH, dmabufBuffer->size().width(), + EGL_HEIGHT, dmabufBuffer->size().height(), + EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()), + EGL_DMA_BUF_PLANE1_FD_EXT, dmabufBuffer->plane(i).fd, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset), + EGL_DMA_BUF_PLANE1_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride), + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff), + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32), + EGL_NONE + }; + break; + case 2: + attribs = { + EGL_WIDTH, dmabufBuffer->size().width(), + EGL_HEIGHT, dmabufBuffer->size().height(), + EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()), + EGL_DMA_BUF_PLANE2_FD_EXT, dmabufBuffer->plane(i).fd, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset), + EGL_DMA_BUF_PLANE2_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride), + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff), + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32), + EGL_NONE + }; + break; + case 3: + attribs = { + EGL_WIDTH, dmabufBuffer->size().width(), + EGL_HEIGHT, dmabufBuffer->size().height(), + EGL_LINUX_DRM_FOURCC_EXT, EGLint(dmabufBuffer->drmFormat()), + EGL_DMA_BUF_PLANE3_FD_EXT, dmabufBuffer->plane(i).fd, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, EGLint(dmabufBuffer->plane(i).offset), + EGL_DMA_BUF_PLANE3_PITCH_EXT, EGLint(dmabufBuffer->plane(i).stride), + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(i).modifiers & 0xffffffff), + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(i).modifiers >> 32), + EGL_NONE + }; + break; + default: + return false; + } + + // note: EGLImageKHR does NOT take ownership of the file descriptors + EGLImageKHR image = egl_create_image(m_eglDisplay, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer) nullptr, + attribs.constData()); + + if (image == EGL_NO_IMAGE_KHR) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i; + success = false; + } + + dmabufBuffer->initImage(i, image); + } + return success; +} + +bool LinuxDmabufClientBufferIntegration::initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer) +{ + bool success = true; + + const YuvFormatConversion conversion = m_yuvFormats.value(dmabufBuffer->drmFormat()); + if (conversion.inputPlanes != dmabufBuffer->planesNumber()) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer for this format must provide" << conversion.inputPlanes + << "planes but only" << dmabufBuffer->planesNumber() << "received"; + return false; + } + + // Resolving GL functions may need a context current, so do it only here. + if (!gl_egl_image_target_texture_2d) + gl_egl_image_target_texture_2d = reinterpret_cast<PFNGLEGLIMAGETARGETTEXTURE2DOESPROC>(eglGetProcAddress("glEGLImageTargetTexture2DOES")); + + + if (dmabufBuffer->plane(0).modifiers != DRM_FORMAT_MOD_INVALID && !m_supportsDmabufModifiers) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Buffer uses dmabuf modifiers, which are not supported."; + success = false; + } + + for (uint32_t i = 0; i < conversion.outputPlanes; ++i) { + const YuvPlaneConversion plane = conversion.plane[i]; + + QVarLengthArray<EGLint, 17> attribs = { + EGL_WIDTH, dmabufBuffer->size().width() / plane.widthDivisor, + EGL_HEIGHT, dmabufBuffer->size().height() / plane.heightDivisor, + EGL_LINUX_DRM_FOURCC_EXT, plane.format, + EGL_DMA_BUF_PLANE0_FD_EXT, dmabufBuffer->plane(plane.planeIndex).fd, + EGL_DMA_BUF_PLANE0_OFFSET_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).offset), + EGL_DMA_BUF_PLANE0_PITCH_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).stride), + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers & 0xffffffff), + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, EGLint(dmabufBuffer->plane(plane.planeIndex).modifiers >> 32), + EGL_NONE + }; + + // note: EGLImageKHR does NOT take ownership of the file descriptors + EGLImageKHR image = egl_create_image(m_eglDisplay, + EGL_NO_CONTEXT, + EGL_LINUX_DMA_BUF_EXT, + (EGLClientBuffer) nullptr, + attribs.constData()); + + if (image == EGL_NO_IMAGE_KHR) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "failed to create EGL image for plane" << i; + success = false; + } + + dmabufBuffer->initImage(i, image); + } + return success; +} + +LinuxDmabufClientBufferIntegration::LinuxDmabufClientBufferIntegration() +{ + m_yuvFormats.insert(DRM_FORMAT_YUYV, + YuvFormatConversion { + .inputPlanes = 1, + .outputPlanes = 2, + {{ + .format = DRM_FORMAT_GR88, + .widthDivisor = 1, + .heightDivisor = 1, + .planeIndex = 0 + }, { + .format = DRM_FORMAT_ARGB8888, + .widthDivisor = 2, + .heightDivisor = 1, + .planeIndex = 0 + }} + }); +} + +LinuxDmabufClientBufferIntegration::~LinuxDmabufClientBufferIntegration() +{ + m_importedBuffers.clear(); +} + +void LinuxDmabufClientBufferIntegration::initializeHardware(struct ::wl_display *display) +{ + m_linuxDmabuf.reset(new LinuxDmabuf(display, this)); + + const bool ignoreBindDisplay = !qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").isEmpty() && qgetenv("QT_WAYLAND_IGNORE_BIND_DISPLAY").toInt() != 0; + + // initialize hardware extensions + egl_query_dmabuf_modifiers_ext = reinterpret_cast<PFNEGLQUERYDMABUFMODIFIERSEXTPROC>(eglGetProcAddress("eglQueryDmaBufModifiersEXT")); + egl_query_dmabuf_formats_ext = reinterpret_cast<PFNEGLQUERYDMABUFFORMATSEXTPROC>(eglGetProcAddress("eglQueryDmaBufFormatsEXT")); + if (!egl_query_dmabuf_modifiers_ext || !egl_query_dmabuf_formats_ext) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglQueryDmaBufModifiersEXT and eglQueryDmaBufFormatsEXT."; + return; + } + + egl_bind_wayland_display = reinterpret_cast<PFNEGLBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglBindWaylandDisplayWL")); + egl_unbind_wayland_display = reinterpret_cast<PFNEGLUNBINDWAYLANDDISPLAYWL>(eglGetProcAddress("eglUnbindWaylandDisplayWL")); + if ((!egl_bind_wayland_display || !egl_unbind_wayland_display) && !ignoreBindDisplay) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglBindWaylandDisplayWL and eglUnbindWaylandDisplayWL."; + return; + } + + egl_create_image = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>(eglGetProcAddress("eglCreateImageKHR")); + egl_destroy_image = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>(eglGetProcAddress("eglDestroyImageKHR")); + if (!egl_create_image || !egl_destroy_image) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not find eglCreateImageKHR and eglDestroyImageKHR."; + return; + } + + // initialize EGL display + QPlatformNativeInterface *nativeInterface = QGuiApplication::platformNativeInterface(); + if (!nativeInterface) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. No native platform interface available."; + return; + } + + m_eglDisplay = nativeInterface->nativeResourceForIntegration("EglDisplay"); + if (!m_eglDisplay) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not get EglDisplay for window."; + return; + } + + const char *extensionString = eglQueryString(m_eglDisplay, EGL_EXTENSIONS); + if (!extensionString || !strstr(extensionString, "EGL_EXT_image_dma_buf_import")) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. There is no EGL_EXT_image_dma_buf_import extension."; + return; + } + if (strstr(extensionString, "EGL_EXT_image_dma_buf_import_modifiers")) + m_supportsDmabufModifiers = true; + + if (egl_bind_wayland_display && egl_unbind_wayland_display) { + m_displayBound = egl_bind_wayland_display(m_eglDisplay, display); + if (!m_displayBound) { + if (ignoreBindDisplay) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Could not bind Wayland display. Ignoring."; + } else { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "Failed to initialize EGL display. Could not bind Wayland display."; + return; + } + } + } + + // request and sent formats/modifiers only after egl_display is bound + QHash<uint32_t, QVector<uint64_t>> modifiers; + for (const auto &format : supportedDrmFormats()) { + modifiers[format] = supportedDrmModifiers(format); + } + m_linuxDmabuf->setSupportedModifiers(modifiers); +} + +QVector<uint32_t> LinuxDmabufClientBufferIntegration::supportedDrmFormats() +{ + if (!egl_query_dmabuf_formats_ext) + return QVector<uint32_t>(); + + // request total number of formats + EGLint count = 0; + EGLBoolean success = egl_query_dmabuf_formats_ext(m_eglDisplay, 0, nullptr, &count); + + if (success && count > 0) { + QVector<uint32_t> drmFormats(count); + if (egl_query_dmabuf_formats_ext(m_eglDisplay, count, (EGLint *) drmFormats.data(), &count)) + return drmFormats; + } + + return QVector<uint32_t>(); +} + +QVector<uint64_t> LinuxDmabufClientBufferIntegration::supportedDrmModifiers(uint32_t format) +{ + if (!egl_query_dmabuf_modifiers_ext) + return QVector<uint64_t>(); + + // request total number of formats + EGLint count = 0; + EGLBoolean success = egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, 0, nullptr, nullptr, &count); + + if (success && count > 0) { + QVector<uint64_t> modifiers(count); + if (egl_query_dmabuf_modifiers_ext(m_eglDisplay, format, count, modifiers.data(), nullptr, &count)) { + return modifiers; + } + } + + return QVector<uint64_t>(); +} + +void LinuxDmabufClientBufferIntegration::deleteOrphanedTextures() +{ + Q_ASSERT(QOpenGLContext::currentContext()); + qDeleteAll(m_orphanedTextures); + m_orphanedTextures.clear(); +} + +void LinuxDmabufClientBufferIntegration::deleteImage(EGLImageKHR image) +{ + egl_destroy_image(m_eglDisplay, image); +} + +QtWayland::ClientBuffer *LinuxDmabufClientBufferIntegration::createBufferFor(wl_resource *resource) +{ + // fallback for shared memory buffers + if (wl_shm_buffer_get(resource)) + return nullptr; + + auto it = m_importedBuffers.find(resource); + if (it != m_importedBuffers.end()) { + m_importedBuffers.value(resource); + return new LinuxDmabufClientBuffer(this, it.value()->resource()->handle, m_importedBuffers.value(resource)); + } + qCWarning(qLcWaylandCompositorHardwareIntegration) << "could not create client buffer for dmabuf buffer"; + return nullptr; +} + +bool LinuxDmabufClientBufferIntegration::importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer) +{ + if (m_importedBuffers.contains(resource)) { + qCWarning(qLcWaylandCompositorHardwareIntegration) << "buffer has already been added"; + return false; + } + m_importedBuffers[resource] = linuxDmabufBuffer; + if (m_yuvFormats.contains(linuxDmabufBuffer->drmFormat())) + return initYuvTexture(linuxDmabufBuffer); + else + return initSimpleTexture(linuxDmabufBuffer); +} + +void LinuxDmabufClientBufferIntegration::removeBuffer(wl_resource *resource) +{ + m_importedBuffers.remove(resource); +} + +LinuxDmabufClientBuffer::LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration *integration, + wl_resource *bufferResource, + LinuxDmabufWlBuffer *dmabufBuffer) + : ClientBuffer(bufferResource) + , m_integration(integration) +{ + d = dmabufBuffer; +} + +QOpenGLTexture *LinuxDmabufClientBuffer::toOpenGlTexture(int plane) +{ + // At this point we should have a valid OpenGL context, so it's safe to destroy textures + m_integration->deleteOrphanedTextures(); + + if (!m_buffer) + return nullptr; + + QOpenGLTexture *texture = d->texture(plane); + + const auto target = static_cast<QOpenGLTexture::Target>(GL_TEXTURE_2D); + + if (!texture) { + texture = new QOpenGLTexture(target); + texture->setFormat(openGLFormatFromBufferFormat(formatFromDrmFormat(d->drmFormat()))); + texture->setSize(d->size().width(), d->size().height()); + texture->create(); + d->initTexture(plane, texture); + } + + if (m_textureDirty) { + texture->bind(); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + m_integration->gl_egl_image_target_texture_2d(target, d->image(plane)); + } + return texture; +} + +void LinuxDmabufClientBuffer::setDestroyed() +{ + m_integration->removeBuffer(m_buffer); + ClientBuffer::setDestroyed(); +} + +LinuxDmabufClientBuffer::~LinuxDmabufClientBuffer() +{ + // resources are deleted by buffer_destroy_resource + m_buffer = nullptr; + d = nullptr; +} + +QWaylandBufferRef::BufferFormatEgl LinuxDmabufClientBuffer::bufferFormatEgl() const +{ + return formatFromDrmFormat(d->drmFormat()); +} + +QSize LinuxDmabufClientBuffer::size() const +{ + return d->size(); +} + +QWaylandSurface::Origin LinuxDmabufClientBuffer::origin() const +{ + return (d->flags() & QtWaylandServer::zwp_linux_buffer_params_v1::flags_y_invert) ? QWaylandSurface::OriginBottomLeft : QWaylandSurface::OriginTopLeft; +} + +QT_END_NAMESPACE diff --git a/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h new file mode 100644 index 000000000..914c1e7a6 --- /dev/null +++ b/src/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linuxdmabufclientbufferintegration.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** 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 LINUXDMABUFCLIENTBUFFERINTEGRATION_H +#define LINUXDMABUFCLIENTBUFFERINTEGRATION_H + +#include "linuxdmabuf.h" + +#include <QtWaylandCompositor/private/qwlclientbufferintegration_p.h> +#include <QtWaylandCompositor/private/qwlclientbuffer_p.h> +#include <QtWaylandCompositor/private/qwayland-server-wayland.h> + +#include <drm_fourcc.h> + +QT_BEGIN_NAMESPACE + +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL_compat) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFFORMATSEXTPROC) (EGLDisplay dpy, EGLint max_formats, EGLint *formats, EGLint *num_formats); +typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYDMABUFMODIFIERSEXTPROC) (EGLDisplay dpy, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers); + +class LinuxDmabufClientBufferIntegrationPrivate; +class LinuxDmabufParams; +class LinuxDmabufClientBuffer; + +// buffer conversion definitions to import YUV buffers +struct YuvPlaneConversion { + EGLint format = DRM_FORMAT_YUYV; + EGLint widthDivisor = 1; + EGLint heightDivisor = 1; + EGLint planeIndex = 0; +}; +struct YuvFormatConversion { + uint32_t inputPlanes = 1; + uint32_t outputPlanes = 1; + struct YuvPlaneConversion plane[LinuxDmabufWlBuffer::MaxDmabufPlanes]; +}; + +class LinuxDmabufClientBufferIntegration : public QtWayland::ClientBufferIntegration +{ +public: + LinuxDmabufClientBufferIntegration(); + ~LinuxDmabufClientBufferIntegration() override; + + void initializeHardware(struct ::wl_display *display) override; + QtWayland::ClientBuffer *createBufferFor(wl_resource *resource) override; + bool importBuffer(wl_resource *resource, LinuxDmabufWlBuffer *linuxDmabufBuffer); + void removeBuffer(wl_resource *resource); + void deleteOrphanedTextures(); + void deleteImage(EGLImageKHR image); + void deleteGLTextureWhenPossible(QOpenGLTexture *texture) { m_orphanedTextures << texture; } + PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2d = nullptr; + +private: + Q_DISABLE_COPY(LinuxDmabufClientBufferIntegration) + + PFNEGLBINDWAYLANDDISPLAYWL egl_bind_wayland_display = nullptr; + PFNEGLUNBINDWAYLANDDISPLAYWL egl_unbind_wayland_display = nullptr; + PFNEGLCREATEIMAGEKHRPROC egl_create_image = nullptr; + PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image = nullptr; + PFNEGLQUERYDMABUFMODIFIERSEXTPROC egl_query_dmabuf_modifiers_ext = nullptr; + PFNEGLQUERYDMABUFFORMATSEXTPROC egl_query_dmabuf_formats_ext = nullptr; + + bool initSimpleTexture(LinuxDmabufWlBuffer *dmabufBuffer); + bool initYuvTexture(LinuxDmabufWlBuffer *dmabufBuffer); + QVector<uint32_t> supportedDrmFormats(); + QVector<uint64_t> supportedDrmModifiers(uint32_t format); + + EGLDisplay m_eglDisplay = EGL_NO_DISPLAY; + bool m_displayBound = false; + QVector<QOpenGLTexture *> m_orphanedTextures; + QHash<EGLint, YuvFormatConversion> m_yuvFormats; + bool m_supportsDmabufModifiers = false; + QHash<struct ::wl_resource *, LinuxDmabufWlBuffer *> m_importedBuffers; + QScopedPointer<LinuxDmabuf> m_linuxDmabuf; +}; + +class LinuxDmabufClientBuffer : public QtWayland::ClientBuffer +{ +public: + ~LinuxDmabufClientBuffer() override; + + QWaylandBufferRef::BufferFormatEgl bufferFormatEgl() const override; + QSize size() const override; + QWaylandSurface::Origin origin() const override; + QOpenGLTexture *toOpenGlTexture(int plane) override; + +protected: + void setDestroyed() override; + +private: + friend class LinuxDmabufClientBufferIntegration; + friend class LinuxDmabufClientBufferIntegrationPrivate; + + LinuxDmabufClientBuffer(LinuxDmabufClientBufferIntegration* integration, wl_resource *bufferResource, LinuxDmabufWlBuffer *dmabufBuffer); + + LinuxDmabufWlBuffer *d = nullptr; + LinuxDmabufClientBufferIntegration *m_integration = nullptr; +}; + +QT_END_NAMESPACE + +#endif // LINUXDMABUFCLIENTBUFFERINTEGRATION_H diff --git a/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h b/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h index b3f1cf3a9..73a66d3af 100644 --- a/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h +++ b/src/hardwareintegration/compositor/xcomposite_share/xcompositebuffer.h @@ -41,6 +41,9 @@ #define XCOMPOSITEBUFFER_H #include <qwayland-server-wayland.h> + +#include <QtWaylandCompositor/private/qwaylandutils_p.h> + #include <QtWaylandCompositor/QWaylandSurface> #include <QtWaylandCompositor/QWaylandCompositor> @@ -68,7 +71,7 @@ public: QSize size() const { return mSize; } - static XCompositeBuffer *fromResource(struct ::wl_resource *resource) { return static_cast<XCompositeBuffer*>(Resource::fromResource(resource)->buffer_object); } + static XCompositeBuffer *fromResource(struct ::wl_resource *resource) { return QtWayland::fromResource<XCompositeBuffer *>(resource); } protected: void buffer_destroy_resource(Resource *) override; diff --git a/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h b/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h index bb43ed1af..28168994c 100644 --- a/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h +++ b/src/hardwareintegration/compositor/xcomposite_share/xcompositehandler.h @@ -45,7 +45,7 @@ #include "xlibinclude.h" #include "qwayland-server-xcomposite.h" -#include <wayland-server.h> +#include <wayland-server-core.h> QT_BEGIN_NAMESPACE diff --git a/src/plugins/decorations/bradient/main.cpp b/src/plugins/decorations/bradient/main.cpp index 3fa723446..83dc8604b 100644 --- a/src/plugins/decorations/bradient/main.cpp +++ b/src/plugins/decorations/bradient/main.cpp @@ -315,19 +315,19 @@ void QWaylandBradientDecoration::processMouseTop(QWaylandInputDevice *inputDevic #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); #endif - startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP_LEFT,b); + startResize(inputDevice, Qt::TopEdge | Qt::LeftEdge, b); } else if (local.x() > window()->width() + margins().left()) { //top right bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); #endif - startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP_RIGHT,b); + startResize(inputDevice, Qt::TopEdge | Qt::RightEdge, b); } else { //top resize bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor); #endif - startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_TOP,b); + startResize(inputDevice, Qt::TopEdge, b); } } else if (local.x() <= margins().left()) { processMouseLeft(inputDevice, local, b, mods); @@ -358,19 +358,19 @@ void QWaylandBradientDecoration::processMouseBottom(QWaylandInputDevice *inputDe #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeBDiagCursor); #endif - startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_BOTTOM_LEFT,b); + startResize(inputDevice, Qt::BottomEdge | Qt::LeftEdge, b); } else if (local.x() > window()->width() + margins().left()) { //bottom right bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SizeFDiagCursor); #endif - startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT,b); + startResize(inputDevice, Qt::BottomEdge | Qt::RightEdge, b); } else { //bottom bit #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitVCursor); #endif - startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_BOTTOM,b); + startResize(inputDevice, Qt::BottomEdge, b); } } @@ -381,7 +381,7 @@ void QWaylandBradientDecoration::processMouseLeft(QWaylandInputDevice *inputDevi #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor); #endif - startResize(inputDevice,WL_SHELL_SURFACE_RESIZE_LEFT,b); + startResize(inputDevice, Qt::LeftEdge, b); } void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDevice, const QPointF &local, Qt::MouseButtons b, Qt::KeyboardModifiers mods) @@ -391,7 +391,7 @@ void QWaylandBradientDecoration::processMouseRight(QWaylandInputDevice *inputDev #if QT_CONFIG(cursor) waylandWindow()->setMouseCursor(inputDevice, Qt::SplitHCursor); #endif - startResize(inputDevice, WL_SHELL_SURFACE_RESIZE_RIGHT,b); + startResize(inputDevice, Qt::RightEdge, b); } class QWaylandBradientDecorationPlugin : public QWaylandDecorationPlugin diff --git a/src/plugins/hardwareintegration/compositor/compositor.pro b/src/plugins/hardwareintegration/compositor/compositor.pro index cd47b2676..94e0f8bf1 100644 --- a/src/plugins/hardwareintegration/compositor/compositor.pro +++ b/src/plugins/hardwareintegration/compositor/compositor.pro @@ -1,6 +1,8 @@ TEMPLATE = subdirs QT_FOR_CONFIG += waylandcompositor-private +qtConfig(wayland-dmabuf-client-buffer): \ + SUBDIRS += linux-dmabuf-unstable-v1 qtConfig(wayland-egl): \ SUBDIRS += wayland-egl qtConfig(wayland-brcm): \ diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json new file mode 100644 index 000000000..1fab86fef --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "linux-dmabuf-unstable-v1" ] +} diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro new file mode 100644 index 000000000..bc4311423 --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pro @@ -0,0 +1,12 @@ +QT = waylandcompositor waylandcompositor-private core-private gui-private + +OTHER_FILES += linux-dmabuf.json + +SOURCES += \ + main.cpp \ + +include(../../../../hardwareintegration/compositor/linux-dmabuf-unstable-v1/linux-dmabuf-unstable-v1.pri) + +PLUGIN_TYPE = wayland-graphics-integration-server +PLUGIN_CLASS_NAME = QWaylandDmabufClientBufferIntegrationPlugin +load(qt_plugin) diff --git a/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp new file mode 100644 index 000000000..d16268a2c --- /dev/null +++ b/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1/main.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandCompositor/private/qwlclientbufferintegrationfactory_p.h> +#include <QtWaylandCompositor/private/qwlclientbufferintegrationplugin_p.h> +#include "linuxdmabufclientbufferintegration.h" + +QT_BEGIN_NAMESPACE + +class QWaylandDmabufClientBufferIntegrationPlugin : public QtWayland::ClientBufferIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QtWaylandClientBufferIntegrationFactoryInterface_iid FILE "linux-dmabuf-unstable-v1.json") +public: + QtWayland::ClientBufferIntegration *create(const QString& key, const QStringList& paramList) override; +}; + +QtWayland::ClientBufferIntegration *QWaylandDmabufClientBufferIntegrationPlugin::create(const QString& key, const QStringList& paramList) +{ + Q_UNUSED(paramList); + Q_UNUSED(key); + return new LinuxDmabufClientBufferIntegration(); +} + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json new file mode 100644 index 000000000..f32637bcc --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.json @@ -0,0 +1,3 @@ +{ + "Keys": [ "fullscreen-shell-v1" ] +} diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro new file mode 100644 index 000000000..a522f87a8 --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/fullscreen-shell-v1.pro @@ -0,0 +1,23 @@ +QT += gui-private waylandclient-private +CONFIG += wayland-scanner + +QMAKE_USE += wayland-client + +WAYLANDCLIENTSOURCES += \ + ../../../3rdparty/protocol/fullscreen-shell-unstable-v1.xml + +HEADERS += \ + qwaylandfullscreenshellv1integration.h \ + qwaylandfullscreenshellv1surface.h + +SOURCES += \ + main.cpp \ + qwaylandfullscreenshellv1integration.cpp \ + qwaylandfullscreenshellv1surface.cpp + +OTHER_FILES += \ + fullscreen-shell-v1.json + +PLUGIN_TYPE = wayland-shell-integration +PLUGIN_CLASS_NAME = QWaylandFullScreenShellV1IntegrationPlugin +load(qt_plugin) diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp new file mode 100644 index 000000000..dfcd997da --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/main.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandClient/private/qwaylandshellintegrationplugin_p.h> + +#include "qwaylandfullscreenshellv1integration.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class QWaylandFullScreenShellV1IntegrationPlugin : public QWaylandShellIntegrationPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QWaylandShellIntegrationFactoryInterface_iid FILE "fullscreen-shell-v1.json") +public: + QWaylandShellIntegration *create(const QString &key, const QStringList ¶mList) override; +}; + +QWaylandShellIntegration *QWaylandFullScreenShellV1IntegrationPlugin::create(const QString &key, const QStringList ¶mList) +{ + Q_UNUSED(paramList); + + if (key == QLatin1String("fullscreen-shell-v1")) + return new QWaylandFullScreenShellV1Integration(); + return nullptr; +} + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#include "main.moc" diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp new file mode 100644 index 000000000..0cd2cb1e8 --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwaylandfullscreenshellv1integration.h" +#include "qwaylandfullscreenshellv1surface.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +bool QWaylandFullScreenShellV1Integration::initialize(QWaylandDisplay *display) +{ + for (const QWaylandDisplay::RegistryGlobal &global : display->globals()) { + if (global.interface == QLatin1String("zwp_fullscreen_shell_v1") && !m_shell) { + m_shell.reset(new QtWayland::zwp_fullscreen_shell_v1(display->wl_registry(), global.id, global.version)); + break; + } + } + + if (!m_shell) { + qCDebug(lcQpaWayland) << "Couldn't find global zwp_fullscreen_shell_v1 for fullscreen-shell"; + return false; + } + + return QWaylandShellIntegration::initialize(display); +} + +QWaylandShellSurface *QWaylandFullScreenShellV1Integration::createShellSurface(QWaylandWindow *window) +{ + return new QWaylandFullScreenShellV1Surface(m_shell.data(), window); +} + +} // namespace QtWaylandClient + +QT_END_NAMESPACE diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h new file mode 100644 index 000000000..131f9e720 --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1integration.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDFULLSCREENSHELLV1INTEGRATION_H +#define QWAYLANDFULLSCREENSHELLV1INTEGRATION_H + +#include <wayland-client.h> +#include <QtWaylandClient/private/qwayland-wayland.h> +#include <QtWaylandClient/private/qwaylandshellintegration_p.h> + +#include "qwayland-fullscreen-shell-unstable-v1.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Integration : public QWaylandShellIntegration +{ +public: + bool initialize(QWaylandDisplay *display) override; + QWaylandShellSurface *createShellSurface(QWaylandWindow *window) override; + +private: + QScopedPointer<QtWayland::zwp_fullscreen_shell_v1> m_shell; +}; + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#endif // QWAYLANDFULLSCREENSHELLV1INTEGRATION_H diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp new file mode 100644 index 000000000..9a829f6e9 --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtWaylandClient/private/qwaylandscreen_p.h> + +#include "qwaylandfullscreenshellv1surface.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +QWaylandFullScreenShellV1Surface::QWaylandFullScreenShellV1Surface(QtWayland::zwp_fullscreen_shell_v1 *shell, QWaylandWindow *window) + : QWaylandShellSurface(window) + , m_shell(shell) + , m_window(window) +{ + auto screen = static_cast<QWaylandScreen *>(m_window->screen()); + m_shell->present_surface(m_window->object(), + QtWayland::zwp_fullscreen_shell_v1::present_method_default, + screen->output()); +} + +} // namespace QtWaylandClient + +QT_END_NAMESPACE diff --git a/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h new file mode 100644 index 000000000..718e1e861 --- /dev/null +++ b/src/plugins/shellintegration/fullscreen-shell-v1/qwaylandfullscreenshellv1surface.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWAYLANDFULLSCREENSHELLV1SURFACE_H +#define QWAYLANDFULLSCREENSHELLV1SURFACE_H + +#include <QtWaylandClient/qtwaylandclientglobal.h> +#include <QtWaylandClient/private/qwaylandshellsurface_p.h> +#include <QtWaylandClient/private/qwaylandwindow_p.h> + +#include "qwayland-fullscreen-shell-unstable-v1.h" + +QT_BEGIN_NAMESPACE + +namespace QtWaylandClient { + +class Q_WAYLAND_CLIENT_EXPORT QWaylandFullScreenShellV1Surface : public QWaylandShellSurface +{ +public: + QWaylandFullScreenShellV1Surface(QtWayland::zwp_fullscreen_shell_v1 *shell, QWaylandWindow *window); + +private: + QtWayland::zwp_fullscreen_shell_v1 *m_shell = nullptr; + QWaylandWindow *m_window = nullptr; +}; + +} // namespace QtWaylandClient + +QT_END_NAMESPACE + +#endif // QWAYLANDFULLSCREENSHELLV1SURFACE_H diff --git a/src/plugins/shellintegration/shellintegration.pro b/src/plugins/shellintegration/shellintegration.pro index 48b6efa36..39c57940a 100644 --- a/src/plugins/shellintegration/shellintegration.pro +++ b/src/plugins/shellintegration/shellintegration.pro @@ -1,9 +1,9 @@ TEMPLATE = subdirs +QT_FOR_CONFIG += waylandclient-private -SUBDIRS += \ - ivi-shell \ - xdg-shell \ - xdg-shell-v5 \ - xdg-shell-v6 \ - wl-shell \ - +qtConfig(wayland-client-fullscreen-shell-v1): SUBDIRS += fullscreen-shell-v1 +qtConfig(wayland-client-ivi-shell): SUBDIRS += ivi-shell +qtConfig(wayland-client-wl-shell): SUBDIRS += wl-shell +qtConfig(wayland-client-xdg-shell): SUBDIRS += xdg-shell +qtConfig(wayland-client-xdg-shell-v5): SUBDIRS += xdg-shell-v5 +qtConfig(wayland-client-xdg-shell-v6): SUBDIRS += xdg-shell-v6 diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h index 80a7507d4..3d76cc310 100644 --- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellintegration_p.h @@ -51,7 +51,6 @@ // We mean it. // -#include <wayland-client.h> #include <private/qwayland-wayland.h> #include <QtWaylandClient/private/qwaylandshellintegration_p.h> diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp index 88a63655c..4506c312a 100644 --- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp @@ -76,11 +76,10 @@ QWaylandWlShellSurface::~QWaylandWlShellSurface() delete m_extendedWindow; } -void QWaylandWlShellSurface::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) +void QWaylandWlShellSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) { - resize(inputDevice->wl_seat(), - inputDevice->serial(), - edges); + enum resize resizeEdges = convertToResizeEdges(edges); + resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges); } bool QWaylandWlShellSurface::move(QWaylandInputDevice *inputDevice) @@ -193,6 +192,14 @@ void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states) m_pending.states = states & ~Qt::WindowMinimized; } +enum QWaylandWlShellSurface::resize QWaylandWlShellSurface::convertToResizeEdges(Qt::Edges edges) +{ + return static_cast<enum resize>( + ((edges & Qt::TopEdge) ? resize_top : 0) + | ((edges & Qt::BottomEdge) ? resize_bottom : 0) + | ((edges & Qt::LeftEdge) ? resize_left : 0) + | ((edges & Qt::RightEdge) ? resize_right : 0)); +} void QWaylandWlShellSurface::setTopLevel() { diff --git a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h index 57e06525a..324c10aac 100644 --- a/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h +++ b/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface_p.h @@ -53,8 +53,6 @@ #include <QtCore/QSize> -#include <wayland-client.h> - #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwayland-wayland.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h> @@ -78,7 +76,7 @@ public: ~QWaylandWlShellSurface() override; using QtWayland::wl_shell_surface::resize; - void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override; + void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override; using QtWayland::wl_shell_surface::move; bool move(QWaylandInputDevice *inputDevice) override; @@ -99,6 +97,7 @@ protected: void requestWindowStates(Qt::WindowStates states) override; private: + static enum resize convertToResizeEdges(Qt::Edges edges); void setTopLevel(); void updateTransientParent(QWindow *parent); void setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial); diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp index 0115eb1da..51979acf7 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5.cpp @@ -25,6 +25,7 @@ * DEALINGS IN THE SOFTWARE. */ #include "qwayland-xdg-shell-unstable-v5_p.h" +#include <QtWaylandClient/private/wayland-wayland-client-protocol.h> QT_BEGIN_NAMESPACE QT_WARNING_PUSH diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h index 3d8a6c13d..8fb1ea7b8 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/qwayland-xdg-shell-unstable-v5_p.h @@ -31,6 +31,8 @@ #include <QByteArray> #include <QString> +struct wl_registry; + QT_BEGIN_NAMESPACE QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers") diff --git a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h index 46644610b..8877e8830 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/pregenerated/3rdparty/wayland-xdg-shell-unstable-v5-client-protocol_p.h @@ -7,7 +7,7 @@ #include <stdint.h> #include <stddef.h> -#include "wayland-client.h" +#include "wayland-client-core.h" #ifdef __cplusplus extern "C" { diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h index ff8e5639f..7494f6a67 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgpopupv5_p.h @@ -53,8 +53,6 @@ #include "qwayland-xdg-shell-unstable-v5_p.h" -#include <wayland-client.h> - #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h> diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h index 67e5d32a7..2b0a59f17 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgshellv5_p.h @@ -56,8 +56,6 @@ #include <QtCore/QSize> #include <QtCore/QVector> -#include <wayland-client.h> - #include <QtWaylandClient/qtwaylandclientglobal.h> #include <QtWaylandClient/private/qwaylandshellsurface_p.h> diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp index 61a865cb2..b691ee747 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5.cpp @@ -73,18 +73,19 @@ QWaylandXdgSurfaceV5::~QWaylandXdgSurfaceV5() delete m_extendedWindow; } -void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) +QtWayland::xdg_surface_v5::resize_edge QWaylandXdgSurfaceV5::convertToResizeEdges(Qt::Edges edges) { - // May need some conversion if types get incompatibles, ATM they're identical - enum resize_edge const * const arg = reinterpret_cast<enum resize_edge const *>(&edges); - resize(inputDevice, *arg); + return static_cast<enum resize_edge>( + ((edges & Qt::TopEdge) ? resize_edge_top : 0) + | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0) + | ((edges & Qt::LeftEdge) ? resize_edge_left : 0) + | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); } -void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, enum resize_edge edges) +void QWaylandXdgSurfaceV5::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) { - resize(inputDevice->wl_seat(), - inputDevice->serial(), - edges); + resize_edge resizeEdges = convertToResizeEdges(edges); + resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges); } bool QWaylandXdgSurfaceV5::move(QWaylandInputDevice *inputDevice) diff --git a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h index b938047ec..231a56d84 100644 --- a/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h +++ b/src/plugins/shellintegration/xdg-shell-v5/qwaylandxdgsurfacev5_p.h @@ -59,8 +59,6 @@ #include <QtCore/QSize> #include <QtCore/QMargins> -#include <wayland-client.h> - QT_BEGIN_NAMESPACE class QWindow; @@ -81,9 +79,8 @@ public: ~QWaylandXdgSurfaceV5() override; using QtWayland::xdg_surface_v5::resize; - void resize(QWaylandInputDevice *inputDevice, enum resize_edge edges); - - void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override; + static resize_edge convertToResizeEdges(Qt::Edges edges); + void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override; using QtWayland::xdg_surface_v5::move; bool move(QWaylandInputDevice *inputDevice) override; diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp index 9e55e3e16..e12573ad0 100644 --- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp +++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6.cpp @@ -157,6 +157,15 @@ void QWaylandXdgSurfaceV6::Toplevel::requestWindowStates(Qt::WindowStates states } } +QtWayland::zxdg_toplevel_v6::resize_edge QWaylandXdgSurfaceV6::Toplevel::convertToResizeEdges(Qt::Edges edges) +{ + return static_cast<enum resize_edge>( + ((edges & Qt::TopEdge) ? resize_edge_top : 0) + | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0) + | ((edges & Qt::LeftEdge) ? resize_edge_left : 0) + | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); +} + QWaylandXdgSurfaceV6::Popup::Popup(QWaylandXdgSurfaceV6 *xdgSurface, QWaylandXdgSurfaceV6 *parent, QtWayland::zxdg_positioner_v6 *positioner) : zxdg_popup_v6(xdgSurface->get_popup(parent->object(), positioner->object())) @@ -226,19 +235,13 @@ QWaylandXdgSurfaceV6::~QWaylandXdgSurfaceV6() destroy(); } -void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, zxdg_toplevel_v6_resize_edge edges) +void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) { Q_ASSERT(m_toplevel && m_toplevel->isInitialized()); - m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), edges); + auto resizeEdges = Toplevel::convertToResizeEdges(edges); + m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges); } -void QWaylandXdgSurfaceV6::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) -{ - auto xdgEdges = reinterpret_cast<enum zxdg_toplevel_v6_resize_edge const *>(&edges); - resize(inputDevice, *xdgEdges); -} - - bool QWaylandXdgSurfaceV6::move(QWaylandInputDevice *inputDevice) { if (m_toplevel && m_toplevel->isInitialized()) { diff --git a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h index 659aad047..5a2867379 100644 --- a/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h +++ b/src/plugins/shellintegration/xdg-shell-v6/qwaylandxdgshellv6_p.h @@ -60,8 +60,6 @@ #include <QtCore/QSize> #include <QtGui/QRegion> -#include <wayland-client.h> - QT_BEGIN_NAMESPACE class QWindow; @@ -79,8 +77,7 @@ public: QWaylandXdgSurfaceV6(QWaylandXdgShellV6 *shell, ::zxdg_surface_v6 *surface, QWaylandWindow *window); ~QWaylandXdgSurfaceV6() override; - void resize(QWaylandInputDevice *inputDevice, enum zxdg_toplevel_v6_resize_edge edges); - void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override; + void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override; bool move(QWaylandInputDevice *inputDevice) override; void setTitle(const QString &title) override; void setAppId(const QString &appId) override; @@ -108,6 +105,9 @@ private: void zxdg_toplevel_v6_close() override; void requestWindowStates(Qt::WindowStates states); + + static resize_edge convertToResizeEdges(Qt::Edges edges); + struct { QSize size = {0, 0}; Qt::WindowStates states = Qt::WindowNoState; diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp index 1310e340d..8759cb8c9 100644 --- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp +++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp @@ -185,6 +185,15 @@ void QWaylandXdgSurface::Toplevel::requestWindowStates(Qt::WindowStates states) } } +QtWayland::xdg_toplevel::resize_edge QWaylandXdgSurface::Toplevel::convertToResizeEdges(Qt::Edges edges) +{ + return static_cast<enum resize_edge>( + ((edges & Qt::TopEdge) ? resize_edge_top : 0) + | ((edges & Qt::BottomEdge) ? resize_edge_bottom : 0) + | ((edges & Qt::LeftEdge) ? resize_edge_left : 0) + | ((edges & Qt::RightEdge) ? resize_edge_right : 0)); +} + QWaylandXdgSurface::Popup::Popup(QWaylandXdgSurface *xdgSurface, QWaylandXdgSurface *parent, QtWayland::xdg_positioner *positioner) : xdg_popup(xdgSurface->get_popup(parent->object(), positioner->object())) @@ -254,19 +263,13 @@ QWaylandXdgSurface::~QWaylandXdgSurface() destroy(); } -void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, xdg_toplevel_resize_edge edges) +void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) { Q_ASSERT(m_toplevel && m_toplevel->isInitialized()); - m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), edges); + auto resizeEdges = Toplevel::convertToResizeEdges(edges); + m_toplevel->resize(inputDevice->wl_seat(), inputDevice->serial(), resizeEdges); } -void QWaylandXdgSurface::resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) -{ - auto xdgEdges = reinterpret_cast<enum xdg_toplevel_resize_edge const *>(&edges); - resize(inputDevice, *xdgEdges); -} - - bool QWaylandXdgSurface::move(QWaylandInputDevice *inputDevice) { if (m_toplevel && m_toplevel->isInitialized()) { diff --git a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h index 741b83cfd..1dc1def23 100644 --- a/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h +++ b/src/plugins/shellintegration/xdg-shell/qwaylandxdgshell_p.h @@ -62,8 +62,6 @@ #include <QtCore/QSize> #include <QtGui/QRegion> -#include <wayland-client.h> - QT_BEGIN_NAMESPACE class QWindow; @@ -82,8 +80,7 @@ public: QWaylandXdgSurface(QWaylandXdgShell *shell, ::xdg_surface *surface, QWaylandWindow *window); ~QWaylandXdgSurface() override; - void resize(QWaylandInputDevice *inputDevice, enum xdg_toplevel_resize_edge edges); - void resize(QWaylandInputDevice *inputDevice, enum wl_shell_surface_resize edges) override; + void resize(QWaylandInputDevice *inputDevice, Qt::Edges edges) override; bool move(QWaylandInputDevice *inputDevice) override; void setTitle(const QString &title) override; void setAppId(const QString &appId) override; @@ -114,6 +111,9 @@ private: void requestWindowFlags(Qt::WindowFlags flags); void requestWindowStates(Qt::WindowStates states); + + static resize_edge convertToResizeEdges(Qt::Edges edges); + struct { QSize size = {0, 0}; Qt::WindowStates states = Qt::WindowNoState; diff --git a/src/qtwaylandscanner/qtwaylandscanner.cpp b/src/qtwaylandscanner/qtwaylandscanner.cpp index 6f4a33b5f..56045e880 100644 --- a/src/qtwaylandscanner/qtwaylandscanner.cpp +++ b/src/qtwaylandscanner/qtwaylandscanner.cpp @@ -441,7 +441,7 @@ bool Scanner::process() printf("#ifndef %s\n", inclusionGuard.constData()); printf("#define %s\n", inclusionGuard.constData()); printf("\n"); - printf("#include \"wayland-server.h\"\n"); + printf("#include \"wayland-server-core.h\"\n"); if (m_headerPath.isEmpty()) printf("#include \"wayland-%s-server-protocol.h\"\n", QByteArray(m_protocolName).replace('_', '-').constData()); else @@ -505,6 +505,7 @@ bool Scanner::process() printf(" virtual ~Resource() {}\n"); printf("\n"); printf(" %s *%s_object;\n", interfaceName, interfaceNameStripped); + printf(" %s *object() { return %s_object; } \n", interfaceName, interfaceNameStripped); printf(" struct ::wl_resource *handle;\n"); printf("\n"); printf(" struct ::wl_client *client() const { return wl_resource_get_client(handle); }\n"); @@ -769,6 +770,7 @@ bool Scanner::process() printf(" void %s::destroy_func(struct ::wl_resource *client_resource)\n", interfaceName); printf(" {\n"); printf(" Resource *resource = Resource::fromResource(client_resource);\n"); + printf(" Q_ASSERT(resource);\n"); printf(" %s *that = resource->%s_object;\n", interfaceName, interfaceNameStripped); printf(" that->m_resource_map.remove(resource->client(), resource);\n"); printf(" that->%s_destroy_resource(resource);\n", interfaceNameStripped); @@ -939,6 +941,8 @@ bool Scanner::process() printf("#include <QByteArray>\n"); printf("#include <QString>\n"); printf("\n"); + printf("struct wl_registry;\n"); + printf("\n"); printf("QT_BEGIN_NAMESPACE\n"); printf("QT_WARNING_PUSH\n"); printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); @@ -1056,6 +1060,22 @@ bool Scanner::process() printf("QT_WARNING_DISABLE_GCC(\"-Wmissing-field-initializers\")\n"); printf("\n"); printf("namespace QtWayland {\n"); + printf("\n"); + + // wl_registry_bind is part of the protocol, so we can't use that... instead we use core + // libwayland API to do the same thing a wayland-scanner generated wl_registry_bind would. + printf("static inline void *wlRegistryBind(struct ::wl_registry *registry, uint32_t name, const struct ::wl_interface *interface, uint32_t version)\n"); + printf("{\n"); + printf(" const uint32_t bindOpCode = 0;\n"); + printf("#if (WAYLAND_VERSION_MAJOR == 1 && WAYLAND_VERSION_MINOR > 10) || WAYLAND_VERSION_MAJOR > 1\n"); + printf(" return (void *) wl_proxy_marshal_constructor_versioned((struct wl_proxy *) registry,\n"); + printf(" bindOpCode, interface, version, name, interface->name, version, nullptr);\n"); + printf("#else\n"); + printf(" return (void *) wl_proxy_marshal_constructor((struct wl_proxy *) registry,\n"); + printf(" bindOpCode, interface, name, interface->name, version, nullptr);\n"); + printf("#endif\n"); + printf("}\n"); + printf("\n"); for (int j = 0; j < interfaces.size(); ++j) { const WaylandInterface &interface = interfaces.at(j); @@ -1096,7 +1116,7 @@ bool Scanner::process() printf(" void %s::init(struct ::wl_registry *registry, int id, int version)\n", interfaceName); printf(" {\n"); - printf(" m_%s = static_cast<struct ::%s *>(wl_registry_bind(registry, id, &%s_interface, version));\n", interfaceName, interfaceName, interfaceName); + printf(" m_%s = static_cast<struct ::%s *>(wlRegistryBind(registry, id, &%s_interface, version));\n", interfaceName, interfaceName, interfaceName); if (hasEvents) printf(" init_listener();\n"); printf(" }\n"); diff --git a/sync.profile b/sync.profile index 756674cda..147f2782f 100644 --- a/sync.profile +++ b/sync.profile @@ -61,6 +61,7 @@ "^qwayland-server-server-buffer-extension.h", "^qwayland-server-text-input-unstable-v2.h", "^qwayland-server-touch-extension.h", + "^qwayland-server-viewporter.h", "^qwayland-server-xdg-decoration-unstable-v1.h", "^qwayland-server-xdg-shell-unstable-v5.h", "^qwayland-server-xdg-shell-unstable-v6.h", @@ -71,6 +72,7 @@ "^wayland-qt-key-unstable-v1-server-protocol.h", "^wayland-server-buffer-extension-server-protocol.h", "^wayland-text-input-unstable-v2-server-protocol.h", + "^wayland-viewporter-server-protocol.h", "^wayland-touch-extension-server-protocol.h", "^wayland-wayland-server-protocol.h", "^wayland-xdg-decoration-unstable-v1-server-protocol.h", @@ -78,5 +80,9 @@ "^wayland-xdg-shell-unstable-v5-server-protocol.h", "^wayland-xdg-shell-unstable-v6-server-protocol.h", ], + "$basedir/src/plugins/hardwareintegration/compositor/linux-dmabuf-unstable-v1" => [ + "^qwayland-server-linux-dmabuf-unstable-v1.h", + "^wayland-linux-dmabuf-unstable-v1-server-protocol.h", + ], ); @private_headers = ( qr/^qwayland-.*\.h/, qr/^wayland-.*-protocol\.h/ ); diff --git a/tests/auto/client/client.pro b/tests/auto/client/client.pro index 9fd8fc3f9..af7889d5f 100644 --- a/tests/auto/client/client.pro +++ b/tests/auto/client/client.pro @@ -2,6 +2,10 @@ TEMPLATE=subdirs SUBDIRS += \ client \ + fullscreenshellv1 \ iviapplication \ - xdgshellv6 \ - wl_connect + seatv4 \ + surface \ + wl_connect \ + xdgshell \ + xdgshellv6 diff --git a/tests/auto/client/client/client.pro b/tests/auto/client/client/client.pro index f4ced252c..7c3a934d0 100644 --- a/tests/auto/client/client/client.pro +++ b/tests/auto/client/client/client.pro @@ -1,4 +1,4 @@ -include (../shared/shared.pri) +include (../shared_old/shared_old.pri) TARGET = tst_client SOURCES += tst_client.cpp diff --git a/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro new file mode 100644 index 000000000..49d19d5c3 --- /dev/null +++ b/tests/auto/client/fullscreenshellv1/fullscreenshellv1.pro @@ -0,0 +1,4 @@ +include (../shared_old/shared_old.pri) + +TARGET = tst_client_fullscreenshell1 +SOURCES += tst_fullscreenshellv1.cpp diff --git a/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp b/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp new file mode 100644 index 000000000..f93d9fbc5 --- /dev/null +++ b/tests/auto/client/fullscreenshellv1/tst_fullscreenshellv1.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" + +#include <QWindow> + +#include <QtTest/QtTest> + +static const QSize screenSize(1600, 1200); + +class TestWindow : public QWindow +{ +public: + TestWindow() + { + setSurfaceType(QSurface::RasterSurface); + setGeometry(0, 0, 800, 600); + create(); + } +}; + +class tst_WaylandClientFullScreenShellV1 : public QObject +{ + Q_OBJECT +public: + tst_WaylandClientFullScreenShellV1(MockCompositor *c) + : m_compositor(c) + { + QSocketNotifier *notifier = new QSocketNotifier(m_compositor->waylandFileDescriptor(), QSocketNotifier::Read, this); + connect(notifier, &QSocketNotifier::activated, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents); + // connect to the event dispatcher to make sure to flush out the outgoing message queue + connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents); + connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, this, &tst_WaylandClientFullScreenShellV1::processWaylandEvents); + } + +public slots: + void processWaylandEvents() + { + m_compositor->processWaylandEvents(); + } + + void cleanup() + { + // make sure the surfaces from the last test are properly cleaned up + // and don't show up as false positives in the next test + QTRY_VERIFY(!m_compositor->fullScreenShellV1Surface()); + } + +private slots: + void createDestroyWindow(); + +private: + MockCompositor *m_compositor = nullptr; +}; + +void tst_WaylandClientFullScreenShellV1::createDestroyWindow() +{ + TestWindow window; + window.show(); + + QTRY_VERIFY(m_compositor->fullScreenShellV1Surface()); + + window.destroy(); + QTRY_VERIFY(!m_compositor->fullScreenShellV1Surface()); +} + +int main(int argc, char **argv) +{ + setenv("XDG_RUNTIME_DIR", ".", 1); + setenv("QT_QPA_PLATFORM", "wayland", 1); // force QGuiApplication to use wayland plugin + setenv("QT_WAYLAND_SHELL_INTEGRATION", "fullscreen-shell-v1", 1); + setenv("QT_WAYLAND_DISABLE_WINDOWDECORATION", "1", 1); // window decorations don't make much sense here + + MockCompositor compositor; + compositor.setOutputMode(screenSize); + + QGuiApplication app(argc, argv); + compositor.applicationInitialized(); + + tst_WaylandClientFullScreenShellV1 tc(&compositor); + return QTest::qExec(&tc, argc, argv); +} + +#include <tst_fullscreenshellv1.moc> diff --git a/tests/auto/client/iviapplication/iviapplication.pro b/tests/auto/client/iviapplication/iviapplication.pro index 326921373..f2d596e6c 100644 --- a/tests/auto/client/iviapplication/iviapplication.pro +++ b/tests/auto/client/iviapplication/iviapplication.pro @@ -1,4 +1,4 @@ -include (../shared/shared.pri) +include (../shared_old/shared_old.pri) TARGET = tst_client_iviapplication SOURCES += tst_iviapplication.cpp diff --git a/tests/auto/client/seatv4/seatv4.pro b/tests/auto/client/seatv4/seatv4.pro new file mode 100644 index 000000000..c02db5855 --- /dev/null +++ b/tests/auto/client/seatv4/seatv4.pro @@ -0,0 +1,4 @@ +include (../shared/shared.pri) + +TARGET = tst_seatv4 +SOURCES += tst_seatv4.cpp diff --git a/tests/auto/client/seatv4/tst_seatv4.cpp b/tests/auto/client/seatv4/tst_seatv4.cpp new file mode 100644 index 000000000..0e0ada5d0 --- /dev/null +++ b/tests/auto/client/seatv4/tst_seatv4.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" + +#include <QtGui/QRasterWindow> +#include <QtGui/QOpenGLWindow> + +using namespace MockCompositor; + +// wl_seat version 5 was introduced in wayland 1.10, and although that's pretty old, +// there are still compositors that have yet to update their implementation to support +// the new version (most importantly our own QtWaylandCompositor). +// As long as that's the case, this test makes sure input events still works on version 4. +class SeatV4Compositor : public DefaultCompositor { +public: + explicit SeatV4Compositor() + { + exec([this] { + m_config.autoConfigure = true; + + removeAll<Seat>(); + + uint capabilities = MockCompositor::Seat::capability_pointer; + int version = 4; + add<Seat>(capabilities, version); + }); + } +}; + +class tst_seatv4 : public QObject, private SeatV4Compositor +{ + Q_OBJECT +private slots: + void cleanup(); + void bindsToSeat(); + void createsPointer(); + void setsCursorOnEnter(); + void usesEnterSerial(); + void mousePress(); + void simpleAxis_data(); + void simpleAxis(); + void invalidPointerEvents(); + void scaledCursor(); +}; + +void tst_seatv4::cleanup() +{ + QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); + QCOMPOSITOR_COMPARE(getAll<Output>().size(), 1); // No extra outputs left +} + +void tst_seatv4::bindsToSeat() +{ + QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().size(), 1); + QCOMPOSITOR_COMPARE(get<Seat>()->resourceMap().first()->version(), 4); +} + +void tst_seatv4::createsPointer() +{ + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().size(), 1); + QCOMPOSITOR_TRY_COMPARE(pointer()->resourceMap().first()->version(), 4); +} + +void tst_seatv4::setsCursorOnEnter() +{ + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); +} + +void tst_seatv4::usesEnterSerial() +{ + QSignalSpy setCursorSpy(exec([=] { return pointer(); }), &Pointer::setCursor); + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + uint enterSerial = exec([=] { + return pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); + }); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); + + QTRY_COMPARE(setCursorSpy.count(), 1); + QCOMPARE(setCursorSpy.takeFirst().at(0).toUInt(), enterSerial); +} + +void tst_seatv4::mousePress() +{ + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *) override { m_pressed = true; } + bool m_pressed = false; + }; + + Window window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([&] { + auto *surface = xdgSurface()->m_surface; + pointer()->sendEnter(surface, {32, 32}); + pointer()->sendButton(client(), BTN_LEFT, 1); + pointer()->sendButton(client(), BTN_LEFT, 0); + }); + QTRY_VERIFY(window.m_pressed); +} + +void tst_seatv4::simpleAxis_data() +{ + QTest::addColumn<uint>("axis"); + QTest::addColumn<qreal>("value"); + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<QPoint>("angleDelta"); + + // Directions in regular windows/linux terms (no "natural" scrolling) + QTest::newRow("down") << uint(Pointer::axis_vertical_scroll) << 1.0 << Qt::Vertical << QPoint{0, -12}; + QTest::newRow("up") << uint(Pointer::axis_vertical_scroll) << -1.0 << Qt::Vertical << QPoint{0, 12}; + QTest::newRow("left") << uint(Pointer::axis_horizontal_scroll) << 1.0 << Qt::Horizontal << QPoint{-12, 0}; + QTest::newRow("right") << uint(Pointer::axis_horizontal_scroll) << -1.0 << Qt::Horizontal << QPoint{12, 0}; + QTest::newRow("up big") << uint(Pointer::axis_vertical_scroll) << -10.0 << Qt::Vertical << QPoint{0, 120}; +} + +void tst_seatv4::simpleAxis() +{ + QFETCH(uint, axis); + QFETCH(qreal, value); + QFETCH(Qt::Orientation, orientation); + QFETCH(QPoint, angleDelta); + + class WheelWindow : QRasterWindow { + public: + explicit WheelWindow() + { + resize(64, 64); + show(); + } + void wheelEvent(QWheelEvent *event) override + { + QRasterWindow::wheelEvent(event); + // Angle delta should always be provided (says docs) + QVERIFY(!event->angleDelta().isNull()); + + // There are now scroll phases on Wayland prior to v5 + QCOMPARE(event->phase(), Qt::NoScrollPhase); + + // Pixel delta should only be set if we know it's a high-res input device (which we don't) + QCOMPARE(event->pixelDelta(), QPoint(0, 0)); + + // The axis vector of the event is already in surface space, so there is now way to tell + // whether it is inverted or not. + QCOMPARE(event->inverted(), false); + + // We didn't press any buttons + QCOMPARE(event->buttons(), Qt::NoButton); + + if (event->orientation() == Qt::Horizontal) + QCOMPARE(event->delta(), event->angleDelta().x()); + else + QCOMPARE(event->delta(), event->angleDelta().y()); + + // There has been no information about what created the event. + // Documentation says not synthesized is appropriate in such cases + QCOMPARE(event->source(), Qt::MouseEventNotSynthesized); + + m_events.append(Event(event->pixelDelta(), event->angleDelta(), event->orientation())); + } + struct Event // Because I didn't find a convenient way to copy it entirely + { + // TODO: Constructors can be removed when we start supporting brace-initializers + Event() = default; + Event(const QPoint &pixelDelta, const QPoint &angleDelta, Qt::Orientation orientation) + : pixelDelta(pixelDelta), angleDelta(angleDelta), orientation(orientation) + {} + const QPoint pixelDelta; + const QPoint angleDelta; // eights of a degree, positive is upwards, left + const Qt::Orientation orientation{}; + }; + QVector<Event> m_events; + }; + + WheelWindow window; + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + Surface *surface = xdgSurface()->m_surface; + pointer()->sendEnter(surface, {32, 32}); + wl_client *client = surface->resource()->client(); + // Length of vector in surface-local space. i.e. positive is downwards + pointer()->sendAxis( + client, + Pointer::axis(axis), + value // Length of vector in surface-local space. i.e. positive is downwards + ); + }); + + QTRY_COMPARE(window.m_events.size(), 1); + auto event = window.m_events.takeFirst(); + QCOMPARE(event.angleDelta, angleDelta); + QCOMPARE(event.orientation, orientation); +} + +void tst_seatv4::invalidPointerEvents() +{ + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { + auto *p = pointer(); + auto *c = client(); + // Purposefully send events without a wl_pointer.enter + p->sendMotion(c, {32, 32}); + p->sendButton(c, BTN_LEFT, Pointer::button_state_pressed); + p->sendAxis(c, Pointer::axis_vertical_scroll, 1.0); + }); + + // Make sure we get here without crashing + xdgPingAndWaitForPong(); +} + +void tst_seatv4::scaledCursor() +{ + QSKIP("Currently broken and should be fixed"); + // Add a highdpi output + exec([&] { + int scale = 2; + add<Output>(scale); + }); + + QRasterWindow window; + window.resize(64, 64); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgSurface() && xdgSurface()->m_committedConfigureSerial); + + exec([=] { pointer()->sendEnter(xdgSurface()->m_surface, {32, 32}); }); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()); + QCOMPOSITOR_TRY_VERIFY(pointer()->cursorSurface()->m_committed.buffer); + QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.bufferScale, 1); + QSize unscaledPixelSize = exec([=] { + return pointer()->cursorSurface()->m_committed.buffer->size(); + }); + + exec([=] { + auto *surface = pointer()->cursorSurface(); + surface->sendEnter(getAll<Output>()[1]); + surface->sendLeave(getAll<Output>()[0]); + }); + + QCOMPOSITOR_TRY_COMPARE(pointer()->cursorSurface()->m_committed.buffer->size(), unscaledPixelSize * 2); + + // Remove the extra output to clean up for the next test + exec([&] { remove(getAll<Output>()[1]); }); +} + +QCOMPOSITOR_TEST_MAIN(tst_seatv4) +#include "tst_seatv4.moc" diff --git a/tests/auto/client/shared/corecompositor.cpp b/tests/auto/client/shared/corecompositor.cpp new file mode 100644 index 000000000..afa25e94c --- /dev/null +++ b/tests/auto/client/shared/corecompositor.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "corecompositor.h" + +namespace MockCompositor { + +CoreCompositor::CoreCompositor() + : m_display(wl_display_create()) + , m_socketName(wl_display_add_socket_auto(m_display)) + , m_eventLoop(wl_display_get_event_loop(m_display)) + + // Start dispatching + , m_dispatchThread([this](){ + while (m_running) { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + dispatch(); + } + }) +{ + m_timer.start(); + Q_ASSERT(isClean()); +} + +CoreCompositor::~CoreCompositor() +{ + m_running = false; + m_dispatchThread.join(); + wl_display_destroy(m_display); +} + +bool CoreCompositor::isClean() +{ + Lock lock(this); + for (auto *global : qAsConst(m_globals)) { + if (!global->isClean()) + return false; + } + return true; +} + +QString CoreCompositor::dirtyMessage() +{ + Lock lock(this); + QStringList messages; + for (auto *global : qAsConst(m_globals)) { + if (!global->isClean()) + messages << (global->metaObject()->className() % QLatin1String(": ") % global->dirtyMessage()); + } + return messages.join(", "); +} + +void CoreCompositor::dispatch() +{ + Lock lock(this); + wl_display_flush_clients(m_display); + constexpr int timeout = 0; // immediate return + wl_event_loop_dispatch(m_eventLoop, timeout); +} + +/*! + * \brief Adds a new global interface for the compositor + * + * Takes ownership of \a global + */ +void CoreCompositor::add(Global *global) +{ + warnIfNotLockedByThread(Q_FUNC_INFO); + m_globals.append(global); +} + +void CoreCompositor::remove(Global *global) +{ + warnIfNotLockedByThread(Q_FUNC_INFO); + //TODO: Need to delete global as well! + m_globals.removeAll(global); +} + +uint CoreCompositor::nextSerial() +{ + warnIfNotLockedByThread(Q_FUNC_INFO); + return wl_display_next_serial(m_display); +} + +uint CoreCompositor::currentTimeMilliseconds() +{ + warnIfNotLockedByThread(Q_FUNC_INFO); + return uint(m_timer.elapsed()); +} + +wl_client *CoreCompositor::client(int index) +{ + warnIfNotLockedByThread(Q_FUNC_INFO); + wl_list *clients = wl_display_get_client_list(m_display); + wl_client *client = nullptr; + int i = 0; + wl_client_for_each(client, clients) { + if (i++ == index) + return client; + } + return nullptr; +} + +void CoreCompositor::warnIfNotLockedByThread(const char *caller) +{ + if (!m_lock || !m_lock->isOwnedByCurrentThread()) { + qWarning() << caller << "called without locking the compositor to the current thread." + << "This means the compositor can start dispatching at any moment," + << "potentially leading to threading issues." + << "Unless you know what you are doing you should probably fix the test" + << "by locking the compositor before accessing it (see mutex())."; + } +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/corecompositor.h b/tests/auto/client/shared/corecompositor.h new file mode 100644 index 000000000..875b7d050 --- /dev/null +++ b/tests/auto/client/shared/corecompositor.h @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKCOMPOSITOR_CORECOMPOSITOR_H +#define MOCKCOMPOSITOR_CORECOMPOSITOR_H + +#include <QtTest/QtTest> + +#include <wayland-server-core.h> + +struct wl_resource; + +namespace MockCompositor { + +class Global : public QObject +{ + Q_OBJECT +public: + virtual bool isClean() { return true; } + virtual QString dirtyMessage() { return isClean() ? "clean" : "dirty"; } +}; + +class CoreCompositor +{ +public: + explicit CoreCompositor(); + ~CoreCompositor(); + bool isClean(); + QString dirtyMessage(); + void dispatch(); + + template<typename function_type, typename... arg_types> + auto exec(function_type func, arg_types&&... args) -> decltype(func()) + { + Lock lock(this); + return func(std::forward<arg_types>(args)...); + } + + template<typename function_type, typename... arg_types> + auto call(function_type func, arg_types&&... args) -> decltype(func()) + { + Lock lock(this); + auto boundFunc = std::bind(func, this); + return boundFunc(this, std::forward<arg_types>(args)...); + } + + // Unsafe section below, YOU are responsible that the compositor is locked or + // this is run through the mutex() method! + + void add(Global *global); + void remove(Global *global); + + /*! + * \brief Constructs and adds a new global with the given parameters + * + * Convenience function. i.e. + * + * compositor->add(new MyGlobal(compositor, version); + * + * can be written as: + * + * compositor->add<MyGlobal>(version); + * + * Returns the new global + */ + template<typename global_type, typename... arg_types> + global_type *add(arg_types&&... args) + { + warnIfNotLockedByThread(Q_FUNC_INFO); + auto *global = new global_type(this, std::forward<arg_types>(args)...); + m_globals.append(global); + return global; + } + + /*! + * \brief Removes all globals of the given type + * + * Convenience function + */ + template<typename global_type, typename... arg_types> + void removeAll() + { + const auto globals = getAll<global_type>(); + for (auto global : globals) + remove(global); + } + + /*! + * \brief Returns a global with the given type, if any + */ + template<typename global_type> + global_type *get() + { + warnIfNotLockedByThread(Q_FUNC_INFO); + for (auto *global : qAsConst(m_globals)) { + if (auto *casted = qobject_cast<global_type *>(global)) + return casted; + } + return nullptr; + } + + /*! + * \brief Returns all globals with the given type, if any + */ + template<typename global_type> + QVector<global_type *> getAll() + { + warnIfNotLockedByThread(Q_FUNC_INFO); + QVector<global_type *> matching; + for (auto *global : qAsConst(m_globals)) { + if (auto *casted = qobject_cast<global_type *>(global)) + matching.append(casted); + } + return matching; + } + + uint nextSerial(); + uint currentTimeMilliseconds(); + wl_client *client(int index = 0); + void warnIfNotLockedByThread(const char* caller = "warnIfNotLockedbyThread"); + +public: + // Only use this carefully from the test thread (i.e. lock first) + wl_display *m_display = nullptr; +protected: + class Lock { + public: + explicit Lock(CoreCompositor *compositor) + : m_compositor(compositor) + , m_threadId(std::this_thread::get_id()) + { + // Can't use a QMutexLocker here, as it's not movable + compositor->m_mutex.lock(); + Q_ASSERT(compositor->m_lock == nullptr); + compositor->m_lock = this; + } + ~Lock() + { + Q_ASSERT(m_compositor->m_lock == this); + m_compositor->m_lock = nullptr; + m_compositor->m_mutex.unlock(); + } + + // Move semantics + Lock(Lock &&) = default; + Lock &operator=(Lock &&) = default; + + // Disable copying + Lock(const Lock &) = delete; + Lock &operator=(const Lock &) = delete; + + bool isOwnedByCurrentThread() const { return m_threadId == std::this_thread::get_id(); } + private: + CoreCompositor *m_compositor = nullptr; + std::thread::id m_threadId; + }; + QByteArray m_socketName; + wl_event_loop *m_eventLoop = nullptr; + bool m_running = true; + QVector<Global *> m_globals; + QElapsedTimer m_timer; + +private: + Lock *m_lock = nullptr; + QMutex m_mutex; + std::thread m_dispatchThread; +}; + +template<typename container_type> +QByteArray toByteArray(container_type container) +{ + return QByteArray(reinterpret_cast<const char *>(container.data()), sizeof (container[0]) * container.size()); +} + +template<typename return_type> +return_type *fromResource(::wl_resource *resource) { + if (auto *r = return_type::Resource::fromResource(resource)) + return static_cast<return_type *>(r->object()); + return nullptr; +} + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_CORECOMPOSITOR_H diff --git a/tests/auto/client/shared/coreprotocol.cpp b/tests/auto/client/shared/coreprotocol.cpp new file mode 100644 index 000000000..46d46d980 --- /dev/null +++ b/tests/auto/client/shared/coreprotocol.cpp @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "coreprotocol.h" + +namespace MockCompositor { + +void Surface::sendFrameCallbacks() +{ + uint time = m_wlCompositor->m_compositor->currentTimeMilliseconds(); + for (auto *callback : m_waitingFrameCallbacks) + callback->sendDone(time); + m_waitingFrameCallbacks.clear(); +} + +void Surface::sendEnter(Output *output) +{ + m_outputs.append(output); + const auto outputResources = output->resourceMap().values(resource()->client()); + for (auto outputResource: outputResources) + wl_surface::send_enter(resource()->handle, outputResource->handle); +} + +void Surface::sendLeave(Output *output) +{ + m_outputs.removeOne(output); + const auto outputResources = output->resourceMap().values(resource()->client()); + for (auto outputResource: outputResources) + wl_surface::send_leave(resource()->handle, outputResource->handle); +} + +void Surface::surface_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + for (auto *commit : m_commits) + delete commit->commitSpecific.frame; + bool removed = m_wlCompositor->m_surfaces.removeOne(this); + Q_ASSERT(removed); + delete this; +} + +void Surface::surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y) +{ + Q_UNUSED(resource); + QPoint offset(x, y); + m_pending.buffer = fromResource<Buffer>(buffer); + m_pending.commitSpecific.attachOffset = offset; + m_pending.commitSpecific.attached = true; + emit attach(buffer, offset); +} + +void Surface::surface_set_buffer_scale(QtWaylandServer::wl_surface::Resource *resource, int32_t scale) +{ + Q_UNUSED(resource); + m_pending.bufferScale = scale; +} + +void Surface::surface_commit(Resource *resource) +{ + Q_UNUSED(resource); + m_committed = m_pending; + m_commits.append(new DoubleBufferedState(m_committed)); + + if (auto *frame = m_pending.commitSpecific.frame) + m_waitingFrameCallbacks.append(frame); + + m_pending.commitSpecific = PerCommitData(); + emit commit(); + if (m_committed.commitSpecific.attached) + emit bufferCommitted(); +} + +void Surface::surface_frame(Resource *resource, uint32_t callback) +{ + // Although valid, there is really no point having multiple frame requests in the same commit. + // Make sure we don't do it + QCOMPARE(m_pending.commitSpecific.frame, nullptr); + + auto *frame = new Callback(resource->client(), callback, 1); + m_pending.commitSpecific.frame = frame; +} + +bool WlCompositor::isClean() { + for (auto *surface : qAsConst(m_surfaces)) { + if (!CursorRole::fromSurface(surface)) + return false; + } + return true; +} + +QString WlCompositor::dirtyMessage() +{ + if (isClean()) + return "clean"; + QStringList messages; + for (auto *s : qAsConst(m_surfaces)) { + QString role = s->m_role ? s->m_role->staticMetaObject.className(): "none/unknown"; + messages << "Surface with role: " + role; + } + return "Dirty, surfaces left:\n\t" + messages.join("\n\t"); +} + +void Output::sendScale(int factor) +{ + Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION); + m_scale = factor; + const auto resources = resourceMap().values(); + for (auto r: resources) + wl_output::send_scale(r->handle, factor); +} + +void Output::sendDone() +{ + Q_ASSERT(m_version >= WL_OUTPUT_DONE_SINCE_VERSION); + const auto resources = resourceMap().values(); + for (auto r: resources) + wl_output::send_done(r->handle); +} + +void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource) +{ + if (m_version >= WL_OUTPUT_SCALE_SINCE_VERSION) + wl_output::send_scale(resource->handle, m_scale); + //TODO: send other required stuff as well + if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output::send_done(resource->handle); +} + +// Seat stuff +Seat::Seat(CoreCompositor *compositor, uint capabilities, int version) //TODO: check version + : QtWaylandServer::wl_seat(compositor->m_display, version) + , m_compositor(compositor) +{ + setCapabilities(capabilities); +} + +Seat::~Seat() +{ + qDeleteAll(m_oldPointers); + delete m_pointer; +} + +void Seat::setCapabilities(uint capabilities) { + // TODO: Add support for touch and keyboard + Q_ASSERT(capabilities == 0 || capabilities == capability_pointer); + + m_capabilities = capabilities; + + if (m_capabilities & capability_pointer) { + if (!m_pointer) + m_pointer = (new Pointer(this)); + } else if (m_pointer) { + m_oldPointers << m_pointer; + m_pointer = nullptr; + } + + for (auto *resource : resourceMap()) + wl_seat::send_capabilities(resource->handle, capabilities); +} + +void Seat::seat_get_pointer(Resource *resource, uint32_t id) +{ + if (~m_capabilities & capability_pointer) { + qWarning() << "Client requested a wl_pointer without the capability being available." + << "This Could be a race condition when hotunplugging," + << "but is most likely a client error"; + Pointer *pointer = new Pointer(this); + pointer->add(resource->client(), id, resource->version()); + // TODO: mark as destroyed + m_oldPointers << pointer; + return; + } + m_pointer->add(resource->client(), id, resource->version()); +} + +Surface *Pointer::cursorSurface() +{ + return m_cursorRole ? m_cursorRole->m_surface : nullptr; +} + +uint Pointer::sendEnter(Surface *surface, const QPointF &position) +{ + wl_fixed_t x = wl_fixed_from_double(position.x()); + wl_fixed_t y = wl_fixed_from_double(position.y()); + m_enterSerial = m_seat->m_compositor->nextSerial(); + + wl_client *client = surface->resource()->client(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_enter(r->handle, m_enterSerial, surface->resource()->handle, x ,y); + return m_enterSerial; +} + +// Make sure you call enter, frame etc. first +void Pointer::sendMotion(wl_client *client, const QPointF &position) +{ + wl_fixed_t x = wl_fixed_from_double(position.x()); + wl_fixed_t y = wl_fixed_from_double(position.y()); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_motion(r->handle, time, x, y); +} + +// Make sure you call enter, frame etc. first +uint Pointer::sendButton(wl_client *client, uint button, uint state) +{ + Q_ASSERT(state == button_state_pressed || state == button_state_released); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + uint serial = m_seat->m_compositor->nextSerial(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_button(r->handle, serial, time, button, state); + return serial; +} + +// Make sure you call enter, frame etc. first +void Pointer::sendAxis(wl_client *client, axis axis, qreal value) +{ + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + wl_fixed_t val = wl_fixed_from_double(value); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis(r->handle, time, axis, val); +} + +void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) +{ + Q_UNUSED(resource); + Q_UNUSED(hotspot_x); + Q_UNUSED(hotspot_y); + auto *s = fromResource<Surface>(surface); + QVERIFY(s); + + if (s->m_role) { + auto *cursorRole = CursorRole::fromSurface(s); + QVERIFY(cursorRole); + QVERIFY(cursorRole == m_cursorRole); + } else { + m_cursorRole = new CursorRole(s); //TODO: make sure we don't leak CursorRole + s->m_role = m_cursorRole; + } +// QCOMPARE(serial, m_enterSerial); //TODO: uncomment when this bug is fixed + emit setCursor(serial); +} + +// Shm implementation +Shm::Shm(CoreCompositor *compositor, QVector<format> formats, int version) + : QtWaylandServer::wl_shm(compositor->m_display, version) + , m_compositor(compositor) + , m_formats(formats) +{ + // Some formats are specified as mandatory + Q_ASSERT(m_formats.contains(format_argb8888)); + Q_ASSERT(m_formats.contains(format_xrgb8888)); +} + +bool Shm::isClean() +{ +// for (ShmPool *pool : qAsConst(m_pools)) { +// //TODO: return false if not cursor buffer +// if (pool->m_buffers.isEmpty()) { +// return false; +// } +// } + return true; +} + +void Shm::shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) +{ + Q_UNUSED(fd); + Q_UNUSED(size); + auto *pool = new ShmPool(this, resource->client(), id, 1); + m_pools.append(pool); +} + +ShmPool::ShmPool(Shm *shm, wl_client *client, int id, int version) + : QtWaylandServer::wl_shm_pool(client, id, version) + , m_shm(shm) +{ +} + +void ShmPool::shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) +{ + QSize size(width, height); + new ShmBuffer(offset, size, stride, Shm::format(format), resource->client(), id); +} + +void ShmPool::shm_pool_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + bool removed = m_shm->m_pools.removeOne(this); + Q_ASSERT(removed); + delete this; +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h new file mode 100644 index 000000000..2fbe9b139 --- /dev/null +++ b/tests/auto/client/shared/coreprotocol.h @@ -0,0 +1,313 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKCOMPOSITOR_COREPROTOCOL_H +#define MOCKCOMPOSITOR_COREPROTOCOL_H + +#include "corecompositor.h" + +#include <qwayland-server-wayland.h> + +namespace MockCompositor { + +class WlCompositor; +class Output; +class Pointer; +class CursorRole; +class ShmPool; +class ShmBuffer; + +class Buffer : public QObject, public QtWaylandServer::wl_buffer +{ + Q_OBJECT +public: + explicit Buffer(wl_client *client, int id, int version) + : QtWaylandServer::wl_buffer(client, id, version) + { + } + virtual QSize size() const = 0; + bool m_destroyed = false; + +protected: + void buffer_destroy_resource(Resource *resource) override + { + Q_UNUSED(resource); + m_destroyed = true; + // The client side resource has been destroyed, but we keep this object because it may be + // be used as a reference by e.g. surface for the currently committed buffer so it's not + // yet safe to free it. + + //TODO: The memory should be freed by its factory + } + void buffer_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); } +}; + +class Callback : public QObject, public QtWaylandServer::wl_callback +{ + Q_OBJECT +public: + explicit Callback(wl_client *client, int id, int version = 1) + : QtWaylandServer::wl_callback(client, id, version) + { + } + ~Callback() override { if (!m_destroyed) wl_resource_destroy(resource()->handle); } + void send_done(uint32_t data) = delete; // use state-tracking method below instead + void sendDone(uint data) { Q_ASSERT(!m_done); QtWaylandServer::wl_callback::send_done(data); m_done = true; } + void sendDoneAndDestroy(uint data) { sendDone(data); wl_resource_destroy(resource()->handle); } + bool m_done = false; + bool m_destroyed = false; +protected: + void callback_destroy_resource(Resource *resource) override { Q_UNUSED(resource); m_destroyed = true; } +}; + +class SurfaceRole : public QObject { + Q_OBJECT +}; + +class Surface : public QObject, public QtWaylandServer::wl_surface +{ + Q_OBJECT +public: + explicit Surface(WlCompositor *wlCompositor, wl_client *client, int id, int version) + : QtWaylandServer::wl_surface(client, id, version) + , m_wlCompositor(wlCompositor) + { + } + ~Surface() override { qDeleteAll(m_commits); } // TODO: maybe make sure buffers are released? + void sendFrameCallbacks(); + void sendEnter(Output *output); + void send_enter(::wl_resource *output) = delete; + void sendLeave(Output *output); + void send_leave(::wl_resource *output) = delete; + + WlCompositor *m_wlCompositor; + struct PerCommitData { + Callback *frame = nullptr; + QPoint attachOffset; + bool attached = false; + }; + struct DoubleBufferedState { + PerCommitData commitSpecific; + Buffer *buffer = nullptr; + uint configureSerial = 0; + int bufferScale = 1; + } m_pending, m_committed; + QVector<DoubleBufferedState *> m_commits; + QVector<Callback *> m_waitingFrameCallbacks; + QVector<Output *> m_outputs; + SurfaceRole *m_role = nullptr; + +signals: + void attach(void *buffer, QPoint offset); + void commit(); + void bufferCommitted(); + +protected: + void surface_destroy_resource(Resource *resource) override; + void surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); } + void surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y) override; + void surface_set_buffer_scale(Resource *resource, int32_t scale) override; + void surface_commit(Resource *resource) override; + void surface_frame(Resource *resource, uint32_t callback) override; +}; + +class WlCompositor : public Global, public QtWaylandServer::wl_compositor +{ + Q_OBJECT +public: + explicit WlCompositor(CoreCompositor *compositor, int version = 3) + : QtWaylandServer::wl_compositor(compositor->m_display, version) + , m_compositor(compositor) + {} + bool isClean() override; + QString dirtyMessage() override; + QVector<Surface *> m_surfaces; + CoreCompositor *m_compositor = nullptr; + +signals: + void surfaceCreated(Surface *surface); + +protected: + void compositor_create_surface(Resource *resource, uint32_t id) override + { + auto *surface = new Surface(this, resource->client(), id, resource->version()); + m_surfaces.append(surface); + emit surfaceCreated(surface); + } +}; + +class SubCompositor : public Global, public QtWaylandServer::wl_subcompositor +{ + Q_OBJECT +public: + explicit SubCompositor(CoreCompositor *compositor, int version = 1) + : QtWaylandServer::wl_subcompositor(compositor->m_display, version) + {} + // TODO +}; + +class Output : public Global, public QtWaylandServer::wl_output +{ + Q_OBJECT +public: + explicit Output(CoreCompositor *compositor, int scale = 1, int version = 2) + : QtWaylandServer::wl_output(compositor->m_display, version) + , m_scale(scale) + , m_version(version) + {} + void sendScale(int factor); + void send_scale(int32_t factor) = delete; + void send_scale(struct ::wl_resource *resource, int32_t factor) = delete; + void sendDone(); + int m_scale = 1; + int m_version = 1; // TODO: remove on libwayland upgrade + +protected: + void output_bind_resource(Resource *resource) override; +}; + +class Seat : public Global, public QtWaylandServer::wl_seat +{ + Q_OBJECT +public: + explicit Seat(CoreCompositor *compositor, uint capabilities, int version = 4); + ~Seat() override; + void send_capabilities(Resource *resource, uint capabilities) = delete; // Use wrapper instead + void send_capabilities(uint capabilities) = delete; // Use wrapper instead + void setCapabilities(uint capabilities); + + CoreCompositor *m_compositor = nullptr; + + Pointer* m_pointer = nullptr; + QVector<Pointer *> m_oldPointers; + + uint m_capabilities = 0; + +protected: + void seat_bind_resource(Resource *resource) override + { + wl_seat::send_capabilities(resource->handle, m_capabilities); + } + + void seat_get_pointer(Resource *resource, uint32_t id) override; +// void seat_get_keyboard(Resource *resource, uint32_t id) override; +// void seat_get_touch(Resource *resource, uint32_t id) override; + +// void seat_release(Resource *resource) override; +}; + +class Pointer : public QObject, public QtWaylandServer::wl_pointer +{ + Q_OBJECT +public: + explicit Pointer(Seat *seat) : m_seat(seat) {} + Surface *cursorSurface(); + CursorRole* m_cursorRole = nullptr; //TODO: cleanup + uint sendEnter(Surface *surface, const QPointF &position); + void sendMotion(wl_client *client, const QPointF &position); + uint sendButton(wl_client *client, uint button, uint state); + void sendAxis(wl_client *client, axis axis, qreal value); + + Seat *m_seat = nullptr; + uint m_enterSerial = 0; + +signals: + void setCursor(uint serial); //TODO: add arguments? + +protected: + void pointer_set_cursor(Resource *resource, uint32_t serial, ::wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) override; + //TODO +}; + +class CursorRole : public SurfaceRole { + Q_OBJECT +public: + explicit CursorRole(Surface *surface) // TODO: needs some more args + : m_surface(surface) + { + } + static CursorRole *fromSurface(Surface *surface) { return qobject_cast<CursorRole *>(surface->m_role); } + Surface *m_surface = nullptr; +}; + +class Shm : public Global, public QtWaylandServer::wl_shm +{ + Q_OBJECT +public: + explicit Shm(CoreCompositor *compositor, QVector<format> formats = {format_argb8888, format_xrgb8888, format_rgb888}, int version = 1); + bool isClean() override; + CoreCompositor *m_compositor = nullptr; + QVector<ShmPool *> m_pools; + const QVector<format> m_formats; + +protected: + void shm_create_pool(Resource *resource, uint32_t id, int32_t fd, int32_t size) override; + void shm_bind_resource(Resource *resource) override + { + for (auto format : qAsConst(m_formats)) + send_format(resource->handle, format); + } +}; + +class ShmPool : QObject, public QtWaylandServer::wl_shm_pool +{ + Q_OBJECT +public: + explicit ShmPool(Shm *shm, wl_client *client, int id, int version = 1); + Shm *m_shm = nullptr; + QVector<ShmBuffer *> m_buffers; + +protected: + void shm_pool_create_buffer(Resource *resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) override; + void shm_pool_destroy_resource(Resource *resource) override; + void shm_pool_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); } +}; + +class ShmBuffer : public Buffer +{ + Q_OBJECT +public: + static ShmBuffer *fromBuffer(Buffer *buffer) { return qobject_cast<ShmBuffer *>(buffer); } + explicit ShmBuffer(int offset, const QSize &size, int stride, Shm::format format, wl_client *client, int id, int version = 1) + : Buffer(client, id, version) + , m_offset(offset) + , m_size(size) + , m_stride(stride) + , m_format(format) + { + } + QSize size() const override { return m_size; } + const int m_offset; + const QSize m_size; + const int m_stride; + const Shm::format m_format; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_COREPROTOCOL_H diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp index 797c05c44..45d62a153 100644 --- a/tests/auto/client/shared/mockcompositor.cpp +++ b/tests/auto/client/shared/mockcompositor.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -27,477 +27,64 @@ ****************************************************************************/ #include "mockcompositor.h" -#include "mockinput.h" -#include "mockoutput.h" -#include "mocksurface.h" -#include "mockwlshell.h" -#include "mockxdgshellv6.h" -#include "mockiviapplication.h" -#include <wayland-xdg-shell-unstable-v6-server-protocol.h> - -#include <stdio.h> -MockCompositor::MockCompositor() -{ - pthread_create(&m_thread, 0, run, this); - - m_mutex.lock(); - m_waitCondition.wait(&m_mutex); - m_mutex.unlock(); -} - -MockCompositor::~MockCompositor() -{ - m_alive = false; - m_waitCondition.wakeOne(); - pthread_join(m_thread, 0); -} - -void MockCompositor::lock() -{ - m_mutex.lock(); -} - -void MockCompositor::unlock() -{ - m_mutex.unlock(); -} - -void MockCompositor::applicationInitialized() -{ - m_ready = true; -} - -int MockCompositor::waylandFileDescriptor() const -{ - return m_compositor->fileDescriptor(); -} - -void MockCompositor::processWaylandEvents() -{ - m_waitCondition.wakeOne(); -} - -void MockCompositor::setOutputMode(const QSize &size) -{ - Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor); - command.parameters << size; - processCommand(command); -} - -void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos) -{ - Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor); - command.parameters << QVariant::fromValue(surface) << pos; - processCommand(command); -} - -void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code) -{ - Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor); - command.parameters << QVariant::fromValue(surface) << code; - processCommand(command); -} - -void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code) -{ - Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor); - command.parameters << QVariant::fromValue(surface) << code; - processCommand(command); -} - -void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) -{ - Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor); - command.parameters << QVariant::fromValue(surface) << position << id; - processCommand(command); -} - -void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) -{ - Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor); - command.parameters << QVariant::fromValue(surface) << position << id; - processCommand(command); -} - -void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id) -{ - Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor); - command.parameters << QVariant::fromValue(surface) << id; - processCommand(command); -} - -void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position) -{ - Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor); - command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position); - processCommand(command); -} - -void MockCompositor::sendDataDeviceMotion(const QPoint &position) -{ - Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor); - command.parameters << QVariant::fromValue(position); - processCommand(command); -} - -void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface) -{ - Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor); - command.parameters << QVariant::fromValue(surface); - processCommand(command); -} - -void MockCompositor::sendAddOutput() -{ - Command command = makeCommand(Impl::Compositor::sendAddOutput, m_compositor); - processCommand(command); -} - -void MockCompositor::sendRemoveOutput(const QSharedPointer<MockOutput> &output) -{ - Command command = makeCommand(Impl::Compositor::sendRemoveOutput, m_compositor); - command.parameters << QVariant::fromValue(output); - processCommand(command); -} - -void MockCompositor::sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry) -{ - Command command = makeCommand(Impl::Compositor::sendOutputGeometry, m_compositor); - command.parameters << QVariant::fromValue(output); - command.parameters << QVariant::fromValue(geometry); - processCommand(command); -} - -void MockCompositor::sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output) -{ - Command command = makeCommand(Impl::Compositor::sendSurfaceEnter, m_compositor); - command.parameters << QVariant::fromValue(surface); - command.parameters << QVariant::fromValue(output); - processCommand(command); -} - -void MockCompositor::sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output) -{ - Command command = makeCommand(Impl::Compositor::sendSurfaceLeave, m_compositor); - command.parameters << QVariant::fromValue(surface); - command.parameters << QVariant::fromValue(output); - processCommand(command); -} - -void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size) -{ - Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor); - command.parameters << QVariant::fromValue(surface); - command.parameters << QVariant::fromValue(size); - processCommand(command); -} - -void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size) -{ - Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor); - command.parameters << QVariant::fromValue(iviSurface); - command.parameters << QVariant::fromValue(size); - processCommand(command); -} - -void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states) -{ - Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor); - command.parameters << QVariant::fromValue(toplevel); - command.parameters << QVariant::fromValue(size); - QByteArray statesBytes(reinterpret_cast<const char *>(states.data()), - states.size() * static_cast<int>(sizeof(uint))); - command.parameters << statesBytes; - processCommand(command); -} - -void MockCompositor::waitForStartDrag() -{ - Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor); - processCommand(command); -} - -QSharedPointer<MockSurface> MockCompositor::surface() -{ - QSharedPointer<MockSurface> result; - lock(); - QVector<Impl::Surface *> surfaces = m_compositor->surfaces(); - foreach (Impl::Surface *surface, surfaces) { - // we don't want to mistake the cursor surface for a window surface - if (surface->isMapped()) { - result = surface->mockSurface(); - break; - } - } - unlock(); - return result; -} - -QSharedPointer<MockOutput> MockCompositor::output(int index) -{ - QSharedPointer<MockOutput> result; - lock(); - if (Impl::Output *output = m_compositor->outputs().value(index, nullptr)) - result = output->mockOutput(); - unlock(); - return result; -} - -QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index) -{ - QSharedPointer<MockIviSurface> result; - lock(); - if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr)) - result = toplevel->mockIviSurface(); - unlock(); - return result; -} - -QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index) -{ - QSharedPointer<MockXdgToplevelV6> result; - lock(); - if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr)) - result = toplevel->mockToplevel(); - unlock(); - return result; -} - -MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target) -{ - Command command; - command.callback = callback; - command.target = target; - return command; -} - -void MockCompositor::processCommand(const Command &command) -{ - lock(); - m_commandQueue << command; - unlock(); - - m_waitCondition.wakeOne(); -} - -void MockCompositor::dispatchCommands() -{ - lock(); - int count = m_commandQueue.length(); - unlock(); - - for (int i = 0; i < count; ++i) { - lock(); - const Command command = m_commandQueue.takeFirst(); - unlock(); - command.callback(command.target, command.parameters); - } -} - -void *MockCompositor::run(void *data) -{ - MockCompositor *controller = static_cast<MockCompositor *>(data); - - Impl::Compositor compositor; - - controller->m_compositor = &compositor; - controller->m_waitCondition.wakeOne(); - - while (!controller->m_ready) { - controller->dispatchCommands(); - compositor.dispatchEvents(20); - } - - while (controller->m_alive) { - { - QMutexLocker locker(&controller->m_mutex); - if (controller->m_commandQueue.isEmpty()) - controller->m_waitCondition.wait(&controller->m_mutex); - } - controller->dispatchCommands(); - compositor.dispatchEvents(20); - } - - return 0; -} - -namespace Impl { - -Compositor::Compositor() - : m_display(wl_display_create()) -{ - if (wl_display_add_socket(m_display, 0)) { - fprintf(stderr, "Fatal: Failed to open server socket\n"); - exit(EXIT_FAILURE); +namespace MockCompositor { + +DefaultCompositor::DefaultCompositor() +{ + { + Lock l(this); + + // Globals: Should ideally always be at least the latest versions we support. + // Legacy versions can override in separate tests by removing and adding. + add<WlCompositor>(); + add<SubCompositor>(); + add<Output>(); + add<Seat>(Seat::capability_pointer); + add<XdgWmBase>(); + add<Shm>(); + // TODO: other shells, viewporter, xdgoutput etc + + QObject::connect(get<WlCompositor>(), &WlCompositor::surfaceCreated, [&] (Surface *surface){ + QObject::connect(surface, &Surface::bufferCommitted, [=] { + if (m_config.autoRelease) { + // Pretend we made a copy of the buffer and just release it immediately + surface->m_committed.buffer->send_release(); + } + if (m_config.autoEnter && surface->m_outputs.empty()) + surface->sendEnter(get<Output>()); + wl_display_flush_clients(m_display); + }); + }); + + QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, [&] (XdgToplevel *toplevel) { + // Needed because lambdas don't support Qt::DirectConnection + exec([&]{ + if (m_config.autoConfigure) + toplevel->sendCompleteConfigure(); + }); + }); } - - wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); - - m_data_device_manager.reset(new DataDeviceManager(this, m_display)); - - wl_display_init_shm(m_display); - - m_seat.reset(new Seat(this, m_display)); - m_pointer = m_seat->pointer(); - m_keyboard = m_seat->keyboard(); - m_touch = m_seat->touch(); - - m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0))); - m_iviApplication.reset(new IviApplication(m_display)); - m_wlShell.reset(new WlShell(m_display)); - m_xdgShellV6.reset(new XdgShellV6(m_display)); - - m_loop = wl_display_get_event_loop(m_display); - m_fd = wl_event_loop_get_fd(m_loop); -} - -Compositor::~Compositor() -{ - wl_display_destroy(m_display); + Q_ASSERT(isClean()); } -void Compositor::dispatchEvents(int timeout) +uint DefaultCompositor::sendXdgShellPing() { - wl_display_flush_clients(m_display); - wl_event_loop_dispatch(m_loop, timeout); + warnIfNotLockedByThread(Q_FUNC_INFO); + uint serial = nextSerial(); + auto *base = get<XdgWmBase>(); + const auto resourceMap = base->resourceMap(); + Q_ASSERT(resourceMap.size() == 1); // binding more than once shouldn't be needed + base->send_ping(resourceMap.first()->handle, serial); + return serial; } -static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id) +void DefaultCompositor::xdgPingAndWaitForPong() { - Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource)); - compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor)); + QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); + uint serial = exec([=] { return sendXdgShellPing(); }); + QTRY_COMPARE(pongSpy.count(), 1); + QTRY_COMPARE(pongSpy.first().at(0).toUInt(), serial); } -static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id) -{ - Q_UNUSED(client); - Q_UNUSED(compositorResource); - Q_UNUSED(id); -} - -void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id) -{ - static const struct wl_compositor_interface compositorInterface = { - compositor_create_surface, - compositor_create_region - }; - - wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id); - wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr); -} - -static void unregisterResourceCallback(wl_listener *listener, void *data) -{ - struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data); - wl_list_remove(wl_resource_get_link(resource)); - delete listener; -} - -void registerResource(wl_list *list, wl_resource *resource) -{ - wl_list_insert(list, wl_resource_get_link(resource)); - - wl_listener *listener = new wl_listener; - listener->notify = unregisterResourceCallback; - - wl_resource_add_destroy_listener(resource, listener); -} - -QVector<Surface *> Compositor::surfaces() const -{ - return m_surfaces; -} - -QVector<Output *> Compositor::outputs() const -{ - return m_outputs; -} - -IviApplication *Compositor::iviApplication() const -{ - return m_iviApplication.data(); -} - -XdgShellV6 *Compositor::xdgShellV6() const -{ - return m_xdgShellV6.data(); -} - -uint32_t Compositor::nextSerial() -{ - return wl_display_next_serial(m_display); -} - -void Compositor::addSurface(Surface *surface) -{ - m_surfaces << surface; -} - -void Compositor::removeSurface(Surface *surface) -{ - m_surfaces.removeOne(surface); - m_keyboard->handleSurfaceDestroyed(surface); - m_pointer->handleSurfaceDestroyed(surface); -} - -Surface *Compositor::resolveSurface(const QVariant &v) -{ - QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >(); - return mockSurface ? mockSurface->handle() : nullptr; -} - -Output *Compositor::resolveOutput(const QVariant &v) -{ - QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >(); - return mockOutput ? mockOutput->handle() : nullptr; -} - -IviSurface *Compositor::resolveIviSurface(const QVariant &v) -{ - QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>(); - return mockIviSurface ? mockIviSurface->handle() : nullptr; -} - -XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v) -{ - QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>(); - return mockToplevel ? mockToplevel->handle() : nullptr; -} - -} +} // namespace MockCompositor diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h index 51b6f4bfb..07366a493 100644 --- a/tests/auto/client/shared/mockcompositor.h +++ b/tests/auto/client/shared/mockcompositor.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. @@ -29,259 +29,55 @@ #ifndef MOCKCOMPOSITOR_H #define MOCKCOMPOSITOR_H -#include "mockxdgshellv6.h" -#include "mockiviapplication.h" +#include "corecompositor.h" +#include "coreprotocol.h" +#include "xdgshell.h" -#include <pthread.h> -#include <qglobal.h> -#include <wayland-server.h> +#include <QtGui/QGuiApplication> -#include <QImage> -#include <QMutex> -#include <QRect> -#include <QSharedPointer> -#include <QVariant> -#include <QVector> -#include <QWaitCondition> - -namespace Impl { - -typedef void (**Implementation)(void); - -class Keyboard; -class Pointer; -class Touch; -class Seat; -class DataDeviceManager; -class Surface; -class Output; -class IviApplication; -class WlShell; -class XdgShellV6; - -class Compositor -{ -public: - Compositor(); - ~Compositor(); - - int fileDescriptor() const { return m_fd; } - void dispatchEvents(int timeout = 0); - - uint32_t nextSerial(); - uint32_t time() { return ++m_time; } - - QVector<Surface *> surfaces() const; - QVector<Output *> outputs() const; - - IviApplication *iviApplication() const; - XdgShellV6 *xdgShellV6() const; - - void addSurface(Surface *surface); - void removeSurface(Surface *surface); - - static void setKeyboardFocus(void *data, const QList<QVariant> ¶meters); - static void sendMousePress(void *data, const QList<QVariant> ¶meters); - static void sendMouseRelease(void *data, const QList<QVariant> ¶meters); - static void sendKeyPress(void *data, const QList<QVariant> ¶meters); - static void sendKeyRelease(void *data, const QList<QVariant> ¶meters); - static void sendTouchDown(void *data, const QList<QVariant> ¶meters); - static void sendTouchUp(void *data, const QList<QVariant> ¶meters); - static void sendTouchMotion(void *data, const QList<QVariant> ¶meters); - static void sendTouchFrame(void *data, const QList<QVariant> ¶meters); - static void sendDataDeviceDataOffer(void *data, const QList<QVariant> ¶meters); - static void sendDataDeviceEnter(void *data, const QList<QVariant> ¶meters); - static void sendDataDeviceMotion(void *data, const QList<QVariant> ¶meters); - static void sendDataDeviceDrop(void *data, const QList<QVariant> ¶meters); - static void sendDataDeviceLeave(void *data, const QList<QVariant> ¶meters); - static void waitForStartDrag(void *data, const QList<QVariant> ¶meters); - static void setOutputMode(void *compositor, const QList<QVariant> ¶meters); - static void sendAddOutput(void *data, const QList<QVariant> ¶meters); - static void sendRemoveOutput(void *data, const QList<QVariant> ¶meters); - static void sendOutputGeometry(void *data, const QList<QVariant> ¶meters); - static void sendSurfaceEnter(void *data, const QList<QVariant> ¶meters); - static void sendSurfaceLeave(void *data, const QList<QVariant> ¶meters); - static void sendShellSurfaceConfigure(void *data, const QList<QVariant> ¶meters); - static void sendIviSurfaceConfigure(void *data, const QList<QVariant> ¶meters); - static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> ¶meters); - -public: - bool m_startDragSeen = false; - -private: - static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id); - static Surface *resolveSurface(const QVariant &v); - static Output *resolveOutput(const QVariant &v); - static IviSurface *resolveIviSurface(const QVariant &v); - static XdgToplevelV6 *resolveToplevel(const QVariant &v); - - void initShm(); - - QRect m_outputGeometry; - - wl_display *m_display = nullptr; - wl_event_loop *m_loop = nullptr; - wl_shm *m_shm = nullptr; - int m_fd = -1; - - uint32_t m_time = 0; - - QScopedPointer<Seat> m_seat; - Pointer *m_pointer = nullptr; - Keyboard *m_keyboard = nullptr; - Touch *m_touch = nullptr; - QScopedPointer<DataDeviceManager> m_data_device_manager; - QVector<Surface *> m_surfaces; - QVector<Output *> m_outputs; - QScopedPointer<IviApplication> m_iviApplication; - QScopedPointer<WlShell> m_wlShell; - QScopedPointer<XdgShellV6> m_xdgShellV6; -}; - -void registerResource(wl_list *list, wl_resource *resource); - -} - -class MockSurface -{ -public: - Impl::Surface *handle() const { return m_surface; } - - QImage image; - -private: - MockSurface(Impl::Surface *surface); - friend class Impl::Compositor; - friend class Impl::Surface; - - Impl::Surface *m_surface = nullptr; -}; - -Q_DECLARE_METATYPE(QSharedPointer<MockSurface>) - -class MockIviSurface -{ -public: - Impl::IviSurface *handle() const { return m_iviSurface; } - const uint iviId; - -private: - MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {} - friend class Impl::Compositor; - friend class Impl::IviSurface; - - Impl::IviSurface *m_iviSurface; -}; +#ifndef BTN_LEFT +// As defined in linux/input-event-codes.h +#define BTN_LEFT 0x110 +#endif -Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>) +namespace MockCompositor { -class MockXdgToplevelV6 : public QObject +class DefaultCompositor : public CoreCompositor { - Q_OBJECT -public: - Impl::XdgToplevelV6 *handle() const { return m_toplevel; } - - void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel); - -signals: - uint setMinimizedRequested(); - uint setMaximizedRequested(); - uint unsetMaximizedRequested(); - uint setFullscreenRequested(); - uint unsetFullscreenRequested(); - void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event - -private: - MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {} - friend class Impl::Compositor; - friend class Impl::XdgToplevelV6; - - Impl::XdgToplevelV6 *m_toplevel; -}; - -Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>) - -class MockOutput { public: - Impl::Output *handle() const { return m_output; } - MockOutput(Impl::Output *output); -private: - Impl::Output *m_output = nullptr; + explicit DefaultCompositor(); + // Convenience functions + Surface *surface(int i = 0) { return get<WlCompositor>()->m_surfaces.value(i, nullptr); } + XdgSurface *xdgSurface(int i = 0) { return get<XdgWmBase>()->m_xdgSurfaces.value(i, nullptr); } + XdgToplevel *xdgToplevel(int i = 0) { return get<XdgWmBase>()->toplevel(i); } + XdgPopup *xdgPopup(int i = 0) { return get<XdgWmBase>()->popup(i); } + Pointer *pointer() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_pointer; } + uint sendXdgShellPing(); + void xdgPingAndWaitForPong(); + // Things that can be changed run-time without confusing the client (i.e. don't require separate tests) + struct Config { + bool autoEnter = true; + bool autoRelease = true; + bool autoConfigure = false; + } m_config; + void resetConfig() { exec([&] { m_config = Config{}; }); } }; -Q_DECLARE_METATYPE(QSharedPointer<MockOutput>) - -class MockCompositor -{ -public: - MockCompositor(); - ~MockCompositor(); - - void applicationInitialized(); - - int waylandFileDescriptor() const; - void processWaylandEvents(); - - void setOutputMode(const QSize &size); - void setKeyboardFocus(const QSharedPointer<MockSurface> &surface); - void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos); - void sendMouseRelease(const QSharedPointer<MockSurface> &surface); - void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code); - void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code); - void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id); - void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id); - void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id); - void sendTouchFrame(const QSharedPointer<MockSurface> &surface); - void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface); - void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position); - void sendDataDeviceMotion(const QPoint &position); - void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface); - void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface); - void sendAddOutput(); - void sendRemoveOutput(const QSharedPointer<MockOutput> &output); - void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry); - void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); - void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); - void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0)); - void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size); - void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0), - const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); - void waitForStartDrag(); - - QSharedPointer<MockSurface> surface(); - QSharedPointer<MockOutput> output(int index = 0); - QSharedPointer<MockIviSurface> iviSurface(int index = 0); - QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0); - - void lock(); - void unlock(); - -private: - struct Command - { - typedef void (*Callback)(void *target, const QList<QVariant> ¶meters); - - Callback callback; - void *target = nullptr; - QList<QVariant> parameters; - }; - - static Command makeCommand(Command::Callback callback, void *target); - - void processCommand(const Command &command); - void dispatchCommands(); - - static void *run(void *data); - - bool m_alive = true; - bool m_ready = false; - pthread_t m_thread; - QMutex m_mutex; - QWaitCondition m_waitCondition; - - Impl::Compositor *m_compositor = nullptr; - - QList<Command> m_commandQueue; -}; +} // namespace MockCompositor + +#define QCOMPOSITOR_VERIFY(expr) QVERIFY(exec([&]{ return expr; })) +#define QCOMPOSITOR_TRY_VERIFY(expr) QTRY_VERIFY(exec([&]{ return expr; })) +#define QCOMPOSITOR_COMPARE(expr, expr2) QCOMPARE(exec([&]{ return expr; }), expr2) +#define QCOMPOSITOR_TRY_COMPARE(expr, expr2) QTRY_COMPARE(exec([&]{ return expr; }), expr2) + +#define QCOMPOSITOR_TEST_MAIN(test) \ +int main(int argc, char **argv) \ +{ \ + setenv("XDG_RUNTIME_DIR", ".", 1); \ + setenv("QT_QPA_PLATFORM", "wayland", 1); \ + test tc; \ + QGuiApplication app(argc, argv); \ + return QTest::qExec(&tc, argc, argv); \ +} \ #endif diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri index f3cb4d5a2..3376c73bc 100644 --- a/tests/auto/client/shared/shared.pri +++ b/tests/auto/client/shared/shared.pri @@ -1,31 +1,21 @@ -CONFIG += testcase link_pkgconfig -QT += testlib -QT += core-private gui-private waylandclient-private +QT += testlib waylandclient-private +CONFIG += testcase wayland-scanner +QMAKE_USE += wayland-server -QMAKE_USE += wayland-client wayland-server - -CONFIG += wayland-scanner WAYLANDSERVERSOURCES += \ - ../../../../src/3rdparty/protocol/ivi-application.xml \ - ../../../../src/3rdparty/protocol/wayland.xml \ - ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml + $$PWD/../../../../src/3rdparty/protocol/wayland.xml \ + $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml INCLUDEPATH += ../shared -SOURCES += \ - ../shared/mockcompositor.cpp \ - ../shared/mockinput.cpp \ - ../shared/mockiviapplication.cpp \ - ../shared/mockwlshell.cpp \ - ../shared/mockxdgshellv6.cpp \ - ../shared/mocksurface.cpp \ - ../shared/mockoutput.cpp - HEADERS += \ - ../shared/mockcompositor.h \ - ../shared/mockinput.h \ - ../shared/mockiviapplication.h \ - ../shared/mockwlshell.h \ - ../shared/mockxdgshellv6.h \ - ../shared/mocksurface.h \ - ../shared/mockoutput.h + $$PWD/corecompositor.h \ + $$PWD/coreprotocol.h \ + $$PWD/mockcompositor.h \ + $$PWD/xdgshell.h + +SOURCES += \ + $$PWD/corecompositor.cpp \ + $$PWD/coreprotocol.cpp \ + $$PWD/mockcompositor.cpp \ + $$PWD/xdgshell.cpp diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp new file mode 100644 index 000000000..ebbcc2942 --- /dev/null +++ b/tests/auto/client/shared/xdgshell.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xdgshell.h" + +namespace MockCompositor { + +XdgWmBase::XdgWmBase(CoreCompositor *compositor, int version) + : QtWaylandServer::xdg_wm_base(compositor->m_display, version) + , m_compositor(compositor) +{ +} + +XdgToplevel *XdgWmBase::toplevel(int i) +{ + int j = 0; + for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) { + if (auto *toplevel = xdgSurface->m_toplevel) { + if (j == i) + return toplevel; + ++j; + } + } + return nullptr; +} + +XdgPopup *XdgWmBase::popup(int i) +{ + int j = 0; + for (auto *xdgSurface : qAsConst(m_xdgSurfaces)) { + if (auto *popup = xdgSurface->m_popup) { + if (j == i) + return popup; + ++j; + } + } + return nullptr; +} + +void XdgWmBase::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, wl_resource *surface) +{ + auto *s = fromResource<Surface>(surface); + auto *xdgSurface = new XdgSurface(this, s, resource->client(), id, resource->version()); + m_xdgSurfaces << xdgSurface; + emit xdgSurfaceCreated(xdgSurface); +} + +void XdgWmBase::xdg_wm_base_pong(Resource *resource, uint32_t serial) +{ + Q_UNUSED(resource); + emit pong(serial); +} + +XdgSurface::XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version) + : QtWaylandServer::xdg_surface(client, id, version) + , m_xdgWmBase(xdgWmBase) + , m_surface(surface) +{ + QVERIFY(!surface->m_pending.buffer); + QVERIFY(!surface->m_committed.buffer); + connect(this, &XdgSurface::toplevelCreated, xdgWmBase, &XdgWmBase::toplevelCreated); + connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured); + connect(surface, &Surface::commit, this, [this] { + if (m_ackedConfigureSerial != m_committedConfigureSerial) { + m_committedConfigureSerial = m_ackedConfigureSerial; + emit configureCommitted(m_committedConfigureSerial); + } + }); +} + +void XdgSurface::sendConfigure(uint serial) +{ + Q_ASSERT(serial); + m_pendingConfigureSerials.append(serial); + m_configureSent = true; + xdg_surface::send_configure(serial); +} + +uint XdgSurface::sendConfigure() +{ + const uint serial = m_xdgWmBase->m_compositor->nextSerial(); + sendConfigure(serial); + return serial; +} + +void XdgSurface::xdg_surface_get_toplevel(Resource *resource, uint32_t id) +{ + QVERIFY(!m_toplevel); + QVERIFY(!m_popup); + m_toplevel = new XdgToplevel(this, id, resource->version()); + emit toplevelCreated(m_toplevel); +} + +void XdgSurface::xdg_surface_get_popup(Resource *resource, uint32_t id, wl_resource *parent, wl_resource *positioner) +{ + Q_UNUSED(parent); + Q_UNUSED(positioner); + QVERIFY(!m_toplevel); + QVERIFY(!m_popup); + m_popup = new XdgPopup(this, id, resource->version()); +} + +void XdgSurface::xdg_surface_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + bool removed = m_xdgWmBase->m_xdgSurfaces.removeOne(this); + Q_ASSERT(removed); + delete this; +} + +void XdgSurface::xdg_surface_ack_configure(Resource *resource, uint32_t serial) +{ + Q_UNUSED(resource); + QVERIFY2(m_pendingConfigureSerials.contains(serial), qPrintable(QString::number(serial))); + m_ackedConfigureSerial = serial; + while (!m_pendingConfigureSerials.empty()) { + uint s = m_pendingConfigureSerials.takeFirst(); + if (s == serial) + return; + } +} + +XdgToplevel::XdgToplevel(XdgSurface *xdgSurface, int id, int version) + : QtWaylandServer::xdg_toplevel(xdgSurface->resource()->client(), id, version) + , m_xdgSurface(xdgSurface) +{ +} + +void XdgToplevel::sendConfigure(const QSize &size, const QVector<uint> &states) +{ + send_configure(size.width(), size.height(), toByteArray(states)); +} + +uint XdgToplevel::sendCompleteConfigure(const QSize &size, const QVector<uint> &states) +{ + sendConfigure(size, states); + return m_xdgSurface->sendConfigure(); +} + +XdgPopup::XdgPopup(XdgSurface *xdgSurface, int id, int version) + : QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version) + , m_xdgSurface(xdgSurface) +{ +} + +void XdgPopup::sendConfigure(const QRect &geometry) +{ + send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height()); +} + +void XdgPopup::xdg_popup_grab(QtWaylandServer::xdg_popup::Resource *resource, wl_resource *seat, uint32_t serial) +{ + Q_UNUSED(resource); + Q_UNUSED(seat); // TODO: verify correct seat as well + //TODO: verify no other popup has grabbed + QVERIFY(!m_grabbed); + m_grabbed = true; + m_grabSerial = serial; +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h new file mode 100644 index 000000000..3fcec7983 --- /dev/null +++ b/tests/auto/client/shared/xdgshell.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKCOMPOSITOR_XDGSHELL_H +#define MOCKCOMPOSITOR_XDGSHELL_H + +#include "coreprotocol.h" +#include <qwayland-server-xdg-shell.h> + +namespace MockCompositor { + +class XdgSurface; +class XdgToplevel; +class XdgPopup; +using XdgPositioner = QtWaylandServer::xdg_positioner; + +class XdgWmBase : public Global, public QtWaylandServer::xdg_wm_base +{ + Q_OBJECT +public: + explicit XdgWmBase(CoreCompositor *compositor, int version = 1); + using QtWaylandServer::xdg_wm_base::send_ping; + void send_ping(uint32_t) = delete; // It's a global, use resource specific instead + bool isClean() override { return m_xdgSurfaces.empty(); } + QString dirtyMessage() override { return m_xdgSurfaces.empty() ? "clean" : "remaining xdg surfaces"; } + QVector<XdgSurface *> m_xdgSurfaces; + XdgToplevel *toplevel(int i = 0); + XdgPopup *popup(int i = 0); + CoreCompositor *m_compositor = nullptr; + +signals: + void pong(uint serial); + void xdgSurfaceCreated(XdgSurface *xdgSurface); + void toplevelCreated(XdgToplevel *toplevel); + +protected: + void xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override; + void xdg_wm_base_pong(Resource *resource, uint32_t serial) override; + void xdg_wm_base_create_positioner(Resource *resource, uint32_t id) override + { + new XdgPositioner(resource->client(), id, resource->version()); + } +}; + +class XdgSurface : public QObject, public QtWaylandServer::xdg_surface +{ + Q_OBJECT +public: + explicit XdgSurface(XdgWmBase *xdgWmBase, Surface *surface, wl_client *client, int id, int version); + void send_configure(uint serial) = delete; // Use the one below instead, as it tracks state + void sendConfigure(uint serial); + uint sendConfigure(); + XdgToplevel *m_toplevel = nullptr; + XdgPopup *m_popup = nullptr; + XdgWmBase *m_xdgWmBase = nullptr; + Surface *m_surface = nullptr; + bool m_configureSent = false; + QVector<uint> m_pendingConfigureSerials; + uint m_ackedConfigureSerial = 0; + uint m_committedConfigureSerial = 0; + +public slots: + void verifyConfigured() { QVERIFY(m_configureSent); } + +signals: + void configureCommitted(uint); + void toplevelCreated(XdgToplevel *toplevel); + +protected: + void xdg_surface_get_toplevel(Resource *resource, uint32_t id) override; + void xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parent, ::wl_resource *positioner) override; + void xdg_surface_destroy_resource(Resource *resource) override; + void xdg_surface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); } + void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; +}; + +class XdgToplevel : public QtWaylandServer::xdg_toplevel +{ +public: + explicit XdgToplevel(XdgSurface *xdgSurface, int id, int version = 1); + void sendConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {}); + uint sendCompleteConfigure(const QSize &size = {0, 0}, const QVector<uint> &states = {}); + Surface *surface() { return m_xdgSurface->m_surface; } + XdgSurface *m_xdgSurface = nullptr; +}; + +class XdgPopup : public QtWaylandServer::xdg_popup +{ +public: + explicit XdgPopup(XdgSurface *xdgSurface, int id, int version = 1); + void sendConfigure(const QRect &geometry); + Surface *surface() { return m_xdgSurface->m_surface; } + XdgSurface *m_xdgSurface = nullptr; + bool m_grabbed = false; + uint m_grabSerial = 0; +protected: + void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_XDGSHELL_H diff --git a/tests/auto/client/shared_old/mockcompositor.cpp b/tests/auto/client/shared_old/mockcompositor.cpp new file mode 100644 index 000000000..df24b4091 --- /dev/null +++ b/tests/auto/client/shared_old/mockcompositor.cpp @@ -0,0 +1,520 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" +#include "mockinput.h" +#include "mockoutput.h" +#include "mocksurface.h" +#include "mockwlshell.h" +#include "mockxdgshellv6.h" +#include "mockiviapplication.h" + +#include <wayland-xdg-shell-unstable-v6-server-protocol.h> + +#include <stdio.h> +MockCompositor::MockCompositor() +{ + pthread_create(&m_thread, 0, run, this); + + m_mutex.lock(); + m_waitCondition.wait(&m_mutex); + m_mutex.unlock(); +} + +MockCompositor::~MockCompositor() +{ + m_alive = false; + m_waitCondition.wakeOne(); + pthread_join(m_thread, 0); +} + +void MockCompositor::lock() +{ + m_mutex.lock(); +} + +void MockCompositor::unlock() +{ + m_mutex.unlock(); +} + +void MockCompositor::applicationInitialized() +{ + m_ready = true; +} + +int MockCompositor::waylandFileDescriptor() const +{ + return m_compositor->fileDescriptor(); +} + +void MockCompositor::processWaylandEvents() +{ + m_waitCondition.wakeOne(); +} + +void MockCompositor::setOutputMode(const QSize &size) +{ + Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor); + command.parameters << size; + processCommand(command); +} + +void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos) +{ + Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor); + command.parameters << QVariant::fromValue(surface) << pos; + processCommand(command); +} + +void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code) +{ + Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor); + command.parameters << QVariant::fromValue(surface) << code; + processCommand(command); +} + +void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code) +{ + Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor); + command.parameters << QVariant::fromValue(surface) << code; + processCommand(command); +} + +void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) +{ + Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor); + command.parameters << QVariant::fromValue(surface) << position << id; + processCommand(command); +} + +void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) +{ + Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor); + command.parameters << QVariant::fromValue(surface) << position << id; + processCommand(command); +} + +void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id) +{ + Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor); + command.parameters << QVariant::fromValue(surface) << id; + processCommand(command); +} + +void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor); + command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position); + processCommand(command); +} + +void MockCompositor::sendDataDeviceMotion(const QPoint &position) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor); + command.parameters << QVariant::fromValue(position); + processCommand(command); +} + +void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface) +{ + Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor); + command.parameters << QVariant::fromValue(surface); + processCommand(command); +} + +void MockCompositor::sendAddOutput() +{ + Command command = makeCommand(Impl::Compositor::sendAddOutput, m_compositor); + processCommand(command); +} + +void MockCompositor::sendRemoveOutput(const QSharedPointer<MockOutput> &output) +{ + Command command = makeCommand(Impl::Compositor::sendRemoveOutput, m_compositor); + command.parameters << QVariant::fromValue(output); + processCommand(command); +} + +void MockCompositor::sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry) +{ + Command command = makeCommand(Impl::Compositor::sendOutputGeometry, m_compositor); + command.parameters << QVariant::fromValue(output); + command.parameters << QVariant::fromValue(geometry); + processCommand(command); +} + +void MockCompositor::sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output) +{ + Command command = makeCommand(Impl::Compositor::sendSurfaceEnter, m_compositor); + command.parameters << QVariant::fromValue(surface); + command.parameters << QVariant::fromValue(output); + processCommand(command); +} + +void MockCompositor::sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output) +{ + Command command = makeCommand(Impl::Compositor::sendSurfaceLeave, m_compositor); + command.parameters << QVariant::fromValue(surface); + command.parameters << QVariant::fromValue(output); + processCommand(command); +} + +void MockCompositor::sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size) +{ + Command command = makeCommand(Impl::Compositor::sendShellSurfaceConfigure, m_compositor); + command.parameters << QVariant::fromValue(surface); + command.parameters << QVariant::fromValue(size); + processCommand(command); +} + +void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size) +{ + Command command = makeCommand(Impl::Compositor::sendIviSurfaceConfigure, m_compositor); + command.parameters << QVariant::fromValue(iviSurface); + command.parameters << QVariant::fromValue(size); + processCommand(command); +} + +void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states) +{ + Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor); + command.parameters << QVariant::fromValue(toplevel); + command.parameters << QVariant::fromValue(size); + QByteArray statesBytes(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(uint))); + command.parameters << statesBytes; + processCommand(command); +} + +void MockCompositor::waitForStartDrag() +{ + Command command = makeCommand(Impl::Compositor::waitForStartDrag, m_compositor); + processCommand(command); +} + +QSharedPointer<MockSurface> MockCompositor::surface() +{ + QSharedPointer<MockSurface> result; + lock(); + QVector<Impl::Surface *> surfaces = m_compositor->surfaces(); + foreach (Impl::Surface *surface, surfaces) { + // we don't want to mistake the cursor surface for a window surface + if (surface->isMapped()) { + result = surface->mockSurface(); + break; + } + } + unlock(); + return result; +} + +QSharedPointer<MockOutput> MockCompositor::output(int index) +{ + QSharedPointer<MockOutput> result; + lock(); + if (Impl::Output *output = m_compositor->outputs().value(index, nullptr)) + result = output->mockOutput(); + unlock(); + return result; +} + +QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index) +{ + QSharedPointer<MockIviSurface> result; + lock(); + if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr)) + result = toplevel->mockIviSurface(); + unlock(); + return result; +} + +QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index) +{ + QSharedPointer<MockXdgToplevelV6> result; + lock(); + if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr)) + result = toplevel->mockToplevel(); + unlock(); + return result; +} + +QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index) +{ + QSharedPointer<MockSurface> result; + lock(); + if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr)) + result = surface->mockSurface(); + unlock(); + return result; +} + +MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target) +{ + Command command; + command.callback = callback; + command.target = target; + return command; +} + +void MockCompositor::processCommand(const Command &command) +{ + lock(); + m_commandQueue << command; + unlock(); + + m_waitCondition.wakeOne(); +} + +void MockCompositor::dispatchCommands() +{ + lock(); + int count = m_commandQueue.length(); + unlock(); + + for (int i = 0; i < count; ++i) { + lock(); + const Command command = m_commandQueue.takeFirst(); + unlock(); + command.callback(command.target, command.parameters); + } +} + +void *MockCompositor::run(void *data) +{ + MockCompositor *controller = static_cast<MockCompositor *>(data); + + Impl::Compositor compositor; + + controller->m_compositor = &compositor; + controller->m_waitCondition.wakeOne(); + + while (!controller->m_ready) { + controller->dispatchCommands(); + compositor.dispatchEvents(20); + } + + while (controller->m_alive) { + { + QMutexLocker locker(&controller->m_mutex); + if (controller->m_commandQueue.isEmpty()) + controller->m_waitCondition.wait(&controller->m_mutex); + } + controller->dispatchCommands(); + compositor.dispatchEvents(20); + } + + return 0; +} + +namespace Impl { + +Compositor::Compositor() + : m_display(wl_display_create()) +{ + if (wl_display_add_socket(m_display, 0)) { + fprintf(stderr, "Fatal: Failed to open server socket\n"); + exit(EXIT_FAILURE); + } + + wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); + + m_data_device_manager.reset(new DataDeviceManager(this, m_display)); + + wl_display_init_shm(m_display); + + m_seat.reset(new Seat(this, m_display)); + m_pointer = m_seat->pointer(); + m_keyboard = m_seat->keyboard(); + m_touch = m_seat->touch(); + + m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0))); + m_iviApplication.reset(new IviApplication(m_display)); + m_wlShell.reset(new WlShell(m_display)); + m_xdgShellV6.reset(new XdgShellV6(m_display)); + m_fullScreenShellV1.reset(new FullScreenShellV1(m_display)); + + m_loop = wl_display_get_event_loop(m_display); + m_fd = wl_event_loop_get_fd(m_loop); +} + +Compositor::~Compositor() +{ + wl_display_destroy(m_display); +} + +void Compositor::dispatchEvents(int timeout) +{ + wl_display_flush_clients(m_display); + wl_event_loop_dispatch(m_loop, timeout); +} + +static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id) +{ + Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource)); + compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor)); +} + +static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id) +{ + Q_UNUSED(client); + Q_UNUSED(compositorResource); + Q_UNUSED(id); +} + +void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id) +{ + static const struct wl_compositor_interface compositorInterface = { + compositor_create_surface, + compositor_create_region + }; + + wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id); + wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr); +} + +static void unregisterResourceCallback(wl_listener *listener, void *data) +{ + struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data); + wl_list_remove(wl_resource_get_link(resource)); + delete listener; +} + +void registerResource(wl_list *list, wl_resource *resource) +{ + wl_list_insert(list, wl_resource_get_link(resource)); + + wl_listener *listener = new wl_listener; + listener->notify = unregisterResourceCallback; + + wl_resource_add_destroy_listener(resource, listener); +} + +QVector<Surface *> Compositor::surfaces() const +{ + return m_surfaces; +} + +QVector<Output *> Compositor::outputs() const +{ + return m_outputs; +} + +IviApplication *Compositor::iviApplication() const +{ + return m_iviApplication.data(); +} + +XdgShellV6 *Compositor::xdgShellV6() const +{ + return m_xdgShellV6.data(); +} + +FullScreenShellV1 *Compositor::fullScreenShellV1() const +{ + return m_fullScreenShellV1.data(); +} + +uint32_t Compositor::nextSerial() +{ + return wl_display_next_serial(m_display); +} + +void Compositor::addSurface(Surface *surface) +{ + m_surfaces << surface; +} + +void Compositor::removeSurface(Surface *surface) +{ + m_surfaces.removeOne(surface); + m_keyboard->handleSurfaceDestroyed(surface); + m_pointer->handleSurfaceDestroyed(surface); + m_fullScreenShellV1->removeSurface(surface); +} + +Surface *Compositor::resolveSurface(const QVariant &v) +{ + QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >(); + return mockSurface ? mockSurface->handle() : nullptr; +} + +Output *Compositor::resolveOutput(const QVariant &v) +{ + QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >(); + return mockOutput ? mockOutput->handle() : nullptr; +} + +IviSurface *Compositor::resolveIviSurface(const QVariant &v) +{ + QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>(); + return mockIviSurface ? mockIviSurface->handle() : nullptr; +} + +XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v) +{ + QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>(); + return mockToplevel ? mockToplevel->handle() : nullptr; +} + +} diff --git a/tests/auto/client/shared_old/mockcompositor.h b/tests/auto/client/shared_old/mockcompositor.h new file mode 100644 index 000000000..4bab1ed67 --- /dev/null +++ b/tests/auto/client/shared_old/mockcompositor.h @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKCOMPOSITOR_H +#define MOCKCOMPOSITOR_H + +#include "mockxdgshellv6.h" +#include "mockiviapplication.h" +#include "mockfullscreenshellv1.h" + +#include <pthread.h> +#include <qglobal.h> +#include <wayland-server-core.h> + +#include <QImage> +#include <QMutex> +#include <QRect> +#include <QSharedPointer> +#include <QVariant> +#include <QVector> +#include <QWaitCondition> + +namespace Impl { + +typedef void (**Implementation)(void); + +class Keyboard; +class Pointer; +class Touch; +class Seat; +class DataDeviceManager; +class Surface; +class Output; +class IviApplication; +class WlShell; +class XdgShellV6; + +class Compositor +{ +public: + Compositor(); + ~Compositor(); + + int fileDescriptor() const { return m_fd; } + void dispatchEvents(int timeout = 0); + + uint32_t nextSerial(); + uint32_t time() { return ++m_time; } + + QVector<Surface *> surfaces() const; + QVector<Output *> outputs() const; + + IviApplication *iviApplication() const; + XdgShellV6 *xdgShellV6() const; + FullScreenShellV1 *fullScreenShellV1() const; + + void addSurface(Surface *surface); + void removeSurface(Surface *surface); + + static void setKeyboardFocus(void *data, const QList<QVariant> ¶meters); + static void sendMousePress(void *data, const QList<QVariant> ¶meters); + static void sendMouseRelease(void *data, const QList<QVariant> ¶meters); + static void sendKeyPress(void *data, const QList<QVariant> ¶meters); + static void sendKeyRelease(void *data, const QList<QVariant> ¶meters); + static void sendTouchDown(void *data, const QList<QVariant> ¶meters); + static void sendTouchUp(void *data, const QList<QVariant> ¶meters); + static void sendTouchMotion(void *data, const QList<QVariant> ¶meters); + static void sendTouchFrame(void *data, const QList<QVariant> ¶meters); + static void sendDataDeviceDataOffer(void *data, const QList<QVariant> ¶meters); + static void sendDataDeviceEnter(void *data, const QList<QVariant> ¶meters); + static void sendDataDeviceMotion(void *data, const QList<QVariant> ¶meters); + static void sendDataDeviceDrop(void *data, const QList<QVariant> ¶meters); + static void sendDataDeviceLeave(void *data, const QList<QVariant> ¶meters); + static void waitForStartDrag(void *data, const QList<QVariant> ¶meters); + static void setOutputMode(void *compositor, const QList<QVariant> ¶meters); + static void sendAddOutput(void *data, const QList<QVariant> ¶meters); + static void sendRemoveOutput(void *data, const QList<QVariant> ¶meters); + static void sendOutputGeometry(void *data, const QList<QVariant> ¶meters); + static void sendSurfaceEnter(void *data, const QList<QVariant> ¶meters); + static void sendSurfaceLeave(void *data, const QList<QVariant> ¶meters); + static void sendShellSurfaceConfigure(void *data, const QList<QVariant> ¶meters); + static void sendIviSurfaceConfigure(void *data, const QList<QVariant> ¶meters); + static void sendXdgToplevelV6Configure(void *data, const QList<QVariant> ¶meters); + +public: + bool m_startDragSeen = false; + +private: + static void bindCompositor(wl_client *client, void *data, uint32_t version, uint32_t id); + static Surface *resolveSurface(const QVariant &v); + static Output *resolveOutput(const QVariant &v); + static IviSurface *resolveIviSurface(const QVariant &v); + static XdgToplevelV6 *resolveToplevel(const QVariant &v); + + void initShm(); + + QRect m_outputGeometry; + + wl_display *m_display = nullptr; + wl_event_loop *m_loop = nullptr; + int m_fd = -1; + + uint32_t m_time = 0; + + QScopedPointer<Seat> m_seat; + Pointer *m_pointer = nullptr; + Keyboard *m_keyboard = nullptr; + Touch *m_touch = nullptr; + QScopedPointer<DataDeviceManager> m_data_device_manager; + QVector<Surface *> m_surfaces; + QVector<Output *> m_outputs; + QScopedPointer<IviApplication> m_iviApplication; + QScopedPointer<WlShell> m_wlShell; + QScopedPointer<XdgShellV6> m_xdgShellV6; + QScopedPointer<FullScreenShellV1> m_fullScreenShellV1; +}; + +void registerResource(wl_list *list, wl_resource *resource); + +} + +class MockSurface +{ +public: + Impl::Surface *handle() const { return m_surface; } + + QImage image; + +private: + MockSurface(Impl::Surface *surface); + friend class Impl::Compositor; + friend class Impl::Surface; + + Impl::Surface *m_surface = nullptr; +}; + +Q_DECLARE_METATYPE(QSharedPointer<MockSurface>) + +class MockIviSurface +{ +public: + Impl::IviSurface *handle() const { return m_iviSurface; } + const uint iviId; + +private: + MockIviSurface(Impl::IviSurface *iviSurface) : iviId(iviSurface->iviId()), m_iviSurface(iviSurface) {} + friend class Impl::Compositor; + friend class Impl::IviSurface; + + Impl::IviSurface *m_iviSurface; +}; + +Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>) + +class MockXdgToplevelV6 : public QObject +{ + Q_OBJECT +public: + Impl::XdgToplevelV6 *handle() const { return m_toplevel; } + + void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel); + +signals: + uint setMinimizedRequested(); + uint setMaximizedRequested(); + uint unsetMaximizedRequested(); + uint setFullscreenRequested(); + uint unsetFullscreenRequested(); + void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event + +private: + MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {} + friend class Impl::Compositor; + friend class Impl::XdgToplevelV6; + + Impl::XdgToplevelV6 *m_toplevel; +}; + +Q_DECLARE_METATYPE(QSharedPointer<MockXdgToplevelV6>) + +class MockOutput { +public: + Impl::Output *handle() const { return m_output; } + MockOutput(Impl::Output *output); +private: + Impl::Output *m_output = nullptr; +}; + +Q_DECLARE_METATYPE(QSharedPointer<MockOutput>) + +class MockCompositor +{ +public: + MockCompositor(); + ~MockCompositor(); + + void applicationInitialized(); + + int waylandFileDescriptor() const; + void processWaylandEvents(); + + void setOutputMode(const QSize &size); + void setKeyboardFocus(const QSharedPointer<MockSurface> &surface); + void sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos); + void sendMouseRelease(const QSharedPointer<MockSurface> &surface); + void sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code); + void sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code); + void sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id); + void sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id); + void sendTouchUp(const QSharedPointer<MockSurface> &surface, int id); + void sendTouchFrame(const QSharedPointer<MockSurface> &surface); + void sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface); + void sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint &position); + void sendDataDeviceMotion(const QPoint &position); + void sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface); + void sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface); + void sendAddOutput(); + void sendRemoveOutput(const QSharedPointer<MockOutput> &output); + void sendOutputGeometry(const QSharedPointer<MockOutput> &output, const QRect &geometry); + void sendSurfaceEnter(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); + void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); + void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0)); + void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size); + void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0), + const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); + void waitForStartDrag(); + + QSharedPointer<MockSurface> surface(); + QSharedPointer<MockOutput> output(int index = 0); + QSharedPointer<MockIviSurface> iviSurface(int index = 0); + QSharedPointer<MockXdgToplevelV6> xdgToplevelV6(int index = 0); + QSharedPointer<MockSurface> fullScreenShellV1Surface(int index = 0); + + void lock(); + void unlock(); + +private: + struct Command + { + typedef void (*Callback)(void *target, const QList<QVariant> ¶meters); + + Callback callback; + void *target = nullptr; + QList<QVariant> parameters; + }; + + static Command makeCommand(Command::Callback callback, void *target); + + void processCommand(const Command &command); + void dispatchCommands(); + + static void *run(void *data); + + bool m_alive = true; + bool m_ready = false; + pthread_t m_thread; + QMutex m_mutex; + QWaitCondition m_waitCondition; + + Impl::Compositor *m_compositor = nullptr; + + QList<Command> m_commandQueue; +}; + +#endif diff --git a/tests/auto/client/shared_old/mockfullscreenshellv1.cpp b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp new file mode 100644 index 000000000..22c49cde6 --- /dev/null +++ b/tests/auto/client/shared_old/mockfullscreenshellv1.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockfullscreenshellv1.h" +#include "mocksurface.h" + +namespace Impl { + +void FullScreenShellV1::zwp_fullscreen_shell_v1_present_surface(Resource *resource, struct ::wl_resource *surface, uint32_t method, struct ::wl_resource *output) +{ + Q_UNUSED(resource) + Q_UNUSED(method) + Q_UNUSED(output) + + m_surfaces.append(Surface::fromResource(surface)); +} + +} // namespace Impl diff --git a/tests/auto/client/shared_old/mockfullscreenshellv1.h b/tests/auto/client/shared_old/mockfullscreenshellv1.h new file mode 100644 index 000000000..819bbc186 --- /dev/null +++ b/tests/auto/client/shared_old/mockfullscreenshellv1.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKFULLSCREENSHELLV1_H +#define MOCKFULLSCREENSHELLV1_H + +#include <qwayland-server-fullscreen-shell-unstable-v1.h> + +#include <QVector> + +namespace Impl { + +class Surface; +class FullScreenShellV1; + +class FullScreenShellV1 : public QtWaylandServer::zwp_fullscreen_shell_v1 +{ +public: + explicit FullScreenShellV1(::wl_display *display) : zwp_fullscreen_shell_v1(display, 1) {} + + QVector<Surface *> surfaces() const { return m_surfaces; } + void removeSurface(Surface *surface) { m_surfaces.removeOne(surface); } + +protected: + void zwp_fullscreen_shell_v1_present_surface(Resource *resource, struct ::wl_resource *surface, uint32_t method, struct ::wl_resource *output) override; + +private: + QVector<Surface *> m_surfaces; +}; + +} // namespace Impl + +#endif // MOCKFULLSCREENSHELLV1_H diff --git a/tests/auto/client/shared/mockinput.cpp b/tests/auto/client/shared_old/mockinput.cpp index 8b7592824..8b7592824 100644 --- a/tests/auto/client/shared/mockinput.cpp +++ b/tests/auto/client/shared_old/mockinput.cpp diff --git a/tests/auto/client/shared/mockinput.h b/tests/auto/client/shared_old/mockinput.h index d9adb3621..d9adb3621 100644 --- a/tests/auto/client/shared/mockinput.h +++ b/tests/auto/client/shared_old/mockinput.h diff --git a/tests/auto/client/shared/mockiviapplication.cpp b/tests/auto/client/shared_old/mockiviapplication.cpp index 29a308993..29a308993 100644 --- a/tests/auto/client/shared/mockiviapplication.cpp +++ b/tests/auto/client/shared_old/mockiviapplication.cpp diff --git a/tests/auto/client/shared/mockiviapplication.h b/tests/auto/client/shared_old/mockiviapplication.h index 4d65eeaba..4d65eeaba 100644 --- a/tests/auto/client/shared/mockiviapplication.h +++ b/tests/auto/client/shared_old/mockiviapplication.h diff --git a/tests/auto/client/shared/mockoutput.cpp b/tests/auto/client/shared_old/mockoutput.cpp index 13e0524ad..13e0524ad 100644 --- a/tests/auto/client/shared/mockoutput.cpp +++ b/tests/auto/client/shared_old/mockoutput.cpp diff --git a/tests/auto/client/shared/mockoutput.h b/tests/auto/client/shared_old/mockoutput.h index 9f261d5d7..9f261d5d7 100644 --- a/tests/auto/client/shared/mockoutput.h +++ b/tests/auto/client/shared_old/mockoutput.h diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared_old/mocksurface.cpp index 84dcda6b0..84dcda6b0 100644 --- a/tests/auto/client/shared/mocksurface.cpp +++ b/tests/auto/client/shared_old/mocksurface.cpp diff --git a/tests/auto/client/shared/mocksurface.h b/tests/auto/client/shared_old/mocksurface.h index 949dc23dd..949dc23dd 100644 --- a/tests/auto/client/shared/mocksurface.h +++ b/tests/auto/client/shared_old/mocksurface.h diff --git a/tests/auto/client/shared/mockwlshell.cpp b/tests/auto/client/shared_old/mockwlshell.cpp index 50e539932..50e539932 100644 --- a/tests/auto/client/shared/mockwlshell.cpp +++ b/tests/auto/client/shared_old/mockwlshell.cpp diff --git a/tests/auto/client/shared/mockwlshell.h b/tests/auto/client/shared_old/mockwlshell.h index 3da586ca8..3da586ca8 100644 --- a/tests/auto/client/shared/mockwlshell.h +++ b/tests/auto/client/shared_old/mockwlshell.h diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared_old/mockxdgshellv6.cpp index 05eff74ad..05eff74ad 100644 --- a/tests/auto/client/shared/mockxdgshellv6.cpp +++ b/tests/auto/client/shared_old/mockxdgshellv6.cpp diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared_old/mockxdgshellv6.h index a238fa562..a238fa562 100644 --- a/tests/auto/client/shared/mockxdgshellv6.h +++ b/tests/auto/client/shared_old/mockxdgshellv6.h diff --git a/tests/auto/client/shared_old/shared_old.pri b/tests/auto/client/shared_old/shared_old.pri new file mode 100644 index 000000000..467e98115 --- /dev/null +++ b/tests/auto/client/shared_old/shared_old.pri @@ -0,0 +1,34 @@ +CONFIG += testcase link_pkgconfig +QT += testlib +QT += core-private gui-private waylandclient-private + +QMAKE_USE += wayland-client wayland-server + +CONFIG += wayland-scanner +WAYLANDSERVERSOURCES += \ + ../../../../src/3rdparty/protocol/ivi-application.xml \ + ../../../../src/3rdparty/protocol/wayland.xml \ + ../../../../src/3rdparty/protocol/xdg-shell-unstable-v6.xml \ + ../../../../src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml + +INCLUDEPATH += ../shared_old + +SOURCES += \ + ../shared_old/mockcompositor.cpp \ + ../shared_old/mockfullscreenshellv1.cpp \ + ../shared_old/mockinput.cpp \ + ../shared_old/mockiviapplication.cpp \ + ../shared_old/mockwlshell.cpp \ + ../shared_old/mockxdgshellv6.cpp \ + ../shared_old/mocksurface.cpp \ + ../shared_old/mockoutput.cpp + +HEADERS += \ + ../shared_old/mockcompositor.h \ + ../shared_old/mockfullscreenshellv1.h \ + ../shared_old/mockinput.h \ + ../shared_old/mockiviapplication.h \ + ../shared_old/mockwlshell.h \ + ../shared_old/mockxdgshellv6.h \ + ../shared_old/mocksurface.h \ + ../shared_old/mockoutput.h diff --git a/tests/auto/client/surface/surface.pro b/tests/auto/client/surface/surface.pro new file mode 100644 index 000000000..36882aa2d --- /dev/null +++ b/tests/auto/client/surface/surface.pro @@ -0,0 +1,5 @@ +include (../shared/shared.pri) + +TARGET = tst_surface +SOURCES += tst_surface.cpp + diff --git a/tests/auto/client/surface/tst_surface.cpp b/tests/auto/client/surface/tst_surface.cpp new file mode 100644 index 000000000..dddff0866 --- /dev/null +++ b/tests/auto/client/surface/tst_surface.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" +#include <QtGui/QRasterWindow> +#include <QtGui/QOpenGLWindow> + +using namespace MockCompositor; + +class tst_surface : public QObject, private DefaultCompositor +{ + Q_OBJECT +private slots: + void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } + void createDestroySurface(); + void waitForFrameCallbackRaster(); + void waitForFrameCallbackGl(); + void negotiateShmFormat(); +}; + +void tst_surface::createDestroySurface() +{ + QWindow window; + window.show(); + + QCOMPOSITOR_TRY_VERIFY(surface()); + + window.destroy(); + QCOMPOSITOR_TRY_VERIFY(!surface()); +} + +void tst_surface::waitForFrameCallbackRaster() +{ + QSKIP("TODO: This currently fails, needs a fix"); + class TestWindow : public QRasterWindow { + public: + explicit TestWindow() { resize(40, 40); } + void paintEvent(QPaintEvent *event) override + { + Q_UNUSED(event); + update(); + } + }; + TestWindow window; + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + + // We should get the first buffer without waiting for a frame callback + QTRY_COMPARE(bufferSpy.count(), 1); + bufferSpy.removeFirst(); + + // Make sure we follow frame callbacks for some frames + for (int i = 0; i < 5; ++i) { + xdgPingAndWaitForPong(); // Make sure things have happened on the client + exec([&] { + QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived + QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty()); + xdgToplevel()->surface()->sendFrameCallbacks(); + }); + QTRY_COMPARE(bufferSpy.count(), 1); + bufferSpy.removeFirst(); + } +} + +void tst_surface::waitForFrameCallbackGl() +{ + QSKIP("TODO: This currently fails, needs a fix"); + class TestWindow : public QOpenGLWindow { + public: + explicit TestWindow() + { + resize(40, 40); + connect(this, &QOpenGLWindow::frameSwapped, + this, QOverload<>::of(&QPaintDeviceWindow::update)); + update(); + } + void paintGL() override + { + glClearColor(1, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + } + }; + TestWindow window; + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + + // We should get the first buffer without waiting for a frame callback + QTRY_COMPARE(bufferSpy.count(), 1); + bufferSpy.removeFirst(); + + // Make sure we follow frame callbacks for some frames + for (int i = 0; i < 5; ++i) { + xdgPingAndWaitForPong(); // Make sure things have happened on the client + exec([&] { + QVERIFY(bufferSpy.empty()); // Make sure no extra buffers have arrived + QVERIFY(!xdgToplevel()->surface()->m_waitingFrameCallbacks.empty()); + xdgToplevel()->surface()->sendFrameCallbacks(); + }); + QTRY_COMPARE(bufferSpy.count(), 1); + bufferSpy.removeFirst(); + } +} + +void tst_surface::negotiateShmFormat() +{ + QSKIP("TODO: I'm not sure why we're choosing xrgb over argb in this case..."); + QRasterWindow window; + window.setFlag(Qt::FramelessWindowHint); // decorations force alpha + QSurfaceFormat format; + format.setAlphaBufferSize(0); + window.setFormat(format); + window.resize(64, 48); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + QSignalSpy bufferSpy(exec([=] { return xdgSurface()->m_surface; }), &Surface::bufferCommitted); + const uint serial = exec([=] { return xdgToplevel()->sendCompleteConfigure(); }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, serial); + exec([&] { + Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer; + QVERIFY(buffer); + auto *shmBuffer = ShmBuffer::fromBuffer(buffer); + QVERIFY(shmBuffer); + qDebug() << "shmBuffer->m_format" << shmBuffer->m_format; + QCOMPARE(shmBuffer->m_format, Shm::format_xrgb8888); + }); +} + +QCOMPOSITOR_TEST_MAIN(tst_surface) +#include "tst_surface.moc" diff --git a/tests/auto/client/xdgshell/tst_xdgshell.cpp b/tests/auto/client/xdgshell/tst_xdgshell.cpp new file mode 100644 index 000000000..55e994b06 --- /dev/null +++ b/tests/auto/client/xdgshell/tst_xdgshell.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockcompositor.h" +#include <QtGui/QRasterWindow> +#include <QtGui/QOpenGLWindow> + +using namespace MockCompositor; + +class tst_xdgshell : public QObject, private DefaultCompositor +{ + Q_OBJECT +private slots: + void cleanup() { QTRY_VERIFY2(isClean(), qPrintable(dirtyMessage())); } + void showMinimized(); + void basicConfigure(); + void configureSize(); + void configureStates(); + void popup(); + void pongs(); +}; + +void tst_xdgshell::showMinimized() +{ + QSKIP("TODO: This currently fails, needs a fix"); + // On xdg-shell there's really no way for the compositor to tell the window if it's minimized + // There are wl_surface.enter events and so on, but there's really no way to differentiate + // between a window preview and an unminimized window. + QWindow window; + window.showMinimized(); + QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged + + // Make sure the window on the compositor side is/was created here, and not after the test + // finishes, as that may mess up for later tests. + QCOMPOSITOR_TRY_VERIFY(surface()); + QVERIFY(!window.isExposed()); +} + +void tst_xdgshell::basicConfigure() +{ + QRasterWindow window; + window.resize(64, 48); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); + + QTRY_VERIFY(window.isVisible()); + // The window should not be exposed before the first xdg_surface configure event + QTRY_VERIFY(!window.isExposed()); + + exec([=] { + xdgToplevel()->sendConfigure({0, 0}, {}); // Let the window decide the size + }); + + // Nothing should happen before the *xdg_surface* configure + QTRY_VERIFY(!window.isExposed()); //Window should not be exposed before the first configure event + QVERIFY(configureSpy.isEmpty()); + + const uint serial = exec([=] { return nextSerial(); }); + + exec([=] { + xdgSurface()->sendConfigure(serial); + }); + + // Finally, we're exposed + QTRY_VERIFY(window.isExposed()); + + // The client is now going to ack the configure + QTRY_COMPARE(configureSpy.count(), 1); + QCOMPARE(configureSpy.takeFirst().at(0).toUInt(), serial); + + // And attach a buffer + exec([&] { + Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer; + QVERIFY(buffer); + QCOMPARE(buffer->size(), window.frameGeometry().size()); + }); +} + +void tst_xdgshell::configureSize() +{ + QRasterWindow window; + window.resize(64, 48); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + QSignalSpy configureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); + + const QSize configureSize(60, 40); + + exec([=] { + xdgToplevel()->sendCompleteConfigure(configureSize); + }); + + QTRY_COMPARE(configureSpy.count(), 1); + + exec([=] { + Buffer *buffer = xdgToplevel()->surface()->m_committed.buffer; + QVERIFY(buffer); + QCOMPARE(buffer->size(), configureSize); + }); +} + +void tst_xdgshell::configureStates() +{ + QRasterWindow window; + window.resize(64, 48); + window.show(); + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + + const QSize windowedSize(320, 240); + const uint windowedSerial = exec([=] { + return xdgToplevel()->sendCompleteConfigure(windowedSize, { XdgToplevel::state_activated }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, windowedSerial); + QCOMPARE(window.visibility(), QWindow::Windowed); + QCOMPARE(window.windowStates(), Qt::WindowNoState); + QCOMPARE(window.frameGeometry().size(), windowedSize); + // Toplevel windows don't know their position on xdg-shell +// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + +// QEXPECT_FAIL("", "configure has already been acked, we shouldn't have to wait for isActive", Continue); +// QVERIFY(window.isActive()); + QTRY_VERIFY(window.isActive()); // Just make sure it eventually get's set correctly + + const QSize screenSize(640, 480); + const uint maximizedSerial = exec([=] { + return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_maximized }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, maximizedSerial); + QCOMPARE(window.visibility(), QWindow::Maximized); + QCOMPARE(window.windowStates(), Qt::WindowMaximized); + QCOMPARE(window.frameGeometry().size(), screenSize); +// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + + const uint fullscreenSerial = exec([=] { + return xdgToplevel()->sendCompleteConfigure(screenSize, { XdgToplevel::state_activated, XdgToplevel::state_fullscreen }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, fullscreenSerial); + QCOMPARE(window.visibility(), QWindow::FullScreen); + QCOMPARE(window.windowStates(), Qt::WindowFullScreen); + QCOMPARE(window.frameGeometry().size(), screenSize); +// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled + + // The window should remember its original size + const uint restoreSerial = exec([=] { + return xdgToplevel()->sendCompleteConfigure({0, 0}, { XdgToplevel::state_activated }); + }); + QCOMPOSITOR_TRY_COMPARE(xdgSurface()->m_committedConfigureSerial, restoreSerial); + QCOMPARE(window.visibility(), QWindow::Windowed); + QCOMPARE(window.windowStates(), Qt::WindowNoState); + QCOMPARE(window.frameGeometry().size(), windowedSize); +// QCOMPARE(window.frameGeometry().topLeft(), QPoint()); // TODO: this doesn't currently work when window decorations are enabled +} + +void tst_xdgshell::popup() +{ + class Window : public QRasterWindow { + public: + void mousePressEvent(QMouseEvent *event) override + { + QRasterWindow::mousePressEvent(event); + m_popup.reset(new QRasterWindow); + m_popup->setTransientParent(this); + m_popup->setFlags(Qt::Popup); + m_popup->resize(100, 100); + m_popup->show(); + } + QScopedPointer<QRasterWindow> m_popup; + }; + Window window; + window.resize(200, 200); + window.show(); + + QCOMPOSITOR_TRY_VERIFY(xdgToplevel()); + QSignalSpy toplevelConfigureSpy(exec([=] { return xdgSurface(); }), &XdgSurface::configureCommitted); + exec([=] { xdgToplevel()->sendCompleteConfigure(); }); + QTRY_COMPARE(toplevelConfigureSpy.count(), 1); + + uint clickSerial = exec([=] { + auto *surface = xdgToplevel()->surface(); + auto *p = pointer(); + p->sendEnter(surface, {100, 100}); +// p->sendFrame(); //TODO: uncomment when we support seat v5 + uint serial = p->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed); + p->sendButton(client(), BTN_LEFT, Pointer::button_state_released); + return serial; +// p->sendFrame(); //TODO: uncomment when we support seat v5 + }); + + QTRY_VERIFY(window.m_popup); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()); + QSignalSpy popupConfigureSpy(exec([=] { return xdgPopup()->m_xdgSurface; }), &XdgSurface::configureCommitted); + QCOMPOSITOR_TRY_VERIFY(xdgPopup()->m_grabbed); + QCOMPOSITOR_TRY_COMPARE(xdgPopup()->m_grabSerial, clickSerial); + + QRasterWindow *popup = window.m_popup.get(); + QVERIFY(!popup->isExposed()); // wait for configure + + //TODO: Verify it works with a different configure window geometry + exec([=] { xdgPopup()->sendConfigure(QRect(100, 100, 100, 100)); }); + + // Nothing should happen before the *xdg_surface* configure + QTRY_VERIFY(!popup->isExposed()); // Popup shouldn't be exposed before the first configure event + QVERIFY(popupConfigureSpy.isEmpty()); + + const uint configureSerial = exec([=] { + return xdgPopup()->m_xdgSurface->sendConfigure(); + }); + + // Finally, we're exposed + QTRY_VERIFY(popup->isExposed()); + + // The client is now going to ack the configure + QTRY_COMPARE(popupConfigureSpy.count(), 1); + QCOMPARE(popupConfigureSpy.takeFirst().at(0).toUInt(), configureSerial); + + // And attach a buffer + exec([&] { + Buffer *buffer = xdgPopup()->surface()->m_committed.buffer; + QVERIFY(buffer); + QCOMPARE(buffer->size(), popup->frameGeometry().size()); + }); +} + +void tst_xdgshell::pongs() +{ + QSignalSpy pongSpy(exec([=] { return get<XdgWmBase>(); }), &XdgWmBase::pong); + // Verify that the client has bound to the global + QCOMPOSITOR_TRY_COMPARE(get<XdgWmBase>()->resourceMap().size(), 1); + const uint serial = exec([=] { return nextSerial(); }); + exec([=] { + auto *base = get<XdgWmBase>(); + wl_resource *resource = base->resourceMap().first()->handle; + base->send_ping(resource, serial); + }); + QTRY_COMPARE(pongSpy.count(), 1); + QCOMPARE(pongSpy.first().at(0).toUInt(), serial); +} + +QCOMPOSITOR_TEST_MAIN(tst_xdgshell) +#include "tst_xdgshell.moc" diff --git a/tests/auto/client/xdgshell/xdgshell.pro b/tests/auto/client/xdgshell/xdgshell.pro new file mode 100644 index 000000000..d7c3f9df6 --- /dev/null +++ b/tests/auto/client/xdgshell/xdgshell.pro @@ -0,0 +1,5 @@ +include (../shared/shared.pri) + +TARGET = tst_xdgshell +SOURCES += tst_xdgshell.cpp + diff --git a/tests/auto/client/xdgshellv6/xdgshellv6.pro b/tests/auto/client/xdgshellv6/xdgshellv6.pro index 4fec593df..cc8a22d83 100644 --- a/tests/auto/client/xdgshellv6/xdgshellv6.pro +++ b/tests/auto/client/xdgshellv6/xdgshellv6.pro @@ -1,4 +1,4 @@ -include (../shared/shared.pri) +include (../shared_old/shared_old.pri) TARGET = tst_client_xdgshellv6 SOURCES += tst_xdgshellv6.cpp diff --git a/tests/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro index 0ce2c6be0..4e5cf50b8 100644 --- a/tests/auto/compositor/compositor/compositor.pro +++ b/tests/auto/compositor/compositor/compositor.pro @@ -13,6 +13,8 @@ qtConfig(xkbcommon): \ WAYLANDCLIENTSOURCES += \ ../../../../src/3rdparty/protocol/xdg-shell-unstable-v5.xml \ ../../../../src/3rdparty/protocol/ivi-application.xml \ + ../../../../src/3rdparty/protocol/wayland.xml \ + ../../../../src/3rdparty/protocol/viewporter.xml SOURCES += \ tst_compositor.cpp \ diff --git a/tests/auto/compositor/compositor/mockclient.cpp b/tests/auto/compositor/compositor/mockclient.cpp index f74314407..b6cb4ab46 100644 --- a/tests/auto/compositor/compositor/mockclient.cpp +++ b/tests/auto/compositor/compositor/mockclient.cpp @@ -173,6 +173,8 @@ void MockClient::handleGlobal(uint32_t id, const QByteArray &interface) wl_output_add_listener(output, &outputListener, this); } else if (interface == "wl_shm") { shm = static_cast<wl_shm *>(wl_registry_bind(registry, id, &wl_shm_interface, 1)); + } else if (interface == "wp_viewporter") { + viewporter = static_cast<wp_viewporter *>(wl_registry_bind(registry, id, &wp_viewporter_interface, 1)); } else if (interface == "wl_shell") { wlshell = static_cast<wl_shell *>(wl_registry_bind(registry, id, &wl_shell_interface, 1)); } else if (interface == "xdg_shell") { diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h index 6bfb652ed..bf5d8fc88 100644 --- a/tests/auto/compositor/compositor/mockclient.h +++ b/tests/auto/compositor/compositor/mockclient.h @@ -26,9 +26,10 @@ ** ****************************************************************************/ -#include <wayland-client.h> +#include "wayland-wayland-client-protocol.h" #include <qwayland-xdg-shell-unstable-v5.h> #include <wayland-ivi-application-client-protocol.h> +#include "wayland-viewporter-client-protocol.h" #include <QObject> #include <QImage> @@ -69,6 +70,7 @@ public: wl_registry *registry = nullptr; wl_shell *wlshell = nullptr; xdg_shell *xdgShell = nullptr; + wp_viewporter *viewporter = nullptr; ivi_application *iviApplication = nullptr; QList<MockSeat *> m_seats; diff --git a/tests/auto/compositor/compositor/mockkeyboard.h b/tests/auto/compositor/compositor/mockkeyboard.h index 1090db597..fd7f06aee 100644 --- a/tests/auto/compositor/compositor/mockkeyboard.h +++ b/tests/auto/compositor/compositor/mockkeyboard.h @@ -30,7 +30,7 @@ #define MOCKKEYBOARD_H #include <QObject> -#include <wayland-client.h> +#include "wayland-wayland-client-protocol.h" class MockKeyboard : public QObject { diff --git a/tests/auto/compositor/compositor/mockpointer.h b/tests/auto/compositor/compositor/mockpointer.h index 2054040fd..db6b2b69c 100644 --- a/tests/auto/compositor/compositor/mockpointer.h +++ b/tests/auto/compositor/compositor/mockpointer.h @@ -30,7 +30,7 @@ #define MOCKPOINTER_H #include <QObject> -#include <wayland-client.h> +#include "wayland-wayland-client-protocol.h" class MockPointer : public QObject { diff --git a/tests/auto/compositor/compositor/mockseat.h b/tests/auto/compositor/compositor/mockseat.h index f8c103ed4..0d0f4074c 100644 --- a/tests/auto/compositor/compositor/mockseat.h +++ b/tests/auto/compositor/compositor/mockseat.h @@ -32,7 +32,7 @@ #include "mockkeyboard.h" #include <QObject> -#include <wayland-client.h> +#include "wayland-wayland-client-protocol.h" class MockSeat : public QObject { diff --git a/tests/auto/compositor/compositor/testcompositor.cpp b/tests/auto/compositor/compositor/testcompositor.cpp index 710bb7b3a..22ecf28cb 100644 --- a/tests/auto/compositor/compositor/testcompositor.cpp +++ b/tests/auto/compositor/compositor/testcompositor.cpp @@ -30,7 +30,7 @@ #include "testseat.h" #include "testkeyboardgrabber.h" -#include <wayland-server.h> +#include <wayland-server-core.h> TestCompositor::TestCompositor(bool createInputDev) : shell(new QWaylandWlShell(this)) diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index e12aa564e..0e11618aa 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -46,6 +46,7 @@ #include <QtWaylandCompositor/QWaylandSurface> #include <QtWaylandCompositor/QWaylandResource> #include <QtWaylandCompositor/QWaylandKeymap> +#include <QtWaylandCompositor/QWaylandViewporter> #include <qwayland-xdg-shell-unstable-v5.h> #include <qwayland-ivi-application.h> @@ -68,6 +69,7 @@ private slots: void seatKeyboardFocus(); void seatMouseFocus(); void inputRegion(); + void defaultInputRegionHiDpi(); void singleClient(); void multipleClients(); void geometry(); @@ -94,6 +96,20 @@ private slots: void convertsXdgEdgesToQtEdges(); void xdgShellV6Positioner(); + + void viewporterGlobal(); + void viewportDestination(); + void viewportSource(); + void viewportSourceAndDestination(); + void viewportDestruction(); + void viewportProtocolErrors_data(); + void viewportProtocolErrors(); + void viewportClearDestination(); + void viewportClearSource(); + void viewportExistsError(); + void viewportDestinationNoSurfaceError(); + void viewportSourceNoSurfaceError(); + void viewportHiDpi(); }; void tst_WaylandCompositor::init() { @@ -434,7 +450,8 @@ void tst_WaylandCompositor::mapSurface() QSignalSpy hasContentSpy(waylandSurface, SIGNAL(hasContentChanged())); - QCOMPARE(waylandSurface->size(), QSize()); + QCOMPARE(waylandSurface->bufferSize(), QSize()); + QCOMPARE(waylandSurface->destinationSize(), QSize()); QCOMPARE(waylandSurface->hasContent(), false); QSize size(256, 256); @@ -448,7 +465,8 @@ void tst_WaylandCompositor::mapSurface() QTRY_COMPARE(hasContentSpy.count(), 1); QCOMPARE(waylandSurface->hasContent(), true); - QCOMPARE(waylandSurface->size(), size); + QCOMPARE(waylandSurface->bufferSize(), size); + QCOMPARE(waylandSurface->destinationSize(), size); wl_surface_destroy(surface); } @@ -478,7 +496,8 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() wl_surface_damage(surface, 0, 0, surfaceSize.width(), surfaceSize.height()); auto verifyComittedState = [=]() { - QCOMPARE(waylandSurface->size(), bufferSize); + QCOMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), surfaceSize); QCOMPARE(waylandSurface->bufferScale(), bufferScale); QCOMPARE(waylandSurface->hasContent(), true); }; @@ -497,6 +516,9 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() QObject::connect(waylandSurface, &QWaylandSurface::sizeChanged, verifyComittedState); QSignalSpy sizeSpy(waylandSurface, SIGNAL(sizeChanged())); + QObject::connect(waylandSurface, &QWaylandSurface::destinationSizeChanged, verifyComittedState); + QSignalSpy destinationSizeSpy(waylandSurface, SIGNAL(destinationSizeChanged())); + QObject::connect(waylandSurface, &QWaylandSurface::bufferScaleChanged, verifyComittedState); QSignalSpy bufferScaleSpy(waylandSurface, SIGNAL(bufferScaleChanged())); @@ -507,7 +529,8 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() QSignalSpy offsetSpy(waylandSurface, SIGNAL(offsetForNextFrame(const QPoint &))); // No state should be applied before the commit - QCOMPARE(waylandSurface->size(), QSize()); + QCOMPARE(waylandSurface->bufferSize(), QSize()); + QCOMPARE(waylandSurface->destinationSize(), QSize()); QCOMPARE(waylandSurface->hasContent(), false); QCOMPARE(waylandSurface->bufferScale(), 1); QCOMPARE(offsetSpy.count(), 0); @@ -516,6 +539,7 @@ void tst_WaylandCompositor::mapSurfaceHiDpi() QTRY_COMPARE(hasContentSpy.count(), 1); QTRY_COMPARE(sizeSpy.count(), 1); + QTRY_COMPARE(destinationSizeSpy.count(), 1); QTRY_COMPARE(bufferScaleSpy.count(), 1); QTRY_COMPARE(offsetSpy.count(), 1); @@ -826,6 +850,33 @@ void tst_WaylandCompositor::inputRegion() QVERIFY(!waylandSurface->inputRegionContains(QPoint(1, 2))); } +void tst_WaylandCompositor::defaultInputRegionHiDpi() +{ + TestCompositor compositor(true); + compositor.create(); + + MockClient client; + wl_surface *surface = client.createSurface(); + + int bufferScale = 2; + QSize surfaceSize(16, 16); + QSize bufferSize = surfaceSize * bufferScale; + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, surfaceSize.width(), surfaceSize.height()); + wl_surface_set_buffer_scale(surface, bufferScale); + wl_surface_commit(surface); + + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->bufferScale(), bufferScale); + QVERIFY(waylandSurface->inputRegionContains(QPoint(0, 0))); + QVERIFY(waylandSurface->inputRegionContains(QPoint(15, 15))); + QVERIFY(!waylandSurface->inputRegionContains(QPoint(-1, -1))); + QVERIFY(!waylandSurface->inputRegionContains(QPoint(16, 16))); +} + class XdgTestCompositor: public TestCompositor { Q_OBJECT public: @@ -1211,5 +1262,406 @@ void tst_WaylandCompositor::xdgShellV6Positioner() QCOMPARE(p.unconstrainedPosition(), QPoint(1 + 800 - 100 / 2 + 4, 2 + 600 / 2 - 50 + 8)); } +class ViewporterTestCompositor: public TestCompositor { + Q_OBJECT +public: + ViewporterTestCompositor() : viewporter(this) {} + QWaylandViewporter viewporter; +}; + +void tst_WaylandCompositor::viewporterGlobal() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); +} + +void tst_WaylandCompositor::viewportDestination() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + const QSize destinationSize(128, 123); + wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), QSize(128, 123)); + QCOMPARE(waylandSurface->sourceGeometry(), QRect(QPoint(), bufferSize)); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportSource() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + const QRectF sourceGeometry(QPointF(10.5, 20.5), QSizeF(30, 40)); + wp_viewport_set_source(viewport, + wl_fixed_from_double(sourceGeometry.x()), + wl_fixed_from_double(sourceGeometry.y()), + wl_fixed_from_double(sourceGeometry.width()), + wl_fixed_from_double(sourceGeometry.height())); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), sourceGeometry.size().toSize()); + QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportSourceAndDestination() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + + const QSize destinationSize(128, 123); + wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height()); + + const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40)); + wp_viewport_set_source(viewport, + wl_fixed_from_double(sourceGeometry.x()), + wl_fixed_from_double(sourceGeometry.y()), + wl_fixed_from_double(sourceGeometry.width()), + wl_fixed_from_double(sourceGeometry.height())); + + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), destinationSize); + QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportDestruction() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + + const QSize destinationSize(128, 123); + wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height()); + + const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40)); + wp_viewport_set_source(viewport, + wl_fixed_from_double(sourceGeometry.x()), + wl_fixed_from_double(sourceGeometry.y()), + wl_fixed_from_double(sourceGeometry.width()), + wl_fixed_from_double(sourceGeometry.height())); + + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), destinationSize); + QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry); + + wp_viewport_destroy(viewport); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize); + QCOMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize)); + + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportProtocolErrors_data() +{ + QTest::addColumn<QRectF>("source"); + QTest::addColumn<QSize>("destination"); + QTest::addColumn<uint>("error"); + + QTest::newRow("invalid source position") << QRectF(-1, 0, 16, 16) << QSize(64, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE); + QTest::newRow("invalid source size") << QRectF(0, 0, -1, 16) << QSize(64, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE); + QTest::newRow("invalid destination size") << QRectF(0, 0, 16, 16) << QSize(-16, 64) << uint(WP_VIEWPORT_ERROR_BAD_VALUE); + QTest::newRow("invalid non-integer source with unset size") << QRectF(0, 0, 15.5, 15.5) << QSize(-1, -1) << uint(WP_VIEWPORT_ERROR_BAD_SIZE); + QTest::newRow("bigger source than buffer") << QRectF(0, 0, 13337, 13337) << QSize(-1, -1) << uint(WP_VIEWPORT_ERROR_OUT_OF_BUFFER); +} + +void tst_WaylandCompositor::viewportProtocolErrors() +{ + QFETCH(QRectF, source); + QFETCH(QSize, destination); + QFETCH(uint, error); + + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + wp_viewport_set_source(viewport, + wl_fixed_from_double(source.x()), + wl_fixed_from_double(source.y()), + wl_fixed_from_double(source.width()), + wl_fixed_from_double(source.height())); + wp_viewport_set_destination(viewport, destination.width(), destination.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(client.error, EPROTO); + QCOMPARE(client.protocolError.interface, &wp_viewport_interface); + QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), error); +} + +void tst_WaylandCompositor::viewportClearDestination() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + + const QSize destinationSize(128, 123); + wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->bufferSize(), bufferSize); + QCOMPARE(waylandSurface->destinationSize(), destinationSize); + + wp_viewport_set_destination(viewport, -1, -1); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize); + QCOMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize)); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportClearSource() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + QCOMPARE(waylandSurface->destinationSize(), QSize()); + QCOMPARE(waylandSurface->sourceGeometry(), QRect()); + + const QSize bufferSize(64, 64); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + QRectF source(10, 20, 30, 40); + wp_viewport_set_source(viewport, + wl_fixed_from_double(source.x()), + wl_fixed_from_double(source.y()), + wl_fixed_from_double(source.width()), + wl_fixed_from_double(source.height())); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->sourceGeometry(), source); + + wp_viewport_set_source(viewport, + wl_fixed_from_double(-1), + wl_fixed_from_double(-1), + wl_fixed_from_double(-1), + wl_fixed_from_double(-1)); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->sourceGeometry(), QRectF(QPoint(), bufferSize)); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); + QCOMPARE(client.error, 0); +} + +void tst_WaylandCompositor::viewportExistsError() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + wp_viewporter_get_viewport(client.viewporter, surface); + wp_viewporter_get_viewport(client.viewporter, surface); + + QTRY_COMPARE(client.error, EPROTO); + QCOMPARE(client.protocolError.interface, &wp_viewporter_interface); + QCOMPARE(static_cast<wp_viewporter_error>(client.protocolError.code), WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS); +} + +void tst_WaylandCompositor::viewportDestinationNoSurfaceError() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + wl_surface_destroy(surface); + wp_viewport_set_destination(viewport, 32, 32); + + QTRY_COMPARE(client.error, EPROTO); + QCOMPARE(client.protocolError.interface, &wp_viewport_interface); + QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), WP_VIEWPORT_ERROR_NO_SURFACE); +} + +void tst_WaylandCompositor::viewportSourceNoSurfaceError() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + wl_surface_destroy(surface); + wp_viewport_set_source(viewport, + wl_fixed_from_double(0), + wl_fixed_from_double(0), + wl_fixed_from_double(1), + wl_fixed_from_double(1)); + + QTRY_COMPARE(client.error, EPROTO); + QCOMPARE(client.protocolError.interface, &wp_viewport_interface); + QCOMPARE(static_cast<wp_viewport_error>(client.protocolError.code), WP_VIEWPORT_ERROR_NO_SURFACE); +} + +void tst_WaylandCompositor::viewportHiDpi() +{ + ViewporterTestCompositor compositor; + compositor.create(); + MockClient client; + QTRY_VERIFY(client.viewporter); + + wl_surface *surface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + QWaylandSurface *waylandSurface = compositor.surfaces.at(0); + + const QSize bufferSize(128, 128); + ShmBuffer buffer(bufferSize, client.shm); + wl_surface_attach(surface, buffer.handle, 0, 0); + wl_surface_damage(surface, 0, 0, bufferSize.width(), bufferSize.height()); + constexpr int bufferScale = 2; + wl_surface_set_buffer_scale(surface, bufferScale); + + wl_surface_commit(surface); + QTRY_COMPARE(waylandSurface->destinationSize(), bufferSize / bufferScale); + + wp_viewport *viewport = wp_viewporter_get_viewport(client.viewporter, surface); + const QRectF sourceGeometry(QPointF(10, 20), QSizeF(30, 40)); + wp_viewport_set_source(viewport, + wl_fixed_from_double(sourceGeometry.x()), + wl_fixed_from_double(sourceGeometry.y()), + wl_fixed_from_double(sourceGeometry.width()), + wl_fixed_from_double(sourceGeometry.height())); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->destinationSize(), sourceGeometry.size()); + QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry); + QCOMPARE(waylandSurface->bufferSize(), bufferSize); + + const QSize destinationSize(128, 123); + wp_viewport_set_destination(viewport, destinationSize.width(), destinationSize.height()); + wl_surface_commit(surface); + + QTRY_COMPARE(waylandSurface->destinationSize(), destinationSize); + QCOMPARE(waylandSurface->sourceGeometry(), sourceGeometry); + QCOMPARE(waylandSurface->bufferSize(), bufferSize); + + QCOMPARE(client.error, 0); + + wp_viewport_destroy(viewport); + wl_surface_destroy(surface); +} + #include <tst_compositor.moc> QTEST_MAIN(tst_WaylandCompositor); |