Revision control
Copy as Markdown
// 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();
void unmap();
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 WlShellSurfaceRole : public SurfaceRole
{
Q_OBJECT
};
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;
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 *surfaceResource,
::wl_resource *parent) override;
};
class SubSurfaceRole : public SurfaceRole
{
Q_OBJECT
};
class Subsurface : public QObject, public QtWaylandServer::wl_subsurface
{
Q_OBJECT
public:
explicit Subsurface(SubCompositor *subCompositor, wl_client *client, int id, int version)
: QtWaylandServer::wl_subsurface(client, id, version), m_subcompositor(subCompositor)
{
}
~Subsurface()
{
m_subcompositor->m_subsurfaces.removeOne(this);
qDebug() << m_subcompositor->m_subsurfaces;
}
void subsurface_destroy(Resource *resource) override { wl_resource_destroy(resource->handle); }
void subsurface_destroy_resource(Resource *resource) override
{
Q_UNUSED(resource)
delete this;
}
private:
SubCompositor *m_subcompositor;
};
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, const QString &seatName = QLatin1String("seat0"));
~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);
QString m_seatName;
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);
wl_seat::send_name(resource->handle, m_seatName); // in any normal world this is would be set before capabilities. Weston does it after
}
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