| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the test suite of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "mockcompositor.h" |
| #include "mockinput.h" |
| #include "mockoutput.h" |
| #include "mocksurface.h" |
| #include "mockwlshell.h" |
| #include "mockxdgshellv6.h" |
| #include "mockiviapplication.h" |
| |
| #include <wayland-xdg-shell-unstable-v6-server-protocol.h> |
| |
| #include <stdio.h> |
| MockCompositor::MockCompositor() |
| { |
| pthread_create(&m_thread, 0, run, this); |
| |
| m_mutex.lock(); |
| m_waitCondition.wait(&m_mutex); |
| m_mutex.unlock(); |
| } |
| |
| MockCompositor::~MockCompositor() |
| { |
| m_alive = false; |
| m_waitCondition.wakeOne(); |
| pthread_join(m_thread, 0); |
| } |
| |
| void MockCompositor::lock() |
| { |
| m_mutex.lock(); |
| } |
| |
| void MockCompositor::unlock() |
| { |
| m_mutex.unlock(); |
| } |
| |
| void MockCompositor::applicationInitialized() |
| { |
| m_ready = true; |
| } |
| |
| int MockCompositor::waylandFileDescriptor() const |
| { |
| return m_compositor->fileDescriptor(); |
| } |
| |
| void MockCompositor::processWaylandEvents() |
| { |
| m_waitCondition.wakeOne(); |
| } |
| |
| void MockCompositor::setOutputMode(const QSize &size) |
| { |
| Command command = makeCommand(Impl::Compositor::setOutputMode, m_compositor); |
| command.parameters << size; |
| processCommand(command); |
| } |
| |
| void MockCompositor::setKeyboardFocus(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::setKeyboardFocus, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendMousePress(const QSharedPointer<MockSurface> &surface, const QPoint &pos) |
| { |
| Command command = makeCommand(Impl::Compositor::sendMousePress, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << pos; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendMouseRelease(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::sendMouseRelease, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendKeyPress(const QSharedPointer<MockSurface> &surface, uint code) |
| { |
| Command command = makeCommand(Impl::Compositor::sendKeyPress, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << code; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendKeyRelease(const QSharedPointer<MockSurface> &surface, uint code) |
| { |
| Command command = makeCommand(Impl::Compositor::sendKeyRelease, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << code; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendTouchDown(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) |
| { |
| Command command = makeCommand(Impl::Compositor::sendTouchDown, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << position << id; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendTouchMotion(const QSharedPointer<MockSurface> &surface, const QPoint &position, int id) |
| { |
| Command command = makeCommand(Impl::Compositor::sendTouchMotion, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << position << id; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendTouchUp(const QSharedPointer<MockSurface> &surface, int id) |
| { |
| Command command = makeCommand(Impl::Compositor::sendTouchUp, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << id; |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendTouchFrame(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::sendTouchFrame, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendDataDeviceDataOffer(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::sendDataDeviceDataOffer, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendDataDeviceEnter(const QSharedPointer<MockSurface> &surface, const QPoint& position) |
| { |
| Command command = makeCommand(Impl::Compositor::sendDataDeviceEnter, m_compositor); |
| command.parameters << QVariant::fromValue(surface) << QVariant::fromValue(position); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendDataDeviceMotion(const QPoint &position) |
| { |
| Command command = makeCommand(Impl::Compositor::sendDataDeviceMotion, m_compositor); |
| command.parameters << QVariant::fromValue(position); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendDataDeviceDrop(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::sendDataDeviceDrop, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::sendDataDeviceLeave(const QSharedPointer<MockSurface> &surface) |
| { |
| Command command = makeCommand(Impl::Compositor::sendDataDeviceLeave, m_compositor); |
| command.parameters << QVariant::fromValue(surface); |
| processCommand(command); |
| } |
| |
| void MockCompositor::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(); |
| { |
| const QVector<Impl::Surface *> surfaces = m_compositor->surfaces(); |
| for (Impl::Surface *surface : surfaces) { |
| // we don't want to mistake the cursor surface for a window surface |
| if (surface->isMapped()) { |
| result = surface->mockSurface(); |
| break; |
| } |
| } |
| } |
| unlock(); |
| return result; |
| } |
| |
| QSharedPointer<MockOutput> MockCompositor::output(int index) |
| { |
| QSharedPointer<MockOutput> result; |
| lock(); |
| if (Impl::Output *output = m_compositor->outputs().value(index, nullptr)) |
| result = output->mockOutput(); |
| unlock(); |
| return result; |
| } |
| |
| QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index) |
| { |
| QSharedPointer<MockIviSurface> result; |
| lock(); |
| if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr)) |
| result = toplevel->mockIviSurface(); |
| unlock(); |
| return result; |
| } |
| |
| QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index) |
| { |
| QSharedPointer<MockXdgToplevelV6> result; |
| lock(); |
| if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr)) |
| result = toplevel->mockToplevel(); |
| unlock(); |
| return result; |
| } |
| |
| QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index) |
| { |
| QSharedPointer<MockSurface> result; |
| lock(); |
| if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr)) |
| result = surface->mockSurface(); |
| unlock(); |
| return result; |
| } |
| |
| MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target) |
| { |
| Command command; |
| command.callback = callback; |
| command.target = target; |
| return command; |
| } |
| |
| void MockCompositor::processCommand(const Command &command) |
| { |
| lock(); |
| m_commandQueue << command; |
| unlock(); |
| |
| m_waitCondition.wakeOne(); |
| } |
| |
| void MockCompositor::dispatchCommands() |
| { |
| lock(); |
| int count = m_commandQueue.length(); |
| unlock(); |
| |
| for (int i = 0; i < count; ++i) { |
| lock(); |
| const Command command = m_commandQueue.takeFirst(); |
| unlock(); |
| command.callback(command.target, command.parameters); |
| } |
| } |
| |
| void *MockCompositor::run(void *data) |
| { |
| MockCompositor *controller = static_cast<MockCompositor *>(data); |
| |
| Impl::Compositor compositor(controller); |
| |
| controller->m_compositor = &compositor; |
| |
| while (!controller->m_ready) { |
| controller->m_waitCondition.wakeOne(); |
| controller->dispatchCommands(); |
| compositor.dispatchEvents(20); |
| } |
| |
| while (controller->m_alive) { |
| { |
| QMutexLocker locker(&controller->m_mutex); |
| if (controller->m_commandQueue.isEmpty()) |
| controller->m_waitCondition.wait(&controller->m_mutex); |
| } |
| controller->dispatchCommands(); |
| compositor.dispatchEvents(20); |
| } |
| |
| return 0; |
| } |
| |
| namespace Impl { |
| |
| Compositor::Compositor(MockCompositor *mockCompositor) |
| : m_mockCompositor(mockCompositor), m_display(wl_display_create()) |
| { |
| if (wl_display_add_socket(m_display, 0)) { |
| fprintf(stderr, "Fatal: Failed to open server socket\n"); |
| exit(EXIT_FAILURE); |
| } |
| |
| wl_global_create(m_display, &wl_compositor_interface, 1, this, bindCompositor); |
| |
| m_data_device_manager.reset(new DataDeviceManager(this, m_display)); |
| |
| wl_display_init_shm(m_display); |
| |
| m_seat.reset(new Seat(this, m_display)); |
| m_pointer = m_seat->pointer(); |
| m_keyboard = m_seat->keyboard(); |
| m_touch = m_seat->touch(); |
| |
| m_outputs.append(new Output(m_display, QSize(1920, 1080), QPoint(0, 0))); |
| m_iviApplication.reset(new IviApplication(m_display)); |
| m_wlShell.reset(new WlShell(m_display)); |
| m_xdgShellV6.reset(new XdgShellV6(m_display)); |
| m_fullScreenShellV1.reset(new FullScreenShellV1(m_display)); |
| |
| m_loop = wl_display_get_event_loop(m_display); |
| m_fd = wl_event_loop_get_fd(m_loop); |
| } |
| |
| Compositor::~Compositor() |
| { |
| wl_display_destroy(m_display); |
| } |
| |
| void Compositor::dispatchEvents(int timeout) |
| { |
| wl_display_flush_clients(m_display); |
| wl_event_loop_dispatch(m_loop, timeout); |
| } |
| |
| static void compositor_create_surface(wl_client *client, wl_resource *compositorResource, uint32_t id) |
| { |
| Compositor *compositor = static_cast<Compositor *>(wl_resource_get_user_data(compositorResource)); |
| compositor->addSurface(new Surface(client, id, wl_resource_get_version(compositorResource), compositor)); |
| } |
| |
| static void compositor_create_region(wl_client *client, wl_resource *compositorResource, uint32_t id) |
| { |
| Q_UNUSED(client); |
| Q_UNUSED(compositorResource); |
| Q_UNUSED(id); |
| } |
| |
| void Compositor::bindCompositor(wl_client *client, void *compositorData, uint32_t version, uint32_t id) |
| { |
| static const struct wl_compositor_interface compositorInterface = { |
| compositor_create_surface, |
| compositor_create_region |
| }; |
| |
| wl_resource *resource = wl_resource_create(client, &wl_compositor_interface, static_cast<int>(version), id); |
| wl_resource_set_implementation(resource, &compositorInterface, compositorData, nullptr); |
| } |
| |
| static void unregisterResourceCallback(wl_listener *listener, void *data) |
| { |
| struct wl_resource *resource = reinterpret_cast<struct wl_resource *>(data); |
| wl_list_remove(wl_resource_get_link(resource)); |
| delete listener; |
| } |
| |
| void registerResource(wl_list *list, wl_resource *resource) |
| { |
| wl_list_insert(list, wl_resource_get_link(resource)); |
| |
| wl_listener *listener = new wl_listener; |
| listener->notify = unregisterResourceCallback; |
| |
| wl_resource_add_destroy_listener(resource, listener); |
| } |
| |
| QVector<Surface *> Compositor::surfaces() const |
| { |
| return m_surfaces; |
| } |
| |
| QVector<Output *> Compositor::outputs() const |
| { |
| return m_outputs; |
| } |
| |
| IviApplication *Compositor::iviApplication() const |
| { |
| return m_iviApplication.data(); |
| } |
| |
| XdgShellV6 *Compositor::xdgShellV6() const |
| { |
| return m_xdgShellV6.data(); |
| } |
| |
| FullScreenShellV1 *Compositor::fullScreenShellV1() const |
| { |
| return m_fullScreenShellV1.data(); |
| } |
| |
| uint32_t Compositor::nextSerial() |
| { |
| return wl_display_next_serial(m_display); |
| } |
| |
| void Compositor::addSurface(Surface *surface) |
| { |
| m_mockCompositor->lock(); |
| m_surfaces << surface; |
| m_mockCompositor->unlock(); |
| } |
| |
| void Compositor::removeSurface(Surface *surface) |
| { |
| m_mockCompositor->lock(); |
| m_surfaces.removeOne(surface); |
| m_keyboard->handleSurfaceDestroyed(surface); |
| m_pointer->handleSurfaceDestroyed(surface); |
| m_fullScreenShellV1->removeSurface(surface); |
| m_mockCompositor->unlock(); |
| } |
| |
| Surface *Compositor::resolveSurface(const QVariant &v) |
| { |
| QSharedPointer<MockSurface> mockSurface = v.value<QSharedPointer<MockSurface> >(); |
| return mockSurface ? mockSurface->handle() : nullptr; |
| } |
| |
| Output *Compositor::resolveOutput(const QVariant &v) |
| { |
| QSharedPointer<MockOutput> mockOutput = v.value<QSharedPointer<MockOutput> >(); |
| return mockOutput ? mockOutput->handle() : nullptr; |
| } |
| |
| IviSurface *Compositor::resolveIviSurface(const QVariant &v) |
| { |
| QSharedPointer<MockIviSurface> mockIviSurface = v.value<QSharedPointer<MockIviSurface>>(); |
| return mockIviSurface ? mockIviSurface->handle() : nullptr; |
| } |
| |
| XdgToplevelV6 *Compositor::resolveToplevel(const QVariant &v) |
| { |
| QSharedPointer<MockXdgToplevelV6> mockToplevel = v.value<QSharedPointer<MockXdgToplevelV6>>(); |
| return mockToplevel ? mockToplevel->handle() : nullptr; |
| } |
| |
| } |