summaryrefslogtreecommitdiff
path: root/src/protocols/Viewporter.cpp
blob: 9612d3f8068ac13e9bfda959104c2c94f260c88d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include "Viewporter.hpp"
#include "core/Compositor.hpp"
#include <algorithm>
#include <cmath>

CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceResource> surface_) : m_surface(surface_), m_resource(resource_) {
    if UNLIKELY (!good())
        return;

    m_resource->setDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); });
    m_resource->setOnDestroy([this](CWpViewport* r) { PROTO::viewport->destroyResource(this); });

    m_resource->setSetDestination([this](CWpViewport* r, int32_t x, int32_t y) {
        if UNLIKELY (!m_surface) {
            r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone");
            return;
        }

        m_surface->m_pending.updated.bits.viewport = true;

        if (x == -1 && y == -1) {
            m_surface->m_pending.viewport.hasDestination = false;
            return;
        }

        if UNLIKELY (x <= 0 || y <= 0) {
            r->error(WP_VIEWPORT_ERROR_BAD_SIZE, "Size was <= 0");
            return;
        }

        m_surface->m_pending.viewport.hasDestination = true;
        m_surface->m_pending.viewport.destination    = {x, y};
    });

    m_resource->setSetSource([this](CWpViewport* r, wl_fixed_t fx, wl_fixed_t fy, wl_fixed_t fw, wl_fixed_t fh) {
        if UNLIKELY (!m_surface) {
            r->error(WP_VIEWPORT_ERROR_NO_SURFACE, "Surface is gone");
            return;
        }

        m_surface->m_pending.updated.bits.viewport = true;

        double x = wl_fixed_to_double(fx), y = wl_fixed_to_double(fy), w = wl_fixed_to_double(fw), h = wl_fixed_to_double(fh);

        if (x == -1 && y == -1 && w == -1 && h == -1) {
            m_surface->m_pending.viewport.hasSource = false;
            return;
        }

        if UNLIKELY (x < 0 || y < 0) {
            r->error(WP_VIEWPORT_ERROR_BAD_SIZE, "Pos was < 0");
            return;
        }

        m_surface->m_pending.viewport.hasSource = true;
        m_surface->m_pending.viewport.source    = {x, y, w, h};
    });

    m_listeners.surfacePrecommit = m_surface->m_events.precommit.listen([this] {
        if (!m_surface || !m_surface->m_pending.buffer)
            return;

        if (m_surface->m_pending.viewport.hasSource) {
            auto&       src  = m_surface->m_pending.viewport.source;
            const auto& size = m_surface->m_pending.bufferSize;

            if (size.x <= 0.0 || size.y <= 0.0)
                return;

            constexpr wl_fixed_t MAX_TOLERANCE = 1 << 8; // wl_fixed 1.0 = 256

            auto                 clampAxis = [&](double& start, double& length, double limit) -> bool {
                const double     originalStart = start;
                const double     originalEnd   = start + length;
                const double     clampedStart  = std::clamp(start, 0.0, std::max(0.0, limit));
                const double     clampedEnd    = std::clamp(originalEnd, 0.0, std::max(0.0, limit));
                const wl_fixed_t startDelta    = std::abs(wl_fixed_from_double(clampedStart) - wl_fixed_from_double(originalStart));
                const wl_fixed_t endDelta      = std::abs(wl_fixed_from_double(clampedEnd) - wl_fixed_from_double(originalEnd));

                if (startDelta > MAX_TOLERANCE || endDelta > MAX_TOLERANCE)
                    return false;

                start  = clampedStart;
                length = clampedEnd - clampedStart;
                return length > 0.0;
            };

            if (!clampAxis(src.x, src.w, size.x) || !clampAxis(src.y, src.h, size.y)) {
                m_resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit");
                m_surface->m_pending.rejected = true;
                return;
            }
        }
    });
}

CViewportResource::~CViewportResource() {
    if (!m_surface)
        return;

    m_surface->m_pending.viewport.hasDestination = false;
    m_surface->m_pending.viewport.hasSource      = false;
}

bool CViewportResource::good() {
    return m_resource->resource();
}

CViewporterResource::CViewporterResource(SP<CWpViewporter> resource_) : m_resource(resource_) {
    if UNLIKELY (!good())
        return;

    m_resource->setDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); });
    m_resource->setOnDestroy([this](CWpViewporter* r) { PROTO::viewport->destroyResource(this); });

    m_resource->setGetViewport([](CWpViewporter* r, uint32_t id, wl_resource* surf) {
        const auto RESOURCE = PROTO::viewport->m_viewports.emplace_back(
            makeShared<CViewportResource>(makeShared<CWpViewport>(r->client(), r->version(), id), CWLSurfaceResource::fromResource(surf)));

        if UNLIKELY (!RESOURCE->good()) {
            r->noMemory();
            PROTO::viewport->m_viewports.pop_back();
            return;
        }
    });
}

bool CViewporterResource::good() {
    return m_resource->resource();
}

CViewporterProtocol::CViewporterProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
    ;
}

void CViewporterProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
    const auto RESOURCE = m_managers.emplace_back(makeShared<CViewporterResource>(makeShared<CWpViewporter>(client, ver, id)));

    if UNLIKELY (!RESOURCE->good()) {
        wl_client_post_no_memory(client);
        m_managers.pop_back();
        return;
    }
}

void CViewporterProtocol::destroyResource(CViewporterResource* resource) {
    std::erase_if(m_managers, [&](const auto& other) { return other.get() == resource; });
}

void CViewporterProtocol::destroyResource(CViewportResource* resource) {
    std::erase_if(m_viewports, [&](const auto& other) { return other.get() == resource; });
}