The QVector m_surface is modified in both threads. Change-Id: I1818a5e1307d191f1613513b86703eaa7bda3c6e Reviewed-by: Johan Helsing <johan.helsing@qt.io>
490 lines
15 KiB
C++
490 lines
15 KiB
C++
/****************************************************************************
|
|
**
|
|
** 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();
|
|
{
|
|
QVector<Impl::Surface *> surfaces = m_compositor->surfaces();
|
|
foreach (Impl::Surface *surface, surfaces) {
|
|
// we don't want to mistake the cursor surface for a window surface
|
|
if (surface->isMapped()) {
|
|
result = surface->mockSurface();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
QSharedPointer<MockOutput> MockCompositor::output(int index)
|
|
{
|
|
QSharedPointer<MockOutput> result;
|
|
lock();
|
|
if (Impl::Output *output = m_compositor->outputs().value(index, nullptr))
|
|
result = output->mockOutput();
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
QSharedPointer<MockIviSurface> MockCompositor::iviSurface(int index)
|
|
{
|
|
QSharedPointer<MockIviSurface> result;
|
|
lock();
|
|
if (Impl::IviSurface *toplevel = m_compositor->iviApplication()->iviSurfaces().value(index, nullptr))
|
|
result = toplevel->mockIviSurface();
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
QSharedPointer<MockXdgToplevelV6> MockCompositor::xdgToplevelV6(int index)
|
|
{
|
|
QSharedPointer<MockXdgToplevelV6> result;
|
|
lock();
|
|
if (Impl::XdgToplevelV6 *toplevel = m_compositor->xdgShellV6()->toplevels().value(index, nullptr))
|
|
result = toplevel->mockToplevel();
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
QSharedPointer<MockSurface> MockCompositor::fullScreenShellV1Surface(int index)
|
|
{
|
|
QSharedPointer<MockSurface> result;
|
|
lock();
|
|
if (Impl::Surface *surface = m_compositor->fullScreenShellV1()->surfaces().value(index, nullptr))
|
|
result = surface->mockSurface();
|
|
unlock();
|
|
return result;
|
|
}
|
|
|
|
MockCompositor::Command MockCompositor::makeCommand(Command::Callback callback, void *target)
|
|
{
|
|
Command command;
|
|
command.callback = callback;
|
|
command.target = target;
|
|
return command;
|
|
}
|
|
|
|
void MockCompositor::processCommand(const Command &command)
|
|
{
|
|
lock();
|
|
m_commandQueue << command;
|
|
unlock();
|
|
|
|
m_waitCondition.wakeOne();
|
|
}
|
|
|
|
void MockCompositor::dispatchCommands()
|
|
{
|
|
lock();
|
|
int count = m_commandQueue.length();
|
|
unlock();
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
lock();
|
|
const Command command = m_commandQueue.takeFirst();
|
|
unlock();
|
|
command.callback(command.target, command.parameters);
|
|
}
|
|
}
|
|
|
|
void *MockCompositor::run(void *data)
|
|
{
|
|
MockCompositor *controller = static_cast<MockCompositor *>(data);
|
|
|
|
Impl::Compositor compositor(controller);
|
|
|
|
controller->m_compositor = &compositor;
|
|
controller->m_waitCondition.wakeOne();
|
|
|
|
while (!controller->m_ready) {
|
|
controller->dispatchCommands();
|
|
compositor.dispatchEvents(20);
|
|
}
|
|
|
|
while (controller->m_alive) {
|
|
{
|
|
QMutexLocker locker(&controller->m_mutex);
|
|
if (controller->m_commandQueue.isEmpty())
|
|
controller->m_waitCondition.wait(&controller->m_mutex);
|
|
}
|
|
controller->dispatchCommands();
|
|
compositor.dispatchEvents(20);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
namespace Impl {
|
|
|
|
Compositor::Compositor(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;
|
|
}
|
|
|
|
}
|