diff options
Diffstat (limited to 'tests/auto/client/shared')
40 files changed, 2949 insertions, 2417 deletions
diff --git a/tests/auto/client/shared/CMakeLists.txt b/tests/auto/client/shared/CMakeLists.txt new file mode 100644 index 000000000..1d64f2956 --- /dev/null +++ b/tests/auto/client/shared/CMakeLists.txt @@ -0,0 +1,73 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +##################################################################### +##Client test shared components: +##################################################################### + +qt_manual_moc(moc_files + mockcompositor.h + coreprotocol.h + corecompositor.h + datadevice.h + fullscreenshellv1.h + fractionalscalev1.h + iviapplication.h + textinput.h + qttextinput.h + viewport.h + xdgdialog.h + xdgoutputv1.h + xdgshell.h +) + +add_library(SharedClientTest + OBJECT + corecompositor.cpp corecompositor.h + coreprotocol.cpp coreprotocol.h + datadevice.cpp datadevice.h + fullscreenshellv1.cpp fullscreenshellv1.h + fractionalscalev1.cpp fractionalscalev1.h + iviapplication.cpp iviapplication.h + mockcompositor.cpp mockcompositor.h + textinput.cpp textinput.h + qttextinput.cpp qttextinput.h + xdgoutputv1.cpp xdgoutputv1.h + xdgshell.cpp xdgshell.h + xdgdialog.cpp xdgdialog.h + viewport.cpp viewport.h + ${moc_files} +) + +qt6_generate_wayland_protocol_server_sources(SharedClientTest + FILES + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/cursor-shape-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/fullscreen-shell-unstable-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/ivi-application.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/wp-primary-selection-unstable-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/tablet-unstable-v2.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/text-input-unstable-v2.xml + ${PROJECT_SOURCE_DIR}/src/extensions/qt-text-input-method-unstable-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/fractional-scale-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/viewporter.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/wayland.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/xdg-decoration-unstable-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/xdg-dialog-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/xdg-output-unstable-v1.xml + ${PROJECT_SOURCE_DIR}/src/3rdparty/protocol/xdg-shell.xml +) + +if(QT_FEATURE_opengl) + set(optional_libraries Qt::OpenGL) +endif() + +target_link_libraries(SharedClientTest + PUBLIC + Qt::Gui + ${optional_libraries} + Qt::WaylandClientPrivate + Wayland::Server + Threads::Threads # special case +) + +target_include_directories(SharedClientTest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/auto/client/shared/corecompositor.cpp b/tests/auto/client/shared/corecompositor.cpp new file mode 100644 index 000000000..d110768ec --- /dev/null +++ b/tests/auto/client/shared/corecompositor.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "corecompositor.h" +#include <thread> + +namespace MockCompositor { + +CoreCompositor::CoreCompositor(CompositorType t, int socketFd) + : m_type(t) + , m_display(wl_display_create()) + , 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(); + } + }) +{ + if (socketFd == -1) { + QByteArray socketName = wl_display_add_socket_auto(m_display); + qputenv("WAYLAND_DISPLAY", socketName); + } else { + wl_display_add_socket_fd(m_display, socketFd); + } + m_timer.start(); + Q_ASSERT(isClean()); +} + +CoreCompositor::~CoreCompositor() +{ + m_running = false; + m_dispatchThread.join(); + wl_display_destroy_clients(m_display); + wl_display_destroy(m_display); + qDebug() << "cleanup"; +} + +bool CoreCompositor::isClean() +{ + Lock lock(this); + for (auto *global : std::as_const(m_globals)) { + if (!global->isClean()) + return false; + } + return true; +} + +QString CoreCompositor::dirtyMessage() +{ + Lock lock(this); + QStringList messages; + for (auto *global : std::as_const(m_globals)) { + if (!global->isClean()) + messages << (global->metaObject()->className() % QLatin1String(": ") % global->dirtyMessage()); + } + return messages.join(", "); +} + +void CoreCompositor::dispatch(int timeout) +{ + Lock lock(this); + wl_display_flush_clients(m_display); + 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); + m_globals.removeAll(global); + delete 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..2df1b5758 --- /dev/null +++ b/tests/auto/client/shared/corecompositor.h @@ -0,0 +1,208 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#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: + enum CompositorType { + Default, + Legacy // wl-shell + }; + + CompositorType m_type = Default; + explicit CoreCompositor(CompositorType t = Default, int socketFd = -1); + + ~CoreCompositor(); + bool isClean(); + QString dirtyMessage(); + void dispatch(int timeout = 0); + + 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 : std::as_const(m_globals)) { + if (auto *casted = qobject_cast<global_type *>(global)) + return casted; + } + return nullptr; + } + + /*! + * \brief Returns the nth global with the given type, if any + */ + template<typename global_type> + global_type *get(int index) + { + warnIfNotLockedByThread(Q_FUNC_INFO); + for (auto *global : std::as_const(m_globals)) { + if (auto *casted = qobject_cast<global_type *>(global)) { + if (index--) + continue; + return casted; + } + } + return nullptr; + } + + /*! + * \brief Returns all globals with the given type, if any + */ + template<typename global_type> + QList<global_type *> getAll() + { + warnIfNotLockedByThread(Q_FUNC_INFO); + QList<global_type *> matching; + for (auto *global : std::as_const(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; + }; + wl_event_loop *m_eventLoop = nullptr; + bool m_running = true; + QList<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..5d9c4e9a3 --- /dev/null +++ b/tests/auto/client/shared/coreprotocol.cpp @@ -0,0 +1,650 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "coreprotocol.h" +#include "datadevice.h" + +namespace MockCompositor { + +Surface::Surface(WlCompositor *wlCompositor, wl_client *client, int id, int version) + : QtWaylandServer::wl_surface(client, id, version) + , m_wlCompositor(wlCompositor) + , m_wlshell(wlCompositor->m_compositor->m_type == CoreCompositor::CompositorType::Legacy) +{ +} + +Surface::~Surface() +{ + // TODO: maybe make sure buffers are released? + qDeleteAll(m_commits); + if (m_wlShellSurface) + m_wlShellSurface->m_surface = nullptr; +} + +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::map() +{ + m_mapped = true; +} + +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_destroy(Resource *resource) +{ + if (m_wlShellSurface) // on wl-shell the shell surface is automatically destroyed with the surface + wl_resource_destroy(m_wlShellSurface->resource()->handle); + Q_ASSERT(!m_wlShellSurface); + wl_resource_destroy(resource->handle); +} + +void Surface::surface_attach(Resource *resource, wl_resource *buffer, int32_t x, int32_t y) +{ + Q_UNUSED(resource); + if (m_wlshell) { + m_buffer = buffer; + if (!buffer) + m_image = QImage(); + } else { + QPoint offset(x, y); + if (resource->version() < 5) + m_pending.commitSpecific.attachOffset = offset; + + m_pending.buffer = fromResource<Buffer>(buffer); + 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); + + if (m_wlshell) { + if (m_buffer) { + struct ::wl_shm_buffer *shm_buffer = wl_shm_buffer_get(m_buffer); + if (shm_buffer) { + int stride = wl_shm_buffer_get_stride(shm_buffer); + uint format = wl_shm_buffer_get_format(shm_buffer); + Q_UNUSED(format); + void *data = wl_shm_buffer_get_data(shm_buffer); + const uchar *char_data = static_cast<const uchar *>(data); + QImage img(char_data, wl_shm_buffer_get_width(shm_buffer), wl_shm_buffer_get_height(shm_buffer), stride, QImage::Format_ARGB32_Premultiplied); + m_image = img; + } + } + + for (wl_resource *frameCallback : std::exchange(m_frameCallbackList, {})) { + auto time = m_wlCompositor->m_compositor->currentTimeMilliseconds(); + wl_callback_send_done(frameCallback, time); + wl_resource_destroy(frameCallback); + } + } else { + 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) +{ + if (m_wlshell) { + wl_resource *frameCallback = wl_resource_create(resource->client(), &wl_callback_interface, 1, callback); + m_frameCallbackList << frameCallback; + } else { + // 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; + } +} + +void Surface::surface_offset(Resource *resource, int32_t x, int32_t y) +{ + Q_UNUSED(resource); + QPoint offset(x, y); + m_pending.commitSpecific.attachOffset = offset; +} + +bool WlCompositor::isClean() { + for (auto *surface : std::as_const(m_surfaces)) { + if (!CursorRole::fromSurface(surface)) { + if (m_compositor->m_type != CoreCompositor::CompositorType::Legacy) + return false; + if (surface->isMapped()) + return false; + } + } + return true; +} + +QString WlCompositor::dirtyMessage() +{ + if (isClean()) + return "clean"; + QStringList messages; + for (auto *s : std::as_const(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::sendGeometry() +{ + const auto resources = resourceMap().values(); + for (auto r : resources) + sendGeometry(r); +} + +void Output::sendGeometry(Resource *resource) +{ + // TODO: check resource version as well? + wl_output::send_geometry(resource->handle, + m_data.position.x(), m_data.position.y(), + m_data.physicalSize.width(), m_data.physicalSize.height(), + m_data.subpixel, m_data.make, m_data.model, m_data.transform); +} + +void Output::sendScale(int factor) +{ + Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION); + m_data.scale = factor; + const auto resources = resourceMap().values(); + for (auto r : resources) + sendScale(r); +} + +void Output::sendScale(Resource *resource) +{ + Q_ASSERT(m_version >= WL_OUTPUT_SCALE_SINCE_VERSION); + // TODO: check resource version as well? + wl_output::send_scale(resource->handle, m_data.scale); +} + +void Output::sendDone(wl_client *client) +{ + Q_ASSERT(m_version >= WL_OUTPUT_DONE_SINCE_VERSION); + auto resources = resourceMap().values(client); + for (auto *r : resources) + wl_output::send_done(r->handle); +} + +void Output::sendDone() +{ + Q_ASSERT(m_version >= WL_OUTPUT_DONE_SINCE_VERSION); + // TODO: check resource version as well? + 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) +{ + sendGeometry(resource); + send_mode(resource->handle, mode_preferred | mode_current, + m_data.mode.resolution.width(), m_data.mode.resolution.height(), m_data.mode.refreshRate); + if (m_version >= WL_OUTPUT_SCALE_SINCE_VERSION) + sendScale(resource); + + if (m_version >= WL_OUTPUT_DONE_SINCE_VERSION) + wl_output::send_done(resource->handle); + + Q_EMIT outputBound(resource); +} + +// 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; + + qDeleteAll(m_oldTouchs); + delete m_touch; + + qDeleteAll(m_oldKeyboards); + delete m_keyboard; +} + +void Seat::setCapabilities(uint capabilities) { + 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; + } + + if (m_capabilities & capability_touch) { + if (!m_touch) + m_touch = (new Touch(this)); + } else if (m_touch) { + m_oldTouchs << m_touch; + m_touch = nullptr; + } + + if (m_capabilities & capability_keyboard) { + if (!m_keyboard) + m_keyboard = (new Keyboard(this)); + } else if (m_keyboard) { + m_oldKeyboards << m_keyboard; + m_keyboard = 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()); +} + +void Seat::seat_get_touch(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) +{ + if (~m_capabilities & capability_touch) { + qWarning() << "Client requested a wl_touch without the capability being available." + << "This Could be a race condition when hotunplugging," + << "but is most likely a client error"; + Touch *touch = new Touch(this); + touch->add(resource->client(), id, resource->version()); + // TODO: mark as destroyed + m_oldTouchs << touch; + return; + } + m_touch->add(resource->client(), id, resource->version()); +} + +void Seat::seat_get_keyboard(QtWaylandServer::wl_seat::Resource *resource, uint32_t id) +{ + if (~m_capabilities & capability_keyboard) { + qWarning() << "Client requested a wl_keyboard without the capability being available." + << "This Could be a race condition when hotunplugging," + << "but is most likely a client error"; + Keyboard *keyboard = new Keyboard(this); + keyboard->add(resource->client(), id, resource->version()); + // TODO: mark as destroyed + m_oldKeyboards << keyboard; + return; + } + m_keyboard->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()); + + uint serial = m_seat->m_compositor->nextSerial(); + m_enterSerials << serial; + m_cursorRole.clear(); // According to the protocol, the pointer image is undefined after enter + + wl_client *client = surface->resource()->client(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) { + wl_pointer::send_enter(r->handle, serial, surface->resource()->handle, x ,y); + } + return serial; +} + +uint Pointer::sendLeave(Surface *surface) +{ + uint serial = m_seat->m_compositor->nextSerial(); + + wl_client *client = surface->resource()->client(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + wl_pointer::send_leave(r->handle, serial, surface->resource()->handle); + return serial; +} + +// 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::sendAxisDiscrete(wl_client *client, QtWaylandServer::wl_pointer::axis axis, int discrete) +{ + // TODO: assert v5 or newer + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_discrete(r->handle, axis, discrete); +} + +void Pointer::sendAxisSource(wl_client *client, QtWaylandServer::wl_pointer::axis_source source) +{ + // TODO: assert v5 or newer + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_source(r->handle, source); +} + +void Pointer::sendAxisStop(wl_client *client, QtWaylandServer::wl_pointer::axis axis) +{ + // TODO: assert v5 or newer + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_stop(r->handle, time, axis); +} + +void Pointer::sendFrame(wl_client *client) +{ + //TODO: assert version 5 or newer? + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_frame(r->handle); +} + +void Pointer::sendAxisValue120(wl_client *client, QtWaylandServer::wl_pointer::axis axis, int value120) +{ + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_value120(r->handle, axis, value120); +} + +void Pointer::sendAxisRelativeDirection(wl_client *client, QtWaylandServer::wl_pointer::axis axis, QtWaylandServer::wl_pointer::axis_relative_direction direction) +{ + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_axis_relative_direction(r->handle, axis, direction); +} + +void Pointer::pointer_set_cursor(Resource *resource, uint32_t serial, wl_resource *surface, int32_t hotspot_x, int32_t hotspot_y) +{ + Q_UNUSED(resource); + + if (!surface) { + m_cursorRole = nullptr; + } else { + auto *s = fromResource<Surface>(surface); + QVERIFY(s); + if (s->m_role) { + m_cursorRole = CursorRole::fromSurface(s); + QVERIFY(m_cursorRole); + } else { + m_cursorRole = new CursorRole(s); //TODO: make sure we don't leak CursorRole + s->m_role = m_cursorRole; + } + } + + // Directly checking the last serial would be racy, we may just have sent leaves/enters which + // the client hasn't yet seen. Instead just check if the serial matches an enter serial since + // the last time the client sent a set_cursor request. + QVERIFY(m_enterSerials.contains(serial)); + while (m_enterSerials.first() < serial) { m_enterSerials.removeFirst(); } + + m_hotspot = QPoint(hotspot_x, hotspot_y); + emit setCursor(serial); +} + +uint Touch::sendDown(Surface *surface, const QPointF &position, int id) +{ + wl_fixed_t x = wl_fixed_from_double(position.x()); + wl_fixed_t y = wl_fixed_from_double(position.y()); + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + wl_client *client = surface->resource()->client(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_down(r->handle, serial, time, surface->resource()->handle, id, x, y); + + return serial; +} + +uint Touch::sendUp(wl_client *client, int id) +{ + uint serial = m_seat->m_compositor->nextSerial(); + auto time = m_seat->m_compositor->currentTimeMilliseconds(); + + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_up(r->handle, serial, time, id); + + return serial; +} + +void Touch::sendMotion(wl_client *client, const QPointF &position, int id) +{ + 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 touchResources = resourceMap().values(client); + for (auto *r : touchResources) + wl_touch::send_motion(r->handle, time, id, x, y); +} + +void Touch::sendFrame(wl_client *client) +{ + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + send_frame(r->handle); +} + +void Touch::sendCancel(wl_client *client) +{ + const auto touchResources = resourceMap().values(client); + for (auto *r : touchResources) + send_cancel(r->handle); +} + +uint Keyboard::sendEnter(Surface *surface) +{ + auto serial = 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, serial, surface->resource()->handle, QByteArray()); + m_enteredSurface = surface; + return serial; +} + +uint Keyboard::sendLeave(Surface *surface) +{ + auto serial = m_seat->m_compositor->nextSerial(); + wl_client *client = surface->resource()->client(); + const auto pointerResources = resourceMap().values(client); + for (auto *r : pointerResources) + send_leave(r->handle, serial, surface->resource()->handle); + m_enteredSurface = nullptr; + return serial; +} + +uint Keyboard::sendKey(wl_client *client, uint key, uint state) +{ + Q_ASSERT(state == key_state_pressed || state == key_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_key(r->handle, serial, time, key, state); + } + return serial; +} + +// Shm implementation +Shm::Shm(CoreCompositor *compositor, QList<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 : std::as_const(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; +} + +WlShell::WlShell(CoreCompositor *compositor, int version) + : QtWaylandServer::wl_shell(compositor->m_display, version) + , m_compositor(compositor) +{ +} + +void WlShell::shell_get_shell_surface(Resource *resource, uint32_t id, wl_resource *surface) +{ + auto *s = fromResource<Surface>(surface); + auto *wlShellSurface = new WlShellSurface(this, resource->client(), id, s); + m_wlShellSurfaces << wlShellSurface; + emit wlShellSurfaceCreated(wlShellSurface); +} + +WlShellSurface::WlShellSurface(WlShell *wlShell, wl_client *client, int id, Surface *surface) + : QtWaylandServer::wl_shell_surface(client, id, 1) + , m_wlShell(wlShell) + , m_surface(surface) +{ + surface->m_wlShellSurface = this; + surface->map(); +} + +WlShellSurface::~WlShellSurface() +{ + if (m_surface) + m_surface->m_wlShellSurface = nullptr; +} + +void WlShellSurface::sendConfigure(uint32_t edges, int32_t width, int32_t height) +{ + wl_shell_surface::send_configure(edges, width, height); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/coreprotocol.h b/tests/auto/client/shared/coreprotocol.h new file mode 100644 index 000000000..bea39dd13 --- /dev/null +++ b/tests/auto/client/shared/coreprotocol.h @@ -0,0 +1,464 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_COREPROTOCOL_H +#define MOCKCOMPOSITOR_COREPROTOCOL_H + +#include "corecompositor.h" + +#include <QtCore/qpointer.h> + +#include <qwayland-server-wayland.h> + +namespace MockCompositor { + +class WlCompositor; +class WlShell; +class WlShellSurface; +class Output; +class Pointer; +class Touch; +class Keyboard; +class CursorRole; +class ShmPool; +class ShmBuffer; +class DataDevice; + +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); + ~Surface() override; + void sendFrameCallbacks(); + void sendEnter(Output *output); + void send_enter(::wl_resource *output) = delete; + void sendLeave(Output *output); + void send_leave(::wl_resource *output) = delete; + + void map(); + bool isMapped() const { return m_mapped; } + WlShellSurface *wlShellSurface() const { return m_wlShellSurface; } + + 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; + QList<DoubleBufferedState *> m_commits; + QList<Callback *> m_waitingFrameCallbacks; + QList<Output *> m_outputs; + SurfaceRole *m_role = nullptr; + + WlShellSurface *m_wlShellSurface = nullptr; + bool m_mapped = false; + QList<wl_resource *> m_frameCallbackList; + + wl_resource *m_buffer = nullptr; + QImage m_image; // checking backingStore + bool m_wlshell = false; + +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; + 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; + void surface_offset(Resource *resource, int32_t x, int32_t y) override; +}; + +class Region : public QtWaylandServer::wl_region +{ +public: + explicit Region(wl_client *client, int id, int version) + : QtWaylandServer::wl_region(client, id, version) + { + } + + void region_destroy_resource(Resource *resource) override + { + Q_UNUSED(resource); + delete this; + } +}; + +class WlCompositor : public Global, public QtWaylandServer::wl_compositor +{ + Q_OBJECT +public: + explicit WlCompositor(CoreCompositor *compositor, int version = 6) + : QtWaylandServer::wl_compositor(compositor->m_display, version) + , m_compositor(compositor) + {} + bool isClean() override; + QString dirtyMessage() override; + QList<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); + } + + void compositor_create_region(Resource *resource, uint32_t id) override + { + new Region(resource->client(), id, resource->version()); + } +}; + +class WlShell : public Global, public QtWaylandServer::wl_shell +{ + Q_OBJECT +public: + explicit WlShell(CoreCompositor *compositor, int version = 1); + QList<WlShellSurface *> m_wlShellSurfaces; + CoreCompositor *m_compositor = nullptr; + +signals: + void wlShellSurfaceCreated(WlShellSurface *wlShellSurface); + +protected: + void shell_get_shell_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override; +}; + +class WlShellSurface : public QObject, public QtWaylandServer::wl_shell_surface +{ + Q_OBJECT +public: + explicit WlShellSurface(WlShell *wlShell, wl_client *client, int id, Surface *surface); + ~WlShellSurface() override; + void sendConfigure(uint32_t edges, int32_t width, int32_t height); + void send_configure(uint32_t edges, int32_t width, int32_t height) = delete; + + void shell_surface_destroy_resource(Resource *) override { delete this; } + + WlShell *m_wlShell = nullptr; + Surface *m_surface = nullptr; +}; + +class Subsurface : public QObject, public QtWaylandServer::wl_subsurface +{ + Q_OBJECT +public: + explicit Subsurface(wl_client *client, int id, int version) + : QtWaylandServer::wl_subsurface(client, id, version) + { + } +}; + +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) + {} + QList<Subsurface *> m_subsurfaces; + +signals: + void subsurfaceCreated(Subsurface *subsurface); + +protected: + void subcompositor_get_subsurface(Resource *resource, uint32_t id, ::wl_resource *surface, ::wl_resource *parent) override + { + QTRY_VERIFY(parent); + QTRY_VERIFY(surface); + auto *subsurface = new Subsurface(resource->client(), id, resource->version()); + m_subsurfaces.append(subsurface); // TODO: clean up? + emit subsurfaceCreated(subsurface); + } +}; + +struct OutputMode { + explicit OutputMode() = default; + explicit OutputMode(const QSize &resolution, int refreshRate = 60000) + : resolution(resolution), refreshRate(refreshRate) + {} + QSize resolution = QSize(1920, 1080); + int refreshRate = 60000; // In mHz + //TODO: flags (they're currently hard-coded) + + // in mm + QSize physicalSizeForDpi(int dpi) { return (QSizeF(resolution) * 25.4 / dpi).toSize(); } +}; + +struct OutputData { + using Subpixel = QtWaylandServer::wl_output::subpixel; + using Transform = QtWaylandServer::wl_output::transform; + explicit OutputData() = default; + + // for geometry event + QPoint position; + QSize physicalSize = QSize(0, 0); // means unknown physical size + QString make = "Make"; + QString model = "Model"; + Subpixel subpixel = Subpixel::subpixel_unknown; + Transform transform = Transform::transform_normal; + + int scale = 1; // for scale event + OutputMode mode; // for mode event +}; + +class Output : public Global, public QtWaylandServer::wl_output +{ + Q_OBJECT +public: + explicit Output(CoreCompositor *compositor, OutputData data = OutputData(), int version = 2) + : QtWaylandServer::wl_output(compositor->m_display, version) + , m_data(std::move(data)) + , m_version(version) + {} + + void send_geometry() = delete; + void sendGeometry(); + void sendGeometry(Resource *resource); // Sends to only one client + + void send_scale(int32_t factor) = delete; + void sendScale(int factor); + void sendScale(Resource *resource); // Sends current scale to only one client + + void sendDone(wl_client *client); + void sendDone(); + + int scale() const { return m_data.scale; } + + OutputData m_data; + int m_version = 1; // TODO: remove on libwayland upgrade + +Q_SIGNALS: + void outputBound(Resource *resource); + +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 = Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch, int version = 9); + ~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; + QList<Pointer *> m_oldPointers; + + Touch* m_touch = nullptr; + QList<Touch *> m_oldTouchs; + + Keyboard* m_keyboard = nullptr; + QList<Keyboard *> m_oldKeyboards; + + 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_touch(Resource *resource, uint32_t id) override; + void seat_get_keyboard(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(); + QPointer<CursorRole> m_cursorRole; + void send_enter() = delete; + uint sendEnter(Surface *surface, const QPointF &position); + void send_leave() = delete; + uint sendLeave(Surface *surface); + 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); + void sendAxisDiscrete(wl_client *client, axis axis, int discrete); + void sendAxisSource(wl_client *client, axis_source source); + void sendAxisStop(wl_client *client, axis axis); + void sendFrame(wl_client *client); + void sendAxisValue120(wl_client *client, axis axis, int value120); + void sendAxisRelativeDirection(wl_client *client, axis axis, axis_relative_direction direction); + + Seat *m_seat = nullptr; + QList<uint> m_enterSerials; + QPoint m_hotspot; + +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) + { + connect(m_surface, &QObject::destroyed, this, &QObject::deleteLater); + } + static CursorRole *fromSurface(Surface *surface) { return qobject_cast<CursorRole *>(surface->m_role); } + Surface *m_surface = nullptr; +}; + +class Touch : public QObject, public QtWaylandServer::wl_touch +{ + Q_OBJECT +public: + explicit Touch(Seat *seat) : m_seat(seat) {} + uint sendDown(Surface *surface, const QPointF &position, int id); + uint sendUp(wl_client *client, int id); + void sendMotion(wl_client *client, const QPointF &position, int id); + void sendFrame(wl_client *client); + void sendCancel(wl_client *client); + + Seat *m_seat = nullptr; +}; + +class Keyboard : public QObject, public QtWaylandServer::wl_keyboard +{ + Q_OBJECT +public: + explicit Keyboard(Seat *seat) : m_seat(seat) {} + //TODO: Keymap event + uint sendEnter(Surface *surface); + uint sendLeave(Surface *surface); + uint sendKey(wl_client *client, uint key, uint state); + Seat *m_seat = nullptr; + Surface *m_enteredSurface = nullptr; +}; + +class Shm : public Global, public QtWaylandServer::wl_shm +{ + Q_OBJECT +public: + explicit Shm(CoreCompositor *compositor, QList<format> formats = {format_argb8888, format_xrgb8888, format_rgb888}, int version = 1); + bool isClean() override; + CoreCompositor *m_compositor = nullptr; + QList<ShmPool *> m_pools; + const QList<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 : std::as_const(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; + QList<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/datadevice.cpp b/tests/auto/client/shared/datadevice.cpp new file mode 100644 index 000000000..efb88d0b1 --- /dev/null +++ b/tests/auto/client/shared/datadevice.cpp @@ -0,0 +1,115 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "datadevice.h" + +namespace MockCompositor { + +bool DataDeviceManager::isClean() +{ + for (auto *device : std::as_const(m_dataDevices)) { + // The client should not leak selection offers, i.e. if this fails, there is a missing + // data_offer.destroy request + if (!device->m_sentSelectionOffers.empty()) + return false; + } + return true; +} + +DataDevice *DataDeviceManager::deviceFor(Seat *seat) +{ + Q_ASSERT(seat); + if (auto *device = m_dataDevices.value(seat, nullptr)) + return device; + + auto *device = new DataDevice(this, seat); + m_dataDevices[seat] = device; + return device; +} + +void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, wl_resource *seatResource) +{ + auto *seat = fromResource<Seat>(seatResource); + QVERIFY(seat); + auto *device = deviceFor(seat); + device->add(resource->client(), id, resource->version()); +} + +void DataDeviceManager::data_device_manager_create_data_source(Resource *resource, uint32_t id) +{ + new QtWaylandServer::wl_data_source(resource->client(), id, 1); +} + +DataDevice::~DataDevice() +{ + // If the client(s) hasn't deleted the wayland object, just ignore subsequent events + for (auto *r : resourceMap()) + wl_resource_set_implementation(r->handle, nullptr, nullptr, nullptr); +} + +DataOffer *DataDevice::sendDataOffer(wl_client *client, const QStringList &mimeTypes) +{ + Q_ASSERT(client); + auto *offer = new DataOffer(this, client, m_manager->m_version); + m_offer = offer; + for (auto *resource : resourceMap().values(client)) + wl_data_device::send_data_offer(resource->handle, offer->resource()->handle); + for (const auto &mimeType : mimeTypes) + offer->send_offer(mimeType); + return offer; +} + +void DataDevice::sendSelection(DataOffer *offer) +{ + auto *client = offer->resource()->client(); + for (auto *resource : resourceMap().values(client)) + wl_data_device::send_selection(resource->handle, offer->resource()->handle); + m_sentSelectionOffers << offer; +} + +void DataDevice::sendEnter(Surface *surface, const QPoint &position) +{ + uint serial = m_manager->m_compositor->nextSerial(); + Resource *resource = resourceMap().value(surface->resource()->client()); + if (m_offer) + wl_data_device::send_enter(resource->handle, serial, surface->resource()->handle, position.x(), position.y(), m_offer->resource()->handle); +} + +void DataDevice::sendMotion(Surface *surface, const QPoint &position) +{ + uint32_t time = m_manager->m_compositor->nextSerial(); + Resource *resource = resourceMap().value(surface->resource()->client()); + wl_data_device::send_motion(resource->handle, time, position.x(), position.y()); +} + +void DataDevice::sendDrop(Surface *surface) +{ + Resource *resource = resourceMap().value(surface->resource()->client()); + wl_data_device::send_drop(resource->handle); +} + +void DataDevice::sendLeave(Surface *surface) +{ + Resource *resource = resourceMap().value(surface->resource()->client()); + wl_data_device::send_leave(resource->handle); +} + +void DataOffer::data_offer_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + delete this; +} + +void DataOffer::data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) +{ + Q_UNUSED(resource); + emit receive(mime_type, fd); +} + +void DataOffer::data_offer_destroy(QtWaylandServer::wl_data_offer::Resource *resource) +{ + m_dataDevice->m_sentSelectionOffers.removeOne(this); + wl_resource_destroy(resource->handle); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/datadevice.h b/tests/auto/client/shared/datadevice.h new file mode 100644 index 000000000..356dfa74a --- /dev/null +++ b/tests/auto/client/shared/datadevice.h @@ -0,0 +1,113 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_DATADEVICE_H +#define MOCKCOMPOSITOR_DATADEVICE_H + +//TODO: need this? +#include "coreprotocol.h" + +namespace MockCompositor { + +class DataOffer; + +class DataDeviceManager : public Global, public QtWaylandServer::wl_data_device_manager +{ + Q_OBJECT +public: + explicit DataDeviceManager(CoreCompositor *compositor, int version = 1) + : QtWaylandServer::wl_data_device_manager(compositor->m_display, version) + , m_version(version) + , m_compositor(compositor) + {} + ~DataDeviceManager() override { qDeleteAll(m_dataDevices); } + bool isClean() override; + DataDevice *deviceFor(Seat *seat); + + int m_version = 1; // TODO: remove on libwayland upgrade + QMap<Seat *, DataDevice *> m_dataDevices; + CoreCompositor *m_compositor; + +protected: + void data_device_manager_get_data_device(Resource *resource, uint32_t id, ::wl_resource *seatResource) override; + void data_device_manager_create_data_source(Resource *resource, uint32_t id) override; +}; + +class DataDevice : public QObject, public QtWaylandServer::wl_data_device +{ + Q_OBJECT +public: + explicit DataDevice(DataDeviceManager *manager, Seat *seat) + : m_manager(manager) + , m_seat(seat) + {} + ~DataDevice() override; + void send_data_offer(::wl_resource *resource) = delete; + DataOffer *sendDataOffer(::wl_client *client, const QStringList &mimeTypes = {}); + + void send_selection(::wl_resource *resource) = delete; + void sendSelection(DataOffer *offer); + + void send_enter(uint32_t serial, ::wl_resource *surface, wl_fixed_t x, wl_fixed_t y, ::wl_resource *id) = delete; + void sendEnter(Surface *surface, const QPoint& position); + + void send_motion(uint32_t time, wl_fixed_t x, wl_fixed_t y) = delete; + void sendMotion(Surface *surface, const QPoint &position); + + void send_drop(::wl_resource *resource) = delete; + void sendDrop(Surface *surface); + + void send_leave(::wl_resource *resource) = delete; + void sendLeave(Surface *surface); + + DataDeviceManager *m_manager = nullptr; + Seat *m_seat = nullptr; + QList<DataOffer *> m_sentSelectionOffers; + QPointer<DataOffer> m_offer; + +signals: + void dragStarted(); + +protected: + void data_device_start_drag(Resource *resource, ::wl_resource *source, ::wl_resource *origin, ::wl_resource *icon, uint32_t serial) override + { + Q_UNUSED(resource); + Q_UNUSED(source); + Q_UNUSED(origin); + Q_UNUSED(icon); + Q_UNUSED(serial); + emit dragStarted(); + } + + void data_device_release(Resource *resource) override + { + int removed = m_manager->m_dataDevices.remove(m_seat); + QVERIFY(removed); + wl_resource_destroy(resource->handle); + } +}; + +class DataOffer : public QObject, public QtWaylandServer::wl_data_offer +{ + Q_OBJECT +public: + explicit DataOffer(DataDevice *dataDevice, ::wl_client *client, int version) + : QtWaylandServer::wl_data_offer (client, 0, version) + , m_dataDevice(dataDevice) + {} + + DataDevice *m_dataDevice = nullptr; + +signals: + void receive(QString mimeType, int fd); + +protected: + void data_offer_destroy_resource(Resource *resource) override; + void data_offer_receive(Resource *resource, const QString &mime_type, int32_t fd) override; +// void data_offer_accept(Resource *resource, uint32_t serial, const QString &mime_type) override; + void data_offer_destroy(Resource *resource) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_DATADEVICE_H diff --git a/tests/auto/client/shared/fractionalscalev1.cpp b/tests/auto/client/shared/fractionalscalev1.cpp new file mode 100644 index 000000000..28c778025 --- /dev/null +++ b/tests/auto/client/shared/fractionalscalev1.cpp @@ -0,0 +1,40 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "fractionalscalev1.h" + +namespace MockCompositor { + +FractionalScaleManager::FractionalScaleManager(CoreCompositor *compositor, int version) + : QtWaylandServer::wp_fractional_scale_manager_v1(compositor->m_display, version) +{ +} + +void FractionalScaleManager::wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, wl_resource *surface) +{ + auto *s = fromResource<Surface>(surface); + auto *scaler = new FractionalScale(s, resource->client(), id, resource->version()); + connect(scaler, &QObject::destroyed, this, [this, scaler]() { + m_fractionalScales.removeOne(scaler); + }); + m_fractionalScales << scaler; +} + +FractionalScale::FractionalScale(Surface *surface, wl_client *client, int id, int version) + : QtWaylandServer::wp_fractional_scale_v1(client, id, version) + , m_surface(surface) +{ +} + +void FractionalScale::wp_fractional_scale_v1_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource) + delete this; +} + +void FractionalScale::wp_fractional_scale_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +} diff --git a/tests/auto/client/shared/fractionalscalev1.h b/tests/auto/client/shared/fractionalscalev1.h new file mode 100644 index 000000000..1ae2fad1f --- /dev/null +++ b/tests/auto/client/shared/fractionalscalev1.h @@ -0,0 +1,39 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_FRACTIONALSCALE_H +#define MOCKCOMPOSITOR_FRACTIONALSCALE_H + +#include "coreprotocol.h" +#include <qwayland-server-fractional-scale-v1.h> + +namespace MockCompositor { + +class FractionalScale; + +class FractionalScaleManager : public Global, public QtWaylandServer::wp_fractional_scale_manager_v1 +{ + Q_OBJECT +public: + explicit FractionalScaleManager(CoreCompositor *compositor, int version = 1); + QList<FractionalScale *> m_fractionalScales; + +protected: + void wp_fractional_scale_manager_v1_get_fractional_scale(Resource *resource, uint32_t id, wl_resource *surface) override; +}; + +class FractionalScale : public QObject, public QtWaylandServer::wp_fractional_scale_v1 +{ + Q_OBJECT +public: + explicit FractionalScale(Surface *surface, wl_client *client, int id, int version); + Surface *m_surface; + +protected: + void wp_fractional_scale_v1_destroy_resource(Resource *resource) override; + void wp_fractional_scale_v1_destroy(Resource *resource) override; +}; + +} + +#endif diff --git a/tests/auto/client/shared/fullscreenshellv1.cpp b/tests/auto/client/shared/fullscreenshellv1.cpp new file mode 100644 index 000000000..24468e14b --- /dev/null +++ b/tests/auto/client/shared/fullscreenshellv1.cpp @@ -0,0 +1,22 @@ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "fullscreenshellv1.h" + +namespace MockCompositor { + +FullScreenShellV1::FullScreenShellV1(CoreCompositor *compositor) +{ + init(compositor->m_display, 1); +} + +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(fromResource<Surface>(surface)); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/fullscreenshellv1.h b/tests/auto/client/shared/fullscreenshellv1.h new file mode 100644 index 000000000..02ebf1e0c --- /dev/null +++ b/tests/auto/client/shared/fullscreenshellv1.h @@ -0,0 +1,34 @@ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_FULLSCREENSHELLV1_H +#define MOCKCOMPOSITOR_FULLSCREENSHELLV1_H + +#include "coreprotocol.h" +#include <qwayland-server-fullscreen-shell-unstable-v1.h> + +#include <QList> + +namespace MockCompositor { + +class Surface; +class FullScreenShellV1; + +class FullScreenShellV1 : public Global, public QtWaylandServer::zwp_fullscreen_shell_v1 +{ + Q_OBJECT +public: + explicit FullScreenShellV1(CoreCompositor *compositor); + + QList<Surface *> surfaces() const { return m_surfaces; } + +protected: + void zwp_fullscreen_shell_v1_present_surface(Resource *resource, struct ::wl_resource *surface, uint32_t method, struct ::wl_resource *output) override; + +private: + QList<Surface *> m_surfaces; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_FULLSCREENSHELLV1_H diff --git a/tests/auto/client/shared/iviapplication.cpp b/tests/auto/client/shared/iviapplication.cpp new file mode 100644 index 000000000..f4f167600 --- /dev/null +++ b/tests/auto/client/shared/iviapplication.cpp @@ -0,0 +1,45 @@ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "iviapplication.h" + +namespace MockCompositor { + +IviApplication::IviApplication(CoreCompositor *compositor) +{ + init(compositor->m_display, 1); +} + +void IviApplication::ivi_application_surface_create(Resource *resource, uint32_t ivi_id, struct ::wl_resource *surface, uint32_t id) +{ + auto *s = fromResource<Surface>(surface); + auto *iviSurface = new IviSurface(this, s, ivi_id, resource->client(), id, resource->version()); + m_iviSurfaces << iviSurface; + qDebug() << "count is " << m_iviSurfaces.size(); +} + +IviSurface::IviSurface(IviApplication *iviApplication, Surface *surface, uint32_t ivi_id, wl_client *client, int id, int version) + : QtWaylandServer::ivi_surface(client, id, version) + , m_iviId(ivi_id) + , m_iviApplication(iviApplication) + , m_surface(surface) +{ + surface->map(); +} + +void IviSurface::ivi_surface_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource); + bool removed = m_iviApplication->m_iviSurfaces.removeOne(this); + Q_ASSERT(removed); + qDebug() << "destroy"; + + delete this; +} + +void IviSurface::ivi_surface_destroy(QtWaylandServer::ivi_surface::Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/iviapplication.h b/tests/auto/client/shared/iviapplication.h new file mode 100644 index 000000000..1644d0cfd --- /dev/null +++ b/tests/auto/client/shared/iviapplication.h @@ -0,0 +1,50 @@ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_IVIAPPLICATION_H +#define MOCKCOMPOSITOR_IVIAPPLICATION_H + +#include "coreprotocol.h" +#include <qwayland-server-ivi-application.h> + +#include <QList> + +namespace MockCompositor { + +class Surface; +class IviApplication; +class IviSurface; + +class IviApplication : public Global, public QtWaylandServer::ivi_application +{ + Q_OBJECT +public: + explicit IviApplication(CoreCompositor *compositor); + + QList<IviSurface *> m_iviSurfaces; +protected: + void ivi_application_surface_create(Resource *resource, uint32_t ivi_id, struct ::wl_resource *surface, uint32_t id) override; + +}; + +class IviSurface : public QObject, public QtWaylandServer::ivi_surface +{ + Q_OBJECT +public: + IviSurface(IviApplication *iviApplication, Surface *surface, uint32_t ivi_id, wl_client *client, int id, int version); + + Surface *surface() const { return m_surface; } + + void ivi_surface_destroy_resource(Resource *resource) override; + void ivi_surface_destroy(Resource *resource) override; + + const uint m_iviId = 0; +private: + IviApplication *m_iviApplication; + Surface *m_surface = nullptr; +}; + + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_IVIAPPLICATION_H diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp index 797c05c44..bbf406d64 100644 --- a/tests/auto/client/shared/mockcompositor.cpp +++ b/tests/auto/client/shared/mockcompositor.cpp @@ -1,503 +1,143 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #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(); +namespace MockCompositor { + +DefaultCompositor::DefaultCompositor(CompositorType t, int socketFd) + : CoreCompositor(t, socketFd) +{ + { + 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>(); + auto *output = add<Output>(); + output->m_data.physicalSize = output->m_data.mode.physicalSizeForDpi(96); + add<Seat>(Seat::capability_pointer | Seat::capability_keyboard | Seat::capability_touch); + add<WlShell>(); + add<XdgWmBase>(); + add<FractionalScaleManager>(); + add<Viewporter>(); + add<XdgWmDialog>(); + + switch (m_type) { + case CompositorType::Default: + add<Shm>(); + break; + case CompositorType::Legacy: + wl_display_init_shm(m_display); break; } + add<FullScreenShellV1>(); + add<IviApplication>(); + + // TODO: other shells, viewporter, xdgoutput etc + + QObject::connect(get<WlCompositor>(), &WlCompositor::surfaceCreated, [this] (Surface *surface){ + QObject::connect(surface, &Surface::bufferCommitted, [this, surface] { + 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.autoFrameCallback) { + surface->sendFrameCallbacks(); + } + if (m_config.autoEnter && get<Output>() && surface->m_outputs.empty()) + surface->sendEnter(get<Output>()); + wl_display_flush_clients(m_display); + }); + }); + + QObject::connect(get<XdgWmBase>(), &XdgWmBase::toplevelCreated, get<XdgWmBase>(), [this] (XdgToplevel *toplevel) { + if (m_config.autoConfigure) + toplevel->sendCompleteConfigure(); + }, Qt::DirectConnection); } - 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); + Q_ASSERT(isClean()); +} + +Surface *DefaultCompositor::surface(int i) +{ + QList<Surface *> surfaces; + switch (m_type) { + case CompositorType::Default: + return get<WlCompositor>()->m_surfaces.value(i, nullptr); + case CompositorType::Legacy: { + QList<Surface *> msurfaces = get<WlCompositor>()->m_surfaces; + for (Surface *surface : msurfaces) { + if (surface->isMapped()) { + surfaces << surface; + } + } } - controller->dispatchCommands(); - compositor.dispatchEvents(20); + break; } - 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(); + if (i >= 0 && i < surfaces.size()) + return surfaces[i]; - 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); -} - -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)); + return nullptr; } -static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id) +uint DefaultCompositor::sendXdgShellPing() { - Q_UNUSED(client); - Q_UNUSED(compositorResource); - Q_UNUSED(id); + 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; } -void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id) +void DefaultCompositor::xdgPingAndWaitForPong() { - 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); + QSignalSpy pongSpy(exec([&] { return get<XdgWmBase>(); }), &XdgWmBase::pong); + uint serial = exec([&] { return sendXdgShellPing(); }); + QTRY_COMPARE(pongSpy.size(), 1); + QTRY_COMPARE(pongSpy.first().at(0).toUInt(), serial); } -static void unregisterResourceCallback(wl_listener *listener, void *data) +void DefaultCompositor::sendShellSurfaceConfigure(Surface *surface) { - 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; -} + switch (m_type) { + case CompositorType::Default: + break; + case CompositorType::Legacy: { + if (auto wlShellSurface = surface->wlShellSurface()) { + wlShellSurface->sendConfigure(0, 0, 0); + return; + } + break; + } + } -void Compositor::removeSurface(Surface *surface) -{ - m_surfaces.removeOne(surface); - m_keyboard->handleSurfaceDestroyed(surface); - m_pointer->handleSurfaceDestroyed(surface); + qWarning() << "The mocking framework doesn't know how to send a configure event for this surface"; } -Surface *Compositor::resolveSurface(const QVariant &v) +WlShellCompositor::WlShellCompositor(CompositorType t) + : DefaultCompositor(t) { - QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >(); - return mockSurface ? mockSurface->handle() : nullptr; } -Output *Compositor::resolveOutput(const QVariant &v) +Surface *DefaultCompositor::wlSurface(int i) { - QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >(); - return mockOutput ? mockOutput->handle() : nullptr; -} + QList<Surface *> surfaces, msurfaces; + msurfaces = get<WlCompositor>()->m_surfaces; + for (Surface *surface : msurfaces) { + if (surface->isMapped()) + surfaces << surface; + } -IviSurface *Compositor::resolveIviSurface(const QVariant &v) -{ - QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>(); - return mockIviSurface ? mockIviSurface->handle() : nullptr; -} + if (i >=0 && i < surfaces.size()) + return surfaces[i]; -XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v) -{ - QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>(); - return mockToplevel ? mockToplevel->handle() : nullptr; + return nullptr; } -} +} // namespace MockCompositor diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h index 51b6f4bfb..9a2c06a17 100644 --- a/tests/auto/client/shared/mockcompositor.h +++ b/tests/auto/client/shared/mockcompositor.h @@ -1,287 +1,97 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ +// Copyright (C) 2021 David Edmundson <davidedmundson@kde.org> +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #ifndef MOCKCOMPOSITOR_H #define MOCKCOMPOSITOR_H -#include "mockxdgshellv6.h" -#include "mockiviapplication.h" - -#include <pthread.h> -#include <qglobal.h> -#include <wayland-server.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; - - 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; -}; +#include "corecompositor.h" +#include "coreprotocol.h" +#include "datadevice.h" +#include "fullscreenshellv1.h" +#include "iviapplication.h" +#include "xdgshell.h" +#include "viewport.h" +#include "fractionalscalev1.h" +#include "xdgdialog.h" + +#include <QtGui/QGuiApplication> + +// As defined in linux/input-event-codes.h +#ifndef BTN_LEFT +#define BTN_LEFT 0x110 +#endif +#ifndef BTN_RIGHT +#define BTN_RIGHT 0x111 +#endif +#ifndef BTN_MIDDLE +#define BTN_MIDDLE 0x112 +#endif -Q_DECLARE_METATYPE(QSharedPointer<MockSurface>) +namespace MockCompositor { -class MockIviSurface +class DefaultCompositor : public CoreCompositor { 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; + explicit DefaultCompositor(CompositorType t = CompositorType::Default, int socketFd = -1); + // Convenience functions + Output *output(int i = 0) { return getAll<Output>().value(i, nullptr); } + Surface *surface(int i = 0); + Subsurface *subSurface(int i = 0) { return get<SubCompositor>()->m_subsurfaces.value(i, nullptr); } + WlShellSurface *wlShellSurface(int i = 0) { return get<WlShell>()->m_wlShellSurfaces.value(i, nullptr); } + Surface *wlSurface(int i = 0); + 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; } + Touch *touch() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_touch; } + Surface *cursorSurface() { auto *p = pointer(); return p ? p->cursorSurface() : nullptr; } + Keyboard *keyboard() { auto *seat = get<Seat>(); Q_ASSERT(seat); return seat->m_keyboard; } + FullScreenShellV1 *fullScreenShellV1() {return get<FullScreenShellV1>();}; + IviSurface *iviSurface(int i = 0) { return get<IviApplication>()->m_iviSurfaces.value(i, nullptr); } + FractionalScale *fractionalScale(int i = 0) {return get<FractionalScaleManager>()->m_fractionalScales.value(i, nullptr); } + Viewport *viewport(int i = 0) {return get<Viewporter>()->m_viewports.value(i, nullptr); } + XdgDialog *xdgDialog(int i = 0) { return get<XdgWmDialog>()->m_dialogs.value(i, nullptr); } + + uint sendXdgShellPing(); + void xdgPingAndWaitForPong(); + + void sendShellSurfaceConfigure(Surface *surface); + + // 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; + bool autoFrameCallback = true; + } m_config; + void resetConfig() { exec([&] { m_config = Config{}; }); } }; -Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>) - -class MockXdgToplevelV6 : public QObject +class WlShellCompositor : public DefaultCompositor { - 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 WlShellCompositor(CompositorType t = CompositorType::Legacy); }; -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) \ +{ \ + QTemporaryDir tmpRuntimeDir; \ + setenv("XDG_RUNTIME_DIR", tmpRuntimeDir.path().toLocal8Bit(), 1); \ + setenv("XDG_CURRENT_DESKTOP", "qtwaylandtests", 1); \ + setenv("QT_QPA_PLATFORM", "wayland", 1); \ + test tc; \ + QGuiApplication app(argc, argv); \ + QTEST_SET_MAIN_SOURCE_PATH \ + return QTest::qExec(&tc, argc, argv); \ +} \ #endif diff --git a/tests/auto/client/shared/mockinput.cpp b/tests/auto/client/shared/mockinput.cpp deleted file mode 100644 index 8b7592824..000000000 --- a/tests/auto/client/shared/mockinput.cpp +++ /dev/null @@ -1,474 +0,0 @@ -/**************************************************************************** -** -** 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 "mocksurface.h" - -namespace Impl { - -void Compositor::setKeyboardFocus(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - compositor->m_keyboard->setFocus(resolveSurface(parameters.first())); -} - -void Compositor::sendMousePress(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - if (!surface) - return; - - QPoint pos = parameters.last().toPoint(); - compositor->m_pointer->setFocus(surface, pos); - compositor->m_pointer->sendMotion(pos); - compositor->m_pointer->sendButton(0x110, 1); -} - -void Compositor::sendMouseRelease(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - if (!surface) - return; - - compositor->m_pointer->sendButton(0x110, 0); -} - -void Compositor::sendKeyPress(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - if (!surface) - return; - - compositor->m_keyboard->sendKey(parameters.last().toUInt() - 8, 1); -} - -void Compositor::sendKeyRelease(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - if (!surface) - return; - - compositor->m_keyboard->sendKey(parameters.last().toUInt() - 8, 0); -} - -void Compositor::sendTouchDown(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - QPoint position = parameters.at(1).toPoint(); - int id = parameters.at(2).toInt(); - - compositor->m_touch->sendDown(surface, position, id); -} - -void Compositor::sendTouchUp(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - int id = parameters.at(1).toInt(); - - compositor->m_touch->sendUp(surface, id); -} - -void Compositor::sendTouchMotion(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - QPoint position = parameters.at(1).toPoint(); - int id = parameters.at(2).toInt(); - - compositor->m_touch->sendMotion(surface, position, id); -} - -void Compositor::sendTouchFrame(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - compositor->m_touch->sendFrame(surface); -} - -void Compositor::sendDataDeviceDataOffer(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - compositor->m_data_device_manager->dataDevice()->sendDataOffer(surface->resource()->client()); -} - -void Compositor::sendDataDeviceEnter(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - QPoint position = parameters.at(1).toPoint(); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - compositor->m_data_device_manager->dataDevice()->sendEnter(surface, position); -} - -void Compositor::sendDataDeviceMotion(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Q_ASSERT(compositor); - QPoint position = parameters.first().toPoint(); - compositor->m_data_device_manager->dataDevice()->sendMotion(position); -} - -void Compositor::sendDataDeviceDrop(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - compositor->m_data_device_manager->dataDevice()->sendDrop(surface); -} - -void Compositor::sendDataDeviceLeave(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.first()); - - Q_ASSERT(compositor); - Q_ASSERT(surface); - - compositor->m_data_device_manager->dataDevice()->sendLeave(surface); -} - -void Compositor::waitForStartDrag(void *data, const QList<QVariant> ¶meters) -{ - Q_UNUSED(parameters); - Compositor *compositor = static_cast<Compositor *>(data); - Q_ASSERT(compositor); - while (!compositor->m_startDragSeen) { - wl_display_flush_clients(compositor->m_display); - wl_event_loop_dispatch(compositor->m_loop, 100); - } - compositor->m_startDragSeen = false; -} - -Seat::Seat(Compositor *compositor, struct ::wl_display *display) - : wl_seat(display, 2) - , m_compositor(compositor) - , m_keyboard(new Keyboard(compositor)) - , m_pointer(new Pointer(compositor)) - , m_touch(new Touch(compositor)) -{ -} - -Seat::~Seat() -{ -} - -void Seat::seat_bind_resource(Resource *resource) -{ - send_capabilities(resource->handle, capability_keyboard | capability_pointer | capability_touch); -} - -void Seat::seat_get_keyboard(Resource *resource, uint32_t id) -{ - m_keyboard->add(resource->client(), id, resource->version()); -} - -void Seat::seat_get_pointer(Resource *resource, uint32_t id) -{ - m_pointer->add(resource->client(), id, resource->version()); -} - -void Seat::seat_get_touch(Resource *resource, uint32_t id) -{ - m_touch->add(resource->client(), id, resource->version()); -} - -Keyboard::Keyboard(Compositor *compositor) - : m_compositor(compositor) -{ -} - -Keyboard::~Keyboard() -{ -} - -void Keyboard::setFocus(Surface *surface) -{ - if (m_focusResource && m_focus != surface) { - uint32_t serial = m_compositor->nextSerial(); - send_leave(m_focusResource->handle, serial, m_focus->resource()->handle); - } - - Resource *resource = surface ? resourceMap().value(surface->resource()->client()) : 0; - - if (resource && (m_focus != surface || m_focusResource != resource)) { - uint32_t serial = m_compositor->nextSerial(); - send_modifiers(resource->handle, serial, 0, 0, 0, 0); - send_enter(resource->handle, serial, surface->resource()->handle, QByteArray()); - } - - m_focusResource = resource; - m_focus = surface; -} - -void Keyboard::handleSurfaceDestroyed(Surface *surface) -{ - if (surface == m_focus) { - m_focusResource = nullptr; - m_focus = nullptr; - } -} - -void Keyboard::sendKey(uint32_t key, uint32_t state) -{ - if (m_focusResource) { - uint32_t serial = m_compositor->nextSerial(); - send_key(m_focusResource->handle, serial, m_compositor->time(), key, state); - } -} - - -void Keyboard::keyboard_destroy_resource(wl_keyboard::Resource *resource) -{ - if (m_focusResource == resource) - m_focusResource = 0; -} - -Pointer::Pointer(Compositor *compositor) - : m_compositor(compositor) -{ -} - -Pointer::~Pointer() -{ - -} - -void Pointer::setFocus(Surface *surface, const QPoint &pos) -{ - if (m_focusResource && m_focus != surface) { - uint32_t serial = m_compositor->nextSerial(); - send_leave(m_focusResource->handle, serial, m_focus->resource()->handle); - } - - Resource *resource = surface ? resourceMap().value(surface->resource()->client()) : 0; - - if (resource && (m_focus != surface || resource != m_focusResource)) { - uint32_t serial = m_compositor->nextSerial(); - send_enter(resource->handle, serial, surface->resource()->handle, - wl_fixed_from_int(pos.x()), wl_fixed_from_int(pos.y())); - } - - m_focusResource = resource; - m_focus = surface; -} - -void Pointer::handleSurfaceDestroyed(Surface *surface) -{ - if (m_focus == surface) { - m_focus = nullptr; - m_focusResource = nullptr; - } -} - -void Pointer::sendMotion(const QPoint &pos) -{ - if (m_focusResource) - send_motion(m_focusResource->handle, m_compositor->time(), - wl_fixed_from_int(pos.x()), wl_fixed_from_int(pos.y())); -} - -void Pointer::sendButton(uint32_t button, uint32_t state) -{ - if (m_focusResource) { - uint32_t serial = m_compositor->nextSerial(); - send_button(m_focusResource->handle, serial, m_compositor->time(), - button, state); - } -} - -void Pointer::pointer_destroy_resource(wl_pointer::Resource *resource) -{ - if (m_focusResource == resource) - m_focusResource = 0; -} - -Touch::Touch(Compositor *compositor) - : wl_touch() - , m_compositor(compositor) -{ -} - -void Touch::sendDown(Surface *surface, const QPoint &position, int id) -{ - uint32_t serial = m_compositor->nextSerial(); - uint32_t time = m_compositor->time(); - Q_ASSERT(surface); - Resource *resource = resourceMap().value(surface->resource()->client()); - Q_ASSERT(resource); - auto x = wl_fixed_from_int(position.x()); - auto y = wl_fixed_from_int(position.y()); - wl_touch_send_down(resource->handle, serial, time, surface->resource()->handle, id, x, y); -} - -void Touch::sendUp(Surface *surface, int id) -{ - Resource *resource = resourceMap().value(surface->resource()->client()); - wl_touch_send_up(resource->handle, m_compositor->nextSerial(), m_compositor->time(), id); -} - -void Touch::sendMotion(Surface *surface, const QPoint &position, int id) -{ - Resource *resource = resourceMap().value(surface->resource()->client()); - uint32_t time = m_compositor->time(); - auto x = wl_fixed_from_int(position.x()); - auto y = wl_fixed_from_int(position.y()); - wl_touch_send_motion(resource->handle, time, id, x, y); -} - -void Touch::sendFrame(Surface *surface) -{ - Resource *resource = resourceMap().value(surface->resource()->client()); - wl_touch_send_frame(resource->handle); -} - -DataOffer::DataOffer() - : wl_data_offer() -{ - -} - -DataDevice::DataDevice(Compositor *compositor) - : m_compositor(compositor) -{ - -} - -void DataDevice::sendDataOffer(wl_client *client) -{ - m_dataOffer = new QtWaylandServer::wl_data_offer(client, 0, 1); - Resource *resource = resourceMap().value(client); - send_data_offer(resource->handle, m_dataOffer->resource()->handle); -} - -void DataDevice::sendEnter(Surface *surface, const QPoint& position) -{ - uint serial = m_compositor->nextSerial(); - m_focus = surface; - Resource *resource = resourceMap().value(surface->resource()->client()); - send_enter(resource->handle, serial, surface->resource()->handle, position.x(), position.y(), m_dataOffer->resource()->handle); -} - -void DataDevice::sendMotion(const QPoint &position) -{ - uint32_t time = m_compositor->time(); - Resource *resource = resourceMap().value(m_focus->resource()->client()); - send_motion(resource->handle, time, position.x(), position.y()); -} - -void DataDevice::sendDrop(Surface *surface) -{ - Resource *resource = resourceMap().value(surface->resource()->client()); - send_drop(resource->handle); -} - -void DataDevice::sendLeave(Surface *surface) -{ - Resource *resource = resourceMap().value(surface->resource()->client()); - send_leave(resource->handle); -} - -DataDevice::~DataDevice() -{ - -} - -void DataDevice::data_device_start_drag(QtWaylandServer::wl_data_device::Resource *resource, wl_resource *source, wl_resource *origin, wl_resource *icon, uint32_t serial) -{ - Q_UNUSED(resource); - Q_UNUSED(source); - Q_UNUSED(origin); - Q_UNUSED(icon); - Q_UNUSED(serial); - m_compositor->m_startDragSeen = true; -} - -DataDeviceManager::DataDeviceManager(Compositor *compositor, wl_display *display) - : wl_data_device_manager(display, 1) - , m_compositor(compositor) -{ - -} - -DataDeviceManager::~DataDeviceManager() -{ - -} - -DataDevice *DataDeviceManager::dataDevice() const -{ - return m_data_device.data(); -} - -void DataDeviceManager::data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat) -{ - Q_UNUSED(seat); - if (!m_data_device) - m_data_device.reset(new DataDevice(m_compositor)); - m_data_device->add(resource->client(), id, 1); -} - -void DataDeviceManager::data_device_manager_create_data_source(QtWaylandServer::wl_data_device_manager::Resource *resource, uint32_t id) -{ - new QtWaylandServer::wl_data_source(resource->client(), id, 1); -} - -} diff --git a/tests/auto/client/shared/mockinput.h b/tests/auto/client/shared/mockinput.h deleted file mode 100644 index d9adb3621..000000000 --- a/tests/auto/client/shared/mockinput.h +++ /dev/null @@ -1,172 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2016 Klarälvdalens Datakonsult AB (KDAB). -** 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 MOCKINPUT_H -#define MOCKINPUT_H - -#include <qglobal.h> - -#include "qwayland-server-wayland.h" - -#include "mockcompositor.h" - -namespace Impl { - -class Keyboard; -class Pointer; - -class Seat : public QtWaylandServer::wl_seat -{ -public: - Seat(Compositor *compositor, struct ::wl_display *display); - ~Seat(); - - Compositor *compositor() const { return m_compositor; } - - Keyboard *keyboard() const { return m_keyboard.data(); } - Pointer *pointer() const { return m_pointer.data(); } - Touch *touch() const { return m_touch.data(); } - -protected: - void seat_bind_resource(Resource *resource) override; - void seat_get_keyboard(Resource *resource, uint32_t id) override; - void seat_get_pointer(Resource *resource, uint32_t id) override; - void seat_get_touch(Resource *resource, uint32_t id) override; - -private: - Compositor *m_compositor = nullptr; - - QScopedPointer<Keyboard> m_keyboard; - QScopedPointer<Pointer> m_pointer; - QScopedPointer<Touch> m_touch; -}; - -class Keyboard : public QtWaylandServer::wl_keyboard -{ -public: - Keyboard(Compositor *compositor); - ~Keyboard(); - - Surface *focus() const { return m_focus; } - void setFocus(Surface *surface); - void handleSurfaceDestroyed(Surface *surface); - - void sendKey(uint32_t key, uint32_t state); - -protected: - void keyboard_destroy_resource(wl_keyboard::Resource *resource) override; - -private: - Compositor *m_compositor = nullptr; - - Resource *m_focusResource = nullptr; - Surface *m_focus = nullptr; -}; - -class Pointer : public QtWaylandServer::wl_pointer -{ -public: - Pointer(Compositor *compositor); - ~Pointer(); - - Surface *focus() const { return m_focus; } - - void setFocus(Surface *surface, const QPoint &pos); - void handleSurfaceDestroyed(Surface *surface); - void sendMotion(const QPoint &pos); - void sendButton(uint32_t button, uint32_t state); - -protected: - void pointer_destroy_resource(wl_pointer::Resource *resource) override; - -private: - Compositor *m_compositor = nullptr; - - Resource *m_focusResource = nullptr; - Surface *m_focus = nullptr; -}; - -class Touch : public QtWaylandServer::wl_touch -{ -public: - Touch(Compositor *compositor); - void sendDown(Surface *surface, const QPoint &position, int id); - void sendUp(Surface *surface, int id); - void sendMotion(Surface *surface, const QPoint &position, int id); - void sendFrame(Surface *surface); -private: - Compositor *m_compositor = nullptr; -}; - -class DataOffer : public QtWaylandServer::wl_data_offer -{ -public: - DataOffer(); -}; - -class DataDevice : public QtWaylandServer::wl_data_device -{ -public: - DataDevice(Compositor *compositor); - void sendDataOffer(wl_client *client); - void sendEnter(Surface *surface, const QPoint &position); - void sendMotion(const QPoint &position); - void sendDrop(Surface *surface); - void sendLeave(Surface *surface); - ~DataDevice(); - -protected: - void data_device_start_drag(Resource *resource, struct ::wl_resource *source, struct ::wl_resource *origin, struct ::wl_resource *icon, uint32_t serial) override; - -private: - Compositor *m_compositor = nullptr; - QtWaylandServer::wl_data_offer *m_dataOffer = nullptr; - Surface* m_focus = nullptr; -}; - -class DataDeviceManager : public QtWaylandServer::wl_data_device_manager -{ -public: - DataDeviceManager(Compositor *compositor, struct ::wl_display *display); - ~DataDeviceManager(); - DataDevice *dataDevice() const; - -protected: - void data_device_manager_get_data_device(Resource *resource, uint32_t id, struct ::wl_resource *seat) override; - void data_device_manager_create_data_source(Resource *resource, uint32_t id) override; - -private: - Compositor *m_compositor = nullptr; - - QScopedPointer<DataDevice> m_data_device; -}; - -} - -#endif // MOCKINPUT_H diff --git a/tests/auto/client/shared/mockiviapplication.cpp b/tests/auto/client/shared/mockiviapplication.cpp deleted file mode 100644 index 29a308993..000000000 --- a/tests/auto/client/shared/mockiviapplication.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/**************************************************************************** -** -** 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 "mockiviapplication.h" -#include "mocksurface.h" -#include "mockcompositor.h" - -namespace Impl { - -void Compositor::sendIviSurfaceConfigure(void *data, const QList<QVariant> ¶meters) -{ - Q_UNUSED(data); - IviSurface *iviSurface = resolveIviSurface(parameters.at(0)); - Q_ASSERT(iviSurface && iviSurface->resource()); - QSize size = parameters.at(1).toSize(); - Q_ASSERT(!size.isEmpty()); - iviSurface->send_configure(size.width(), size.height()); -} - -IviSurface::IviSurface(IviApplication *iviApplication, Surface *surface, uint iviId, wl_client *client, uint32_t id) - : QtWaylandServer::ivi_surface(client, id, 1) - , m_surface(surface) - , m_iviApplication(iviApplication) - , m_iviId(iviId) - , m_mockIviSurface(new MockIviSurface(this)) -{ - iviApplication->addIviSurface(this); - surface->map(); -} - -IviSurface::~IviSurface() -{ - m_iviApplication->removeIviSurface(this); - m_mockIviSurface->m_iviSurface = nullptr; -} - -void IviSurface::ivi_surface_destroy(Resource *resource) -{ - wl_resource_destroy(resource->handle); -} - -void IviApplication::ivi_application_surface_create(Resource *resource, uint32_t ivi_id, ::wl_resource *surface, uint32_t id) -{ - new IviSurface(this, Surface::fromResource(surface), ivi_id, resource->client(), id); -} - -} // namespace Impl diff --git a/tests/auto/client/shared/mockiviapplication.h b/tests/auto/client/shared/mockiviapplication.h deleted file mode 100644 index 4d65eeaba..000000000 --- a/tests/auto/client/shared/mockiviapplication.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** 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 MOCKIVIAPPLICATION_H -#define MOCKIVIAPPLICATION_H - -#include <qwayland-server-ivi-application.h> - -#include <QSharedPointer> -#include <QVector> - -class MockIviSurface; - -namespace Impl { - -class Surface; -class IviApplication; - -class IviSurface : public QtWaylandServer::ivi_surface -{ -public: - IviSurface(IviApplication *iviApplication, Surface *surface, uint iviId, wl_client *client, uint32_t id); - ~IviSurface() override; - IviApplication *iviApplication() const { return m_iviApplication; } - Surface *surface() const { return m_surface; } - uint iviId() const { return m_iviId; } - - QSharedPointer<MockIviSurface> mockIviSurface() const { return m_mockIviSurface; } - -protected: - void ivi_surface_destroy_resource(Resource *) override { delete this; } - void ivi_surface_destroy(Resource *resource) override; - -private: - Surface *m_surface = nullptr; - IviApplication *m_iviApplication = nullptr; - const uint m_iviId = 0; - QSharedPointer<MockIviSurface> m_mockIviSurface; -}; - -class IviApplication : public QtWaylandServer::ivi_application -{ -public: - explicit IviApplication(::wl_display *display) : ivi_application(display, 1) {} - QVector<IviSurface *> iviSurfaces() const { return m_iviSurfaces; } - -protected: - void ivi_application_surface_create(Resource *resource, uint32_t ivi_id, ::wl_resource *surface, uint32_t id) override; - -private: - void addIviSurface(IviSurface *iviSurface) { m_iviSurfaces.append(iviSurface); } - void removeIviSurface(IviSurface *iviSurface) { m_iviSurfaces.removeOne(iviSurface); } - QVector<IviSurface *> m_iviSurfaces; - - friend class IviSurface; -}; - -} // namespace Impl - -#endif // MOCKIVIAPPLICATION_H diff --git a/tests/auto/client/shared/mockoutput.cpp b/tests/auto/client/shared/mockoutput.cpp deleted file mode 100644 index 13e0524ad..000000000 --- a/tests/auto/client/shared/mockoutput.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/**************************************************************************** -** -** 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 "mockoutput.h" - -#include <QDebug> - -namespace Impl { - -void Compositor::sendAddOutput(void *data, const QList<QVariant> ¶meters) { - Q_UNUSED(parameters); - Compositor *compositor = static_cast<Compositor *>(data); - auto output = new Output(compositor->m_display, QSize(1920, 1200), QPoint(0, 0)); - compositor->m_outputs.append(output); - - // Wait for the client to bind to the output - while (output->resourceMap().isEmpty()) - compositor->dispatchEvents(); -} - -void Compositor::sendRemoveOutput(void *data, const QList<QVariant> ¶meters) { - Compositor *compositor = static_cast<Compositor *>(data); - Q_ASSERT(compositor); - Output *output = resolveOutput(parameters.first()); - Q_ASSERT(output); - bool wasRemoved = compositor->m_outputs.removeOne(output); - Q_ASSERT(wasRemoved); - delete output; -} - -void Compositor::sendOutputGeometry(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Q_ASSERT(compositor); - Output *output = resolveOutput(parameters.first()); - Q_ASSERT(output); - QRect geometry = parameters.at(1).toRect(); - output->sendGeometryAndMode(geometry); -} - -void Compositor::setOutputMode(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - QSize size = parameters.first().toSize(); - Output *output = compositor->m_outputs.first(); - Q_ASSERT(output); - output->setCurrentMode(size); -} - -Output::Output(wl_display *display, const QSize &resolution, const QPoint &position) - : wl_output(display, 2) - , m_size(resolution) - , m_position(position) - , m_physicalSize(520, 320) - , m_mockOutput(new MockOutput(this)) -{ -} - -void Output::setCurrentMode(const QSize &size) -{ - m_size = size; - for (Resource *resource : resourceMap()) { - sendCurrentMode(resource); - send_done(resource->handle); - } -} - -void Output::sendGeometryAndMode(const QRect &geometry) -{ - m_size = geometry.size(); - m_position = geometry.topLeft(); - for (Resource *resource : resourceMap()) { - sendGeometry(resource); - sendCurrentMode(resource); - send_done(resource->handle); - } -} - -void Output::output_bind_resource(QtWaylandServer::wl_output::Resource *resource) -{ - sendGeometry(resource); - sendCurrentMode(resource); - send_done(resource->handle); -} - -void Output::sendGeometry(Resource *resource) -{ - const int subPixel = 0; - const int transform = 0; - - send_geometry(resource->handle, - m_position.x(), m_position.y(), - m_physicalSize.width(), m_physicalSize.height(), - subPixel, "", "", transform ); -} - -void Output::sendCurrentMode(Resource *resource) -{ - send_mode(resource->handle, - WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED, - m_size.width(), m_size.height(), 60000); -} - -} // Impl - -MockOutput::MockOutput(Impl::Output *output) - : m_output(output) -{ -} diff --git a/tests/auto/client/shared/mockoutput.h b/tests/auto/client/shared/mockoutput.h deleted file mode 100644 index 9f261d5d7..000000000 --- a/tests/auto/client/shared/mockoutput.h +++ /dev/null @@ -1,63 +0,0 @@ -/**************************************************************************** -** -** 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$ -** -****************************************************************************/ - -#ifndef MOCKOUTPUT_H -#define MOCKOUTPUT_H - -#include <qglobal.h> - -#include "qwayland-server-wayland.h" - -#include "mockcompositor.h" - -namespace Impl { - -class Output : public QtWaylandServer::wl_output -{ -public: - Output(::wl_display *display, const QSize &resolution, const QPoint &position); - - QSharedPointer<MockOutput> mockOutput() const { return m_mockOutput; } - void setCurrentMode(const QSize &size); - void sendGeometryAndMode(const QRect &geometry); - -protected: - void output_bind_resource(Resource *resource) override; - -private: - void sendGeometry(Resource *resource); - void sendCurrentMode(Resource *resource); - QSize m_size; - QPoint m_position; - const QSize m_physicalSize; - QSharedPointer<MockOutput> m_mockOutput; -}; - -} - -#endif // MOCKOUTPUT_H diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared/mocksurface.cpp deleted file mode 100644 index 84dcda6b0..000000000 --- a/tests/auto/client/shared/mocksurface.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** 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 "mocksurface.h" -#include "mockoutput.h" -#include "mockcompositor.h" -#include "mockwlshell.h" - -#include <QDebug> - -namespace Impl { - -void Compositor::sendSurfaceEnter(void *data, const QList<QVariant> ¶meters) -{ - Q_UNUSED(data); - Surface *surface = resolveSurface(parameters.at(0)); - Output *output = resolveOutput(parameters.at(1)); - Q_ASSERT(surface && surface->resource()); - Q_ASSERT(output); - auto outputResources = output->resourceMap().values(surface->resource()->client()); - Q_ASSERT(!outputResources.isEmpty()); - - for (auto outputResource : outputResources) - surface->send_enter(outputResource->handle); -} - -void Compositor::sendSurfaceLeave(void *data, const QList<QVariant> ¶meters) -{ - Q_UNUSED(data); - Surface *surface = resolveSurface(parameters.at(0)); - Output *output = resolveOutput(parameters.at(1)); - Q_ASSERT(surface && surface->resource()); - Q_ASSERT(output); - auto outputResources = output->resourceMap().values(surface->resource()->client()); - Q_ASSERT(!outputResources.isEmpty()); - - for (auto outputResource : outputResources) - surface->send_leave(outputResource->handle); -} - -void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - Surface *surface = resolveSurface(parameters.at(0)); - QSize size = parameters.at(1).toSize(); - Q_ASSERT(size.isValid()); - if (auto toplevel = surface->xdgToplevelV6()) { - QVector<uint> states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }; - auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), - states.size() * static_cast<int>(sizeof(uint))); - toplevel->send_configure(size.width(), size.height(), statesBytes); - toplevel->xdgSurface()->sendConfigure(compositor->nextSerial()); - } else if (auto wlShellSurface = surface->wlShellSurface()) { - const uint edges = 0; - wlShellSurface->send_configure(edges, size.width(), size.height()); - } else { - qWarning() << "The mocking framework doesn't know how to send a configure event for this surface"; - } -} - -Surface::Surface(wl_client *client, uint32_t id, int v, Compositor *compositor) - : QtWaylandServer::wl_surface(client, id, v) - , m_compositor(compositor) - , m_mockSurface(new MockSurface(this)) -{ -} - -Surface::~Surface() -{ - m_mockSurface->m_surface = 0; -} - -void Surface::map() -{ - m_mapped = true; -} - -bool Surface::isMapped() const -{ - return m_mapped; -} - -Surface *Surface::fromResource(struct ::wl_resource *resource) -{ - if (auto *r = Resource::fromResource(resource)) - return static_cast<Surface *>(r->surface_object); - return nullptr; -} - -void Surface::surface_destroy_resource(Resource *) -{ - compositor()->removeSurface(this); - delete this; -} - -void Surface::surface_destroy(Resource *resource) -{ - if (m_wlShellSurface) // on wl-shell the shell surface is automatically destroyed with the surface - wl_resource_destroy(m_wlShellSurface->resource()->handle); - Q_ASSERT(!m_wlShellSurface); - Q_ASSERT(!m_xdgSurfaceV6); - wl_resource_destroy(resource->handle); -} - -void Surface::surface_attach(Resource *resource, struct wl_resource *buffer, int x, int y) -{ - if (m_xdgSurfaceV6) { - // It's a protocol error to attach a buffer to an xdgSurface that's not configured - Q_ASSERT(xdgSurfaceV6()->configureSent()); - } - - Q_UNUSED(resource); - Q_UNUSED(x); - Q_UNUSED(y); - m_buffer = buffer; - - if (!buffer) - m_mockSurface->image = QImage(); -} - -void Surface::surface_damage(Resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) -{ - Q_UNUSED(resource); - Q_UNUSED(x); - Q_UNUSED(y); - Q_UNUSED(width); - Q_UNUSED(height); -} - -void Surface::surface_frame(Resource *resource, - uint32_t callback) -{ - wl_resource *frameCallback = wl_resource_create(resource->client(), &wl_callback_interface, 1, callback); - m_frameCallbackList << frameCallback; -} - -void Surface::surface_commit(Resource *resource) -{ - Q_UNUSED(resource); - - if (m_buffer) { - struct ::wl_shm_buffer *shm_buffer = wl_shm_buffer_get(m_buffer); - if (shm_buffer) { - int stride = wl_shm_buffer_get_stride(shm_buffer); - uint format = wl_shm_buffer_get_format(shm_buffer); - Q_UNUSED(format); - void *data = wl_shm_buffer_get_data(shm_buffer); - const uchar *char_data = static_cast<const uchar *>(data); - QImage img(char_data, wl_shm_buffer_get_width(shm_buffer), wl_shm_buffer_get_height(shm_buffer), stride, QImage::Format_ARGB32_Premultiplied); - m_mockSurface->image = img; - } - } - - foreach (wl_resource *frameCallback, m_frameCallbackList) { - wl_callback_send_done(frameCallback, m_compositor->time()); - wl_resource_destroy(frameCallback); - } - m_frameCallbackList.clear(); -} - -} -MockSurface::MockSurface(Impl::Surface *surface) - : m_surface(surface) -{ -} diff --git a/tests/auto/client/shared/mocksurface.h b/tests/auto/client/shared/mocksurface.h deleted file mode 100644 index 949dc23dd..000000000 --- a/tests/auto/client/shared/mocksurface.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** 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 MOCKSURFACE_H -#define MOCKSURFACE_H - -#include <qglobal.h> - -#include "qwayland-server-wayland.h" - -#include "mockcompositor.h" - -namespace Impl { - -class XdgToplevelV6; -class WlShellSurface; - -class Surface : public QtWaylandServer::wl_surface -{ -public: - Surface(wl_client *client, uint32_t id, int v, Compositor *compositor); - ~Surface(); - - Compositor *compositor() const { return m_compositor; } - static Surface *fromResource(struct ::wl_resource *resource); - void map(); - bool isMapped() const; - XdgSurfaceV6 *xdgSurfaceV6() const { return m_xdgSurfaceV6; } - XdgToplevelV6 *xdgToplevelV6() const { return m_xdgSurfaceV6 ? m_xdgSurfaceV6->toplevel() : nullptr; } - WlShellSurface *wlShellSurface() const { return m_wlShellSurface; } - - QSharedPointer<MockSurface> mockSurface() const { return m_mockSurface; } - -protected: - - void surface_destroy_resource(Resource *resource) override; - - void surface_destroy(Resource *resource) override; - void surface_attach(Resource *resource, - struct wl_resource *buffer, int x, int y) override; - void surface_damage(Resource *resource, - int32_t x, int32_t y, int32_t width, int32_t height) override; - void surface_frame(Resource *resource, - uint32_t callback) override; - void surface_commit(Resource *resource) override; -private: - wl_resource *m_buffer = nullptr; - XdgSurfaceV6 *m_xdgSurfaceV6 = nullptr; - WlShellSurface *m_wlShellSurface = nullptr; - - Compositor *m_compositor = nullptr; - QSharedPointer<MockSurface> m_mockSurface; - QList<wl_resource *> m_frameCallbackList; - bool m_mapped = false; - - friend class XdgSurfaceV6; - friend class WlShellSurface; -}; - -} - -#endif // MOCKSURFACE_H diff --git a/tests/auto/client/shared/mockwlshell.cpp b/tests/auto/client/shared/mockwlshell.cpp deleted file mode 100644 index 50e539932..000000000 --- a/tests/auto/client/shared/mockwlshell.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/**************************************************************************** -** -** 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 "mockwlshell.h" -#include "mocksurface.h" - -namespace Impl { - -WlShellSurface::WlShellSurface(wl_client *client, int id, Surface *surface) - : QtWaylandServer::wl_shell_surface(client, id, 1) - , m_surface(surface) -{ - surface->m_wlShellSurface = this; - surface->map(); -} - -WlShellSurface::~WlShellSurface() -{ - m_surface->m_wlShellSurface = nullptr; -} - -void WlShell::shell_get_shell_surface(QtWaylandServer::wl_shell::Resource *resource, uint32_t id, wl_resource *surface) -{ - new WlShellSurface(resource->client(), id, Surface::fromResource(surface)); -} - -} // namespace Impl diff --git a/tests/auto/client/shared/mockwlshell.h b/tests/auto/client/shared/mockwlshell.h deleted file mode 100644 index 3da586ca8..000000000 --- a/tests/auto/client/shared/mockwlshell.h +++ /dev/null @@ -1,58 +0,0 @@ -/**************************************************************************** -** -** 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 <qwayland-server-wayland.h> - -#ifndef MOCKWLSHELL_H -#define MOCKWLSHELL_H - -namespace Impl { - -class Surface; - -class WlShellSurface : public QtWaylandServer::wl_shell_surface -{ -public: - explicit WlShellSurface(::wl_client *client, int id, Surface *surface); - ~WlShellSurface() override; - void shell_surface_destroy_resource(Resource *) override { delete this; } - -private: - Surface *m_surface = nullptr; -}; - -class WlShell : public QtWaylandServer::wl_shell -{ -public: - explicit WlShell(::wl_display *display) : wl_shell(display, 1) {} - void shell_get_shell_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override; -}; - -} // namespace Impl - -#endif // MOCKWLSHELL_H diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared/mockxdgshellv6.cpp deleted file mode 100644 index 05eff74ad..000000000 --- a/tests/auto/client/shared/mockxdgshellv6.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/**************************************************************************** -** -** 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 "mockxdgshellv6.h" -#include "mocksurface.h" -#include "mockcompositor.h" - -namespace Impl { - -void Compositor::sendXdgToplevelV6Configure(void *data, const QList<QVariant> ¶meters) -{ - Compositor *compositor = static_cast<Compositor *>(data); - XdgToplevelV6 *toplevel = resolveToplevel(parameters.at(0)); - Q_ASSERT(toplevel && toplevel->resource()); - QSize size = parameters.at(1).toSize(); - Q_ASSERT(size.isValid()); - auto statesBytes = parameters.at(2).toByteArray(); - toplevel->send_configure(size.width(), size.height(), statesBytes); - toplevel->xdgSurface()->send_configure(compositor->nextSerial()); -} - -XdgSurfaceV6::XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *client, uint32_t id) - : QtWaylandServer::zxdg_surface_v6(client, id, 1) - , m_surface(surface) - , m_shell(shell) -{ - m_surface->m_xdgSurfaceV6 = this; -} - -XdgSurfaceV6::~XdgSurfaceV6() -{ - m_surface->m_xdgSurfaceV6 = nullptr; -} - -void XdgSurfaceV6::sendConfigure(uint32_t serial) -{ - send_configure(serial); - m_configureSent = true; -} - -void XdgSurfaceV6::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6::Resource *resource, uint32_t id) -{ - int version = wl_resource_get_version(resource->handle); - m_toplevel = new XdgToplevelV6(this, resource->client(), id, version); -} - -void XdgSurfaceV6::zxdg_surface_v6_set_window_geometry(QtWaylandServer::zxdg_surface_v6::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) -{ - Q_UNUSED(resource); - if (m_toplevel) { - QRect geometry(x, y, width, height); - emit m_toplevel->mockToplevel()->windowGeometryRequested(geometry); - } -} - -void XdgSurfaceV6::zxdg_surface_v6_destroy(QtWaylandServer::zxdg_surface_v6::Resource *resource) -{ - Q_ASSERT(!m_toplevel); - wl_resource_destroy(resource->handle); -} - -XdgToplevelV6::XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32_t id, int version) - : QtWaylandServer::zxdg_toplevel_v6(client, id, version) - , m_xdgSurface(xdgSurface) - , m_mockToplevel(new MockXdgToplevelV6(this)) -{ - auto *surface = m_xdgSurface->surface(); - m_xdgSurface->shell()->addToplevel(this); - surface->map(); -} - -XdgToplevelV6::~XdgToplevelV6() -{ - m_xdgSurface->shell()->removeToplevel(this); - m_mockToplevel->m_toplevel = nullptr; -} - -void XdgToplevelV6::zxdg_toplevel_v6_destroy(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) -{ - m_xdgSurface->m_toplevel = nullptr; - wl_resource_destroy(resource->handle); -} - -void XdgToplevelV6::zxdg_toplevel_v6_set_minimized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) -{ - Q_UNUSED(resource); - emit m_mockToplevel->setMinimizedRequested(); -} - -void XdgToplevelV6::zxdg_toplevel_v6_set_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) -{ - Q_UNUSED(resource); - emit m_mockToplevel->setMaximizedRequested(); -} - -void XdgToplevelV6::zxdg_toplevel_v6_unset_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) -{ - Q_UNUSED(resource); - emit m_mockToplevel->unsetMaximizedRequested(); -} - -void XdgToplevelV6::zxdg_toplevel_v6_set_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *output) -{ - Q_UNUSED(resource); - Q_UNUSED(output); - emit m_mockToplevel->setFullscreenRequested(); -} - -void XdgToplevelV6::zxdg_toplevel_v6_unset_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) -{ - Q_UNUSED(resource); - emit m_mockToplevel->unsetFullscreenRequested(); -} - -void Impl::XdgShellV6::zxdg_shell_v6_get_xdg_surface(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t id, wl_resource *surface) -{ - new XdgSurfaceV6(this, Surface::fromResource(surface), resource->client(), id); -} - -} // namespace Impl diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared/mockxdgshellv6.h deleted file mode 100644 index a238fa562..000000000 --- a/tests/auto/client/shared/mockxdgshellv6.h +++ /dev/null @@ -1,114 +0,0 @@ -/**************************************************************************** -** -** 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 <qwayland-server-xdg-shell-unstable-v6.h> - -#include <QSharedPointer> -#include <QVector> - -#ifndef MOCKXDGSHELLV6_H -#define MOCKXDGSHELLV6_H - -class MockXdgToplevelV6; - -namespace Impl { - -class XdgToplevelV6; -class XdgShellV6; -class Surface; - -class XdgSurfaceV6 : public QtWaylandServer::zxdg_surface_v6 -{ -public: - XdgSurfaceV6(XdgShellV6 *shell, Surface *surface, wl_client *client, uint32_t id); - ~XdgSurfaceV6() override; - XdgShellV6 *shell() const { return m_shell; } - Surface *surface() const { return m_surface; } - XdgToplevelV6 *toplevel() const { return m_toplevel; } - - void sendConfigure(uint32_t serial); - bool configureSent() const { return m_configureSent; } - -protected: - void zxdg_surface_v6_destroy_resource(Resource *) override { delete this; } - void zxdg_surface_v6_get_toplevel(Resource *resource, uint32_t id) override; - void zxdg_surface_v6_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; - void zxdg_surface_v6_destroy(Resource *resource) override; - -private: - Surface *m_surface = nullptr; - XdgToplevelV6 *m_toplevel = nullptr; - XdgShellV6 *m_shell = nullptr; - bool m_configureSent = false; - - friend class XdgToplevelV6; -}; - -class XdgToplevelV6 : public QtWaylandServer::zxdg_toplevel_v6 -{ -public: - XdgToplevelV6(XdgSurfaceV6 *xdgSurface, wl_client *client, uint32_t id, int version); - ~XdgToplevelV6() override; - XdgSurfaceV6 *xdgSurface() const { return m_xdgSurface; } - - QSharedPointer<MockXdgToplevelV6> mockToplevel() const { return m_mockToplevel; } - -protected: - void zxdg_toplevel_v6_destroy_resource(Resource *) override { delete this; } - void zxdg_toplevel_v6_destroy(Resource *resource) override; - void zxdg_toplevel_v6_set_minimized(Resource *resource) override; - void zxdg_toplevel_v6_set_maximized(Resource *resource) override; - void zxdg_toplevel_v6_unset_maximized(Resource *resource) override; - void zxdg_toplevel_v6_set_fullscreen(Resource *resource, struct ::wl_resource *output) override; - void zxdg_toplevel_v6_unset_fullscreen(Resource *resource) override; - -private: - XdgSurfaceV6 *m_xdgSurface = nullptr; - QSharedPointer<MockXdgToplevelV6> m_mockToplevel; -}; - -class XdgShellV6 : public QtWaylandServer::zxdg_shell_v6 -{ -public: - explicit XdgShellV6(::wl_display *display) : zxdg_shell_v6(display, 1) {} - QVector<XdgToplevelV6 *> toplevels() const { return m_toplevels; } - -protected: - void zxdg_shell_v6_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surface) override; - -private: - void addToplevel(XdgToplevelV6 *toplevel) { m_toplevels.append(toplevel); } - void removeToplevel(XdgToplevelV6 *toplevel) { m_toplevels.removeOne(toplevel); } - QVector<XdgToplevelV6 *> m_toplevels; - - friend class XdgToplevelV6; -}; - -} // namespace Impl - -#endif // MOCKXDGSHELLV6_H diff --git a/tests/auto/client/shared/qttextinput.cpp b/tests/auto/client/shared/qttextinput.cpp new file mode 100644 index 000000000..1fb5ef1c4 --- /dev/null +++ b/tests/auto/client/shared/qttextinput.cpp @@ -0,0 +1,20 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "qttextinput.h" + +namespace MockCompositor { + +QtTextInputManager::QtTextInputManager(CoreCompositor *compositor) +{ + init(compositor->m_display, 1); +} + +void QtTextInputManager::text_input_method_manager_v1_get_text_input_method(Resource *resource, uint32_t id, wl_resource *seatResource) +{ + Q_UNUSED(resource); + Q_UNUSED(id); + Q_UNUSED(seatResource); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/qttextinput.h b/tests/auto/client/shared/qttextinput.h new file mode 100644 index 000000000..047cec7d3 --- /dev/null +++ b/tests/auto/client/shared/qttextinput.h @@ -0,0 +1,26 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_QTTEXTINPUT_H +#define MOCKCOMPOSITOR_QTTEXTINPUT_H + +#include "coreprotocol.h" +#include <qwayland-server-qt-text-input-method-unstable-v1.h> + +#include <QtGui/qpa/qplatformnativeinterface.h> + +namespace MockCompositor { + +class QtTextInputManager : public Global, public QtWaylandServer::qt_text_input_method_manager_v1 +{ + Q_OBJECT +public: + QtTextInputManager(CoreCompositor *compositor); + +protected: + void text_input_method_manager_v1_get_text_input_method(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_QTTEXTINPUT_H diff --git a/tests/auto/client/shared/shared.pri b/tests/auto/client/shared/shared.pri index f3cb4d5a2..97202e787 100644 --- a/tests/auto/client/shared/shared.pri +++ b/tests/auto/client/shared/shared.pri @@ -1,31 +1,29 @@ -CONFIG += testcase link_pkgconfig -QT += testlib -QT += core-private gui-private waylandclient-private +QT += testlib waylandclient-private opengl +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-output-unstable-v1.xml \ + $$PWD/../../../../src/3rdparty/protocol/xdg-shell.xml \ + $$PWD/../../../../src/3rdparty/protocol/text-input-unstable-v2.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/datadevice.h \ + $$PWD/mockcompositor.h \ + $$PWD/xdgoutputv1.h \ + $$PWD/xdgshell.h \ + $$PWD/textinput.h + +SOURCES += \ + $$PWD/corecompositor.cpp \ + $$PWD/coreprotocol.cpp \ + $$PWD/datadevice.cpp \ + $$PWD/mockcompositor.cpp \ + $$PWD/xdgoutputv1.cpp \ + $$PWD/xdgshell.cpp \ + $$PWD/textinput.cpp diff --git a/tests/auto/client/shared/textinput.cpp b/tests/auto/client/shared/textinput.cpp new file mode 100644 index 000000000..fc4865d71 --- /dev/null +++ b/tests/auto/client/shared/textinput.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "textinput.h" + +namespace MockCompositor { + +TextInputManager::TextInputManager(CoreCompositor *compositor) +{ + init(compositor->m_display, 1); +} + +void TextInputManager::zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, wl_resource *seatResource) +{ + Q_UNUSED(seatResource); + add(resource->client(), id, resource->version()); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/textinput.h b/tests/auto/client/shared/textinput.h new file mode 100644 index 000000000..ca20ddbad --- /dev/null +++ b/tests/auto/client/shared/textinput.h @@ -0,0 +1,26 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_TEXTINPUT_H +#define MOCKCOMPOSITOR_TEXTINPUT_H + +#include "coreprotocol.h" +#include <qwayland-server-text-input-unstable-v2.h> + +#include <QtGui/qpa/qplatformnativeinterface.h> + +namespace MockCompositor { + +class TextInputManager : public Global, public QtWaylandServer::zwp_text_input_manager_v2 +{ + Q_OBJECT +public: + TextInputManager(CoreCompositor *compositor); + +protected: + void zwp_text_input_manager_v2_get_text_input(Resource *resource, uint32_t id, struct ::wl_resource *seatResource) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_TEXTINPUT_H diff --git a/tests/auto/client/shared/viewport.cpp b/tests/auto/client/shared/viewport.cpp new file mode 100644 index 000000000..df6bbb336 --- /dev/null +++ b/tests/auto/client/shared/viewport.cpp @@ -0,0 +1,58 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "viewport.h" + +namespace MockCompositor { + +Viewporter::Viewporter(CoreCompositor *compositor, int version) + : QtWaylandServer::wp_viewporter(compositor->m_display, version) +{ +} + +void Viewporter::wp_viewporter_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) +{ + auto *s = fromResource<Surface>(surface); + auto *viewport = new Viewport(s, resource->client(), id, resource->version()); + connect(viewport, &QObject::destroyed, this, [this, viewport]() { + m_viewports.removeOne(viewport); + }); + m_viewports << viewport; +} + +Viewport::Viewport(Surface *surface, wl_client *client, int id, int version) + : QtWaylandServer::wp_viewport(client, id, version) + , m_surface(surface) +{ +} + +void Viewport::wp_viewport_set_source(Resource *resource, wl_fixed_t x, wl_fixed_t y, wl_fixed_t width, wl_fixed_t height) +{ + Q_UNUSED(resource) + m_source = QRectF(wl_fixed_to_double(x), + wl_fixed_to_double(y), + wl_fixed_to_double(width), + wl_fixed_to_double(height)); + Q_EMIT sourceChanged(); +} + +void Viewport::wp_viewport_set_destination(Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource) + + m_destination = QSize(width, height); + Q_EMIT destinationChanged(); +} + +void Viewport::wp_viewport_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource) + delete this; +} + +void Viewport::wp_viewport_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +} diff --git a/tests/auto/client/shared/viewport.h b/tests/auto/client/shared/viewport.h new file mode 100644 index 000000000..ddc4297db --- /dev/null +++ b/tests/auto/client/shared/viewport.h @@ -0,0 +1,50 @@ +// Copyright (C) 2022 David Edmundson <davidedmundson@kde.org> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_VIEWPORT_H +#define MOCKCOMPOSITOR_VIEWPORT_H + +#include "coreprotocol.h" +#include <qwayland-server-viewporter.h> + +namespace MockCompositor { + +class Viewport; + +class Viewporter : public Global, public QtWaylandServer::wp_viewporter +{ + Q_OBJECT +public: + explicit Viewporter(CoreCompositor *compositor, int version = 1); + QList<Viewport *> m_viewports; + +protected: + void wp_viewporter_get_viewport(Resource *resource, uint32_t id, wl_resource *surface) override; +}; + +class Viewport : public QObject, public QtWaylandServer::wp_viewport +{ + Q_OBJECT +public: + explicit Viewport(Surface *surface, wl_client *client, int id, int version); + + QRectF m_source; + QSize m_destination; + + Surface* m_surface; + +Q_SIGNALS: + void sourceChanged(); + void destinationChanged(); + +protected: + 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; + + void wp_viewport_destroy_resource(Resource *resource) override; + void wp_viewport_destroy(Resource *resource) override; +}; + +} + +#endif diff --git a/tests/auto/client/shared/xdgdialog.cpp b/tests/auto/client/shared/xdgdialog.cpp new file mode 100644 index 000000000..b973415bd --- /dev/null +++ b/tests/auto/client/shared/xdgdialog.cpp @@ -0,0 +1,59 @@ +// Copyright (C) 2024 David Redondo <kde@david-redondo.de> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include "xdgdialog.h" + +#include "xdgshell.h" + +namespace MockCompositor { + +XdgDialog::XdgDialog(XdgWmDialog *wm, XdgToplevel *toplevel, wl_client *client, int id, int version) + : QtWaylandServer::xdg_dialog_v1(client, id, version), + toplevel(toplevel), + modal(false), + m_wm(wm) +{ +} + +void XdgDialog::xdg_dialog_v1_set_modal(Resource *resource) +{ + Q_UNUSED(resource) + modal = true; +} + +void XdgDialog::xdg_dialog_v1_unset_modal(Resource *resource) +{ + Q_UNUSED(resource) + modal = false; +} + +void XdgDialog::xdg_dialog_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void XdgDialog::xdg_dialog_v1_destroy_resource(Resource *resource) +{ + Q_UNUSED(resource) + m_wm->m_dialogs.removeOne(this); + delete this; +} + +XdgWmDialog::XdgWmDialog(CoreCompositor *compositor, int version) + : QtWaylandServer::xdg_wm_dialog_v1(compositor->m_display, version) +{ +} + +void XdgWmDialog::xdg_wm_dialog_v1_destroy(Resource *resource) +{ + wl_resource_destroy(resource->handle); +} + +void XdgWmDialog::xdg_wm_dialog_v1_get_xdg_dialog(Resource *resource, uint32_t id, + struct ::wl_resource *toplevel) +{ + auto *t = fromResource<XdgToplevel>(toplevel); + auto *dialog = new XdgDialog(this, t, resource->client(), id, resource->version()); + m_dialogs.push_back(dialog); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgdialog.h b/tests/auto/client/shared/xdgdialog.h new file mode 100644 index 000000000..d9e3de996 --- /dev/null +++ b/tests/auto/client/shared/xdgdialog.h @@ -0,0 +1,49 @@ +// Copyright (C) 2024 David Redondo <kde@david-redondo.de> +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_XDG_DIALOG_H +#define MOCKCOMPOSITOR_XDG_DIALOG_H + +#include "corecompositor.h" +#include <qwayland-server-xdg-dialog-v1.h> + +namespace MockCompositor { + +class XdgToplevel; +class XdgWmDialog; + +class XdgDialog : public QtWaylandServer::xdg_dialog_v1 +{ +public: + explicit XdgDialog(XdgWmDialog *wm, XdgToplevel *toplevel, wl_client *client, int id, + int version); + XdgToplevel *toplevel; + bool modal; + +protected: + void xdg_dialog_v1_set_modal(Resource *resource) override; + void xdg_dialog_v1_unset_modal(Resource *resource) override; + void xdg_dialog_v1_destroy(Resource *resource) override; + void xdg_dialog_v1_destroy_resource(Resource *resource) override; + +private: + XdgWmDialog *m_wm; +}; + +class XdgWmDialog : public Global, public QtWaylandServer::xdg_wm_dialog_v1 +{ + Q_OBJECT +public: + explicit XdgWmDialog(CoreCompositor *compositor, int version = 1); + ~XdgWmDialog() = default; + QList<XdgDialog *> m_dialogs; + +protected: + void xdg_wm_dialog_v1_destroy(Resource *resource) override; + void xdg_wm_dialog_v1_get_xdg_dialog(Resource *resource, uint32_t id, + struct ::wl_resource *toplevel) override; +}; + +} // namespace MockCompositor + +#endif diff --git a/tests/auto/client/shared/xdgoutputv1.cpp b/tests/auto/client/shared/xdgoutputv1.cpp new file mode 100644 index 000000000..af72ae2eb --- /dev/null +++ b/tests/auto/client/shared/xdgoutputv1.cpp @@ -0,0 +1,34 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "xdgoutputv1.h" + +namespace MockCompositor { + +int XdgOutputV1::s_nextId = 1; + +void XdgOutputV1::sendLogicalSize(const QSize &size) +{ + m_logicalGeometry.setSize(size); + for (auto *resource : resourceMap()) + zxdg_output_v1::send_logical_size(resource->handle, size.width(), size.height()); +} + +void XdgOutputV1::addResource(wl_client *client, int id, int version) +{ + auto *resource = add(client, id, version)->handle; + zxdg_output_v1::send_logical_size(resource, m_logicalGeometry.width(), m_logicalGeometry.height()); + send_logical_position(resource, m_logicalGeometry.x(), m_logicalGeometry.y()); + if (version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) + send_name(resource, m_name); + if (version >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION) + send_description(resource, m_description); + + if (version < 3) // zxdg_output_v1.done has been deprecated + zxdg_output_v1::send_done(resource); + else { + m_output->sendDone(client); + } +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgoutputv1.h b/tests/auto/client/shared/xdgoutputv1.h new file mode 100644 index 000000000..8c6276741 --- /dev/null +++ b/tests/auto/client/shared/xdgoutputv1.h @@ -0,0 +1,63 @@ +// Copyright (C) 2020 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MOCKCOMPOSITOR_XDGOUTPUTV1_H +#define MOCKCOMPOSITOR_XDGOUTPUTV1_H + +#include "coreprotocol.h" + +#include <qwayland-server-xdg-output-unstable-v1.h> + +namespace MockCompositor { + +class XdgOutputV1 : public QObject, public QtWaylandServer::zxdg_output_v1 +{ +public: + explicit XdgOutputV1(Output *output) + : m_output(output) + , m_logicalGeometry(m_output->m_data.position, QSize(m_output->m_data.mode.resolution / m_output->m_data.scale)) + , m_name(QString("WL-%1").arg(s_nextId++)) + {} + + void send_logical_size(int32_t width, int32_t height) = delete; + void sendLogicalSize(const QSize &size); + + void send_done() = delete; // zxdg_output_v1.done has been deprecated (in protocol version 3) + + void addResource(wl_client *client, int id, int version); + Output *m_output = nullptr; + QRect m_logicalGeometry; + QString m_name; + QString m_description = "This is an Xdg Output description"; + static int s_nextId; +}; + +class XdgOutputManagerV1 : public Global, public QtWaylandServer::zxdg_output_manager_v1 +{ + Q_OBJECT +public: + explicit XdgOutputManagerV1(CoreCompositor *compositor, int version = 3) + : QtWaylandServer::zxdg_output_manager_v1(compositor->m_display, version) + , m_version(version) + {} + int m_version = 1; // TODO: remove on libwayland upgrade + QMap<Output *, XdgOutputV1 *> m_xdgOutputs; + XdgOutputV1 *getXdgOutput(Output *output) + { + if (auto *xdgOutput = m_xdgOutputs.value(output)) + return xdgOutput; + return m_xdgOutputs[output] = new XdgOutputV1(output); // TODO: free memory + } + +protected: + void zxdg_output_manager_v1_get_xdg_output(Resource *resource, uint32_t id, wl_resource *outputResource) override + { + auto *output = fromResource<Output>(outputResource); + auto *xdgOutput = getXdgOutput(output); + xdgOutput->addResource(resource->client(), id, resource->version()); + } +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_XDGOUTPUTV1_H diff --git a/tests/auto/client/shared/xdgshell.cpp b/tests/auto/client/shared/xdgshell.cpp new file mode 100644 index 000000000..2c1639851 --- /dev/null +++ b/tests/auto/client/shared/xdgshell.cpp @@ -0,0 +1,222 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#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 : std::as_const(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 : std::as_const(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, Qt::DirectConnection); + connect(surface, &Surface::attach, this, &XdgSurface::verifyConfigured); + connect(surface, &Surface::commit, this, [this] { + m_committed = m_pending; + + 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(positioner); + QVERIFY(!m_toplevel); + QVERIFY(!m_popup); + auto *p = fromResource<XdgSurface>(parent); + m_popup = new XdgPopup(this, p, 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_destroy(QtWaylandServer::xdg_surface::Resource *resource) +{ + QVERIFY(m_popups.empty()); + wl_resource_destroy(resource->handle); +} + +void XdgSurface::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + QRect rect(x, y, width, height); + QVERIFY(rect.isValid()); + m_pending.windowGeometry = rect; +} + +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) +{ + connect(surface(), &Surface::commit, this, [this] { m_committed = m_pending; }); +} + +void XdgToplevel::sendConfigureBounds(const QSize &size) +{ + send_configure_bounds(size.width(), size.height()); +} + +void XdgToplevel::sendConfigure(const QSize &size, const QList<uint> &states) +{ + send_configure(size.width(), size.height(), toByteArray(states)); +} + +uint XdgToplevel::sendCompleteConfigure(const QSize &size, const QList<uint> &states) +{ + sendConfigure(size, states); + return m_xdgSurface->sendConfigure(); +} + +void XdgToplevel::xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + QSize size(width, height); + QVERIFY(size.isValid()); + m_pending.maxSize = size; +} + +void XdgToplevel::xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) +{ + Q_UNUSED(resource); + QSize size(width, height); + QVERIFY(size.isValid()); + m_pending.minSize = size; +} + +XdgPopup::XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version) + : QtWaylandServer::xdg_popup(xdgSurface->resource()->client(), id, version) + , m_xdgSurface(xdgSurface) + , m_parentXdgSurface(parent) +{ + Q_ASSERT(m_xdgSurface); + Q_ASSERT(m_parentXdgSurface); + m_parentXdgSurface->m_popups << this; +} + +void XdgPopup::sendConfigure(const QRect &geometry) +{ + send_configure(geometry.x(), geometry.y(), geometry.width(), geometry.height()); +} + +uint XdgPopup::sendCompleteConfigure(const QRect &geometry) +{ + sendConfigure(geometry); + return m_xdgSurface->sendConfigure(); +} + +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 + QVERIFY(!m_grabbed); + QVERIFY(m_parentXdgSurface->isValidPopupGrabParent()); + m_xdgSurface->m_xdgWmBase->m_topmostGrabbingPopup = this; + m_grabbed = true; + m_grabSerial = serial; +} + +void XdgPopup::xdg_popup_destroy(Resource *resource) { + Q_UNUSED(resource); + if (m_grabbed) { + auto *base = m_xdgSurface->m_xdgWmBase; + QCOMPARE(base->m_topmostGrabbingPopup, this); + base->m_topmostGrabbingPopup = this->m_parentXdgSurface->m_popup; + } + m_xdgSurface->m_popup = nullptr; + m_parentXdgSurface->m_popups.removeAll(this); + emit destroyRequested(); +} + +} // namespace MockCompositor diff --git a/tests/auto/client/shared/xdgshell.h b/tests/auto/client/shared/xdgshell.h new file mode 100644 index 000000000..3959e0668 --- /dev/null +++ b/tests/auto/client/shared/xdgshell.h @@ -0,0 +1,130 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#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 = 4); + 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"; } + QList<XdgSurface *> m_xdgSurfaces; + XdgToplevel *toplevel(int i = 0); + XdgPopup *popup(int i = 0); + XdgPopup *m_topmostGrabbingPopup = nullptr; + 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(); + bool isTopmostGrabbingPopup() const { return m_popup && m_xdgWmBase->m_topmostGrabbingPopup == m_popup; } + bool isValidPopupGrabParent() const { return isTopmostGrabbingPopup() || (m_toplevel && !m_xdgWmBase->m_topmostGrabbingPopup); } + + // Role objects + XdgToplevel *m_toplevel = nullptr; + XdgPopup *m_popup = nullptr; + + XdgWmBase *m_xdgWmBase = nullptr; + Surface *m_surface = nullptr; + bool m_configureSent = false; + QList<uint> m_pendingConfigureSerials; + uint m_ackedConfigureSerial = 0; + uint m_committedConfigureSerial = 0; + struct DoubleBufferedState { + QRect windowGeometry = {0, 0, 0, 0}; + } m_pending, m_committed; + QList<XdgPopup *> m_popups; + +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; + void xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; + void xdg_surface_ack_configure(Resource *resource, uint32_t serial) override; +}; + +class XdgToplevel : public QObject, public QtWaylandServer::xdg_toplevel +{ + Q_OBJECT +public: + explicit XdgToplevel(XdgSurface *xdgSurface, int id, int version = 1); + void sendConfigureBounds(const QSize &size); + void sendConfigure(const QSize &size = {0, 0}, const QList<uint> &states = {}); + uint sendCompleteConfigure(const QSize &size = {0, 0}, const QList<uint> &states = {}); + Surface *surface() { return m_xdgSurface->m_surface; } + + XdgSurface *m_xdgSurface = nullptr; + struct DoubleBufferedState { + QSize minSize = {0, 0}; + QSize maxSize = {0, 0}; + } m_pending, m_committed; + +protected: + void xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) override; + void xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) override; +}; + +class XdgPopup : public QObject, public QtWaylandServer::xdg_popup +{ + Q_OBJECT +public: + explicit XdgPopup(XdgSurface *xdgSurface, XdgSurface *parent, int id, int version = 1); + void sendConfigure(const QRect &geometry); + uint sendCompleteConfigure(const QRect &geometry); + Surface *surface() { return m_xdgSurface->m_surface; } + XdgSurface *m_xdgSurface = nullptr; + XdgSurface *m_parentXdgSurface = nullptr; + bool m_grabbed = false; + uint m_grabSerial = 0; +signals: + void destroyRequested(); +protected: + void xdg_popup_grab(Resource *resource, ::wl_resource *seat, uint32_t serial) override; + void xdg_popup_destroy(Resource *resource) override; +}; + +} // namespace MockCompositor + +#endif // MOCKCOMPOSITOR_XDGSHELL_H |