wayland/core: move to new impl (#6268)

* wayland/core/dmabuf: move to new impl

it's the final countdown
This commit is contained in:
Vaxry 2024-06-08 10:07:59 +02:00 committed by GitHub
parent c31d9ef417
commit 6967a31450
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
147 changed files with 5388 additions and 2226 deletions

View 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; });
}

View 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;
};

View file

@ -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() {

View file

@ -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();

View 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();
}

View 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;
};

View file

@ -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) {

View file

@ -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
View 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
View 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;
};

View 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; });
}

View 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;
};