wayland/core: move to new impl (#6268)
* wayland/core/dmabuf: move to new impl it's the final countdown
This commit is contained in:
parent
c31d9ef417
commit
6967a31450
147 changed files with 5388 additions and 2226 deletions
467
src/protocols/core/Compositor.cpp
Normal file
467
src/protocols/core/Compositor.cpp
Normal file
|
|
@ -0,0 +1,467 @@
|
|||
#include "Compositor.hpp"
|
||||
#include "Output.hpp"
|
||||
#include "../types/WLBuffer.hpp"
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
#include "Subcompositor.hpp"
|
||||
#include "../Viewporter.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
|
||||
#define LOGM PROTO::compositor->protoLog
|
||||
|
||||
class CDefaultSurfaceRole : public ISurfaceRole {
|
||||
public:
|
||||
virtual eSurfaceRole role() {
|
||||
return SURFACE_ROLE_UNASSIGNED;
|
||||
}
|
||||
};
|
||||
|
||||
SP<CDefaultSurfaceRole> defaultRole = makeShared<CDefaultSurfaceRole>();
|
||||
|
||||
CWLCallbackResource::CWLCallbackResource(SP<CWlCallback> resource_) : resource(resource_) {
|
||||
;
|
||||
}
|
||||
|
||||
bool CWLCallbackResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void CWLCallbackResource::send(timespec* now) {
|
||||
resource->sendDone(now->tv_sec * 1000 + now->tv_nsec / 1000000);
|
||||
}
|
||||
|
||||
CWLRegionResource::CWLRegionResource(SP<CWlRegion> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
resource->setDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CWlRegion* r) { PROTO::compositor->destroyResource(this); });
|
||||
|
||||
resource->setAdd([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.add(CBox{x, y, w, h}); });
|
||||
resource->setSubtract([this](CWlRegion* r, int32_t x, int32_t y, int32_t w, int32_t h) { region.subtract(CBox{x, y, w, h}); });
|
||||
}
|
||||
|
||||
bool CWLRegionResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
SP<CWLRegionResource> CWLRegionResource::fromResource(wl_resource* res) {
|
||||
auto data = (CWLRegionResource*)(((CWlRegion*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
}
|
||||
|
||||
CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
pClient = resource->client();
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
role = defaultRole;
|
||||
|
||||
resource->setDestroy([this](CWlSurface* r) { destroy(); });
|
||||
resource->setOnDestroy([this](CWlSurface* r) { destroy(); });
|
||||
|
||||
resource->setAttach([this](CWlSurface* r, wl_resource* buffer, int32_t x, int32_t y) {
|
||||
pending.offset = {x, y};
|
||||
|
||||
if (!buffer) {
|
||||
pending.buffer.reset();
|
||||
pending.texture.reset();
|
||||
} else {
|
||||
auto res = CWLBufferResource::fromResource(buffer);
|
||||
pending.buffer = res && res->buffer ? res->buffer.lock() : nullptr;
|
||||
pending.size = res && res->buffer ? res->buffer->size : Vector2D{};
|
||||
pending.texture = res && res->buffer ? res->buffer->texture : nullptr;
|
||||
}
|
||||
|
||||
Vector2D oldBufSize = current.buffer ? current.buffer->size : Vector2D{};
|
||||
Vector2D newBufSize = pending.buffer ? pending.buffer->size : Vector2D{};
|
||||
|
||||
if (oldBufSize != newBufSize)
|
||||
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
|
||||
|
||||
bufferReleased = false;
|
||||
});
|
||||
|
||||
resource->setCommit([this](CWlSurface* r) {
|
||||
if (pending.buffer)
|
||||
pending.bufferDamage.intersect(CBox{{}, pending.buffer->size});
|
||||
|
||||
if (!pending.buffer)
|
||||
pending.size = {};
|
||||
else if (pending.viewport.hasDestination)
|
||||
pending.size = pending.viewport.destination;
|
||||
else if (pending.viewport.hasSource)
|
||||
pending.size = pending.viewport.source.size();
|
||||
else {
|
||||
Vector2D tfs = pending.transform % 2 == 1 ? Vector2D{pending.buffer->size.y, pending.buffer->size.x} : pending.buffer->size;
|
||||
pending.size = tfs / pending.scale;
|
||||
}
|
||||
|
||||
if (viewportResource)
|
||||
viewportResource->verify();
|
||||
|
||||
pending.damage.intersect(CBox{{}, pending.size});
|
||||
|
||||
CRegion previousBufferDamage = accumulateCurrentBufferDamage();
|
||||
|
||||
current = pending;
|
||||
pending.damage.clear();
|
||||
pending.bufferDamage.clear();
|
||||
|
||||
if (current.buffer && !bufferReleased) {
|
||||
// without previous dolphin et al are weird vvv
|
||||
//CRegion surfaceDamage =
|
||||
// current.damage.copy().scale(current.scale).transform(current.transform, current.size.x, current.size.y).add(current.bufferDamage).add(previousBufferDamage);
|
||||
current.buffer->update(CBox{{}, {INT32_MAX, INT32_MAX}}); // FIXME: figure this out to not use this hack. QT apps are wonky without this.
|
||||
|
||||
// release the buffer, glTexImage2D is synchronous (as in, data is consumed after the call returns)
|
||||
// so we can let the app know we're done.
|
||||
// for dma buffers, this doesn't matter.
|
||||
current.buffer->sendRelease();
|
||||
bufferReleased = true;
|
||||
}
|
||||
|
||||
// TODO: we should _accumulate_ and not replace above if sync
|
||||
if (role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)role.get();
|
||||
if (subsurface->sync)
|
||||
return;
|
||||
|
||||
events.commit.emit();
|
||||
} else {
|
||||
// send commit to all synced surfaces in this tree.
|
||||
breadthfirst(
|
||||
[](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* data) {
|
||||
if (surf->role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)surf->role.get();
|
||||
if (!subsurface->sync)
|
||||
return;
|
||||
}
|
||||
surf->events.commit.emit();
|
||||
},
|
||||
nullptr);
|
||||
}
|
||||
});
|
||||
|
||||
resource->setDamage([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.damage.add(CBox{x, y, w, h}); });
|
||||
resource->setDamageBuffer([this](CWlSurface* r, int32_t x, int32_t y, int32_t w, int32_t h) { pending.bufferDamage.add(CBox{x, y, w, h}); });
|
||||
|
||||
resource->setSetBufferScale([this](CWlSurface* r, int32_t scale) { pending.scale = scale; });
|
||||
resource->setSetBufferTransform([this](CWlSurface* r, uint32_t tr) { pending.transform = (wl_output_transform)tr; });
|
||||
|
||||
resource->setSetInputRegion([this](CWlSurface* r, wl_resource* region) {
|
||||
if (!region) {
|
||||
pending.input = CBox{{}, {INT32_MAX, INT32_MAX}};
|
||||
return;
|
||||
}
|
||||
|
||||
auto RG = CWLRegionResource::fromResource(region);
|
||||
pending.input = RG->region;
|
||||
});
|
||||
|
||||
resource->setSetOpaqueRegion([this](CWlSurface* r, wl_resource* region) {
|
||||
if (!region) {
|
||||
pending.opaque = CBox{{}, {}};
|
||||
return;
|
||||
}
|
||||
|
||||
auto RG = CWLRegionResource::fromResource(region);
|
||||
pending.opaque = RG->region;
|
||||
});
|
||||
|
||||
resource->setFrame([this](CWlSurface* r, uint32_t id) { callbacks.emplace_back(makeShared<CWLCallbackResource>(makeShared<CWlCallback>(pClient, 1, id))); });
|
||||
|
||||
resource->setOffset([this](CWlSurface* r, int32_t x, int32_t y) { pending.offset = {x, y}; });
|
||||
}
|
||||
|
||||
CWLSurfaceResource::~CWLSurfaceResource() {
|
||||
events.destroy.emit();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::destroy() {
|
||||
if (mapped)
|
||||
unmap();
|
||||
events.destroy.emit();
|
||||
PROTO::compositor->destroyResource(this);
|
||||
}
|
||||
|
||||
SP<CWLSurfaceResource> CWLSurfaceResource::fromResource(wl_resource* res) {
|
||||
auto data = (CWLSurfaceResource*)(((CWlSurface*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
}
|
||||
|
||||
bool CWLSurfaceResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
wl_client* CWLSurfaceResource::client() {
|
||||
return pClient;
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::enter(SP<CMonitor> monitor) {
|
||||
if (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) != enteredOutputs.end())
|
||||
return;
|
||||
|
||||
if (!PROTO::outputs.contains(monitor->szName)) {
|
||||
// can happen on unplug/replug
|
||||
LOGM(ERR, "enter() called on a non-existent output global");
|
||||
return;
|
||||
}
|
||||
|
||||
auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient);
|
||||
|
||||
if (!output || !output->getResource() || !output->getResource()->resource()) {
|
||||
LOGM(ERR, "Cannot enter surface {:x} to {}, client hasn't bound the output", (uintptr_t)this, monitor->szName);
|
||||
return;
|
||||
}
|
||||
|
||||
enteredOutputs.emplace_back(monitor);
|
||||
|
||||
resource->sendEnter(output->getResource().get());
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::leave(SP<CMonitor> monitor) {
|
||||
if (std::find(enteredOutputs.begin(), enteredOutputs.end(), monitor) == enteredOutputs.end())
|
||||
return;
|
||||
|
||||
auto output = PROTO::outputs.at(monitor->szName)->outputResourceFrom(pClient);
|
||||
|
||||
if (!output) {
|
||||
LOGM(ERR, "Cannot leave surface {:x} from {}, client hasn't bound the output", (uintptr_t)this, monitor->szName);
|
||||
return;
|
||||
}
|
||||
|
||||
std::erase(enteredOutputs, monitor);
|
||||
|
||||
resource->sendLeave(output->getResource().get());
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::sendPreferredTransform(wl_output_transform t) {
|
||||
if (resource->version() < 6)
|
||||
return;
|
||||
resource->sendPreferredBufferTransform(t);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::sendPreferredScale(int32_t scale) {
|
||||
if (resource->version() < 6)
|
||||
return;
|
||||
resource->sendPreferredBufferScale(scale);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::frame(timespec* now) {
|
||||
if (callbacks.empty())
|
||||
return;
|
||||
|
||||
for (auto& c : callbacks) {
|
||||
c->send(now);
|
||||
}
|
||||
|
||||
callbacks.clear();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::resetRole() {
|
||||
role = defaultRole;
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
|
||||
for (auto& n : nodes) {
|
||||
|
||||
Vector2D offset = {};
|
||||
if (n->role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)n->role.get();
|
||||
offset = subsurface->posRelativeToParent();
|
||||
}
|
||||
|
||||
fn(n, offset, data);
|
||||
}
|
||||
|
||||
std::vector<SP<CWLSurfaceResource>> nodes2;
|
||||
|
||||
for (auto& n : nodes) {
|
||||
std::erase_if(n->subsurfaces, [](const auto& e) { return e.expired(); });
|
||||
for (auto& c : n->subsurfaces) {
|
||||
nodes2.push_back(c->surface.lock());
|
||||
}
|
||||
}
|
||||
|
||||
if (!nodes2.empty())
|
||||
bfHelper(nodes2, fn, data);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data) {
|
||||
std::vector<SP<CWLSurfaceResource>> surfs;
|
||||
surfs.push_back(self.lock());
|
||||
bfHelper(surfs, fn, data);
|
||||
}
|
||||
|
||||
std::pair<SP<CWLSurfaceResource>, Vector2D> CWLSurfaceResource::at(const Vector2D& localCoords, bool allowsInput) {
|
||||
std::vector<std::pair<SP<CWLSurfaceResource>, Vector2D>> surfs;
|
||||
breadthfirst([](SP<CWLSurfaceResource> surf, const Vector2D& offset,
|
||||
void* data) { ((std::vector<std::pair<SP<CWLSurfaceResource>, Vector2D>>*)data)->emplace_back(std::make_pair<>(surf, offset)); },
|
||||
&surfs);
|
||||
|
||||
for (auto& [surf, pos] : surfs | std::views::reverse) {
|
||||
if (!allowsInput) {
|
||||
const auto BOX = CBox{pos, surf->current.size};
|
||||
if (BOX.containsPoint(localCoords))
|
||||
return {surf, localCoords - pos};
|
||||
} else {
|
||||
const auto REGION = surf->current.input.copy().intersect(CBox{{}, surf->current.size}).translate(pos);
|
||||
if (REGION.containsPoint(localCoords))
|
||||
return {surf, localCoords - pos};
|
||||
}
|
||||
}
|
||||
|
||||
return {nullptr, {}};
|
||||
}
|
||||
|
||||
uint32_t CWLSurfaceResource::id() {
|
||||
return wl_resource_get_id(resource->resource());
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::map() {
|
||||
if (mapped)
|
||||
return;
|
||||
|
||||
mapped = true;
|
||||
|
||||
events.map.emit();
|
||||
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
frame(&now);
|
||||
|
||||
current.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
|
||||
pending.bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}};
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::unmap() {
|
||||
if (!mapped)
|
||||
return;
|
||||
|
||||
mapped = false;
|
||||
|
||||
events.unmap.emit();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::error(int code, const std::string& str) {
|
||||
resource->error(code, str);
|
||||
}
|
||||
|
||||
SP<CWlSurface> CWLSurfaceResource::getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
CBox CWLSurfaceResource::extends() {
|
||||
CRegion full = CBox{{}, current.size};
|
||||
breadthfirst(
|
||||
[](SP<CWLSurfaceResource> surf, const Vector2D& offset, void* d) {
|
||||
if (surf->role->role() != SURFACE_ROLE_SUBSURFACE)
|
||||
return;
|
||||
|
||||
((CRegion*)d)->add(CBox{offset, surf->current.size});
|
||||
},
|
||||
&full);
|
||||
return full.getExtents();
|
||||
}
|
||||
|
||||
Vector2D CWLSurfaceResource::sourceSize() {
|
||||
if (!current.buffer)
|
||||
return {};
|
||||
|
||||
if (current.viewport.hasSource)
|
||||
return current.viewport.source.size();
|
||||
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
|
||||
return trc / current.scale;
|
||||
}
|
||||
|
||||
CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
|
||||
if (!current.buffer)
|
||||
return {};
|
||||
|
||||
CRegion surfaceDamage = current.damage;
|
||||
if (current.viewport.hasDestination) {
|
||||
Vector2D scale = sourceSize() / current.viewport.destination;
|
||||
surfaceDamage.scale(scale);
|
||||
}
|
||||
|
||||
if (current.viewport.hasSource)
|
||||
surfaceDamage.translate(current.viewport.source.pos());
|
||||
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
|
||||
|
||||
return surfaceDamage.scale(current.scale).transform(wlr_output_transform_invert(current.transform), trc.x, trc.y).add(current.bufferDamage);
|
||||
}
|
||||
|
||||
CWLCompositorResource::CWLCompositorResource(SP<CWlCompositor> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWlCompositor* r) { PROTO::compositor->destroyResource(this); });
|
||||
|
||||
resource->setCreateSurface([](CWlCompositor* r, uint32_t id) {
|
||||
const auto RESOURCE = PROTO::compositor->m_vSurfaces.emplace_back(makeShared<CWLSurfaceResource>(makeShared<CWlSurface>(r->client(), r->version(), id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::compositor->m_vSurfaces.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
|
||||
LOGM(LOG, "New wl_surface with id {} at {:x}", id, (uintptr_t)RESOURCE.get());
|
||||
|
||||
PROTO::compositor->events.newSurface.emit(RESOURCE);
|
||||
});
|
||||
|
||||
resource->setCreateRegion([](CWlCompositor* r, uint32_t id) {
|
||||
const auto RESOURCE = PROTO::compositor->m_vRegions.emplace_back(makeShared<CWLRegionResource>(makeShared<CWlRegion>(r->client(), r->version(), id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::compositor->m_vRegions.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
|
||||
LOGM(LOG, "New wl_region with id {} at {:x}", id, (uintptr_t)RESOURCE.get());
|
||||
});
|
||||
}
|
||||
|
||||
bool CWLCompositorResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CWLCompositorProtocol::CWLCompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CWLCompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLCompositorResource>(makeShared<CWlCompositor>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CWLCompositorProtocol::destroyResource(CWLCompositorResource* resource) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CWLCompositorProtocol::destroyResource(CWLSurfaceResource* resource) {
|
||||
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CWLCompositorProtocol::destroyResource(CWLRegionResource* resource) {
|
||||
std::erase_if(m_vRegions, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
175
src/protocols/core/Compositor.hpp
Normal file
175
src/protocols/core/Compositor.hpp
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
Implementations for:
|
||||
- wl_compositor
|
||||
- wl_surface
|
||||
- wl_region
|
||||
- wl_callback
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "../WaylandProtocol.hpp"
|
||||
#include "wayland.hpp"
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
#include "../../helpers/Region.hpp"
|
||||
#include "../types/Buffer.hpp"
|
||||
#include "../types/SurfaceRole.hpp"
|
||||
|
||||
class CWLOutputResource;
|
||||
class CMonitor;
|
||||
class CWLSurface;
|
||||
class CWLSurfaceResource;
|
||||
class CWLSubsurfaceResource;
|
||||
class CViewportResource;
|
||||
|
||||
class CWLCallbackResource {
|
||||
public:
|
||||
CWLCallbackResource(SP<CWlCallback> resource_);
|
||||
|
||||
bool good();
|
||||
void send(timespec* now);
|
||||
|
||||
private:
|
||||
SP<CWlCallback> resource;
|
||||
};
|
||||
|
||||
class CWLRegionResource {
|
||||
public:
|
||||
CWLRegionResource(SP<CWlRegion> resource_);
|
||||
static SP<CWLRegionResource> fromResource(wl_resource* res);
|
||||
|
||||
bool good();
|
||||
|
||||
CRegion region;
|
||||
WP<CWLRegionResource> self;
|
||||
|
||||
private:
|
||||
SP<CWlRegion> resource;
|
||||
};
|
||||
|
||||
class CWLSurfaceResource {
|
||||
public:
|
||||
CWLSurfaceResource(SP<CWlSurface> resource_);
|
||||
~CWLSurfaceResource();
|
||||
|
||||
static SP<CWLSurfaceResource> fromResource(wl_resource* res);
|
||||
|
||||
bool good();
|
||||
wl_client* client();
|
||||
void enter(SP<CMonitor> monitor);
|
||||
void leave(SP<CMonitor> monitor);
|
||||
void sendPreferredTransform(wl_output_transform t);
|
||||
void sendPreferredScale(int32_t scale);
|
||||
void frame(timespec* now);
|
||||
uint32_t id();
|
||||
void map();
|
||||
void unmap();
|
||||
void error(int code, const std::string& str);
|
||||
SP<CWlSurface> getResource();
|
||||
CBox extends();
|
||||
void resetRole();
|
||||
Vector2D sourceSize();
|
||||
|
||||
struct {
|
||||
CSignal commit;
|
||||
CSignal map;
|
||||
CSignal unmap;
|
||||
CSignal newSubsurface;
|
||||
CSignal destroy;
|
||||
} events;
|
||||
|
||||
struct {
|
||||
CRegion opaque, input = CBox{{}, {INT32_MAX, INT32_MAX}}, damage, bufferDamage = CBox{{}, {INT32_MAX, INT32_MAX}} /* initial damage */;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
int scale = 1;
|
||||
SP<IWLBuffer> buffer;
|
||||
SP<CTexture> texture;
|
||||
Vector2D offset;
|
||||
Vector2D size;
|
||||
struct {
|
||||
bool hasDestination = false;
|
||||
bool hasSource = false;
|
||||
Vector2D destination;
|
||||
CBox source;
|
||||
} viewport;
|
||||
|
||||
//
|
||||
void reset() {
|
||||
damage.clear();
|
||||
bufferDamage.clear();
|
||||
transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
scale = 1;
|
||||
offset = {};
|
||||
size = {};
|
||||
}
|
||||
} current, pending;
|
||||
|
||||
std::vector<SP<CWLCallbackResource>> callbacks;
|
||||
WP<CWLSurfaceResource> self;
|
||||
WP<CWLSurface> hlSurface;
|
||||
std::vector<WP<CMonitor>> enteredOutputs;
|
||||
bool mapped = false;
|
||||
std::vector<WP<CWLSubsurfaceResource>> subsurfaces;
|
||||
WP<ISurfaceRole> role;
|
||||
WP<CViewportResource> viewportResource;
|
||||
|
||||
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
CRegion accumulateCurrentBufferDamage();
|
||||
|
||||
// returns a pair: found surface (null if not found) and surface local coords.
|
||||
// localCoords param is relative to 0,0 of this surface
|
||||
std::pair<SP<CWLSurfaceResource>, Vector2D> at(const Vector2D& localCoords, bool allowsInput = false);
|
||||
|
||||
private:
|
||||
SP<CWlSurface> resource;
|
||||
wl_client* pClient = nullptr;
|
||||
|
||||
// tracks whether we should release the buffer
|
||||
bool bufferReleased = false;
|
||||
|
||||
void destroy();
|
||||
void bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
};
|
||||
|
||||
class CWLCompositorResource {
|
||||
public:
|
||||
CWLCompositorResource(SP<CWlCompositor> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CWlCompositor> resource;
|
||||
};
|
||||
|
||||
class CWLCompositorProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CWLCompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
struct {
|
||||
CSignal newSurface; // SP<CWLSurfaceResource>
|
||||
} events;
|
||||
|
||||
private:
|
||||
void destroyResource(CWLCompositorResource* resource);
|
||||
void destroyResource(CWLSurfaceResource* resource);
|
||||
void destroyResource(CWLRegionResource* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CWLCompositorResource>> m_vManagers;
|
||||
std::vector<SP<CWLSurfaceResource>> m_vSurfaces;
|
||||
std::vector<SP<CWLRegionResource>> m_vRegions;
|
||||
|
||||
friend class CWLSurfaceResource;
|
||||
friend class CWLCompositorResource;
|
||||
friend class CWLRegionResource;
|
||||
friend class CWLCallbackResource;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CWLCompositorProtocol> compositor;
|
||||
};
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../../managers/PointerManager.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "Seat.hpp"
|
||||
#include "Compositor.hpp"
|
||||
|
||||
#define LOGM PROTO::data->protoLog
|
||||
|
||||
|
|
@ -233,7 +234,7 @@ CWLDataDeviceResource::CWLDataDeviceResource(SP<CWlDataDevice> resource_) : reso
|
|||
|
||||
source->dnd = true;
|
||||
|
||||
PROTO::data->initiateDrag(source, icon ? wlr_surface_from_resource(icon) : nullptr, wlr_surface_from_resource(origin));
|
||||
PROTO::data->initiateDrag(source, icon ? CWLSurfaceResource::fromResource(icon) : nullptr, CWLSurfaceResource::fromResource(origin));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -252,8 +253,8 @@ void CWLDataDeviceResource::sendDataOffer(SP<CWLDataOfferResource> offer) {
|
|||
resource->sendDataOfferRaw(nullptr);
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP<CWLDataOfferResource> offer) {
|
||||
resource->sendEnterRaw(serial, surf->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource());
|
||||
void CWLDataDeviceResource::sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<CWLDataOfferResource> offer) {
|
||||
resource->sendEnterRaw(serial, surf->getResource()->resource(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y), offer->resource->resource());
|
||||
}
|
||||
|
||||
void CWLDataDeviceResource::sendLeave() {
|
||||
|
|
@ -454,7 +455,7 @@ void CWLDataDeviceProtocol::onKeyboardFocus() {
|
|||
updateDrag();
|
||||
}
|
||||
|
||||
void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource, wlr_surface* dragSurface, wlr_surface* origin) {
|
||||
void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource, SP<CWLSurfaceResource> dragSurface, SP<CWLSurfaceResource> origin) {
|
||||
|
||||
if (dnd.currentSource) {
|
||||
LOGM(WARN, "New drag started while old drag still active??");
|
||||
|
|
@ -472,22 +473,18 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
|
|||
dnd.originSurface = origin;
|
||||
dnd.dndSurface = dragSurface;
|
||||
if (dragSurface) {
|
||||
dnd.hyprListener_dndSurfaceDestroy.initCallback(
|
||||
&dragSurface->events.destroy, [this](void* owner, void* data) { abortDrag(); }, nullptr, "CWLDataDeviceProtocol::drag");
|
||||
dnd.hyprListener_dndSurfaceCommit.initCallback(
|
||||
&dragSurface->events.commit,
|
||||
[this](void* owner, void* data) {
|
||||
if (dnd.dndSurface->pending.buffer_width > 0 && dnd.dndSurface->pending.buffer_height > 0 && !dnd.dndSurface->mapped) {
|
||||
wlr_surface_map(dnd.dndSurface);
|
||||
return;
|
||||
}
|
||||
dnd.dndSurfaceDestroy = dragSurface->events.destroy.registerListener([this](std::any d) { abortDrag(); });
|
||||
dnd.dndSurfaceCommit = dragSurface->events.commit.registerListener([this](std::any d) {
|
||||
if (dnd.dndSurface->current.buffer && !dnd.dndSurface->mapped) {
|
||||
dnd.dndSurface->map();
|
||||
return;
|
||||
}
|
||||
|
||||
if (dnd.dndSurface->pending.buffer_width <= 0 && dnd.dndSurface->pending.buffer_height <= 0 && dnd.dndSurface->mapped) {
|
||||
wlr_surface_unmap(dnd.dndSurface);
|
||||
return;
|
||||
}
|
||||
},
|
||||
nullptr, "CWLDataDeviceProtocol::drag");
|
||||
if (dnd.dndSurface->current.buffer <= 0 && dnd.dndSurface->mapped) {
|
||||
dnd.dndSurface->unmap();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
dnd.mouseButton = g_pHookSystem->hookDynamic("mouseButton", [this](void* self, SCallbackInfo& info, std::any e) {
|
||||
|
|
@ -506,7 +503,7 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
|
|||
dnd.mouseMove = g_pHookSystem->hookDynamic("mouseMove", [this](void* self, SCallbackInfo& info, std::any e) {
|
||||
auto V = std::any_cast<const Vector2D>(e);
|
||||
if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) {
|
||||
auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus);
|
||||
auto surf = CWLSurface::fromResource(g_pSeatManager->state.keyboardFocus.lock());
|
||||
|
||||
if (!surf)
|
||||
return;
|
||||
|
|
@ -524,7 +521,7 @@ void CWLDataDeviceProtocol::initiateDrag(WP<CWLDataSourceResource> currentSource
|
|||
dnd.touchMove = g_pHookSystem->hookDynamic("touchMove", [this](void* self, SCallbackInfo& info, std::any e) {
|
||||
auto E = std::any_cast<ITouch::SMotionEvent>(e);
|
||||
if (dnd.focusedDevice && g_pSeatManager->state.keyboardFocus) {
|
||||
auto surf = CWLSurface::surfaceFromWlr(g_pSeatManager->state.keyboardFocus);
|
||||
auto surf = CWLSurface::fromResource(g_pSeatManager->state.keyboardFocus.lock());
|
||||
|
||||
if (!surf)
|
||||
return;
|
||||
|
|
@ -572,14 +569,14 @@ void CWLDataDeviceProtocol::updateDrag() {
|
|||
|
||||
dnd.focusedDevice->sendDataOffer(OFFER);
|
||||
OFFER->sendData();
|
||||
dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus,
|
||||
Vector2D{g_pSeatManager->state.keyboardFocus->current.width, g_pSeatManager->state.keyboardFocus->current.height} / 2.F, OFFER);
|
||||
dnd.focusedDevice->sendEnter(wl_display_next_serial(g_pCompositor->m_sWLDisplay), g_pSeatManager->state.keyboardFocus.lock(),
|
||||
g_pSeatManager->state.keyboardFocus->current.size / 2.F, OFFER);
|
||||
}
|
||||
|
||||
void CWLDataDeviceProtocol::resetDndState() {
|
||||
dnd.dndSurface = nullptr;
|
||||
dnd.hyprListener_dndSurfaceDestroy.removeCallback();
|
||||
dnd.hyprListener_dndSurfaceCommit.removeCallback();
|
||||
dnd.dndSurface.reset();
|
||||
dnd.dndSurfaceCommit.reset();
|
||||
dnd.dndSurfaceDestroy.reset();
|
||||
dnd.mouseButton.reset();
|
||||
dnd.mouseMove.reset();
|
||||
dnd.touchUp.reset();
|
||||
|
|
@ -638,20 +635,18 @@ void CWLDataDeviceProtocol::abortDrag() {
|
|||
}
|
||||
|
||||
void CWLDataDeviceProtocol::renderDND(CMonitor* pMonitor, timespec* when) {
|
||||
if (!dnd.dndSurface || !wlr_surface_get_texture(dnd.dndSurface))
|
||||
if (!dnd.dndSurface || !dnd.dndSurface->current.buffer || !dnd.dndSurface->current.buffer->texture)
|
||||
return;
|
||||
|
||||
const auto POS = g_pInputManager->getMouseCoordsInternal();
|
||||
|
||||
CBox box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}
|
||||
.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F)
|
||||
.scale(pMonitor->scale);
|
||||
g_pHyprOpenGL->renderTexture(wlr_surface_get_texture(dnd.dndSurface), &box, 1.F);
|
||||
CBox box = CBox{POS, dnd.dndSurface->current.size}.translate(-pMonitor->vecPosition + g_pPointerManager->cursorSizeLogical() / 2.F).scale(pMonitor->scale);
|
||||
g_pHyprOpenGL->renderTexture(dnd.dndSurface->current.buffer->texture, &box, 1.F);
|
||||
|
||||
box = CBox{POS, {dnd.dndSurface->current.width, dnd.dndSurface->current.height}}.translate(g_pPointerManager->cursorSizeLogical() / 2.F);
|
||||
box = CBox{POS, dnd.dndSurface->current.size}.translate(g_pPointerManager->cursorSizeLogical() / 2.F);
|
||||
g_pHyprRenderer->damageBox(&box);
|
||||
|
||||
wlr_surface_send_frame_done(dnd.dndSurface, when);
|
||||
dnd.dndSurface->frame(when);
|
||||
}
|
||||
|
||||
bool CWLDataDeviceProtocol::dndActive() {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ class CWLDataDeviceManagerResource;
|
|||
class CWLDataSourceResource;
|
||||
class CWLDataOfferResource;
|
||||
|
||||
class CWLSurfaceResource;
|
||||
class CMonitor;
|
||||
|
||||
class CWLDataOfferResource {
|
||||
|
|
@ -92,7 +93,7 @@ class CWLDataDeviceResource {
|
|||
wl_client* client();
|
||||
|
||||
void sendDataOffer(SP<CWLDataOfferResource> offer);
|
||||
void sendEnter(uint32_t serial, wlr_surface* surf, const Vector2D& local, SP<CWLDataOfferResource> offer);
|
||||
void sendEnter(uint32_t serial, SP<CWLSurfaceResource> surf, const Vector2D& local, SP<CWLDataOfferResource> offer);
|
||||
void sendLeave();
|
||||
void sendMotion(uint32_t timeMs, const Vector2D& local);
|
||||
void sendDrop();
|
||||
|
|
@ -155,11 +156,11 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
|
|||
struct {
|
||||
WP<CWLDataDeviceResource> focusedDevice;
|
||||
WP<CWLDataSourceResource> currentSource;
|
||||
wlr_surface* dndSurface = nullptr;
|
||||
wlr_surface* originSurface = nullptr; // READ-ONLY
|
||||
WP<CWLSurfaceResource> dndSurface;
|
||||
WP<CWLSurfaceResource> originSurface;
|
||||
bool overriddenCursor = false;
|
||||
DYNLISTENER(dndSurfaceDestroy);
|
||||
DYNLISTENER(dndSurfaceCommit);
|
||||
CHyprSignalListener dndSurfaceDestroy;
|
||||
CHyprSignalListener dndSurfaceCommit;
|
||||
|
||||
// for ending a dnd
|
||||
SP<HOOK_CALLBACK_FN> mouseMove;
|
||||
|
|
@ -169,7 +170,7 @@ class CWLDataDeviceProtocol : public IWaylandProtocol {
|
|||
} dnd;
|
||||
|
||||
void abortDrag();
|
||||
void initiateDrag(WP<CWLDataSourceResource> currentSource, wlr_surface* dragSurface, wlr_surface* origin);
|
||||
void initiateDrag(WP<CWLDataSourceResource> currentSource, SP<CWLSurfaceResource> dragSurface, SP<CWLSurfaceResource> origin);
|
||||
void updateDrag();
|
||||
void dropDrag();
|
||||
void completeDrag();
|
||||
|
|
|
|||
107
src/protocols/core/Output.cpp
Normal file
107
src/protocols/core/Output.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#include "Output.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
|
||||
CWLOutputResource::CWLOutputResource(SP<CWlOutput> resource_, SP<CMonitor> pMonitor) : monitor(pMonitor), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
pClient = resource->client();
|
||||
|
||||
resource->setOnDestroy([this](CWlOutput* r) {
|
||||
if (monitor && PROTO::outputs.contains(monitor->szName))
|
||||
PROTO::outputs.at(monitor->szName)->destroyResource(this);
|
||||
});
|
||||
resource->setRelease([this](CWlOutput* r) {
|
||||
if (monitor && PROTO::outputs.contains(monitor->szName))
|
||||
PROTO::outputs.at(monitor->szName)->destroyResource(this);
|
||||
});
|
||||
|
||||
resource->sendGeometry(0, 0, monitor->output->phys_width, monitor->output->phys_height, monitor->output->subpixel, monitor->output->make ? monitor->output->make : "null",
|
||||
monitor->output->model ? monitor->output->model : "null", monitor->transform);
|
||||
if (resource->version() >= 4) {
|
||||
resource->sendName(monitor->szName.c_str());
|
||||
resource->sendDescription(monitor->szDescription.c_str());
|
||||
}
|
||||
|
||||
updateState();
|
||||
}
|
||||
|
||||
SP<CWLOutputResource> CWLOutputResource::fromResource(wl_resource* res) {
|
||||
auto data = (CWLOutputResource*)(((CWlOutput*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
}
|
||||
|
||||
bool CWLOutputResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
wl_client* CWLOutputResource::client() {
|
||||
return pClient;
|
||||
}
|
||||
|
||||
SP<CWlOutput> CWLOutputResource::getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
void CWLOutputResource::updateState() {
|
||||
if (!monitor)
|
||||
return;
|
||||
|
||||
if (resource->version() >= 2)
|
||||
resource->sendScale(std::ceil(monitor->scale));
|
||||
|
||||
resource->sendMode((wl_output_mode)(WL_OUTPUT_MODE_CURRENT | WL_OUTPUT_MODE_PREFERRED), monitor->vecSize.x, monitor->vecSize.y, monitor->refreshRate * 1000.0);
|
||||
|
||||
if (resource->version() >= 2)
|
||||
resource->sendDone();
|
||||
}
|
||||
|
||||
CWLOutputProtocol::CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP<CMonitor> pMonitor) :
|
||||
IWaylandProtocol(iface, ver, name), monitor(pMonitor), szName(pMonitor->szName) {
|
||||
|
||||
listeners.modeChanged = monitor->events.modeChanged.registerListener([this](std::any d) {
|
||||
for (auto& o : m_vOutputs) {
|
||||
o->updateState();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CWLOutputProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vOutputs.emplace_back(makeShared<CWLOutputResource>(makeShared<CWlOutput>(client, ver, id), monitor.lock()));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vOutputs.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
}
|
||||
|
||||
void CWLOutputProtocol::destroyResource(CWLOutputResource* resource) {
|
||||
std::erase_if(m_vOutputs, [&](const auto& other) { return other.get() == resource; });
|
||||
|
||||
if (m_vOutputs.empty() && defunct)
|
||||
PROTO::outputs.erase(szName);
|
||||
}
|
||||
|
||||
SP<CWLOutputResource> CWLOutputProtocol::outputResourceFrom(wl_client* client) {
|
||||
for (auto& r : m_vOutputs) {
|
||||
if (r->client() != client)
|
||||
continue;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CWLOutputProtocol::remove() {
|
||||
if (defunct)
|
||||
return;
|
||||
|
||||
defunct = true;
|
||||
removeGlobal();
|
||||
}
|
||||
61
src/protocols/core/Output.hpp
Normal file
61
src/protocols/core/Output.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "../WaylandProtocol.hpp"
|
||||
#include "wayland.hpp"
|
||||
#include "../../helpers/signal/Listener.hpp"
|
||||
|
||||
class CMonitor;
|
||||
|
||||
class CWLOutputResource {
|
||||
public:
|
||||
CWLOutputResource(SP<CWlOutput> resource_, SP<CMonitor> pMonitor);
|
||||
static SP<CWLOutputResource> fromResource(wl_resource*);
|
||||
|
||||
bool good();
|
||||
wl_client* client();
|
||||
SP<CWlOutput> getResource();
|
||||
void updateState();
|
||||
|
||||
WP<CMonitor> monitor;
|
||||
|
||||
WP<CWLOutputResource> self;
|
||||
|
||||
private:
|
||||
SP<CWlOutput> resource;
|
||||
wl_client* pClient = nullptr;
|
||||
};
|
||||
|
||||
class CWLOutputProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CWLOutputProtocol(const wl_interface* iface, const int& ver, const std::string& name, SP<CMonitor> pMonitor);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
SP<CWLOutputResource> outputResourceFrom(wl_client* client);
|
||||
|
||||
WP<CMonitor> monitor;
|
||||
|
||||
// will mark the protocol for removal, will be removed when no. of bound outputs is 0 (or when overwritten by a new global)
|
||||
void remove();
|
||||
|
||||
private:
|
||||
void destroyResource(CWLOutputResource* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CWLOutputResource>> m_vOutputs;
|
||||
bool defunct = false;
|
||||
std::string szName = "";
|
||||
|
||||
struct {
|
||||
CHyprSignalListener modeChanged;
|
||||
} listeners;
|
||||
|
||||
friend class CWLOutputResource;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline std::unordered_map<std::string, UP<CWLOutputProtocol>> outputs;
|
||||
};
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
#include "Seat.hpp"
|
||||
#include "Compositor.hpp"
|
||||
#include "../../devices/IKeyboard.hpp"
|
||||
#include "../../managers/SeatManager.hpp"
|
||||
#include "../../config/ConfigValue.hpp"
|
||||
|
|
@ -20,7 +21,7 @@ bool CWLTouchResource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
|
||||
void CWLTouchResource::sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local) {
|
||||
if (!owner)
|
||||
return;
|
||||
|
||||
|
|
@ -29,15 +30,12 @@ void CWLTouchResource::sendDown(wlr_surface* surface, uint32_t timeMs, int32_t i
|
|||
sendUp(timeMs, id);
|
||||
}
|
||||
|
||||
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
|
||||
ASSERT(surface->client() == owner->client());
|
||||
|
||||
currentSurface = surface;
|
||||
hyprListener_surfaceDestroy.initCallback(
|
||||
&surface->events.destroy, [this, id, timeMs](void* owner, void* data) { sendUp(timeMs + 10 /* hack */, id); }, this, "CWLTouchResource");
|
||||
currentSurface = surface;
|
||||
listeners.destroySurface = surface->events.destroy.registerListener([this, timeMs, id](std::any d) { sendUp(timeMs + 10 /* hack */, id); });
|
||||
|
||||
// FIXME:
|
||||
// fix this once we get our own wlr_surface, this is horrible
|
||||
resource->sendDownRaw(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->resource, id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
|
||||
resource->sendDown(g_pSeatManager->nextSerial(owner.lock()), timeMs, surface->getResource().get(), id, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
|
||||
}
|
||||
|
||||
void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
|
||||
|
|
@ -45,8 +43,8 @@ void CWLTouchResource::sendUp(uint32_t timeMs, int32_t id) {
|
|||
return;
|
||||
|
||||
resource->sendUp(g_pSeatManager->nextSerial(owner.lock()), timeMs, id);
|
||||
currentSurface = nullptr;
|
||||
hyprListener_surfaceDestroy.removeCallback();
|
||||
currentSurface.reset();
|
||||
listeners.destroySurface.reset();
|
||||
}
|
||||
|
||||
void CWLTouchResource::sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local) {
|
||||
|
|
@ -97,7 +95,7 @@ CWLPointerResource::CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResou
|
|||
return;
|
||||
}
|
||||
|
||||
g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? wlr_surface_from_resource(surf) : nullptr, {hotX, hotY});
|
||||
g_pSeatManager->onSetCursor(owner.lock(), serial, surf ? CWLSurfaceResource::fromResource(surf) : nullptr, {hotX, hotY});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +103,7 @@ bool CWLPointerResource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local) {
|
||||
void CWLPointerResource::sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local) {
|
||||
if (!owner || currentSurface == surface)
|
||||
return;
|
||||
|
||||
|
|
@ -114,22 +112,21 @@ void CWLPointerResource::sendEnter(wlr_surface* surface, const Vector2D& local)
|
|||
sendLeave();
|
||||
}
|
||||
|
||||
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
|
||||
ASSERT(surface->client() == owner->client());
|
||||
|
||||
currentSurface = surface;
|
||||
hyprListener_surfaceDestroy.initCallback(
|
||||
&surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLPointerResource");
|
||||
currentSurface = surface;
|
||||
listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); });
|
||||
|
||||
resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
|
||||
resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), wl_fixed_from_double(local.x), wl_fixed_from_double(local.y));
|
||||
}
|
||||
|
||||
void CWLPointerResource::sendLeave() {
|
||||
if (!owner || !currentSurface)
|
||||
return;
|
||||
|
||||
resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource);
|
||||
currentSurface = nullptr;
|
||||
hyprListener_surfaceDestroy.removeCallback();
|
||||
resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get());
|
||||
currentSurface.reset();
|
||||
listeners.destroySurface.reset();
|
||||
}
|
||||
|
||||
void CWLPointerResource::sendMotion(uint32_t timeMs, const Vector2D& local) {
|
||||
|
|
@ -237,7 +234,7 @@ void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
|
|||
close(fd);
|
||||
}
|
||||
|
||||
void CWLKeyboardResource::sendEnter(wlr_surface* surface) {
|
||||
void CWLKeyboardResource::sendEnter(SP<CWLSurfaceResource> surface) {
|
||||
if (!owner || currentSurface == surface)
|
||||
return;
|
||||
|
||||
|
|
@ -246,16 +243,15 @@ void CWLKeyboardResource::sendEnter(wlr_surface* surface) {
|
|||
sendLeave();
|
||||
}
|
||||
|
||||
ASSERT(wl_resource_get_client(surface->resource) == owner->client());
|
||||
ASSERT(surface->client() == owner->client());
|
||||
|
||||
currentSurface = surface;
|
||||
hyprListener_surfaceDestroy.initCallback(
|
||||
&surface->events.destroy, [this](void* owner, void* data) { sendLeave(); }, this, "CWLKeyboardResource");
|
||||
currentSurface = surface;
|
||||
listeners.destroySurface = surface->events.destroy.registerListener([this](std::any d) { sendLeave(); });
|
||||
|
||||
wl_array arr;
|
||||
wl_array_init(&arr);
|
||||
|
||||
resource->sendEnterRaw(g_pSeatManager->nextSerial(owner.lock()), surface->resource, &arr);
|
||||
resource->sendEnter(g_pSeatManager->nextSerial(owner.lock()), surface->getResource().get(), &arr);
|
||||
|
||||
wl_array_release(&arr);
|
||||
}
|
||||
|
|
@ -264,9 +260,9 @@ void CWLKeyboardResource::sendLeave() {
|
|||
if (!owner || !currentSurface)
|
||||
return;
|
||||
|
||||
resource->sendLeaveRaw(g_pSeatManager->nextSerial(owner.lock()), currentSurface->resource);
|
||||
currentSurface = nullptr;
|
||||
hyprListener_surfaceDestroy.removeCallback();
|
||||
resource->sendLeave(g_pSeatManager->nextSerial(owner.lock()), currentSurface->getResource().get());
|
||||
currentSurface.reset();
|
||||
listeners.destroySurface.reset();
|
||||
}
|
||||
|
||||
void CWLKeyboardResource::sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
constexpr const char* HL_SEAT_NAME = "Hyprland";
|
||||
|
||||
class IKeyboard;
|
||||
class CWLSurfaceResource;
|
||||
|
||||
class CWLPointerResource;
|
||||
class CWLKeyboardResource;
|
||||
|
|
@ -31,7 +32,7 @@ class CWLTouchResource {
|
|||
CWLTouchResource(SP<CWlTouch> resource_, SP<CWLSeatResource> owner_);
|
||||
|
||||
bool good();
|
||||
void sendDown(wlr_surface* surface, uint32_t timeMs, int32_t id, const Vector2D& local);
|
||||
void sendDown(SP<CWLSurfaceResource> surface, uint32_t timeMs, int32_t id, const Vector2D& local);
|
||||
void sendUp(uint32_t timeMs, int32_t id);
|
||||
void sendMotion(uint32_t timeMs, int32_t id, const Vector2D& local);
|
||||
void sendFrame();
|
||||
|
|
@ -42,10 +43,12 @@ class CWLTouchResource {
|
|||
WP<CWLSeatResource> owner;
|
||||
|
||||
private:
|
||||
SP<CWlTouch> resource;
|
||||
wlr_surface* currentSurface = nullptr;
|
||||
SP<CWlTouch> resource;
|
||||
WP<CWLSurfaceResource> currentSurface;
|
||||
|
||||
DYNLISTENER(surfaceDestroy);
|
||||
struct {
|
||||
CHyprSignalListener destroySurface;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CWLPointerResource {
|
||||
|
|
@ -53,7 +56,7 @@ class CWLPointerResource {
|
|||
CWLPointerResource(SP<CWlPointer> resource_, SP<CWLSeatResource> owner_);
|
||||
|
||||
bool good();
|
||||
void sendEnter(wlr_surface* surface, const Vector2D& local);
|
||||
void sendEnter(SP<CWLSurfaceResource> surface, const Vector2D& local);
|
||||
void sendLeave();
|
||||
void sendMotion(uint32_t timeMs, const Vector2D& local);
|
||||
void sendButton(uint32_t timeMs, uint32_t button, wl_pointer_button_state state);
|
||||
|
|
@ -68,10 +71,12 @@ class CWLPointerResource {
|
|||
WP<CWLSeatResource> owner;
|
||||
|
||||
private:
|
||||
SP<CWlPointer> resource;
|
||||
wlr_surface* currentSurface = nullptr;
|
||||
SP<CWlPointer> resource;
|
||||
WP<CWLSurfaceResource> currentSurface;
|
||||
|
||||
DYNLISTENER(surfaceDestroy);
|
||||
struct {
|
||||
CHyprSignalListener destroySurface;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CWLKeyboardResource {
|
||||
|
|
@ -80,7 +85,7 @@ class CWLKeyboardResource {
|
|||
|
||||
bool good();
|
||||
void sendKeymap(SP<IKeyboard> keeb);
|
||||
void sendEnter(wlr_surface* surface);
|
||||
void sendEnter(SP<CWLSurfaceResource> surface);
|
||||
void sendLeave();
|
||||
void sendKey(uint32_t timeMs, uint32_t key, wl_keyboard_key_state state);
|
||||
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
|
||||
|
|
@ -89,10 +94,12 @@ class CWLKeyboardResource {
|
|||
WP<CWLSeatResource> owner;
|
||||
|
||||
private:
|
||||
SP<CWlKeyboard> resource;
|
||||
wlr_surface* currentSurface = nullptr;
|
||||
SP<CWlKeyboard> resource;
|
||||
WP<CWLSurfaceResource> currentSurface;
|
||||
|
||||
DYNLISTENER(surfaceDestroy);
|
||||
struct {
|
||||
CHyprSignalListener destroySurface;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CWLSeatResource {
|
||||
|
|
|
|||
214
src/protocols/core/Shm.cpp
Normal file
214
src/protocols/core/Shm.cpp
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
#include "Shm.hpp"
|
||||
#include <algorithm>
|
||||
#include <sys/mman.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include "../../render/Texture.hpp"
|
||||
#include "../types/WLBuffer.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include "../../helpers/Format.hpp"
|
||||
|
||||
#define LOGM PROTO::shm->protoLog
|
||||
|
||||
CWLSHMBuffer::CWLSHMBuffer(SP<CWLSHMPoolResource> pool_, uint32_t id, int32_t offset_, const Vector2D& size_, int32_t stride_, uint32_t fmt_) {
|
||||
if (!pool_->pool->data)
|
||||
return;
|
||||
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
size = size_;
|
||||
pool = pool_->pool;
|
||||
stride = stride_;
|
||||
fmt = fmt_;
|
||||
offset = offset_;
|
||||
opaque = FormatUtils::isFormatOpaque(FormatUtils::shmToDRM(fmt_));
|
||||
|
||||
texture = makeShared<CTexture>(FormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, size_);
|
||||
|
||||
resource = CWLBufferResource::create(makeShared<CWlBuffer>(pool_->resource->client(), 1, id));
|
||||
|
||||
listeners.bufferResourceDestroy = events.destroy.registerListener([this](std::any d) {
|
||||
listeners.bufferResourceDestroy.reset();
|
||||
PROTO::shm->destroyResource(this);
|
||||
});
|
||||
|
||||
success = texture->m_iTexID;
|
||||
|
||||
if (!success)
|
||||
Debug::log(ERR, "Failed creating a shm texture: null texture id");
|
||||
}
|
||||
|
||||
CWLSHMBuffer::~CWLSHMBuffer() {
|
||||
;
|
||||
}
|
||||
|
||||
eBufferCapability CWLSHMBuffer::caps() {
|
||||
return BUFFER_CAPABILITY_DATAPTR;
|
||||
}
|
||||
|
||||
eBufferType CWLSHMBuffer::type() {
|
||||
return BUFFER_TYPE_SHM;
|
||||
}
|
||||
|
||||
SSHMAttrs CWLSHMBuffer::shm() {
|
||||
SSHMAttrs attrs;
|
||||
attrs.success = true;
|
||||
attrs.fd = pool->fd;
|
||||
attrs.format = FormatUtils::shmToDRM(fmt);
|
||||
attrs.size = size;
|
||||
attrs.stride = stride;
|
||||
attrs.offset = offset;
|
||||
return attrs;
|
||||
}
|
||||
|
||||
std::tuple<uint8_t*, uint32_t, size_t> CWLSHMBuffer::beginDataPtr(uint32_t flags) {
|
||||
return {(uint8_t*)pool->data + offset, fmt, size.x * size.y * 4};
|
||||
}
|
||||
|
||||
void CWLSHMBuffer::endDataPtr() {
|
||||
;
|
||||
}
|
||||
|
||||
bool CWLSHMBuffer::good() {
|
||||
return success;
|
||||
}
|
||||
|
||||
void CWLSHMBuffer::update(const CRegion& damage) {
|
||||
texture->update(FormatUtils::shmToDRM(fmt), (uint8_t*)pool->data + offset, stride, damage);
|
||||
}
|
||||
|
||||
CSHMPool::CSHMPool(int fd_, size_t size_) : fd(fd_), size(size_) {
|
||||
data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
}
|
||||
|
||||
CSHMPool::~CSHMPool() {
|
||||
munmap(data, size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
void CSHMPool::resize(size_t size_) {
|
||||
LOGM(LOG, "Resizing a SHM pool from {} to {}", size, size_);
|
||||
|
||||
if (data)
|
||||
munmap(data, size);
|
||||
size = size_;
|
||||
data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
if (!data)
|
||||
LOGM(ERR, "Couldn't mmap {} bytes from fd {} of shm client", size, fd);
|
||||
}
|
||||
|
||||
CWLSHMPoolResource::CWLSHMPoolResource(SP<CWlShmPool> resource_, int fd_, size_t size_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
pool = makeShared<CSHMPool>(fd_, size_);
|
||||
|
||||
resource->setDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); });
|
||||
resource->setOnDestroy([this](CWlShmPool* r) { PROTO::shm->destroyResource(this); });
|
||||
|
||||
resource->setResize([this](CWlShmPool* r, int32_t size_) {
|
||||
if (size_ < (int32_t)pool->size) {
|
||||
r->error(-1, "Shrinking a shm pool is illegal");
|
||||
return;
|
||||
}
|
||||
pool->resize(size_);
|
||||
});
|
||||
|
||||
resource->setCreateBuffer([this](CWlShmPool* r, uint32_t id, int32_t offset, int32_t w, int32_t h, int32_t stride, uint32_t fmt) {
|
||||
if (!pool || !pool->data) {
|
||||
r->error(-1, "The provided shm pool failed to allocate properly");
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::find(PROTO::shm->shmFormats.begin(), PROTO::shm->shmFormats.end(), fmt) == PROTO::shm->shmFormats.end()) {
|
||||
r->error(WL_SHM_ERROR_INVALID_FORMAT, "Format invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (offset < 0 || w <= 0 || h <= 0 || stride <= 0) {
|
||||
r->error(WL_SHM_ERROR_INVALID_STRIDE, "Invalid stride, w, h, or offset");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto RESOURCE = PROTO::shm->m_vBuffers.emplace_back(makeShared<CWLSHMBuffer>(self.lock(), id, offset, Vector2D{w, h}, stride, fmt));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::shm->m_vBuffers.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
// append instance so that buffer knows its owner
|
||||
RESOURCE->resource->buffer = RESOURCE;
|
||||
});
|
||||
|
||||
if (!pool->data)
|
||||
resource->error(WL_SHM_ERROR_INVALID_FD, "Couldn't mmap from fd");
|
||||
}
|
||||
|
||||
bool CWLSHMPoolResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CWLSHMResource::CWLSHMResource(SP<CWlShm> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWlShm* r) { PROTO::shm->destroyResource(this); });
|
||||
|
||||
resource->setCreatePool([](CWlShm* r, uint32_t id, int32_t fd, int32_t size) {
|
||||
const auto RESOURCE = PROTO::shm->m_vPools.emplace_back(makeShared<CWLSHMPoolResource>(makeShared<CWlShmPool>(r->client(), r->version(), id), fd, size));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::shm->m_vPools.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
});
|
||||
|
||||
// send a few supported formats. No need for any other I think?
|
||||
for (auto& s : PROTO::shm->shmFormats) {
|
||||
resource->sendFormat((wl_shm_format)s);
|
||||
}
|
||||
}
|
||||
|
||||
bool CWLSHMResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CWLSHMProtocol::CWLSHMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CWLSHMProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
if (shmFormats.empty()) {
|
||||
size_t len = 0;
|
||||
const uint32_t* formats = wlr_renderer_get_shm_texture_formats(g_pCompositor->m_sWLRRenderer, &len);
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
shmFormats.push_back(FormatUtils::drmToShm(formats[i]));
|
||||
}
|
||||
}
|
||||
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLSHMResource>(makeShared<CWlShm>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CWLSHMProtocol::destroyResource(CWLSHMResource* resource) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CWLSHMProtocol::destroyResource(CWLSHMPoolResource* resource) {
|
||||
std::erase_if(m_vPools, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CWLSHMProtocol::destroyResource(CWLSHMBuffer* resource) {
|
||||
std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
111
src/protocols/core/Shm.hpp
Normal file
111
src/protocols/core/Shm.hpp
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
#pragma once
|
||||
|
||||
/*
|
||||
Implementations for:
|
||||
- wl_shm
|
||||
- wl_shm_pool
|
||||
- wl_buffer with shm
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "../WaylandProtocol.hpp"
|
||||
#include "wayland.hpp"
|
||||
#include "../types/Buffer.hpp"
|
||||
#include "../../helpers/Vector2D.hpp"
|
||||
|
||||
class CWLSHMPoolResource;
|
||||
|
||||
class CSHMPool {
|
||||
public:
|
||||
CSHMPool(int fd, size_t size);
|
||||
~CSHMPool();
|
||||
|
||||
int fd = 0;
|
||||
size_t size = 0;
|
||||
void* data = nullptr;
|
||||
|
||||
void resize(size_t size);
|
||||
};
|
||||
|
||||
class CWLSHMBuffer : public IWLBuffer {
|
||||
public:
|
||||
CWLSHMBuffer(SP<CWLSHMPoolResource> pool, uint32_t id, int32_t offset, const Vector2D& size, int32_t stride, uint32_t fmt);
|
||||
virtual ~CWLSHMBuffer();
|
||||
|
||||
virtual eBufferCapability caps();
|
||||
virtual eBufferType type();
|
||||
virtual void update(const CRegion& damage);
|
||||
virtual SSHMAttrs shm();
|
||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||
virtual void endDataPtr();
|
||||
|
||||
bool good();
|
||||
void updateTexture();
|
||||
|
||||
int32_t offset = 0, stride = 0;
|
||||
uint32_t fmt = 0;
|
||||
SP<CSHMPool> pool;
|
||||
|
||||
private:
|
||||
bool success = false;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener bufferResourceDestroy;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CWLSHMPoolResource {
|
||||
public:
|
||||
CWLSHMPoolResource(SP<CWlShmPool> resource_, int fd, size_t size);
|
||||
|
||||
bool good();
|
||||
|
||||
SP<CSHMPool> pool;
|
||||
|
||||
WP<CWLSHMPoolResource> self;
|
||||
|
||||
private:
|
||||
SP<CWlShmPool> resource;
|
||||
|
||||
friend class CWLSHMBuffer;
|
||||
};
|
||||
|
||||
class CWLSHMResource {
|
||||
public:
|
||||
CWLSHMResource(SP<CWlShm> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CWlShm> resource;
|
||||
};
|
||||
|
||||
class CWLSHMProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CWLSHMProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
private:
|
||||
void destroyResource(CWLSHMResource* resource);
|
||||
void destroyResource(CWLSHMPoolResource* resource);
|
||||
void destroyResource(CWLSHMBuffer* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CWLSHMResource>> m_vManagers;
|
||||
std::vector<SP<CWLSHMPoolResource>> m_vPools;
|
||||
std::vector<SP<CWLSHMBuffer>> m_vBuffers;
|
||||
|
||||
//
|
||||
std::vector<uint32_t> shmFormats;
|
||||
|
||||
friend class CWLSHMResource;
|
||||
friend class CWLSHMPoolResource;
|
||||
friend class CWLSHMBuffer;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CWLSHMProtocol> shm;
|
||||
};
|
||||
192
src/protocols/core/Subcompositor.cpp
Normal file
192
src/protocols/core/Subcompositor.cpp
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
#include "Subcompositor.hpp"
|
||||
#include "Compositor.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#define LOGM PROTO::subcompositor->protoLog
|
||||
|
||||
CWLSubsurfaceResource::CWLSubsurfaceResource(SP<CWlSubsurface> resource_, SP<CWLSurfaceResource> surface_, SP<CWLSurfaceResource> parent_) :
|
||||
surface(surface_), parent(parent_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWlSubsurface* r) { destroy(); });
|
||||
resource->setDestroy([this](CWlSubsurface* r) { destroy(); });
|
||||
|
||||
resource->setSetPosition([this](CWlSubsurface* r, int32_t x, int32_t y) { position = {x, y}; });
|
||||
|
||||
resource->setSetDesync([this](CWlSubsurface* r) { sync = false; });
|
||||
resource->setSetSync([this](CWlSubsurface* r) { sync = true; });
|
||||
|
||||
resource->setPlaceAbove([this](CWlSubsurface* r, wl_resource* surf) {
|
||||
auto SURF = CWLSurfaceResource::fromResource(surf);
|
||||
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
std::erase(parent->subsurfaces, self.lock());
|
||||
|
||||
auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF);
|
||||
|
||||
if (it == parent->subsurfaces.end()) {
|
||||
LOGM(ERR, "Invalid surface reference in placeAbove");
|
||||
parent->subsurfaces.emplace_back(self.lock());
|
||||
} else
|
||||
parent->subsurfaces.insert(it, self.lock());
|
||||
});
|
||||
|
||||
resource->setPlaceBelow([this](CWlSubsurface* r, wl_resource* surf) {
|
||||
auto SURF = CWLSurfaceResource::fromResource(surf);
|
||||
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
std::erase(parent->subsurfaces, self.lock());
|
||||
|
||||
auto it = std::find(parent->subsurfaces.begin(), parent->subsurfaces.end(), SURF);
|
||||
|
||||
if (it == parent->subsurfaces.end()) {
|
||||
LOGM(ERR, "Invalid surface reference in placeBelow");
|
||||
parent->subsurfaces.emplace_back(self.lock());
|
||||
} else
|
||||
parent->subsurfaces.insert(it--, self.lock());
|
||||
});
|
||||
|
||||
listeners.commitSurface = surface->events.commit.registerListener([this](std::any d) {
|
||||
if (surface->current.buffer && !surface->mapped) {
|
||||
surface->map();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!surface->current.buffer && surface->mapped) {
|
||||
surface->unmap();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CWLSubsurfaceResource::~CWLSubsurfaceResource() {
|
||||
events.destroy.emit();
|
||||
if (surface)
|
||||
surface->resetRole();
|
||||
}
|
||||
|
||||
void CWLSubsurfaceResource::destroy() {
|
||||
if (surface && surface->mapped)
|
||||
surface->unmap();
|
||||
events.destroy.emit();
|
||||
PROTO::subcompositor->destroyResource(this);
|
||||
}
|
||||
|
||||
Vector2D CWLSubsurfaceResource::posRelativeToParent() {
|
||||
Vector2D pos = position;
|
||||
SP<CWLSurfaceResource> surf = parent.lock();
|
||||
|
||||
// some apps might create cycles, which I believe _technically_ are not a protocol error
|
||||
// in some cases, notably firefox likes to do that, so we keep track of what
|
||||
// surfaces we've visited and if we hit a surface we've visited we bail out.
|
||||
std::vector<SP<CWLSurfaceResource>> surfacesVisited;
|
||||
|
||||
while (surf->role->role() == SURFACE_ROLE_SUBSURFACE &&
|
||||
std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) {
|
||||
surfacesVisited.emplace_back(surf);
|
||||
auto subsurface = (CWLSubsurfaceResource*)parent->role.get();
|
||||
pos += subsurface->position;
|
||||
surf = subsurface->parent.lock();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool CWLSubsurfaceResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
eSurfaceRole CWLSubsurfaceResource::role() {
|
||||
return SURFACE_ROLE_SUBSURFACE;
|
||||
}
|
||||
|
||||
SP<CWLSurfaceResource> CWLSubsurfaceResource::t1Parent() {
|
||||
SP<CWLSurfaceResource> surf = parent.lock();
|
||||
std::vector<SP<CWLSurfaceResource>> surfacesVisited;
|
||||
|
||||
while (surf->role->role() == SURFACE_ROLE_SUBSURFACE &&
|
||||
std::find_if(surfacesVisited.begin(), surfacesVisited.end(), [surf](const auto& other) { return surf == other; }) == surfacesVisited.end()) {
|
||||
surfacesVisited.emplace_back(surf);
|
||||
auto subsurface = (CWLSubsurfaceResource*)parent->role.get();
|
||||
surf = subsurface->parent.lock();
|
||||
}
|
||||
return surf;
|
||||
}
|
||||
|
||||
CWLSubcompositorResource::CWLSubcompositorResource(SP<CWlSubcompositor> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); });
|
||||
resource->setDestroy([this](CWlSubcompositor* r) { PROTO::subcompositor->destroyResource(this); });
|
||||
|
||||
resource->setGetSubsurface([](CWlSubcompositor* r, uint32_t id, wl_resource* surface, wl_resource* parent) {
|
||||
auto SURF = CWLSurfaceResource::fromResource(surface);
|
||||
auto PARENT = CWLSurfaceResource::fromResource(parent);
|
||||
|
||||
if (!SURF || !PARENT || SURF == PARENT) {
|
||||
r->error(WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Invalid surface/parent");
|
||||
return;
|
||||
}
|
||||
|
||||
SP<CWLSurfaceResource> t1Parent = nullptr;
|
||||
|
||||
if (PARENT->role->role() == SURFACE_ROLE_SUBSURFACE) {
|
||||
auto subsurface = (CWLSubsurfaceResource*)PARENT->role.get();
|
||||
t1Parent = subsurface->t1Parent();
|
||||
} else
|
||||
t1Parent = PARENT;
|
||||
|
||||
if (t1Parent == SURF) {
|
||||
r->error(WL_SUBCOMPOSITOR_ERROR_BAD_PARENT, "Bad parent, t1 parent == surf");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto RESOURCE =
|
||||
PROTO::subcompositor->m_vSurfaces.emplace_back(makeShared<CWLSubsurfaceResource>(makeShared<CWlSubsurface>(r->client(), r->version(), id), SURF, PARENT));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
r->noMemory();
|
||||
PROTO::subcompositor->m_vSurfaces.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
SURF->role = RESOURCE;
|
||||
PARENT->subsurfaces.emplace_back(RESOURCE);
|
||||
|
||||
LOGM(LOG, "New wl_subsurface with id {} at {:x}", id, (uintptr_t)RESOURCE.get());
|
||||
|
||||
PARENT->events.newSubsurface.emit(RESOURCE);
|
||||
});
|
||||
}
|
||||
|
||||
bool CWLSubcompositorResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CWLSubcompositorProtocol::CWLSubcompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
||||
void CWLSubcompositorProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CWLSubcompositorResource>(makeShared<CWlSubcompositor>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CWLSubcompositorProtocol::destroyResource(CWLSubcompositorResource* resource) {
|
||||
std::erase_if(m_vManagers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CWLSubcompositorProtocol::destroyResource(CWLSubsurfaceResource* resource) {
|
||||
std::erase_if(m_vSurfaces, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
82
src/protocols/core/Subcompositor.hpp
Normal file
82
src/protocols/core/Subcompositor.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
Implementations for:
|
||||
- wl_subsurface
|
||||
- wl_subcompositor
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include "../WaylandProtocol.hpp"
|
||||
#include "wayland.hpp"
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
#include "../types/SurfaceRole.hpp"
|
||||
|
||||
class CWLSurfaceResource;
|
||||
|
||||
class CWLSubsurfaceResource : public ISurfaceRole {
|
||||
public:
|
||||
CWLSubsurfaceResource(SP<CWlSubsurface> resource_, SP<CWLSurfaceResource> surface_, SP<CWLSurfaceResource> parent_);
|
||||
~CWLSubsurfaceResource();
|
||||
|
||||
Vector2D posRelativeToParent();
|
||||
bool good();
|
||||
virtual eSurfaceRole role();
|
||||
SP<CWLSurfaceResource> t1Parent();
|
||||
|
||||
bool sync = false;
|
||||
Vector2D position;
|
||||
|
||||
WP<CWLSurfaceResource> surface;
|
||||
WP<CWLSurfaceResource> parent;
|
||||
|
||||
WP<CWLSubsurfaceResource> self;
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
} events;
|
||||
|
||||
private:
|
||||
SP<CWlSubsurface> resource;
|
||||
|
||||
void destroy();
|
||||
|
||||
struct {
|
||||
CHyprSignalListener commitSurface;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CWLSubcompositorResource {
|
||||
public:
|
||||
CWLSubcompositorResource(SP<CWlSubcompositor> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CWlSubcompositor> resource;
|
||||
};
|
||||
|
||||
class CWLSubcompositorProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CWLSubcompositorProtocol(const wl_interface* iface, const int& ver, const std::string& name);
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
private:
|
||||
void destroyResource(CWLSubcompositorResource* resource);
|
||||
void destroyResource(CWLSubsurfaceResource* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CWLSubcompositorResource>> m_vManagers;
|
||||
std::vector<SP<CWLSubsurfaceResource>> m_vSurfaces;
|
||||
|
||||
friend class CWLSubcompositorResource;
|
||||
friend class CWLSubsurfaceResource;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CWLSubcompositorProtocol> subcompositor;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue