Core: Move to aquamarine (#6608)
Moves Hyprland from wlroots to aquamarine for the backend. --------- Signed-off-by: Vaxry <vaxry@vaxry.net> Co-authored-by: Mihai Fufezan <mihai@fufexan.net> Co-authored-by: Jan Beich <jbeich@FreeBSD.org> Co-authored-by: vaxerski <vaxerski@users.noreply.github.com> Co-authored-by: UjinT34 <41110182+UjinT34@users.noreply.github.com> Co-authored-by: Tom Englund <tomenglund26@gmail.com> Co-authored-by: Ikalco <73481042+ikalco@users.noreply.github.com> Co-authored-by: diniamo <diniamo53@gmail.com>
This commit is contained in:
parent
f642fb97df
commit
016da234d0
131 changed files with 4755 additions and 3460 deletions
|
|
@ -1,48 +1,9 @@
|
|||
#include "CursorShape.hpp"
|
||||
#include <algorithm>
|
||||
#include "../helpers/CursorShapes.hpp"
|
||||
|
||||
#define LOGM PROTO::cursorShape->protoLog
|
||||
|
||||
// clang-format off
|
||||
constexpr const char* SHAPE_NAMES[] = {
|
||||
"invalid",
|
||||
"default",
|
||||
"context-menu",
|
||||
"help",
|
||||
"pointer",
|
||||
"progress",
|
||||
"wait",
|
||||
"cell",
|
||||
"crosshair",
|
||||
"text",
|
||||
"vertical-text",
|
||||
"alias",
|
||||
"copy",
|
||||
"move",
|
||||
"no-drop",
|
||||
"not-allowed",
|
||||
"grab",
|
||||
"grabbing",
|
||||
"e-resize",
|
||||
"n-resize",
|
||||
"ne-resize",
|
||||
"nw-resize",
|
||||
"s-resize",
|
||||
"se-resize",
|
||||
"sw-resize",
|
||||
"w-resize",
|
||||
"ew-resize",
|
||||
"ns-resize",
|
||||
"nesw-resize",
|
||||
"nwse-resize",
|
||||
"col-resize",
|
||||
"row-resize",
|
||||
"all-scroll",
|
||||
"zoom-in",
|
||||
"zoom-out",
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
CCursorShapeProtocol::CCursorShapeProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
;
|
||||
}
|
||||
|
|
@ -82,7 +43,7 @@ void CCursorShapeProtocol::createCursorShapeDevice(CWpCursorShapeManagerV1* pMgr
|
|||
}
|
||||
|
||||
void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t serial, wpCursorShapeDeviceV1Shape shape) {
|
||||
if ((uint32_t)shape == 0 || (uint32_t)shape > sizeof(SHAPE_NAMES)) {
|
||||
if ((uint32_t)shape == 0 || (uint32_t)shape > CURSOR_SHAPE_NAMES.size()) {
|
||||
pMgr->error(WP_CURSOR_SHAPE_DEVICE_V1_ERROR_INVALID_SHAPE, "The shape is invalid");
|
||||
return;
|
||||
}
|
||||
|
|
@ -90,7 +51,7 @@ void CCursorShapeProtocol::onSetShape(CWpCursorShapeDeviceV1* pMgr, uint32_t ser
|
|||
SSetShapeEvent event;
|
||||
event.pMgr = pMgr;
|
||||
event.shape = shape;
|
||||
event.shapeName = SHAPE_NAMES[shape];
|
||||
event.shapeName = CURSOR_SHAPE_NAMES.at(shape);
|
||||
|
||||
events.setShape.emit(event);
|
||||
}
|
||||
302
src/protocols/DRMLease.cpp
Normal file
302
src/protocols/DRMLease.cpp
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#include "DRMLease.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
#include <aquamarine/backend/DRM.hpp>
|
||||
#include <fcntl.h>
|
||||
|
||||
#define LOGM PROTO::lease->protoLog
|
||||
|
||||
CDRMLeaseResource::CDRMLeaseResource(SP<CWpDrmLeaseV1> resource_, SP<CDRMLeaseRequestResource> request) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); });
|
||||
resource->setDestroy([this](CWpDrmLeaseV1* r) { PROTO::lease->destroyResource(this); });
|
||||
|
||||
parent = request->parent;
|
||||
requested = request->requested;
|
||||
|
||||
for (auto& m : requested) {
|
||||
if (!m->monitor || m->monitor->isBeingLeased) {
|
||||
LOGM(ERR, "Rejecting lease: no monitor or monitor is being leased for {}", (m->monitor ? m->monitor->szName : "null"));
|
||||
resource->sendFinished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// grant the lease if it is seemingly valid
|
||||
|
||||
LOGM(LOG, "Leasing outputs: {}", [this]() {
|
||||
std::string roll;
|
||||
for (auto& o : requested) {
|
||||
roll += std::format("{} ", o->monitor->szName);
|
||||
}
|
||||
return roll;
|
||||
}());
|
||||
|
||||
std::vector<SP<Aquamarine::IOutput>> outputs;
|
||||
for (auto& m : requested) {
|
||||
outputs.emplace_back(m->monitor->output);
|
||||
}
|
||||
|
||||
auto aqlease = Aquamarine::CDRMLease::create(outputs);
|
||||
if (!aqlease) {
|
||||
LOGM(ERR, "Rejecting lease: backend failed to alloc a lease");
|
||||
resource->sendFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "Granting lease, sending fd {}", aqlease->leaseFD);
|
||||
|
||||
resource->sendLeaseFd(aqlease->leaseFD);
|
||||
|
||||
lease = aqlease;
|
||||
|
||||
for (auto& m : requested) {
|
||||
m->monitor->isBeingLeased = true;
|
||||
}
|
||||
|
||||
listeners.destroyLease = lease->events.destroy.registerListener([this](std::any d) {
|
||||
for (auto& m : requested) {
|
||||
if (m && m->monitor)
|
||||
m->monitor->isBeingLeased = false;
|
||||
}
|
||||
|
||||
resource->sendFinished();
|
||||
});
|
||||
|
||||
close(lease->leaseFD);
|
||||
}
|
||||
|
||||
bool CDRMLeaseResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CDRMLeaseRequestResource::CDRMLeaseRequestResource(SP<CWpDrmLeaseRequestV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWpDrmLeaseRequestV1* r) { PROTO::lease->destroyResource(this); });
|
||||
|
||||
resource->setRequestConnector([this](CWpDrmLeaseRequestV1* r, wl_resource* conn) {
|
||||
if (!conn) {
|
||||
resource->error(-1, "Null connector");
|
||||
return;
|
||||
}
|
||||
|
||||
auto CONNECTOR = CDRMLeaseConnectorResource::fromResource(conn);
|
||||
|
||||
if (std::find(requested.begin(), requested.end(), CONNECTOR) != requested.end()) {
|
||||
resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_DUPLICATE_CONNECTOR, "Connector already requested");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: when (if) we add multi, make sure this is from the correct device.
|
||||
|
||||
requested.emplace_back(CONNECTOR);
|
||||
});
|
||||
|
||||
resource->setSubmit([this](CWpDrmLeaseRequestV1* r, uint32_t id) {
|
||||
if (requested.empty()) {
|
||||
resource->error(WP_DRM_LEASE_REQUEST_V1_ERROR_EMPTY_LEASE, "No connectors added");
|
||||
return;
|
||||
}
|
||||
|
||||
auto RESOURCE = makeShared<CDRMLeaseResource>(makeShared<CWpDrmLeaseV1>(resource->client(), resource->version(), -1), self.lock());
|
||||
if (!RESOURCE) {
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
PROTO::lease->m_vLeases.emplace_back(RESOURCE);
|
||||
|
||||
// per protcol, after submit, this is dead.
|
||||
PROTO::lease->destroyResource(this);
|
||||
});
|
||||
}
|
||||
|
||||
bool CDRMLeaseRequestResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
SP<CDRMLeaseConnectorResource> CDRMLeaseConnectorResource::fromResource(wl_resource* res) {
|
||||
auto data = (CDRMLeaseConnectorResource*)(((CWpDrmLeaseConnectorV1*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
}
|
||||
|
||||
CDRMLeaseConnectorResource::CDRMLeaseConnectorResource(SP<CWpDrmLeaseConnectorV1> resource_, SP<CMonitor> monitor_) : monitor(monitor_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); });
|
||||
resource->setDestroy([this](CWpDrmLeaseConnectorV1* r) { PROTO::lease->destroyResource(this); });
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
listeners.destroyMonitor = monitor->events.destroy.registerListener([this](std::any d) {
|
||||
resource->sendWithdrawn();
|
||||
dead = true;
|
||||
});
|
||||
}
|
||||
|
||||
bool CDRMLeaseConnectorResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void CDRMLeaseConnectorResource::sendData() {
|
||||
resource->sendName(monitor->szName.c_str());
|
||||
resource->sendDescription(monitor->szDescription.c_str());
|
||||
|
||||
auto AQDRMOutput = (Aquamarine::CDRMOutput*)monitor->output.get();
|
||||
resource->sendConnectorId(AQDRMOutput->getConnectorID());
|
||||
|
||||
resource->sendDone();
|
||||
}
|
||||
|
||||
CDRMLeaseDeviceResource::CDRMLeaseDeviceResource(SP<CWpDrmLeaseDeviceV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); });
|
||||
resource->setRelease([this](CWpDrmLeaseDeviceV1* r) { PROTO::lease->destroyResource(this); });
|
||||
|
||||
resource->setCreateLeaseRequest([this](CWpDrmLeaseDeviceV1* r, uint32_t id) {
|
||||
auto RESOURCE = makeShared<CDRMLeaseRequestResource>(makeShared<CWpDrmLeaseRequestV1>(resource->client(), resource->version(), id));
|
||||
if (!RESOURCE) {
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
|
||||
PROTO::lease->m_vRequests.emplace_back(RESOURCE);
|
||||
|
||||
LOGM(LOG, "New lease request {}", id);
|
||||
|
||||
RESOURCE->parent = self;
|
||||
});
|
||||
|
||||
int fd = ((Aquamarine::CDRMBackend*)PROTO::lease->primaryDevice->backend.get())->getNonMasterFD();
|
||||
if (fd < 0) {
|
||||
LOGM(ERR, "Failed to dup fd in lease");
|
||||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "Sending DRMFD {} to new lease device", fd);
|
||||
resource->sendDrmFd(fd);
|
||||
close(fd);
|
||||
|
||||
for (auto& m : PROTO::lease->primaryDevice->offeredOutputs) {
|
||||
sendConnector(m.lock());
|
||||
}
|
||||
|
||||
resource->sendDone();
|
||||
}
|
||||
|
||||
bool CDRMLeaseDeviceResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
void CDRMLeaseDeviceResource::sendConnector(SP<CMonitor> monitor) {
|
||||
if (std::find_if(connectorsSent.begin(), connectorsSent.end(), [monitor](const auto& e) { return e->monitor == monitor; }) != connectorsSent.end())
|
||||
return;
|
||||
|
||||
auto RESOURCE = makeShared<CDRMLeaseConnectorResource>(makeShared<CWpDrmLeaseConnectorV1>(resource->client(), resource->version(), -1), monitor);
|
||||
if (!RESOURCE) {
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->parent = self;
|
||||
RESOURCE->self = RESOURCE;
|
||||
|
||||
LOGM(LOG, "Sending new connector {}", monitor->szName);
|
||||
|
||||
connectorsSent.emplace_back(RESOURCE);
|
||||
PROTO::lease->m_vConnectors.emplace_back(RESOURCE);
|
||||
|
||||
resource->sendConnector(RESOURCE->resource.get());
|
||||
|
||||
RESOURCE->sendData();
|
||||
}
|
||||
|
||||
CDRMLeaseDevice::CDRMLeaseDevice(SP<Aquamarine::CDRMBackend> drmBackend) : backend(drmBackend) {
|
||||
auto drm = (Aquamarine::CDRMBackend*)drmBackend.get();
|
||||
|
||||
auto fd = drm->getNonMasterFD();
|
||||
|
||||
if (fd < 0) {
|
||||
LOGM(ERR, "Failed to dup fd for drm node {}", drm->gpuName);
|
||||
return;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
success = true;
|
||||
name = drm->gpuName;
|
||||
}
|
||||
|
||||
CDRMLeaseProtocol::CDRMLeaseProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
for (auto& b : g_pCompositor->m_pAqBackend->getImplementations()) {
|
||||
if (b->type() != Aquamarine::AQ_BACKEND_DRM)
|
||||
continue;
|
||||
|
||||
auto drm = ((Aquamarine::CDRMBackend*)b.get())->self.lock();
|
||||
|
||||
primaryDevice = makeShared<CDRMLeaseDevice>(drm);
|
||||
|
||||
if (primaryDevice->success)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!primaryDevice || primaryDevice->success) {
|
||||
PROTO::lease.reset();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CDRMLeaseDeviceResource>(makeShared<CWpDrmLeaseDeviceV1>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
|
||||
RESOURCE->self = RESOURCE;
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::destroyResource(CDRMLeaseDeviceResource* resource) {
|
||||
std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::destroyResource(CDRMLeaseConnectorResource* resource) {
|
||||
std::erase_if(m_vConnectors, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::destroyResource(CDRMLeaseRequestResource* resource) {
|
||||
std::erase_if(m_vRequests, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::destroyResource(CDRMLeaseResource* resource) {
|
||||
std::erase_if(m_vLeases, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMLeaseProtocol::offer(SP<CMonitor> monitor) {
|
||||
if (std::find(primaryDevice->offeredOutputs.begin(), primaryDevice->offeredOutputs.end(), monitor) != primaryDevice->offeredOutputs.end())
|
||||
return;
|
||||
|
||||
if (monitor->output->getBackend()->type() != Aquamarine::AQ_BACKEND_DRM)
|
||||
return;
|
||||
|
||||
if (monitor->output->getBackend() != primaryDevice->backend) {
|
||||
LOGM(ERR, "Monitor {} cannot be leased: primaryDevice lease is for a different device", monitor->szName);
|
||||
return;
|
||||
}
|
||||
|
||||
primaryDevice->offeredOutputs.emplace_back(monitor);
|
||||
|
||||
for (auto& m : m_vManagers) {
|
||||
m->sendConnector(monitor);
|
||||
m->resource->sendDone();
|
||||
}
|
||||
}
|
||||
137
src/protocols/DRMLease.hpp
Normal file
137
src/protocols/DRMLease.hpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "drm-lease-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
/*
|
||||
TODO: this protocol is not made for systems with multiple DRM nodes (e.g. multigpu)
|
||||
*/
|
||||
|
||||
AQUAMARINE_FORWARD(CDRMBackend);
|
||||
AQUAMARINE_FORWARD(CDRMLease);
|
||||
class CDRMLeaseDeviceResource;
|
||||
class CMonitor;
|
||||
class CDRMLeaseProtocol;
|
||||
class CDRMLeaseConnectorResource;
|
||||
class CDRMLeaseRequestResource;
|
||||
|
||||
class CDRMLeaseResource {
|
||||
public:
|
||||
CDRMLeaseResource(SP<CWpDrmLeaseV1> resource_, SP<CDRMLeaseRequestResource> request);
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CDRMLeaseDeviceResource> parent;
|
||||
std::vector<WP<CDRMLeaseConnectorResource>> requested;
|
||||
WP<Aquamarine::CDRMLease> lease;
|
||||
|
||||
int leaseFD = -1;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroyLease;
|
||||
} listeners;
|
||||
|
||||
private:
|
||||
SP<CWpDrmLeaseV1> resource;
|
||||
};
|
||||
|
||||
class CDRMLeaseRequestResource {
|
||||
public:
|
||||
CDRMLeaseRequestResource(SP<CWpDrmLeaseRequestV1> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CDRMLeaseDeviceResource> parent;
|
||||
WP<CDRMLeaseRequestResource> self;
|
||||
std::vector<WP<CDRMLeaseConnectorResource>> requested;
|
||||
|
||||
private:
|
||||
SP<CWpDrmLeaseRequestV1> resource;
|
||||
};
|
||||
|
||||
class CDRMLeaseConnectorResource {
|
||||
public:
|
||||
CDRMLeaseConnectorResource(SP<CWpDrmLeaseConnectorV1> resource_, SP<CMonitor> monitor_);
|
||||
static SP<CDRMLeaseConnectorResource> fromResource(wl_resource*);
|
||||
|
||||
bool good();
|
||||
void sendData();
|
||||
|
||||
WP<CDRMLeaseConnectorResource> self;
|
||||
WP<CDRMLeaseDeviceResource> parent;
|
||||
WP<CMonitor> monitor;
|
||||
bool dead = false;
|
||||
|
||||
private:
|
||||
SP<CWpDrmLeaseConnectorV1> resource;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener destroyMonitor;
|
||||
} listeners;
|
||||
|
||||
friend class CDRMLeaseDeviceResource;
|
||||
};
|
||||
|
||||
class CDRMLeaseDeviceResource {
|
||||
public:
|
||||
CDRMLeaseDeviceResource(SP<CWpDrmLeaseDeviceV1> resource_);
|
||||
|
||||
bool good();
|
||||
void sendConnector(SP<CMonitor> monitor);
|
||||
|
||||
std::vector<WP<CDRMLeaseConnectorResource>> connectorsSent;
|
||||
|
||||
WP<CDRMLeaseDeviceResource> self;
|
||||
|
||||
private:
|
||||
SP<CWpDrmLeaseDeviceV1> resource;
|
||||
|
||||
friend class CDRMLeaseProtocol;
|
||||
};
|
||||
|
||||
class CDRMLeaseDevice {
|
||||
public:
|
||||
CDRMLeaseDevice(SP<Aquamarine::CDRMBackend> drmBackend);
|
||||
|
||||
std::string name = "";
|
||||
bool success = false;
|
||||
SP<Aquamarine::CDRMBackend> backend;
|
||||
|
||||
std::vector<WP<CMonitor>> offeredOutputs;
|
||||
};
|
||||
|
||||
class CDRMLeaseProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CDRMLeaseProtocol(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);
|
||||
|
||||
void offer(SP<CMonitor> monitor);
|
||||
|
||||
private:
|
||||
void destroyResource(CDRMLeaseDeviceResource* resource);
|
||||
void destroyResource(CDRMLeaseConnectorResource* resource);
|
||||
void destroyResource(CDRMLeaseRequestResource* resource);
|
||||
void destroyResource(CDRMLeaseResource* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CDRMLeaseDeviceResource>> m_vManagers;
|
||||
std::vector<SP<CDRMLeaseConnectorResource>> m_vConnectors;
|
||||
std::vector<SP<CDRMLeaseRequestResource>> m_vRequests;
|
||||
std::vector<SP<CDRMLeaseResource>> m_vLeases;
|
||||
|
||||
SP<CDRMLeaseDevice> primaryDevice;
|
||||
|
||||
friend class CDRMLeaseDeviceResource;
|
||||
friend class CDRMLeaseConnectorResource;
|
||||
friend class CDRMLeaseRequestResource;
|
||||
friend class CDRMLeaseResource;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CDRMLeaseProtocol> lease;
|
||||
};
|
||||
183
src/protocols/DRMSyncobj.cpp
Normal file
183
src/protocols/DRMSyncobj.cpp
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
#include "DRMSyncobj.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
#include "core/Compositor.hpp"
|
||||
#include "../helpers/sync/SyncTimeline.hpp"
|
||||
#include "../Compositor.hpp"
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
#define LOGM PROTO::sync->protoLog
|
||||
|
||||
CDRMSyncobjSurfaceResource::CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurfaceV1> resource_, SP<CWLSurfaceResource> surface_) : surface(surface_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
resource->setOnDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); });
|
||||
resource->setDestroy([this](CWpLinuxDrmSyncobjSurfaceV1* r) { PROTO::sync->destroyResource(this); });
|
||||
|
||||
resource->setSetAcquirePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
|
||||
if (!surface) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone");
|
||||
return;
|
||||
}
|
||||
|
||||
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
||||
acquireTimeline = timeline;
|
||||
acquirePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
||||
});
|
||||
|
||||
resource->setSetReleasePoint([this](CWpLinuxDrmSyncobjSurfaceV1* r, wl_resource* timeline_, uint32_t hi, uint32_t lo) {
|
||||
if (!surface) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_SURFACE, "Surface is gone");
|
||||
return;
|
||||
}
|
||||
|
||||
auto timeline = CDRMSyncobjTimelineResource::fromResource(timeline_);
|
||||
releaseTimeline = timeline;
|
||||
releasePoint = ((uint64_t)hi << 32) | (uint64_t)lo;
|
||||
});
|
||||
|
||||
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
|
||||
if (!!acquireTimeline != !!releaseTimeline) {
|
||||
resource->error(acquireTimeline ? WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_RELEASE_POINT : WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_ACQUIRE_POINT, "Missing timeline");
|
||||
surface->pending.rejected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((acquireTimeline || releaseTimeline) && !surface->pending.buffer) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_SURFACE_V1_ERROR_NO_BUFFER, "Missing buffer");
|
||||
surface->pending.rejected = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!acquireTimeline)
|
||||
return;
|
||||
|
||||
// wait for the acquire timeline to materialize
|
||||
auto materialized = acquireTimeline->timeline->check(acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
||||
if (!materialized.has_value()) {
|
||||
LOGM(ERR, "Failed to check the acquire timeline");
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
if (materialized)
|
||||
return;
|
||||
|
||||
surface->lockPendingState();
|
||||
acquireTimeline->timeline->addWaiter([this]() { surface->unlockPendingState(); }, acquirePoint, DRM_SYNCOBJ_WAIT_FLAGS_WAIT_AVAILABLE);
|
||||
});
|
||||
}
|
||||
|
||||
bool CDRMSyncobjSurfaceResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CDRMSyncobjTimelineResource::CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTimelineV1> resource_, int fd_) : fd(fd_), resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setData(this);
|
||||
|
||||
resource->setOnDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
|
||||
resource->setDestroy([this](CWpLinuxDrmSyncobjTimelineV1* r) { PROTO::sync->destroyResource(this); });
|
||||
|
||||
timeline = CSyncTimeline::create(PROTO::sync->drmFD, fd);
|
||||
|
||||
if (!timeline) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_INVALID_TIMELINE, "Timeline failed importing");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SP<CDRMSyncobjTimelineResource> CDRMSyncobjTimelineResource::fromResource(wl_resource* res) {
|
||||
auto data = (CDRMSyncobjTimelineResource*)(((CWpLinuxDrmSyncobjTimelineV1*)wl_resource_get_user_data(res))->data());
|
||||
return data ? data->self.lock() : nullptr;
|
||||
}
|
||||
|
||||
bool CDRMSyncobjTimelineResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CDRMSyncobjManagerResource::CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjManagerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
resource->setOnDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); });
|
||||
resource->setDestroy([this](CWpLinuxDrmSyncobjManagerV1* r) { PROTO::sync->destroyResource(this); });
|
||||
|
||||
resource->setGetSurface([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, wl_resource* surf) {
|
||||
if (!surf) {
|
||||
resource->error(-1, "Invalid surface");
|
||||
return;
|
||||
}
|
||||
|
||||
auto SURF = CWLSurfaceResource::fromResource(surf);
|
||||
if (!SURF) {
|
||||
resource->error(-1, "Invalid surface (2)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SURF->syncobj) {
|
||||
resource->error(WP_LINUX_DRM_SYNCOBJ_MANAGER_V1_ERROR_SURFACE_EXISTS, "Surface already has a syncobj attached");
|
||||
return;
|
||||
}
|
||||
|
||||
auto RESOURCE = makeShared<CDRMSyncobjSurfaceResource>(makeShared<CWpLinuxDrmSyncobjSurfaceV1>(resource->client(), resource->version(), id), SURF);
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
PROTO::sync->m_vSurfaces.emplace_back(RESOURCE);
|
||||
SURF->syncobj = RESOURCE;
|
||||
|
||||
LOGM(LOG, "New linux_syncobj at {:x} for surface {:x}", (uintptr_t)RESOURCE.get(), (uintptr_t)SURF.get());
|
||||
});
|
||||
|
||||
resource->setImportTimeline([this](CWpLinuxDrmSyncobjManagerV1* r, uint32_t id, int32_t fd) {
|
||||
auto RESOURCE = makeShared<CDRMSyncobjTimelineResource>(makeShared<CWpLinuxDrmSyncobjTimelineV1>(resource->client(), resource->version(), id), fd);
|
||||
if (!RESOURCE->good()) {
|
||||
resource->noMemory();
|
||||
return;
|
||||
}
|
||||
|
||||
PROTO::sync->m_vTimelines.emplace_back(RESOURCE);
|
||||
RESOURCE->self = RESOURCE;
|
||||
|
||||
LOGM(LOG, "New linux_drm_timeline at {:x}", (uintptr_t)RESOURCE.get());
|
||||
});
|
||||
}
|
||||
|
||||
bool CDRMSyncobjManagerResource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
CDRMSyncobjProtocol::CDRMSyncobjProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
drmFD = g_pCompositor->m_iDRMFD;
|
||||
}
|
||||
|
||||
void CDRMSyncobjProtocol::bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id) {
|
||||
const auto RESOURCE = m_vManagers.emplace_back(makeShared<CDRMSyncobjManagerResource>(makeShared<CWpLinuxDrmSyncobjManagerV1>(client, ver, id)));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
wl_client_post_no_memory(client);
|
||||
m_vManagers.pop_back();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjManagerResource* resource) {
|
||||
std::erase_if(m_vManagers, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjTimelineResource* resource) {
|
||||
std::erase_if(m_vTimelines, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
|
||||
void CDRMSyncobjProtocol::destroyResource(CDRMSyncobjSurfaceResource* resource) {
|
||||
std::erase_if(m_vSurfaces, [resource](const auto& e) { return e.get() == resource; });
|
||||
}
|
||||
82
src/protocols/DRMSyncobj.hpp
Normal file
82
src/protocols/DRMSyncobj.hpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "WaylandProtocol.hpp"
|
||||
#include "linux-drm-syncobj-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
|
||||
class CWLSurfaceResource;
|
||||
class CDRMSyncobjTimelineResource;
|
||||
class CSyncTimeline;
|
||||
|
||||
class CDRMSyncobjSurfaceResource {
|
||||
public:
|
||||
CDRMSyncobjSurfaceResource(SP<CWpLinuxDrmSyncobjSurfaceV1> resource_, SP<CWLSurfaceResource> surface_);
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CWLSurfaceResource> surface;
|
||||
WP<CDRMSyncobjTimelineResource> acquireTimeline, releaseTimeline;
|
||||
uint64_t acquirePoint = 0, releasePoint = 0;
|
||||
|
||||
private:
|
||||
SP<CWpLinuxDrmSyncobjSurfaceV1> resource;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener surfacePrecommit;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CDRMSyncobjTimelineResource {
|
||||
public:
|
||||
CDRMSyncobjTimelineResource(SP<CWpLinuxDrmSyncobjTimelineV1> resource_, int fd_);
|
||||
static SP<CDRMSyncobjTimelineResource> fromResource(wl_resource*);
|
||||
|
||||
bool good();
|
||||
|
||||
WP<CDRMSyncobjTimelineResource> self;
|
||||
int fd = -1;
|
||||
SP<CSyncTimeline> timeline;
|
||||
|
||||
private:
|
||||
SP<CWpLinuxDrmSyncobjTimelineV1> resource;
|
||||
};
|
||||
|
||||
class CDRMSyncobjManagerResource {
|
||||
public:
|
||||
CDRMSyncobjManagerResource(SP<CWpLinuxDrmSyncobjManagerV1> resource_);
|
||||
|
||||
bool good();
|
||||
|
||||
private:
|
||||
SP<CWpLinuxDrmSyncobjManagerV1> resource;
|
||||
};
|
||||
|
||||
class CDRMSyncobjProtocol : public IWaylandProtocol {
|
||||
public:
|
||||
CDRMSyncobjProtocol(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(CDRMSyncobjManagerResource* resource);
|
||||
void destroyResource(CDRMSyncobjTimelineResource* resource);
|
||||
void destroyResource(CDRMSyncobjSurfaceResource* resource);
|
||||
|
||||
//
|
||||
std::vector<SP<CDRMSyncobjManagerResource>> m_vManagers;
|
||||
std::vector<SP<CDRMSyncobjTimelineResource>> m_vTimelines;
|
||||
std::vector<SP<CDRMSyncobjSurfaceResource>> m_vSurfaces;
|
||||
|
||||
//
|
||||
int drmFD = -1;
|
||||
|
||||
friend class CDRMSyncobjManagerResource;
|
||||
friend class CDRMSyncobjTimelineResource;
|
||||
friend class CDRMSyncobjSurfaceResource;
|
||||
};
|
||||
|
||||
namespace PROTO {
|
||||
inline UP<CDRMSyncobjProtocol> sync;
|
||||
};
|
||||
|
|
@ -34,7 +34,7 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
|
|||
}
|
||||
}
|
||||
|
||||
gammaSize = wlr_output_get_gamma_size(pMonitor->output);
|
||||
gammaSize = pMonitor->output->getGammaSize();
|
||||
|
||||
if (gammaSize <= 0) {
|
||||
LOGM(ERR, "Output {} doesn't support gamma", pMonitor->szName);
|
||||
|
|
@ -81,6 +81,24 @@ CGammaControl::CGammaControl(SP<CZwlrGammaControlV1> resource_, wl_resource* out
|
|||
|
||||
gammaTableSet = true;
|
||||
close(fd);
|
||||
|
||||
// translate the table to AQ format
|
||||
std::vector<uint16_t> red, green, blue;
|
||||
red.resize(gammaTable.size() / 3);
|
||||
green.resize(gammaTable.size() / 3);
|
||||
blue.resize(gammaTable.size() / 3);
|
||||
for (size_t i = 0; i < gammaTable.size() / 3; ++i) {
|
||||
red.at(i) = gammaTable.at(i);
|
||||
green.at(i) = gammaTable.at(gammaTable.size() / 3 + i);
|
||||
blue.at(i) = gammaTable.at((gammaTable.size() / 3) * 2 + i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < gammaTable.size() / 3; ++i) {
|
||||
gammaTable.at(i * 3) = red.at(i);
|
||||
gammaTable.at(i * 3 + 1) = green.at(i);
|
||||
gammaTable.at(i * 3 + 2) = blue.at(i);
|
||||
}
|
||||
|
||||
applyToMonitor();
|
||||
});
|
||||
|
||||
|
|
@ -95,7 +113,7 @@ CGammaControl::~CGammaControl() {
|
|||
return;
|
||||
|
||||
// reset the LUT if the client dies for whatever reason and doesn't unset the gamma
|
||||
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
|
||||
pMonitor->output->state->setGammaLut({});
|
||||
}
|
||||
|
||||
bool CGammaControl::good() {
|
||||
|
|
@ -109,19 +127,15 @@ void CGammaControl::applyToMonitor() {
|
|||
LOGM(LOG, "setting to monitor {}", pMonitor->szName);
|
||||
|
||||
if (!gammaTableSet) {
|
||||
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
|
||||
pMonitor->output->state->setGammaLut({});
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t* red = &gammaTable.at(0);
|
||||
uint16_t* green = &gammaTable.at(gammaSize);
|
||||
uint16_t* blue = &gammaTable.at(gammaSize * 2);
|
||||
|
||||
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), gammaSize, red, green, blue);
|
||||
pMonitor->output->state->setGammaLut(gammaTable);
|
||||
|
||||
if (!pMonitor->state.test()) {
|
||||
LOGM(LOG, "setting to monitor {} failed", pMonitor->szName);
|
||||
wlr_output_state_set_gamma_lut(pMonitor->state.wlr(), 0, nullptr, nullptr, nullptr);
|
||||
pMonitor->output->state->setGammaLut({});
|
||||
}
|
||||
|
||||
g_pHyprRenderer->damageMonitor(pMonitor);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class CGammaControl {
|
|||
CMonitor* pMonitor = nullptr;
|
||||
size_t gammaSize = 0;
|
||||
bool gammaTableSet = false;
|
||||
std::vector<uint16_t> gammaTable;
|
||||
std::vector<uint16_t> gammaTable; // [r,g,b]+
|
||||
|
||||
void onMonitorDestroy();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../devices/IKeyboard.hpp"
|
||||
#include <sys/mman.h>
|
||||
#include "core/Compositor.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#define LOGM PROTO::ime->protoLog
|
||||
|
||||
|
|
@ -19,7 +20,7 @@ CInputMethodKeyboardGrabV2::CInputMethodKeyboardGrabV2(SP<CZwpInputMethodKeyboar
|
|||
return;
|
||||
}
|
||||
|
||||
sendKeyboardData(g_pSeatManager->keyboard->wlr());
|
||||
sendKeyboardData(g_pSeatManager->keyboard.lock());
|
||||
}
|
||||
|
||||
CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
|
||||
|
|
@ -27,37 +28,36 @@ CInputMethodKeyboardGrabV2::~CInputMethodKeyboardGrabV2() {
|
|||
std::erase_if(owner->grabs, [](const auto& g) { return g.expired(); });
|
||||
}
|
||||
|
||||
void CInputMethodKeyboardGrabV2::sendKeyboardData(wlr_keyboard* keyboard) {
|
||||
void CInputMethodKeyboardGrabV2::sendKeyboardData(SP<IKeyboard> keyboard) {
|
||||
|
||||
if (keyboard == pLastKeyboard)
|
||||
return;
|
||||
|
||||
pLastKeyboard = keyboard;
|
||||
|
||||
int keymapFD = allocateSHMFile(keyboard->keymap_size);
|
||||
int keymapFD = allocateSHMFile(keyboard->xkbKeymapString.length() + 1);
|
||||
if (keymapFD < 0) {
|
||||
LOGM(ERR, "Failed to create a keymap file for keyboard grab");
|
||||
return;
|
||||
}
|
||||
|
||||
void* data = mmap(nullptr, keyboard->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
|
||||
void* data = mmap(nullptr, keyboard->xkbKeymapString.length() + 1, PROT_READ | PROT_WRITE, MAP_SHARED, keymapFD, 0);
|
||||
if (data == MAP_FAILED) {
|
||||
LOGM(ERR, "Failed to mmap a keymap file for keyboard grab");
|
||||
close(keymapFD);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(data, keyboard->keymap_string, keyboard->keymap_size);
|
||||
munmap(data, keyboard->keymap_size);
|
||||
memcpy(data, keyboard->xkbKeymapString.c_str(), keyboard->xkbKeymapString.length());
|
||||
munmap(data, keyboard->xkbKeymapString.length() + 1);
|
||||
|
||||
resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->keymap_size);
|
||||
resource->sendKeymap(WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, keymapFD, keyboard->xkbKeymapString.length() + 1);
|
||||
|
||||
close(keymapFD);
|
||||
|
||||
const auto MODS = keyboard->modifiers;
|
||||
sendMods(MODS.depressed, MODS.latched, MODS.locked, MODS.group);
|
||||
sendMods(keyboard->modifiersState.depressed, keyboard->modifiersState.latched, keyboard->modifiersState.locked, keyboard->modifiersState.group);
|
||||
|
||||
resource->sendRepeatInfo(keyboard->repeat_info.rate, keyboard->repeat_info.delay);
|
||||
resource->sendRepeatInfo(keyboard->repeatRate, keyboard->repeatDelay);
|
||||
}
|
||||
|
||||
void CInputMethodKeyboardGrabV2::sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state) {
|
||||
|
|
@ -316,7 +316,7 @@ void CInputMethodV2::sendMods(uint32_t depressed, uint32_t latched, uint32_t loc
|
|||
}
|
||||
}
|
||||
|
||||
void CInputMethodV2::setKeyboard(wlr_keyboard* keyboard) {
|
||||
void CInputMethodV2::setKeyboard(SP<IKeyboard> keyboard) {
|
||||
for (auto& gw : grabs) {
|
||||
auto g = gw.lock();
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
class CInputMethodKeyboardGrabV2;
|
||||
class CInputMethodPopupV2;
|
||||
class IKeyboard;
|
||||
|
||||
class CInputMethodV2 {
|
||||
public:
|
||||
|
|
@ -58,7 +59,7 @@ class CInputMethodV2 {
|
|||
bool hasGrab();
|
||||
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
|
||||
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
|
||||
void setKeyboard(wlr_keyboard* keyboard);
|
||||
void setKeyboard(SP<IKeyboard> keyboard);
|
||||
|
||||
wl_client* client();
|
||||
wl_client* grabClient();
|
||||
|
|
@ -90,13 +91,13 @@ class CInputMethodKeyboardGrabV2 {
|
|||
|
||||
void sendKey(uint32_t time, uint32_t key, wl_keyboard_key_state state);
|
||||
void sendMods(uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group);
|
||||
void sendKeyboardData(wlr_keyboard* keyboard);
|
||||
void sendKeyboardData(SP<IKeyboard> keyboard);
|
||||
|
||||
private:
|
||||
SP<CZwpInputMethodKeyboardGrabV2> resource;
|
||||
WP<CInputMethodV2> owner;
|
||||
|
||||
wlr_keyboard* pLastKeyboard = nullptr; // READ-ONLY
|
||||
WP<IKeyboard> pLastKeyboard;
|
||||
};
|
||||
|
||||
class CInputMethodPopupV2 {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <sys/mman.h>
|
||||
#include <xf86drm.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "core/Compositor.hpp"
|
||||
#include "types/DMABuffer.hpp"
|
||||
#include "types/WLBuffer.hpp"
|
||||
|
|
@ -22,21 +23,66 @@ static std::optional<dev_t> devIDFromFD(int fd) {
|
|||
return stat.st_rdev;
|
||||
}
|
||||
|
||||
CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches_) {
|
||||
CDMABUFFormatTable::CDMABUFFormatTable(SDMABUFTranche _rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches_) :
|
||||
rendererTranche(_rendererTranche), monitorTranches(tranches_) {
|
||||
|
||||
std::vector<SDMABUFFormatTableEntry> formatsVec;
|
||||
std::set<std::pair<uint32_t, uint64_t>> formats;
|
||||
for (auto& t : tranches_) {
|
||||
for (auto& fmt : t.formats) {
|
||||
for (auto& mod : fmt.mods) {
|
||||
formats.insert(std::make_pair<>(fmt.format, mod));
|
||||
|
||||
// insert formats into vec if they got inserted into set, meaning they're unique
|
||||
size_t i = 0;
|
||||
|
||||
rendererTranche.indicies.clear();
|
||||
for (auto& fmt : rendererTranche.formats) {
|
||||
for (auto& mod : fmt.modifiers) {
|
||||
auto format = std::make_pair<>(fmt.drmFormat, mod);
|
||||
auto [_, inserted] = formats.insert(format);
|
||||
if (inserted) {
|
||||
// if it was inserted into set, then its unique and will have a new index in vec
|
||||
rendererTranche.indicies.push_back(i++);
|
||||
formatsVec.push_back(SDMABUFFormatTableEntry{
|
||||
.fmt = fmt.drmFormat,
|
||||
.modifier = mod,
|
||||
});
|
||||
} else {
|
||||
// if it wasn't inserted then find its index in vec
|
||||
auto it =
|
||||
std::find_if(formatsVec.begin(), formatsVec.end(), [fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
|
||||
rendererTranche.indicies.push_back(it - formatsVec.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tableLen = formats.size() * sizeof(SDMABUFFeedbackTableEntry);
|
||||
int fds[2] = {0};
|
||||
allocateSHMFilePair(tableLen, &fds[0], &fds[1]);
|
||||
for (auto& [monitor, tranche] : monitorTranches) {
|
||||
tranche.indicies.clear();
|
||||
for (auto& fmt : tranche.formats) {
|
||||
for (auto& mod : fmt.modifiers) {
|
||||
// apparently these can implode on planes, so dont use them
|
||||
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
|
||||
continue;
|
||||
auto format = std::make_pair<>(fmt.drmFormat, mod);
|
||||
auto [_, inserted] = formats.insert(format);
|
||||
if (inserted) {
|
||||
tranche.indicies.push_back(i++);
|
||||
formatsVec.push_back(SDMABUFFormatTableEntry{
|
||||
.fmt = fmt.drmFormat,
|
||||
.modifier = mod,
|
||||
});
|
||||
} else {
|
||||
auto it = std::find_if(formatsVec.begin(), formatsVec.end(),
|
||||
[fmt, mod](const SDMABUFFormatTableEntry& oth) { return oth.fmt == fmt.drmFormat && oth.modifier == mod; });
|
||||
tranche.indicies.push_back(it - formatsVec.begin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto arr = (SDMABUFFeedbackTableEntry*)mmap(nullptr, tableLen, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
|
||||
tableSize = formatsVec.size() * sizeof(SDMABUFFormatTableEntry);
|
||||
|
||||
int fds[2] = {0};
|
||||
allocateSHMFilePair(tableSize, &fds[0], &fds[1]);
|
||||
|
||||
auto arr = (SDMABUFFormatTableEntry*)mmap(nullptr, tableSize, PROT_READ | PROT_WRITE, MAP_SHARED, fds[0], 0);
|
||||
|
||||
if (arr == MAP_FAILED) {
|
||||
LOGM(ERR, "mmap failed");
|
||||
|
|
@ -47,33 +93,18 @@ CCompiledDMABUFFeedback::CCompiledDMABUFFeedback(dev_t device, std::vector<SDMAB
|
|||
|
||||
close(fds[0]);
|
||||
|
||||
std::vector<std::pair<uint32_t, uint64_t>> formatsVec;
|
||||
for (auto& f : formats) {
|
||||
formatsVec.push_back(f);
|
||||
}
|
||||
std::copy(formatsVec.begin(), formatsVec.end(), arr);
|
||||
|
||||
size_t i = 0;
|
||||
for (auto& [fmt, mod] : formatsVec) {
|
||||
arr[i++] = SDMABUFFeedbackTableEntry{
|
||||
.fmt = fmt,
|
||||
.modifier = mod,
|
||||
};
|
||||
}
|
||||
munmap(arr, tableSize);
|
||||
|
||||
munmap(arr, tableLen);
|
||||
|
||||
mainDevice = device;
|
||||
tableFD = fds[1];
|
||||
tranches = formatsVec;
|
||||
|
||||
// TODO: maybe calculate indices? currently we send all as available which could be wrong? I ain't no kernel dev tho.
|
||||
tableFD = fds[1];
|
||||
}
|
||||
|
||||
CCompiledDMABUFFeedback::~CCompiledDMABUFFeedback() {
|
||||
CDMABUFFormatTable::~CDMABUFFormatTable() {
|
||||
close(tableFD);
|
||||
}
|
||||
|
||||
CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs) {
|
||||
CLinuxDMABuffer::CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs) {
|
||||
buffer = makeShared<CDMABuffer>(id, client, attrs);
|
||||
|
||||
buffer->resource->buffer = buffer;
|
||||
|
|
@ -103,7 +134,7 @@ CLinuxDMABBUFParamsResource::CLinuxDMABBUFParamsResource(SP<CZwpLinuxBufferParam
|
|||
resource->setOnDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||
resource->setDestroy([this](CZwpLinuxBufferParamsV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||
|
||||
attrs = makeShared<SDMABUFAttrs>();
|
||||
attrs = makeShared<Aquamarine::SDMABUFAttrs>();
|
||||
|
||||
attrs->success = true;
|
||||
|
||||
|
|
@ -190,7 +221,7 @@ void CLinuxDMABBUFParamsResource::create(uint32_t id) {
|
|||
return;
|
||||
}
|
||||
|
||||
LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, attrs->format, attrs->planes);
|
||||
LOGM(LOG, "Creating a dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs->size, FormatUtils::drmFormatName(attrs->format), attrs->planes);
|
||||
for (int i = 0; i < attrs->planes; ++i) {
|
||||
LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs->modifier, attrs->fds[i], attrs->strides[i], attrs->offsets[i]);
|
||||
}
|
||||
|
|
@ -277,32 +308,9 @@ CLinuxDMABUFFeedbackResource::CLinuxDMABUFFeedbackResource(SP<CZwpLinuxDmabufFee
|
|||
resource->setOnDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||
resource->setDestroy([this](CZwpLinuxDmabufFeedbackV1* r) { PROTO::linuxDma->destroyResource(this); });
|
||||
|
||||
if (surface)
|
||||
LOGM(ERR, "FIXME: surface feedback stub");
|
||||
|
||||
auto* feedback = PROTO::linuxDma->defaultFeedback.get();
|
||||
|
||||
resource->sendFormatTable(feedback->tableFD, feedback->tableLen);
|
||||
|
||||
// send default feedback
|
||||
struct wl_array deviceArr = {
|
||||
.size = sizeof(feedback->mainDevice),
|
||||
.data = (void*)&feedback->mainDevice,
|
||||
};
|
||||
resource->sendMainDevice(&deviceArr);
|
||||
resource->sendTrancheTargetDevice(&deviceArr);
|
||||
resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)0);
|
||||
|
||||
wl_array indices;
|
||||
wl_array_init(&indices);
|
||||
for (size_t i = 0; i < feedback->tranches.size(); ++i) {
|
||||
*((uint16_t*)wl_array_add(&indices, sizeof(uint16_t))) = i;
|
||||
}
|
||||
resource->sendTrancheFormats(&indices);
|
||||
wl_array_release(&indices);
|
||||
resource->sendTrancheDone();
|
||||
|
||||
resource->sendDone();
|
||||
auto& formatTable = PROTO::linuxDma->formatTable;
|
||||
resource->sendFormatTable(formatTable->tableFD, formatTable->tableSize);
|
||||
sendDefaultFeedback();
|
||||
}
|
||||
|
||||
CLinuxDMABUFFeedbackResource::~CLinuxDMABUFFeedbackResource() {
|
||||
|
|
@ -313,6 +321,41 @@ bool CLinuxDMABUFFeedbackResource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
void CLinuxDMABUFFeedbackResource::sendTranche(SDMABUFTranche& tranche) {
|
||||
struct wl_array deviceArr = {
|
||||
.size = sizeof(tranche.device),
|
||||
.data = (void*)&tranche.device,
|
||||
};
|
||||
resource->sendTrancheTargetDevice(&deviceArr);
|
||||
|
||||
resource->sendTrancheFlags((zwpLinuxDmabufFeedbackV1TrancheFlags)tranche.flags);
|
||||
|
||||
wl_array indices = {
|
||||
.size = tranche.indicies.size() * sizeof(tranche.indicies.at(0)),
|
||||
.data = tranche.indicies.data(),
|
||||
};
|
||||
resource->sendTrancheFormats(&indices);
|
||||
resource->sendTrancheDone();
|
||||
}
|
||||
|
||||
// default tranche is based on renderer (egl)
|
||||
void CLinuxDMABUFFeedbackResource::sendDefaultFeedback() {
|
||||
auto mainDevice = PROTO::linuxDma->mainDevice;
|
||||
auto& formatTable = PROTO::linuxDma->formatTable;
|
||||
|
||||
struct wl_array deviceArr = {
|
||||
.size = sizeof(mainDevice),
|
||||
.data = (void*)&mainDevice,
|
||||
};
|
||||
resource->sendMainDevice(&deviceArr);
|
||||
|
||||
sendTranche(formatTable->rendererTranche);
|
||||
|
||||
resource->sendDone();
|
||||
|
||||
lastFeedbackWasScanout = false;
|
||||
}
|
||||
|
||||
CLinuxDMABUFResource::CLinuxDMABUFResource(SP<CZwpLinuxDmabufV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
|
@ -361,48 +404,81 @@ bool CLinuxDMABUFResource::good() {
|
|||
}
|
||||
|
||||
void CLinuxDMABUFResource::sendMods() {
|
||||
for (auto& [fmt, mod] : PROTO::linuxDma->defaultFeedback->tranches) {
|
||||
if (resource->version() < 3) {
|
||||
if (mod == DRM_FORMAT_MOD_INVALID)
|
||||
resource->sendFormat(fmt);
|
||||
continue;
|
||||
for (auto& fmt : PROTO::linuxDma->formatTable->rendererTranche.formats) {
|
||||
for (auto& mod : fmt.modifiers) {
|
||||
if (resource->version() < 3) {
|
||||
if (mod == DRM_FORMAT_MOD_INVALID || mod == DRM_FORMAT_MOD_LINEAR)
|
||||
resource->sendFormat(fmt.drmFormat);
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
|
||||
|
||||
resource->sendModifier(fmt.drmFormat, mod >> 32, mod & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
// TODO: https://gitlab.freedesktop.org/xorg/xserver/-/issues/1166
|
||||
|
||||
resource->sendModifier(fmt, mod >> 32, mod & 0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
static auto P = g_pHookSystem->hookDynamic("ready", [this](void* self, SCallbackInfo& info, std::any d) {
|
||||
int rendererFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer);
|
||||
int rendererFD = g_pCompositor->m_iDRMFD;
|
||||
auto dev = devIDFromFD(rendererFD);
|
||||
|
||||
if (!dev.has_value()) {
|
||||
LOGM(ERR, "failed to get drm dev");
|
||||
PROTO::linuxDma.reset();
|
||||
protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
|
||||
removeGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
mainDevice = *dev;
|
||||
|
||||
auto fmts = g_pHyprOpenGL->getDRMFormats();
|
||||
|
||||
SDMABufTranche tranche = {
|
||||
.device = *dev,
|
||||
.formats = fmts,
|
||||
SDMABUFTranche eglTranche = {
|
||||
.device = mainDevice,
|
||||
.flags = 0, // renderer isnt for ds so dont set flag.
|
||||
.formats = g_pHyprOpenGL->getDRMFormats(),
|
||||
};
|
||||
|
||||
std::vector<SDMABufTranche> tches;
|
||||
tches.push_back(tranche);
|
||||
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tches;
|
||||
|
||||
defaultFeedback = std::make_unique<CCompiledDMABUFFeedback>(*dev, tches);
|
||||
if (g_pCompositor->m_pAqBackend->hasSession()) {
|
||||
// this assumes there's only 1 device used for both scanout and rendering
|
||||
// also that each monitor never changes its primary plane
|
||||
|
||||
for (auto& mon : g_pCompositor->m_vMonitors) {
|
||||
auto tranche = SDMABUFTranche{
|
||||
.device = mainDevice,
|
||||
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
|
||||
.formats = mon->output->getRenderFormats(),
|
||||
};
|
||||
tches.push_back(std::make_pair<>(mon, tranche));
|
||||
}
|
||||
|
||||
static auto monitorAdded = g_pHookSystem->hookDynamic("monitorAdded", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
auto pMonitor = std::any_cast<CMonitor*>(param);
|
||||
auto mon = pMonitor->self.lock();
|
||||
auto tranche = SDMABUFTranche{
|
||||
.device = mainDevice,
|
||||
.flags = ZWP_LINUX_DMABUF_FEEDBACK_V1_TRANCHE_FLAGS_SCANOUT,
|
||||
.formats = mon->output->getRenderFormats(),
|
||||
};
|
||||
formatTable->monitorTranches.push_back(std::make_pair<>(mon, tranche));
|
||||
resetFormatTable();
|
||||
});
|
||||
|
||||
static auto monitorRemoved = g_pHookSystem->hookDynamic("monitorRemoved", [this](void* self, SCallbackInfo& info, std::any param) {
|
||||
auto pMonitor = std::any_cast<CMonitor*>(param);
|
||||
auto mon = pMonitor->self.lock();
|
||||
std::erase_if(formatTable->monitorTranches, [mon](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == mon; });
|
||||
resetFormatTable();
|
||||
});
|
||||
}
|
||||
|
||||
formatTable = std::make_unique<CDMABUFFormatTable>(eglTranche, tches);
|
||||
|
||||
drmDevice* device = nullptr;
|
||||
if (drmGetDeviceFromDevId(mainDevice, 0, &device) != 0) {
|
||||
LOGM(ERR, "failed to get drm dev");
|
||||
PROTO::linuxDma.reset();
|
||||
protoLog(ERR, "failed to get drm dev, disabling linux dmabuf");
|
||||
removeGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -411,17 +487,51 @@ CLinuxDMABufV1Protocol::CLinuxDMABufV1Protocol(const wl_interface* iface, const
|
|||
mainDeviceFD = open(name, O_RDWR | O_CLOEXEC);
|
||||
drmFreeDevice(&device);
|
||||
if (mainDeviceFD < 0) {
|
||||
LOGM(ERR, "failed to open drm dev");
|
||||
PROTO::linuxDma.reset();
|
||||
protoLog(ERR, "failed to open drm dev, disabling linux dmabuf");
|
||||
removeGlobal();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOGM(ERR, "DRM device {} has no render node!!", device->nodes[DRM_NODE_PRIMARY]);
|
||||
protoLog(ERR, "DRM device {} has no render node, disabling linux dmabuf", device->nodes[DRM_NODE_PRIMARY] ? device->nodes[DRM_NODE_PRIMARY] : "null");
|
||||
drmFreeDevice(&device);
|
||||
removeGlobal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CLinuxDMABufV1Protocol::resetFormatTable() {
|
||||
if (!formatTable)
|
||||
return;
|
||||
|
||||
LOGM(LOG, "Resetting format table");
|
||||
|
||||
// this might be a big copy
|
||||
auto newFormatTable = std::make_unique<CDMABUFFormatTable>(formatTable->rendererTranche, formatTable->monitorTranches);
|
||||
|
||||
for (auto& feedback : m_vFeedbacks) {
|
||||
feedback->resource->sendFormatTable(newFormatTable->tableFD, newFormatTable->tableSize);
|
||||
if (feedback->lastFeedbackWasScanout) {
|
||||
SP<CMonitor> mon;
|
||||
auto HLSurface = CWLSurface::fromResource(feedback->surface);
|
||||
if (auto w = HLSurface->getWindow(); w)
|
||||
if (auto m = g_pCompositor->getMonitorFromID(w->m_iMonitorID); m)
|
||||
mon = m->self.lock();
|
||||
|
||||
if (!mon) {
|
||||
feedback->sendDefaultFeedback();
|
||||
return;
|
||||
}
|
||||
|
||||
updateScanoutTranche(feedback->surface, mon);
|
||||
} else {
|
||||
feedback->sendDefaultFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
// delete old table after we sent new one
|
||||
formatTable = std::move(newFormatTable);
|
||||
}
|
||||
|
||||
CLinuxDMABufV1Protocol::~CLinuxDMABufV1Protocol() {
|
||||
if (mainDeviceFD >= 0)
|
||||
close(mainDeviceFD);
|
||||
|
|
@ -452,3 +562,52 @@ void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABBUFParamsResource* resour
|
|||
void CLinuxDMABufV1Protocol::destroyResource(CLinuxDMABuffer* resource) {
|
||||
std::erase_if(m_vBuffers, [&](const auto& other) { return other.get() == resource; });
|
||||
}
|
||||
|
||||
void CLinuxDMABufV1Protocol::updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor) {
|
||||
SP<CLinuxDMABUFFeedbackResource> feedbackResource;
|
||||
for (auto& f : m_vFeedbacks) {
|
||||
if (f->surface != surface)
|
||||
continue;
|
||||
|
||||
feedbackResource = f;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!feedbackResource) {
|
||||
LOGM(LOG, "updateScanoutTranche: surface has no dmabuf_feedback");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pMonitor) {
|
||||
LOGM(LOG, "updateScanoutTranche: resetting feedback");
|
||||
feedbackResource->sendDefaultFeedback();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& monitorTranchePair = std::find_if(formatTable->monitorTranches.begin(), formatTable->monitorTranches.end(),
|
||||
[pMonitor](std::pair<SP<CMonitor>, SDMABUFTranche> pair) { return pair.first == pMonitor; });
|
||||
|
||||
if (monitorTranchePair == formatTable->monitorTranches.end()) {
|
||||
LOGM(LOG, "updateScanoutTranche: monitor has no tranche");
|
||||
return;
|
||||
}
|
||||
|
||||
auto& monitorTranche = (*monitorTranchePair).second;
|
||||
|
||||
LOGM(LOG, "updateScanoutTranche: sending a scanout tranche");
|
||||
|
||||
struct wl_array deviceArr = {
|
||||
.size = sizeof(mainDevice),
|
||||
.data = (void*)&mainDevice,
|
||||
};
|
||||
feedbackResource->resource->sendMainDevice(&deviceArr);
|
||||
|
||||
// prioritize scnaout tranche but have renderer fallback tranche
|
||||
// also yes formats can be duped here because different tranche flags (ds and no ds)
|
||||
feedbackResource->sendTranche(monitorTranche);
|
||||
feedbackResource->sendTranche(formatTable->rendererTranche);
|
||||
|
||||
feedbackResource->resource->sendDone();
|
||||
|
||||
feedbackResource->lastFeedbackWasScanout = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,15 +7,16 @@
|
|||
#include "wayland.hpp"
|
||||
#include "linux-dmabuf-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../helpers/Format.hpp"
|
||||
#include "../helpers/Monitor.hpp"
|
||||
#include <aquamarine/buffer/Buffer.hpp>
|
||||
|
||||
class CDMABuffer;
|
||||
struct SDRMFormat;
|
||||
struct SDMABUFAttrs;
|
||||
class CWLSurfaceResource;
|
||||
|
||||
class CLinuxDMABuffer {
|
||||
public:
|
||||
CLinuxDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs attrs);
|
||||
CLinuxDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs);
|
||||
~CLinuxDMABuffer();
|
||||
|
||||
bool good();
|
||||
|
|
@ -31,34 +32,29 @@ class CLinuxDMABuffer {
|
|||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct SDMABUFFeedbackTableEntry {
|
||||
struct SDMABUFFormatTableEntry {
|
||||
uint32_t fmt = 0;
|
||||
char pad[4];
|
||||
uint64_t modifier = 0;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class SCompiledDMABUFTranche {
|
||||
dev_t device = 0;
|
||||
uint32_t flags = 0;
|
||||
std::vector<uint16_t> indices;
|
||||
};
|
||||
|
||||
struct SDMABufTranche {
|
||||
struct SDMABUFTranche {
|
||||
dev_t device = 0;
|
||||
uint32_t flags = 0;
|
||||
std::vector<SDRMFormat> formats;
|
||||
std::vector<uint16_t> indicies;
|
||||
};
|
||||
|
||||
class CCompiledDMABUFFeedback {
|
||||
class CDMABUFFormatTable {
|
||||
public:
|
||||
CCompiledDMABUFFeedback(dev_t device, std::vector<SDMABufTranche> tranches);
|
||||
~CCompiledDMABUFFeedback();
|
||||
CDMABUFFormatTable(SDMABUFTranche rendererTranche, std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> tranches);
|
||||
~CDMABUFFormatTable();
|
||||
|
||||
dev_t mainDevice = 0;
|
||||
int tableFD = -1;
|
||||
size_t tableLen = 0;
|
||||
std::vector<std::pair<uint32_t, uint64_t>> tranches;
|
||||
int tableFD = -1;
|
||||
size_t tableSize = 0;
|
||||
SDMABUFTranche rendererTranche;
|
||||
std::vector<std::pair<SP<CMonitor>, SDMABUFTranche>> monitorTranches;
|
||||
};
|
||||
|
||||
class CLinuxDMABBUFParamsResource {
|
||||
|
|
@ -66,12 +62,12 @@ class CLinuxDMABBUFParamsResource {
|
|||
CLinuxDMABBUFParamsResource(SP<CZwpLinuxBufferParamsV1> resource_);
|
||||
~CLinuxDMABBUFParamsResource();
|
||||
|
||||
bool good();
|
||||
void create(uint32_t id); // 0 means not immed
|
||||
bool good();
|
||||
void create(uint32_t id); // 0 means not immed
|
||||
|
||||
SP<SDMABUFAttrs> attrs;
|
||||
WP<CLinuxDMABuffer> createdBuffer;
|
||||
bool used = false;
|
||||
SP<Aquamarine::SDMABUFAttrs> attrs;
|
||||
WP<CLinuxDMABuffer> createdBuffer;
|
||||
bool used = false;
|
||||
|
||||
private:
|
||||
SP<CZwpLinuxBufferParamsV1> resource;
|
||||
|
|
@ -86,11 +82,16 @@ class CLinuxDMABUFFeedbackResource {
|
|||
~CLinuxDMABUFFeedbackResource();
|
||||
|
||||
bool good();
|
||||
void sendDefaultFeedback();
|
||||
void sendTranche(SDMABUFTranche& tranche);
|
||||
|
||||
SP<CWLSurfaceResource> surface; // optional, for surface feedbacks
|
||||
|
||||
private:
|
||||
SP<CZwpLinuxDmabufFeedbackV1> resource;
|
||||
bool lastFeedbackWasScanout = false;
|
||||
|
||||
friend class CLinuxDMABufV1Protocol;
|
||||
};
|
||||
|
||||
class CLinuxDMABUFResource {
|
||||
|
|
@ -110,6 +111,7 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
|
|||
~CLinuxDMABufV1Protocol();
|
||||
|
||||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
void updateScanoutTranche(SP<CWLSurfaceResource> surface, SP<CMonitor> pMonitor);
|
||||
|
||||
private:
|
||||
void destroyResource(CLinuxDMABUFResource* resource);
|
||||
|
|
@ -117,13 +119,15 @@ class CLinuxDMABufV1Protocol : public IWaylandProtocol {
|
|||
void destroyResource(CLinuxDMABBUFParamsResource* resource);
|
||||
void destroyResource(CLinuxDMABuffer* resource);
|
||||
|
||||
void resetFormatTable();
|
||||
|
||||
//
|
||||
std::vector<SP<CLinuxDMABUFResource>> m_vManagers;
|
||||
std::vector<SP<CLinuxDMABUFFeedbackResource>> m_vFeedbacks;
|
||||
std::vector<SP<CLinuxDMABBUFParamsResource>> m_vParams;
|
||||
std::vector<SP<CLinuxDMABuffer>> m_vBuffers;
|
||||
|
||||
UP<CCompiledDMABUFFeedback> defaultFeedback;
|
||||
UP<CDMABUFFormatTable> formatTable;
|
||||
dev_t mainDevice;
|
||||
int mainDeviceFD = -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
#include <algorithm>
|
||||
#include <xf86drm.h>
|
||||
#include "../Compositor.hpp"
|
||||
#include <wlr/render/drm_format_set.h>
|
||||
#include "types/WLBuffer.hpp"
|
||||
|
||||
#define LOGM PROTO::mesaDRM->protoLog
|
||||
|
||||
CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs_) {
|
||||
CMesaDRMBufferResource::CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs_) {
|
||||
LOGM(LOG, "Creating a Mesa dmabuf, with id {}: size {}, fmt {}, planes {}", id, attrs_.size, attrs_.format, attrs_.planes);
|
||||
for (int i = 0; i < attrs_.planes; ++i) {
|
||||
LOGM(LOG, " | plane {}: mod {} fd {} stride {} offset {}", i, attrs_.modifier, attrs_.fds[i], attrs_.strides[i], attrs_.offsets[i]);
|
||||
|
|
@ -60,10 +59,27 @@ CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : resource(resource_) {
|
|||
return;
|
||||
}
|
||||
|
||||
SDMABUFAttrs attrs;
|
||||
uint64_t mod = DRM_FORMAT_MOD_INVALID;
|
||||
|
||||
auto fmts = g_pHyprOpenGL->getDRMFormats();
|
||||
for (auto& f : fmts) {
|
||||
if (f.drmFormat != fmt)
|
||||
continue;
|
||||
|
||||
for (auto& m : f.modifiers) {
|
||||
if (m == DRM_FORMAT_MOD_LINEAR)
|
||||
continue;
|
||||
|
||||
mod = m;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Aquamarine::SDMABUFAttrs attrs;
|
||||
attrs.success = true;
|
||||
attrs.size = {w, h};
|
||||
attrs.modifier = DRM_FORMAT_MOD_INVALID;
|
||||
attrs.modifier = mod;
|
||||
attrs.planes = 1;
|
||||
attrs.offsets[0] = off0;
|
||||
attrs.strides[0] = str0;
|
||||
|
|
@ -87,7 +103,7 @@ CMesaDRMResource::CMesaDRMResource(SP<CWlDrm> resource_) : resource(resource_) {
|
|||
|
||||
auto fmts = g_pHyprOpenGL->getDRMFormats();
|
||||
for (auto& fmt : fmts) {
|
||||
resource->sendFormat(fmt.format);
|
||||
resource->sendFormat(fmt.drmFormat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,10 +113,10 @@ bool CMesaDRMResource::good() {
|
|||
|
||||
CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
drmDevice* dev = nullptr;
|
||||
int drmFD = wlr_renderer_get_drm_fd(g_pCompositor->m_sWLRRenderer);
|
||||
int drmFD = g_pCompositor->m_iDRMFD;
|
||||
if (drmGetDevice2(drmFD, 0, &dev) != 0) {
|
||||
LOGM(ERR, "Failed to get device");
|
||||
PROTO::mesaDRM.reset();
|
||||
protoLog(ERR, "Failed to get device, disabling MesaDRM");
|
||||
removeGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +124,15 @@ CMesaDRMProtocol::CMesaDRMProtocol(const wl_interface* iface, const int& ver, co
|
|||
nodeName = dev->nodes[DRM_NODE_RENDER];
|
||||
} else {
|
||||
ASSERT(dev->available_nodes & (1 << DRM_NODE_PRIMARY));
|
||||
LOGM(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]);
|
||||
|
||||
if (!dev->nodes[DRM_NODE_PRIMARY]) {
|
||||
protoLog(ERR, "No DRM render node available, both render and primary are null, disabling MesaDRM");
|
||||
drmFreeDevice(&dev);
|
||||
removeGlobal();
|
||||
return;
|
||||
}
|
||||
|
||||
protoLog(WARN, "No DRM render node, falling back to primary {}", dev->nodes[DRM_NODE_PRIMARY]);
|
||||
nodeName = dev->nodes[DRM_NODE_PRIMARY];
|
||||
}
|
||||
drmFreeDevice(&dev);
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
class CMesaDRMBufferResource {
|
||||
public:
|
||||
CMesaDRMBufferResource(uint32_t id, wl_client* client, SDMABUFAttrs attrs);
|
||||
CMesaDRMBufferResource(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs attrs);
|
||||
~CMesaDRMBufferResource();
|
||||
|
||||
bool good();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include <algorithm>
|
||||
#include "../Compositor.hpp"
|
||||
|
||||
using namespace Aquamarine;
|
||||
|
||||
#define LOGM PROTO::outputManagement->protoLog
|
||||
|
||||
COutputManager::COutputManager(SP<CZwlrOutputManagerV1> resource_) : resource(resource_) {
|
||||
|
|
@ -123,8 +125,8 @@ void COutputHead::sendAllData() {
|
|||
|
||||
resource->sendName(pMonitor->szName.c_str());
|
||||
resource->sendDescription(pMonitor->szDescription.c_str());
|
||||
if (pMonitor->output->phys_width > 0 && pMonitor->output->phys_height > 0)
|
||||
resource->sendPhysicalSize(pMonitor->output->phys_width, pMonitor->output->phys_height);
|
||||
if (pMonitor->output->physicalSize.x > 0 && pMonitor->output->physicalSize.y > 0)
|
||||
resource->sendPhysicalSize(pMonitor->output->physicalSize.x, pMonitor->output->physicalSize.y);
|
||||
resource->sendEnabled(pMonitor->m_bEnabled);
|
||||
|
||||
if (pMonitor->m_bEnabled) {
|
||||
|
|
@ -133,12 +135,12 @@ void COutputHead::sendAllData() {
|
|||
resource->sendScale(wl_fixed_from_double(pMonitor->scale));
|
||||
}
|
||||
|
||||
if (pMonitor->output->make && VERSION >= 2)
|
||||
resource->sendMake(pMonitor->output->make);
|
||||
if (pMonitor->output->model && VERSION >= 2)
|
||||
resource->sendModel(pMonitor->output->model);
|
||||
if (pMonitor->output->serial && VERSION >= 2)
|
||||
resource->sendSerialNumber(pMonitor->output->serial);
|
||||
if (!pMonitor->output->make.empty() && VERSION >= 2)
|
||||
resource->sendMake(pMonitor->output->make.c_str());
|
||||
if (!pMonitor->output->model.empty() && VERSION >= 2)
|
||||
resource->sendModel(pMonitor->output->model.c_str());
|
||||
if (!pMonitor->output->serial.empty() && VERSION >= 2)
|
||||
resource->sendSerialNumber(pMonitor->output->serial.c_str());
|
||||
|
||||
if (VERSION >= 4)
|
||||
resource->sendAdaptiveSync(pMonitor->vrrActive ? ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_ENABLED : ZWLR_OUTPUT_HEAD_V1_ADAPTIVE_SYNC_STATE_DISABLED);
|
||||
|
|
@ -146,12 +148,12 @@ void COutputHead::sendAllData() {
|
|||
// send all available modes
|
||||
|
||||
if (modes.empty()) {
|
||||
if (!wl_list_empty(&pMonitor->output->modes)) {
|
||||
wlr_output_mode* mode;
|
||||
|
||||
wl_list_for_each(mode, &pMonitor->output->modes, link) {
|
||||
makeAndSendNewMode(mode);
|
||||
if (!pMonitor->output->modes.empty()) {
|
||||
for (auto& m : pMonitor->output->modes) {
|
||||
makeAndSendNewMode(m);
|
||||
}
|
||||
} else if (pMonitor->output->state->state().customMode) {
|
||||
makeAndSendNewMode(pMonitor->output->state->state().customMode);
|
||||
} else
|
||||
makeAndSendNewMode(nullptr);
|
||||
}
|
||||
|
|
@ -164,9 +166,9 @@ void COutputHead::sendAllData() {
|
|||
if (!m)
|
||||
continue;
|
||||
|
||||
if (m->mode == pMonitor->currentMode) {
|
||||
if (m->mode == pMonitor->output->state->state().mode) {
|
||||
if (m->mode)
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate);
|
||||
else
|
||||
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
|
||||
resource->sendCurrentMode(m->resource.get());
|
||||
|
|
@ -197,7 +199,7 @@ void COutputHead::updateMode() {
|
|||
|
||||
if (m->mode == pMonitor->currentMode) {
|
||||
if (m->mode)
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->width, m->mode->height, m->mode->refresh);
|
||||
LOGM(LOG, " | sending current mode for {}: {}x{}@{}", pMonitor->szName, m->mode->pixelSize.x, m->mode->pixelSize.y, m->mode->refreshRate);
|
||||
else
|
||||
LOGM(LOG, " | sending current mode for {}: null (fake)", pMonitor->szName);
|
||||
resource->sendCurrentMode(m->resource.get());
|
||||
|
|
@ -207,7 +209,7 @@ void COutputHead::updateMode() {
|
|||
}
|
||||
}
|
||||
|
||||
void COutputHead::makeAndSendNewMode(wlr_output_mode* mode) {
|
||||
void COutputHead::makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode) {
|
||||
const auto RESOURCE = PROTO::outputManagement->m_vModes.emplace_back(makeShared<COutputMode>(makeShared<CZwlrOutputModeV1>(resource->client(), resource->version(), 0), mode));
|
||||
|
||||
if (!RESOURCE->good()) {
|
||||
|
|
@ -225,7 +227,7 @@ CMonitor* COutputHead::monitor() {
|
|||
return pMonitor;
|
||||
}
|
||||
|
||||
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_) : resource(resource_), mode(mode_) {
|
||||
COutputMode::COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_) : resource(resource_), mode(mode_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
||||
|
|
@ -237,11 +239,11 @@ void COutputMode::sendAllData() {
|
|||
if (!mode)
|
||||
return;
|
||||
|
||||
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->width, mode->height, mode->refresh, mode->preferred);
|
||||
LOGM(LOG, " | sending mode {}x{}@{}mHz, pref: {}", mode->pixelSize.x, mode->pixelSize.y, mode->refreshRate, mode->preferred);
|
||||
|
||||
resource->sendSize(mode->width, mode->height);
|
||||
if (mode->refresh > 0)
|
||||
resource->sendRefresh(mode->refresh);
|
||||
resource->sendSize(mode->pixelSize.x, mode->pixelSize.y);
|
||||
if (mode->refreshRate > 0)
|
||||
resource->sendRefresh(mode->refreshRate);
|
||||
if (mode->preferred)
|
||||
resource->sendPreferred();
|
||||
}
|
||||
|
|
@ -250,8 +252,8 @@ bool COutputMode::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
wlr_output_mode* COutputMode::getMode() {
|
||||
return mode;
|
||||
SP<Aquamarine::SOutputMode> COutputMode::getMode() {
|
||||
return mode.lock();
|
||||
}
|
||||
|
||||
COutputConfiguration::COutputConfiguration(SP<CZwlrOutputConfigurationV1> resource_, SP<COutputManager> owner_) : resource(resource_), owner(owner_) {
|
||||
|
|
@ -364,8 +366,8 @@ bool COutputConfiguration::applyTestConfiguration(bool test) {
|
|||
newRule.disabled = false;
|
||||
|
||||
if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_MODE) {
|
||||
newRule.resolution = {head->state.mode->getMode()->width, head->state.mode->getMode()->height};
|
||||
newRule.refreshRate = head->state.mode->getMode()->refresh / 1000.F;
|
||||
newRule.resolution = head->state.mode->getMode()->pixelSize;
|
||||
newRule.refreshRate = head->state.mode->getMode()->refreshRate / 1000.F;
|
||||
} else if (head->committedProperties & COutputConfigurationHead::eCommittedProperties::OUTPUT_HEAD_COMMITTED_CUSTOM_MODE) {
|
||||
newRule.resolution = head->state.customMode.size;
|
||||
newRule.refreshRate = head->state.customMode.refresh / 1000.F;
|
||||
|
|
@ -425,7 +427,7 @@ COutputConfigurationHead::COutputConfigurationHead(SP<CZwlrOutputConfigurationHe
|
|||
committedProperties |= OUTPUT_HEAD_COMMITTED_MODE;
|
||||
state.mode = MODE;
|
||||
|
||||
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->width, MODE->getMode()->height, MODE->getMode()->refresh);
|
||||
LOGM(LOG, " | configHead for {}: set mode to {}x{}@{}", pMonitor->szName, MODE->getMode()->pixelSize.x, MODE->getMode()->pixelSize.y, MODE->getMode()->refreshRate);
|
||||
});
|
||||
|
||||
resource->setSetCustomMode([this](CZwlrOutputConfigurationHeadV1* r, int32_t w, int32_t h, int32_t refresh) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "WaylandProtocol.hpp"
|
||||
#include "wlr-output-management-unstable-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
|
||||
class CMonitor;
|
||||
|
||||
|
|
@ -34,15 +35,15 @@ class COutputManager {
|
|||
|
||||
class COutputMode {
|
||||
public:
|
||||
COutputMode(SP<CZwlrOutputModeV1> resource_, wlr_output_mode* mode_);
|
||||
COutputMode(SP<CZwlrOutputModeV1> resource_, SP<Aquamarine::SOutputMode> mode_);
|
||||
|
||||
bool good();
|
||||
wlr_output_mode* getMode();
|
||||
void sendAllData();
|
||||
bool good();
|
||||
SP<Aquamarine::SOutputMode> getMode();
|
||||
void sendAllData();
|
||||
|
||||
private:
|
||||
SP<CZwlrOutputModeV1> resource;
|
||||
wlr_output_mode* mode = nullptr;
|
||||
SP<CZwlrOutputModeV1> resource;
|
||||
WP<Aquamarine::SOutputMode> mode;
|
||||
|
||||
friend class COutputHead;
|
||||
friend class COutputManagementProtocol;
|
||||
|
|
@ -61,7 +62,7 @@ class COutputHead {
|
|||
SP<CZwlrOutputHeadV1> resource;
|
||||
CMonitor* pMonitor = nullptr;
|
||||
|
||||
void makeAndSendNewMode(wlr_output_mode* mode);
|
||||
void makeAndSendNewMode(SP<Aquamarine::SOutputMode> mode);
|
||||
void sendCurrentMode();
|
||||
|
||||
std::vector<WP<COutputMode>> modes;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ COutputPower::COutputPower(SP<CZwlrOutputPowerV1> resource_, CMonitor* pMonitor_
|
|||
|
||||
pMonitor->dpmsStatus = mode == ZWLR_OUTPUT_POWER_V1_MODE_ON;
|
||||
|
||||
wlr_output_state_set_enabled(pMonitor->state.wlr(), pMonitor->dpmsStatus);
|
||||
pMonitor->output->state->setEnabled(mode == ZWLR_OUTPUT_POWER_V1_MODE_ON);
|
||||
|
||||
if (!pMonitor->state.commit())
|
||||
LOGM(ERR, "Couldn't set dpms to {} for {}", pMonitor->dpmsStatus, pMonitor->szName);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
#include "../helpers/Monitor.hpp"
|
||||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
#include "core/Output.hpp"
|
||||
#include <aquamarine/output/Output.hpp>
|
||||
|
||||
#define LOGM PROTO::presentation->protoLog
|
||||
|
||||
|
|
@ -41,13 +43,11 @@ bool CPresentationFeedback::good() {
|
|||
}
|
||||
|
||||
void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespec* when, uint32_t untilRefreshNs, uint64_t seq, uint32_t reportedFlags) {
|
||||
auto client = resource->client();
|
||||
wl_resource* res;
|
||||
wl_resource_for_each(res, &data->pMonitor->output->resources) {
|
||||
if (client == wl_resource_get_client(res)) {
|
||||
resource->sendSyncOutput(res);
|
||||
break;
|
||||
}
|
||||
auto client = resource->client();
|
||||
|
||||
if (PROTO::outputs.contains(data->pMonitor->szName)) {
|
||||
if (auto outputResource = PROTO::outputs.at(data->pMonitor->szName)->outputResourceFrom(client); outputResource)
|
||||
resource->sendSyncOutput(outputResource->getResource()->resource());
|
||||
}
|
||||
|
||||
uint32_t flags = 0;
|
||||
|
|
@ -55,9 +55,9 @@ void CPresentationFeedback::sendQueued(SP<CQueuedPresentationData> data, timespe
|
|||
flags |= WP_PRESENTATION_FEEDBACK_KIND_VSYNC;
|
||||
if (data->zeroCopy)
|
||||
flags |= WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY;
|
||||
if (reportedFlags & WLR_OUTPUT_PRESENT_HW_CLOCK)
|
||||
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_CLOCK)
|
||||
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK;
|
||||
if (reportedFlags & WLR_OUTPUT_PRESENT_HW_COMPLETION)
|
||||
if (reportedFlags & Aquamarine::IOutput::AQ_OUTPUT_PRESENT_HW_COMPLETION)
|
||||
flags |= WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION;
|
||||
|
||||
if (data->wasPresented && when)
|
||||
|
|
|
|||
|
|
@ -266,20 +266,17 @@ void CScreencopyProtocolManager::captureOutput(wl_client* client, wl_resource* r
|
|||
return;
|
||||
}
|
||||
|
||||
if (PFRAME->pMonitor->output->allocator && (PFRAME->pMonitor->output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
||||
PFRAME->dmabufFormat = PFRAME->pMonitor->output->render_format;
|
||||
} else {
|
||||
PFRAME->dmabufFormat = DRM_FORMAT_INVALID;
|
||||
}
|
||||
PFRAME->dmabufFormat = PFRAME->pMonitor->output->state->state().drmFormat;
|
||||
|
||||
if (box.width == 0 && box.height == 0)
|
||||
PFRAME->box = {0, 0, (int)(PFRAME->pMonitor->vecSize.x), (int)(PFRAME->pMonitor->vecSize.y)};
|
||||
else {
|
||||
PFRAME->box = box;
|
||||
}
|
||||
int ow, oh;
|
||||
wlr_output_effective_resolution(PFRAME->pMonitor->output, &ow, &oh);
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PFRAME->pMonitor->transform), ow, oh).scale(PFRAME->pMonitor->scale).round();
|
||||
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PFRAME->pMonitor->transform), PFRAME->pMonitor->vecTransformedSize.x, PFRAME->pMonitor->vecTransformedSize.y)
|
||||
.scale(PFRAME->pMonitor->scale)
|
||||
.round();
|
||||
|
||||
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
|
||||
|
||||
|
|
@ -383,10 +380,10 @@ void CScreencopyProtocolManager::copyFrame(wl_client* client, wl_resource* resou
|
|||
g_pHyprRenderer->damageMonitor(PFRAME->pMonitor);
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e) {
|
||||
m_pLastMonitorBackBuffer = e->state->buffer;
|
||||
void CScreencopyProtocolManager::onOutputCommit(CMonitor* pMonitor) {
|
||||
m_pLastMonitorBackBuffer = pMonitor->output->state->state().buffer;
|
||||
shareAllFrames(pMonitor);
|
||||
m_pLastMonitorBackBuffer = nullptr;
|
||||
m_pLastMonitorBackBuffer.reset();
|
||||
}
|
||||
|
||||
void CScreencopyProtocolManager::shareAllFrames(CMonitor* pMonitor) {
|
||||
|
|
@ -473,11 +470,7 @@ void CScreencopyProtocolManager::sendFrameDamage(SScreencopyFrame* frame) {
|
|||
}
|
||||
|
||||
bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec* now) {
|
||||
wlr_texture* sourceTex = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, m_pLastMonitorBackBuffer);
|
||||
if (!sourceTex)
|
||||
return false;
|
||||
|
||||
auto TEXTURE = makeShared<CTexture>(sourceTex);
|
||||
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
|
||||
|
||||
auto shm = frame->buffer->shm();
|
||||
auto [pixelData, fmt, bufLen] = frame->buffer->beginDataPtr(0); // no need for end, cuz it's shm
|
||||
|
|
@ -487,10 +480,10 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
|
|||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
CFramebuffer fb;
|
||||
fb.alloc(frame->box.w, frame->box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : frame->pMonitor->drmFormat);
|
||||
fb.alloc(frame->box.w, frame->box.h, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : frame->pMonitor->output->state->state().drmFormat);
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_FULL_FAKE, nullptr, &fb, true)) {
|
||||
wlr_texture_destroy(sourceTex);
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to begin rendering");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -509,8 +502,8 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
|
|||
|
||||
const auto PFORMAT = FormatUtils::getPixelFormatFromDRM(shm.format);
|
||||
if (!PFORMAT) {
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to find a pixel format");
|
||||
g_pHyprRenderer->endRender();
|
||||
wlr_texture_destroy(sourceTex);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -539,27 +532,24 @@ bool CScreencopyProtocolManager::copyFrameShm(SScreencopyFrame* frame, timespec*
|
|||
|
||||
g_pHyprOpenGL->m_RenderData.pMonitor = nullptr;
|
||||
|
||||
wlr_texture_destroy(sourceTex);
|
||||
Debug::log(TRACE, "Screencopy: copied frame via shm");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
|
||||
wlr_texture* sourceTex = wlr_texture_from_buffer(g_pCompositor->m_sWLRRenderer, m_pLastMonitorBackBuffer);
|
||||
if (!sourceTex)
|
||||
return false;
|
||||
|
||||
auto TEXTURE = makeShared<CTexture>(sourceTex);
|
||||
auto TEXTURE = makeShared<CTexture>(m_pLastMonitorBackBuffer);
|
||||
|
||||
CRegion fakeDamage = {0, 0, INT16_MAX, INT16_MAX};
|
||||
|
||||
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock(), nullptr, true))
|
||||
if (!g_pHyprRenderer->beginRender(frame->pMonitor, fakeDamage, RENDER_MODE_TO_BUFFER, frame->buffer.lock(), nullptr, true)) {
|
||||
Debug::log(ERR, "Screencopy: can't copy: failed to begin rendering to dma frame");
|
||||
return false;
|
||||
}
|
||||
|
||||
CBox monbox =
|
||||
CBox{0, 0, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y}
|
||||
.translate({-frame->box.x, -frame->box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
|
||||
.transform(wlTransformToHyprutils(wlr_output_transform_invert(frame->pMonitor->output->transform)), frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y);
|
||||
CBox monbox = CBox{0, 0, frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y}
|
||||
.translate({-frame->box.x, -frame->box.y}) // vvvv kinda ass-backwards but that's how I designed the renderer... sigh.
|
||||
.transform(wlTransformToHyprutils(invertTransform(frame->pMonitor->transform)), frame->pMonitor->vecPixelSize.x, frame->pMonitor->vecPixelSize.y);
|
||||
g_pHyprOpenGL->setMonitorTransformEnabled(true);
|
||||
g_pHyprOpenGL->setRenderModifEnabled(false);
|
||||
g_pHyprOpenGL->renderTexture(TEXTURE, &monbox, 1);
|
||||
|
|
@ -569,7 +559,7 @@ bool CScreencopyProtocolManager::copyFrameDmabuf(SScreencopyFrame* frame) {
|
|||
g_pHyprOpenGL->m_RenderData.blockScreenShader = true;
|
||||
g_pHyprRenderer->endRender();
|
||||
|
||||
wlr_texture_destroy(sourceTex);
|
||||
Debug::log(TRACE, "Screencopy: copied frame via dma");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@
|
|||
#include "../managers/HookSystemManager.hpp"
|
||||
#include "../helpers/Timer.hpp"
|
||||
#include "../managers/eventLoop/EventLoopTimer.hpp"
|
||||
#include <aquamarine/buffer/Buffer.hpp>
|
||||
|
||||
class CMonitor;
|
||||
class IWLBuffer;
|
||||
class IHLBuffer;
|
||||
|
||||
enum eClientOwners {
|
||||
CLIENT_SCREENCOPY = 0,
|
||||
|
|
@ -56,7 +57,7 @@ struct SScreencopyFrame {
|
|||
|
||||
bool bufferDMA = false;
|
||||
|
||||
WP<IWLBuffer> buffer;
|
||||
WP<IHLBuffer> buffer;
|
||||
|
||||
CMonitor* pMonitor = nullptr;
|
||||
PHLWINDOWREF pWindow;
|
||||
|
|
@ -79,7 +80,7 @@ class CScreencopyProtocolManager {
|
|||
|
||||
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer);
|
||||
|
||||
void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e);
|
||||
void onOutputCommit(CMonitor* pMonitor);
|
||||
|
||||
private:
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
|
|
@ -93,7 +94,7 @@ class CScreencopyProtocolManager {
|
|||
|
||||
std::vector<SScreencopyFrame*> m_vFramesAwaitingWrite;
|
||||
|
||||
wlr_buffer* m_pLastMonitorBackBuffer = nullptr;
|
||||
SP<Aquamarine::IBuffer> m_pLastMonitorBackBuffer;
|
||||
|
||||
void shareAllFrames(CMonitor* pMonitor);
|
||||
void shareFrame(SScreencopyFrame* frame);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "core/Seat.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#define LOGM PROTO::tablet->protoLog
|
||||
|
||||
|
|
@ -44,17 +45,17 @@ bool CTabletPadGroupV2Resource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
void CTabletPadGroupV2Resource::sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group) {
|
||||
resource->sendModes(group->mode_count);
|
||||
void CTabletPadGroupV2Resource::sendData(SP<CTabletPad> pad, SP<Aquamarine::ITabletPad::STabletPadGroup> group) {
|
||||
resource->sendModes(group->modes);
|
||||
|
||||
wl_array buttonArr;
|
||||
wl_array_init(&buttonArr);
|
||||
wl_array_add(&buttonArr, group->button_count * sizeof(int));
|
||||
memcpy(buttonArr.data, group->buttons, group->button_count * sizeof(int));
|
||||
wl_array_add(&buttonArr, group->buttons.size() * sizeof(int));
|
||||
memcpy(buttonArr.data, group->buttons.data(), group->buttons.size() * sizeof(int));
|
||||
resource->sendButtons(&buttonArr);
|
||||
wl_array_release(&buttonArr);
|
||||
|
||||
for (size_t i = 0; i < group->strip_count; ++i) {
|
||||
for (size_t i = 0; i < group->strips.size(); ++i) {
|
||||
const auto RESOURCE =
|
||||
PROTO::tablet->m_vStrips.emplace_back(makeShared<CTabletPadStripV2Resource>(makeShared<CZwpTabletPadStripV2>(resource->client(), resource->version(), 0), i));
|
||||
|
||||
|
|
@ -67,7 +68,7 @@ void CTabletPadGroupV2Resource::sendData(SP<CTabletPad> pad, wlr_tablet_pad_grou
|
|||
resource->sendStrip(RESOURCE->resource.get());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < group->ring_count; ++i) {
|
||||
for (size_t i = 0; i < group->rings.size(); ++i) {
|
||||
const auto RESOURCE =
|
||||
PROTO::tablet->m_vRings.emplace_back(makeShared<CTabletPadRingV2Resource>(makeShared<CZwpTabletPadRingV2>(resource->client(), resource->version(), 0), i));
|
||||
|
||||
|
|
@ -97,23 +98,20 @@ bool CTabletPadV2Resource::good() {
|
|||
|
||||
void CTabletPadV2Resource::sendData() {
|
||||
// this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
|
||||
const char** path_ptr;
|
||||
for (path_ptr = (const char**)(&pad->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&pad->wlr()->paths)->data + (&pad->wlr()->paths)->size); (path_ptr)++) {
|
||||
resource->sendPath(*path_ptr);
|
||||
for (auto& p : pad->aq()->paths) {
|
||||
resource->sendPath(p.c_str());
|
||||
}
|
||||
|
||||
resource->sendButtons(pad->wlr()->button_count);
|
||||
resource->sendButtons(pad->aq()->buttons);
|
||||
|
||||
wlr_tablet_pad_group* group;
|
||||
size_t i = 0;
|
||||
wl_list_for_each(group, &pad->wlr()->groups, link) {
|
||||
createGroup(group, i++);
|
||||
for (size_t i = 0; i < pad->aq()->groups.size(); ++i) {
|
||||
createGroup(pad->aq()->groups.at(i), i);
|
||||
}
|
||||
|
||||
resource->sendDone();
|
||||
}
|
||||
|
||||
void CTabletPadV2Resource::createGroup(wlr_tablet_pad_group* group, size_t idx) {
|
||||
void CTabletPadV2Resource::createGroup(SP<Aquamarine::ITabletPad::STabletPadGroup> group, size_t idx) {
|
||||
const auto RESOURCE =
|
||||
PROTO::tablet->m_vGroups.emplace_back(makeShared<CTabletPadGroupV2Resource>(makeShared<CZwpTabletPadGroupV2>(resource->client(), resource->version(), 0), idx));
|
||||
|
||||
|
|
@ -142,13 +140,10 @@ bool CTabletV2Resource::good() {
|
|||
|
||||
void CTabletV2Resource::sendData() {
|
||||
resource->sendName(tablet->deviceName.c_str());
|
||||
resource->sendId(tablet->wlr()->usb_vendor_id, tablet->wlr()->usb_product_id);
|
||||
resource->sendId(tablet->aq()->usbVendorID, tablet->aq()->usbProductID);
|
||||
|
||||
// this is dodgy as fuck. I hate wl_array. it's expanded wl_array_for_each because C++ would complain about the implicit casts
|
||||
const char** path_ptr;
|
||||
for (path_ptr = (const char**)(&tablet->wlr()->paths)->data; (const char*)path_ptr < ((const char*)(&tablet->wlr()->paths)->data + (&tablet->wlr()->paths)->size);
|
||||
(path_ptr)++) {
|
||||
resource->sendPath(*path_ptr);
|
||||
for (auto& p : tablet->aq()->paths) {
|
||||
resource->sendPath(p.c_str());
|
||||
}
|
||||
|
||||
resource->sendDone();
|
||||
|
|
@ -179,23 +174,23 @@ bool CTabletToolV2Resource::good() {
|
|||
}
|
||||
|
||||
void CTabletToolV2Resource::sendData() {
|
||||
static auto WLR_TYPE_TO_PROTO = [](uint32_t wlr) -> zwpTabletToolV2Type {
|
||||
switch (wlr) {
|
||||
case WLR_TABLET_TOOL_TYPE_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN;
|
||||
case WLR_TABLET_TOOL_TYPE_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER;
|
||||
case WLR_TABLET_TOOL_TYPE_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH;
|
||||
case WLR_TABLET_TOOL_TYPE_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL;
|
||||
case WLR_TABLET_TOOL_TYPE_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH;
|
||||
case WLR_TABLET_TOOL_TYPE_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE;
|
||||
case WLR_TABLET_TOOL_TYPE_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS;
|
||||
static auto AQ_TYPE_TO_PROTO = [](uint32_t aq) -> zwpTabletToolV2Type {
|
||||
switch (aq) {
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE;
|
||||
case Aquamarine::ITabletTool::AQ_TABLET_TOOL_TYPE_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS;
|
||||
default: ASSERT(false);
|
||||
}
|
||||
UNREACHABLE();
|
||||
};
|
||||
|
||||
resource->sendType(WLR_TYPE_TO_PROTO(tool->wlr()->type));
|
||||
resource->sendHardwareSerial(tool->wlr()->hardware_serial >> 32, tool->wlr()->hardware_serial & 0xFFFFFFFF);
|
||||
resource->sendHardwareIdWacom(tool->wlr()->hardware_wacom >> 32, tool->wlr()->hardware_wacom & 0xFFFFFFFF);
|
||||
resource->sendType(AQ_TYPE_TO_PROTO(tool->aq()->type));
|
||||
resource->sendHardwareSerial(tool->aq()->serial >> 32, tool->aq()->serial & 0xFFFFFFFF);
|
||||
resource->sendHardwareIdWacom(tool->aq()->id >> 32, tool->aq()->id & 0xFFFFFFFF);
|
||||
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_DISTANCE)
|
||||
resource->sendCapability(zwpTabletToolV2Capability::ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE);
|
||||
if (tool->toolCapabilities & CTabletTool::eTabletToolCapabilities::HID_TABLET_TOOL_CAPABILITY_PRESSURE)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "WaylandProtocol.hpp"
|
||||
#include "tablet-v2.hpp"
|
||||
#include "../helpers/math/Math.hpp"
|
||||
#include <aquamarine/input/Input.hpp>
|
||||
|
||||
class CTablet;
|
||||
class CTabletTool;
|
||||
|
|
@ -51,7 +52,7 @@ class CTabletPadGroupV2Resource {
|
|||
CTabletPadGroupV2Resource(SP<CZwpTabletPadGroupV2> resource_, size_t idx);
|
||||
|
||||
bool good();
|
||||
void sendData(SP<CTabletPad> pad, wlr_tablet_pad_group* group);
|
||||
void sendData(SP<CTabletPad> pad, SP<Aquamarine::ITabletPad::STabletPadGroup> group);
|
||||
|
||||
std::vector<WP<CTabletPadRingV2Resource>> rings;
|
||||
std::vector<WP<CTabletPadStripV2Resource>> strips;
|
||||
|
|
@ -83,7 +84,7 @@ class CTabletPadV2Resource {
|
|||
private:
|
||||
SP<CZwpTabletPadV2> resource;
|
||||
|
||||
void createGroup(wlr_tablet_pad_group* group, size_t idx);
|
||||
void createGroup(SP<Aquamarine::ITabletPad::STabletPadGroup> group, size_t idx);
|
||||
|
||||
friend class CTabletSeat;
|
||||
friend class CTabletV2Protocol;
|
||||
|
|
|
|||
|
|
@ -193,16 +193,11 @@ void CToplevelExportProtocolManager::captureToplevel(wl_client* client, wl_resou
|
|||
return;
|
||||
}
|
||||
|
||||
if (PMONITOR->output->allocator && (PMONITOR->output->allocator->buffer_caps & WLR_BUFFER_CAP_DMABUF)) {
|
||||
PFRAME->dmabufFormat = PMONITOR->output->render_format;
|
||||
} else {
|
||||
PFRAME->dmabufFormat = DRM_FORMAT_INVALID;
|
||||
}
|
||||
PFRAME->dmabufFormat = PMONITOR->output->state->state().drmFormat;
|
||||
|
||||
PFRAME->box = {0, 0, (int)(pWindow->m_vRealSize.value().x * PMONITOR->scale), (int)(pWindow->m_vRealSize.value().y * PMONITOR->scale)};
|
||||
int ow, oh;
|
||||
wlr_output_effective_resolution(PMONITOR->output, &ow, &oh);
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PMONITOR->transform), ow, oh).round();
|
||||
|
||||
PFRAME->box.transform(wlTransformToHyprutils(PMONITOR->transform), PMONITOR->vecTransformedSize.x, PMONITOR->vecTransformedSize.y).round();
|
||||
|
||||
PFRAME->shmStride = FormatUtils::minStride(PSHMINFO, PFRAME->box.w);
|
||||
|
||||
|
|
@ -289,12 +284,10 @@ void CToplevelExportProtocolManager::copyFrame(wl_client* client, wl_resource* r
|
|||
m_vFramesAwaitingWrite.emplace_back(PFRAME);
|
||||
}
|
||||
|
||||
void CToplevelExportProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e) {
|
||||
void CToplevelExportProtocolManager::onOutputCommit(CMonitor* pMonitor) {
|
||||
if (m_vFramesAwaitingWrite.empty())
|
||||
return; // nothing to share
|
||||
|
||||
const auto PMONITOR = g_pCompositor->getMonitorFromOutput(e->output);
|
||||
|
||||
std::vector<SScreencopyFrame*> framesToRemove;
|
||||
|
||||
// share frame if correct output
|
||||
|
|
@ -306,7 +299,7 @@ void CToplevelExportProtocolManager::onOutputCommit(CMonitor* pMonitor, wlr_outp
|
|||
continue;
|
||||
}
|
||||
|
||||
if (PMONITOR != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
|
||||
if (pMonitor != g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID))
|
||||
continue;
|
||||
|
||||
CBox geometry = {PWINDOW->m_vRealPosition.value().x, PWINDOW->m_vRealPosition.value().y, PWINDOW->m_vRealSize.value().x, PWINDOW->m_vRealSize.value().y};
|
||||
|
|
@ -370,7 +363,7 @@ bool CToplevelExportProtocolManager::copyFrameShm(SScreencopyFrame* frame, times
|
|||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
CFramebuffer outFB;
|
||||
outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : PMONITOR->drmFormat);
|
||||
outFB.alloc(PMONITOR->vecPixelSize.x, PMONITOR->vecPixelSize.y, g_pHyprRenderer->isNvidia() ? DRM_FORMAT_XBGR8888 : PMONITOR->output->state->state().drmFormat);
|
||||
|
||||
if (frame->overlayCursor) {
|
||||
g_pPointerManager->lockSoftwareForMonitor(PMONITOR->self.lock());
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class CToplevelExportProtocolManager {
|
|||
void copyFrame(wl_client* client, wl_resource* resource, wl_resource* buffer, int32_t ignore_damage);
|
||||
void displayDestroy();
|
||||
void onWindowUnmap(PHLWINDOW pWindow);
|
||||
void onOutputCommit(CMonitor* pMonitor, wlr_output_event_commit* e);
|
||||
void onOutputCommit(CMonitor* pMonitor);
|
||||
|
||||
private:
|
||||
wl_global* m_pGlobal = nullptr;
|
||||
|
|
|
|||
|
|
@ -52,6 +52,21 @@ CViewportResource::CViewportResource(SP<CWpViewport> resource_, SP<CWLSurfaceRes
|
|||
surface->pending.viewport.hasSource = true;
|
||||
surface->pending.viewport.source = {x, y, w, h};
|
||||
});
|
||||
|
||||
listeners.surfacePrecommit = surface->events.precommit.registerListener([this](std::any d) {
|
||||
if (!surface || !surface->pending.buffer)
|
||||
return;
|
||||
|
||||
if (surface->pending.viewport.hasSource) {
|
||||
auto& src = surface->pending.viewport.source;
|
||||
|
||||
if (src.w + src.x > surface->pending.buffer->size.x || src.h + src.y > surface->pending.buffer->size.y) {
|
||||
resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit");
|
||||
surface->pending.rejected = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
CViewportResource::~CViewportResource() {
|
||||
|
|
@ -66,20 +81,6 @@ bool CViewportResource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
void CViewportResource::verify() {
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
if (surface->pending.viewport.hasSource) {
|
||||
auto& src = surface->pending.viewport.source;
|
||||
|
||||
if (src.w + src.x > surface->pending.size.x || src.h + src.y > surface->pending.size.y) {
|
||||
resource->error(WP_VIEWPORT_ERROR_BAD_VALUE, "Box doesn't fit");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CViewporterResource::CViewporterResource(SP<CWpViewporter> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -15,11 +15,14 @@ class CViewportResource {
|
|||
~CViewportResource();
|
||||
|
||||
bool good();
|
||||
void verify();
|
||||
WP<CWLSurfaceResource> surface;
|
||||
|
||||
private:
|
||||
SP<CWpViewport> resource;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener surfacePrecommit;
|
||||
} listeners;
|
||||
};
|
||||
|
||||
class CViewporterResource {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
#include "VirtualKeyboard.hpp"
|
||||
#include <sys/mman.h>
|
||||
#include "../devices/IKeyboard.hpp"
|
||||
|
||||
#define LOGM PROTO::virtualKeyboard->protoLog
|
||||
|
||||
static const struct wlr_keyboard_impl virtualKeyboardImpl = {
|
||||
.name = "virtual-keyboard",
|
||||
};
|
||||
|
||||
CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
|
@ -28,13 +25,17 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
|
|||
return;
|
||||
}
|
||||
|
||||
wlr_keyboard_key_event event = {
|
||||
.time_msec = timeMs,
|
||||
.keycode = key,
|
||||
.update_state = false,
|
||||
.state = (wl_keyboard_key_state)state,
|
||||
};
|
||||
wlr_keyboard_notify_key(&keyboard, &event);
|
||||
events.key.emit(IKeyboard::SKeyEvent{
|
||||
.timeMs = timeMs,
|
||||
.keycode = key,
|
||||
.state = (wl_keyboard_key_state)state,
|
||||
});
|
||||
|
||||
const bool CONTAINS = std::find(pressed.begin(), pressed.end(), key) != pressed.end();
|
||||
if (state && !CONTAINS)
|
||||
pressed.emplace_back(key);
|
||||
else if (!state && CONTAINS)
|
||||
std::erase(pressed, key);
|
||||
});
|
||||
|
||||
resource->setModifiers([this](CZwpVirtualKeyboardV1* r, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
|
||||
|
|
@ -43,7 +44,12 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
|
|||
return;
|
||||
}
|
||||
|
||||
wlr_keyboard_notify_modifiers(&keyboard, depressed, latched, locked, group);
|
||||
events.modifiers.emit(IKeyboard::SModifiersEvent{
|
||||
.depressed = depressed,
|
||||
.latched = latched,
|
||||
.locked = locked,
|
||||
.group = group,
|
||||
});
|
||||
});
|
||||
|
||||
resource->setKeymap([this](CZwpVirtualKeyboardV1* r, uint32_t fmt, int32_t fd, uint32_t len) {
|
||||
|
|
@ -75,7 +81,9 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
|
|||
return;
|
||||
}
|
||||
|
||||
wlr_keyboard_set_keymap(&keyboard, xkbKeymap);
|
||||
events.keymap.emit(IKeyboard::SKeymapEvent{
|
||||
.keymap = xkbKeymap,
|
||||
});
|
||||
hasKeymap = true;
|
||||
|
||||
xkb_keymap_unref(xkbKeymap);
|
||||
|
|
@ -83,22 +91,17 @@ CVirtualKeyboardV1Resource::CVirtualKeyboardV1Resource(SP<CZwpVirtualKeyboardV1>
|
|||
close(fd);
|
||||
});
|
||||
|
||||
wlr_keyboard_init(&keyboard, &virtualKeyboardImpl, "CVirtualKeyboard");
|
||||
name = "hl-virtual-keyboard";
|
||||
}
|
||||
|
||||
CVirtualKeyboardV1Resource::~CVirtualKeyboardV1Resource() {
|
||||
events.destroy.emit();
|
||||
wlr_keyboard_finish(&keyboard);
|
||||
}
|
||||
|
||||
bool CVirtualKeyboardV1Resource::good() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
||||
wlr_keyboard* CVirtualKeyboardV1Resource::wlr() {
|
||||
return &keyboard;
|
||||
}
|
||||
|
||||
wl_client* CVirtualKeyboardV1Resource::client() {
|
||||
return resource->resource() ? resource->client() : nullptr;
|
||||
}
|
||||
|
|
@ -106,17 +109,16 @@ wl_client* CVirtualKeyboardV1Resource::client() {
|
|||
void CVirtualKeyboardV1Resource::releasePressed() {
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
size_t keycodesNum = keyboard.num_keycodes;
|
||||
|
||||
for (size_t i = 0; i < keycodesNum; ++i) {
|
||||
struct wlr_keyboard_key_event event = {
|
||||
.time_msec = (now.tv_sec * 1000 + now.tv_nsec / 1000000),
|
||||
.keycode = keyboard.keycodes[keycodesNum - i - 1],
|
||||
.update_state = false,
|
||||
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
};
|
||||
wlr_keyboard_notify_key(&keyboard, &event); // updates num_keycodes
|
||||
for (auto& p : pressed) {
|
||||
events.key.emit(IKeyboard::SKeyEvent{
|
||||
.timeMs = now.tv_sec * 1000 + now.tv_nsec / 1000000,
|
||||
.keycode = p,
|
||||
.state = WL_KEYBOARD_KEY_STATE_RELEASED,
|
||||
});
|
||||
}
|
||||
|
||||
pressed.clear();
|
||||
}
|
||||
|
||||
CVirtualKeyboardProtocol::CVirtualKeyboardProtocol(const wl_interface* iface, const int& ver, const std::string& name) : IWaylandProtocol(iface, ver, name) {
|
||||
|
|
|
|||
|
|
@ -14,19 +14,24 @@ class CVirtualKeyboardV1Resource {
|
|||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
CSignal key;
|
||||
CSignal modifiers;
|
||||
CSignal keymap;
|
||||
} events;
|
||||
|
||||
bool good();
|
||||
wlr_keyboard* wlr();
|
||||
wl_client* client();
|
||||
bool good();
|
||||
wl_client* client();
|
||||
|
||||
std::string name = "";
|
||||
|
||||
private:
|
||||
SP<CZwpVirtualKeyboardV1> resource;
|
||||
wlr_keyboard keyboard;
|
||||
|
||||
void releasePressed();
|
||||
|
||||
bool hasKeymap = false;
|
||||
|
||||
std::vector<uint32_t> pressed;
|
||||
};
|
||||
|
||||
class CVirtualKeyboardProtocol : public IWaylandProtocol {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,6 @@
|
|||
|
||||
#define LOGM PROTO::virtualPointer->protoLog
|
||||
|
||||
static const wlr_pointer_impl pointerImpl = {
|
||||
.name = "virtual-pointer-v1",
|
||||
};
|
||||
|
||||
CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
return;
|
||||
|
|
@ -19,41 +15,30 @@ CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> r
|
|||
PROTO::virtualPointer->destroyResource(this);
|
||||
});
|
||||
|
||||
wlr_pointer_init(&pointer, &pointerImpl, "CVirtualPointerV1Resource");
|
||||
|
||||
resource->setMotion([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, wl_fixed_t dx, wl_fixed_t dy) {
|
||||
wlr_pointer_motion_event event = {
|
||||
.pointer = &pointer,
|
||||
.time_msec = timeMs,
|
||||
.delta_x = wl_fixed_to_double(dx),
|
||||
.delta_y = wl_fixed_to_double(dy),
|
||||
.unaccel_dx = wl_fixed_to_double(dx),
|
||||
.unaccel_dy = wl_fixed_to_double(dy),
|
||||
};
|
||||
wl_signal_emit_mutable(&pointer.events.motion, &event);
|
||||
events.move.emit(IPointer::SMotionEvent{
|
||||
.timeMs = timeMs,
|
||||
.delta = {wl_fixed_to_double(dx), wl_fixed_to_double(dy)},
|
||||
.unaccel = {wl_fixed_to_double(dx), wl_fixed_to_double(dy)},
|
||||
});
|
||||
});
|
||||
|
||||
resource->setMotionAbsolute([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t x, uint32_t y, uint32_t xExtent, uint32_t yExtent) {
|
||||
if (!xExtent || !yExtent)
|
||||
return;
|
||||
|
||||
wlr_pointer_motion_absolute_event event = {
|
||||
.pointer = &pointer,
|
||||
.time_msec = timeMs,
|
||||
.x = (double)x / xExtent,
|
||||
.y = (double)y / yExtent,
|
||||
};
|
||||
wl_signal_emit_mutable(&pointer.events.motion_absolute, &event);
|
||||
events.warp.emit(IPointer::SMotionAbsoluteEvent{
|
||||
.timeMs = timeMs,
|
||||
.absolute = {(double)x / xExtent, (double)y / yExtent},
|
||||
});
|
||||
});
|
||||
|
||||
resource->setButton([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t button, uint32_t state) {
|
||||
struct wlr_pointer_button_event event = {
|
||||
.pointer = &pointer,
|
||||
.time_msec = timeMs,
|
||||
.button = button,
|
||||
.state = (wl_pointer_button_state)state,
|
||||
};
|
||||
wl_signal_emit_mutable(&pointer.events.button, &event);
|
||||
events.button.emit(IPointer::SButtonEvent{
|
||||
.timeMs = timeMs,
|
||||
.button = button,
|
||||
.state = (wl_pointer_button_state)state,
|
||||
});
|
||||
});
|
||||
|
||||
resource->setAxis([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value) {
|
||||
|
|
@ -63,18 +48,18 @@ CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> r
|
|||
}
|
||||
|
||||
axis = axis_;
|
||||
axisEvents[axis] = wlr_pointer_axis_event{.pointer = &pointer, .time_msec = timeMs, .orientation = (wl_pointer_axis)axis, .delta = wl_fixed_to_double(value)};
|
||||
axisEvents[axis] = IPointer::SAxisEvent{.timeMs = timeMs, .axis = (wl_pointer_axis)axis, .delta = wl_fixed_to_double(value)};
|
||||
});
|
||||
|
||||
resource->setFrame([this](CZwlrVirtualPointerV1* r) {
|
||||
for (auto& e : axisEvents) {
|
||||
if (!e.pointer)
|
||||
if (!e.timeMs)
|
||||
continue;
|
||||
wl_signal_emit_mutable(&pointer.events.axis, &e);
|
||||
e.pointer = nullptr;
|
||||
events.axis.emit(e);
|
||||
e.timeMs = 0;
|
||||
}
|
||||
|
||||
wl_signal_emit_mutable(&pointer.events.frame, &pointer);
|
||||
events.frame.emit();
|
||||
});
|
||||
|
||||
resource->setAxisSource([this](CZwlrVirtualPointerV1* r, uint32_t source) { axisEvents[axis].source = (wl_pointer_axis_source)source; });
|
||||
|
|
@ -85,12 +70,11 @@ CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> r
|
|||
return;
|
||||
}
|
||||
|
||||
axis = axis_;
|
||||
axisEvents[axis].pointer = &pointer;
|
||||
axisEvents[axis].time_msec = timeMs;
|
||||
axisEvents[axis].orientation = (wl_pointer_axis)axis;
|
||||
axisEvents[axis].delta = 0;
|
||||
axisEvents[axis].delta_discrete = 0;
|
||||
axis = axis_;
|
||||
axisEvents[axis].timeMs = timeMs;
|
||||
axisEvents[axis].axis = (wl_pointer_axis)axis;
|
||||
axisEvents[axis].delta = 0;
|
||||
axisEvents[axis].deltaDiscrete = 0;
|
||||
});
|
||||
|
||||
resource->setAxisDiscrete([this](CZwlrVirtualPointerV1* r, uint32_t timeMs, uint32_t axis_, wl_fixed_t value, int32_t discrete) {
|
||||
|
|
@ -99,17 +83,15 @@ CVirtualPointerV1Resource::CVirtualPointerV1Resource(SP<CZwlrVirtualPointerV1> r
|
|||
return;
|
||||
}
|
||||
|
||||
axis = axis_;
|
||||
axisEvents[axis].pointer = &pointer;
|
||||
axisEvents[axis].time_msec = timeMs;
|
||||
axisEvents[axis].orientation = (wl_pointer_axis)axis;
|
||||
axisEvents[axis].delta = wl_fixed_to_double(value);
|
||||
axisEvents[axis].delta_discrete = discrete * 120;
|
||||
axis = axis_;
|
||||
axisEvents[axis].timeMs = timeMs;
|
||||
axisEvents[axis].axis = (wl_pointer_axis)axis;
|
||||
axisEvents[axis].delta = wl_fixed_to_double(value);
|
||||
axisEvents[axis].deltaDiscrete = discrete * 120;
|
||||
});
|
||||
}
|
||||
|
||||
CVirtualPointerV1Resource::~CVirtualPointerV1Resource() {
|
||||
wlr_pointer_finish(&pointer);
|
||||
events.destroy.emit();
|
||||
}
|
||||
|
||||
|
|
@ -117,10 +99,6 @@ bool CVirtualPointerV1Resource::good() {
|
|||
return resource->resource();
|
||||
}
|
||||
|
||||
wlr_pointer* CVirtualPointerV1Resource::wlr() {
|
||||
return &pointer;
|
||||
}
|
||||
|
||||
wl_client* CVirtualPointerV1Resource::client() {
|
||||
return resource->client();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "WaylandProtocol.hpp"
|
||||
#include "wlr-virtual-pointer-unstable-v1.hpp"
|
||||
#include "../helpers/signal/Signal.hpp"
|
||||
#include "../devices/IPointer.hpp"
|
||||
|
||||
class CVirtualPointerV1Resource {
|
||||
public:
|
||||
|
|
@ -15,19 +16,35 @@ class CVirtualPointerV1Resource {
|
|||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
CSignal move;
|
||||
CSignal warp;
|
||||
CSignal button;
|
||||
CSignal axis;
|
||||
CSignal frame;
|
||||
|
||||
CSignal swipeBegin;
|
||||
CSignal swipeUpdate;
|
||||
CSignal swipeEnd;
|
||||
|
||||
CSignal pinchBegin;
|
||||
CSignal pinchUpdate;
|
||||
CSignal pinchEnd;
|
||||
|
||||
CSignal holdBegin;
|
||||
CSignal holdEnd;
|
||||
} events;
|
||||
|
||||
bool good();
|
||||
wlr_pointer* wlr();
|
||||
wl_client* client();
|
||||
bool good();
|
||||
wl_client* client();
|
||||
|
||||
std::string name;
|
||||
|
||||
private:
|
||||
SP<CZwlrVirtualPointerV1> resource;
|
||||
wlr_pointer pointer;
|
||||
SP<CZwlrVirtualPointerV1> resource;
|
||||
|
||||
uint32_t axis = 0;
|
||||
uint32_t axis = 0;
|
||||
|
||||
std::array<wlr_pointer_axis_event, 2> axisEvents;
|
||||
std::array<IPointer::SAxisEvent, 2> axisEvents;
|
||||
};
|
||||
|
||||
class CVirtualPointerProtocol : public IWaylandProtocol {
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ void CXDGOutputProtocol::onManagerGetXDGOutput(CZxdgOutputManagerV1* mgr, uint32
|
|||
|
||||
if (XDGVER >= OUTPUT_NAME_SINCE_VERSION)
|
||||
pXDGOutput->resource->sendName(PMONITOR->szName.c_str());
|
||||
if (XDGVER >= OUTPUT_DESCRIPTION_SINCE_VERSION && PMONITOR->output->description)
|
||||
pXDGOutput->resource->sendDescription(PMONITOR->output->description);
|
||||
if (XDGVER >= OUTPUT_DESCRIPTION_SINCE_VERSION && !PMONITOR->output->description.empty())
|
||||
pXDGOutput->resource->sendDescription(PMONITOR->output->description.c_str());
|
||||
|
||||
pXDGOutput->sendDetails();
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ void CXDGOutputProtocol::updateAllOutputs() {
|
|||
|
||||
o->sendDetails();
|
||||
|
||||
wlr_output_schedule_done(o->monitor->output);
|
||||
o->monitor->scheduleDone();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../managers/SeatManager.hpp"
|
||||
#include "core/Seat.hpp"
|
||||
#include "core/Compositor.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#define LOGM PROTO::xdgShell->protoLog
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
#include "Subcompositor.hpp"
|
||||
#include "../Viewporter.hpp"
|
||||
#include "../../helpers/Monitor.hpp"
|
||||
#include "../PresentationTime.hpp"
|
||||
#include "../DRMSyncobj.hpp"
|
||||
#include "../../render/Renderer.hpp"
|
||||
|
||||
#define LOGM PROTO::compositor->protoLog
|
||||
|
||||
|
|
@ -102,58 +105,14 @@ CWLSurfaceResource::CWLSurfaceResource(SP<CWlSurface> resource_) : resource(reso
|
|||
pending.size = tfs / pending.scale;
|
||||
}
|
||||
|
||||
if (viewportResource)
|
||||
viewportResource->verify();
|
||||
|
||||
pending.damage.intersect(CBox{{}, pending.size});
|
||||
|
||||
auto previousBuffer = current.buffer;
|
||||
CRegion previousBufferDamage = accumulateCurrentBufferDamage();
|
||||
events.precommit.emit();
|
||||
if (pending.rejected)
|
||||
return;
|
||||
|
||||
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 if it's synchronous as update() has done everything thats needed
|
||||
// so we can let the app know we're done.
|
||||
if (current.buffer->isSynchronous()) {
|
||||
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);
|
||||
}
|
||||
|
||||
// for async buffers, we can only release the buffer once we are unrefing it from current.
|
||||
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
|
||||
previousBuffer->sendRelease();
|
||||
bufferReleased = true;
|
||||
}
|
||||
if (stateLocks <= 0)
|
||||
commitPendingState();
|
||||
});
|
||||
|
||||
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}); });
|
||||
|
|
@ -426,7 +385,97 @@ CRegion CWLSurfaceResource::accumulateCurrentBufferDamage() {
|
|||
|
||||
Vector2D trc = current.transform % 2 == 1 ? Vector2D{current.buffer->size.y, current.buffer->size.x} : current.buffer->size;
|
||||
|
||||
return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(wlr_output_transform_invert(current.transform)), trc.x, trc.y).add(current.bufferDamage);
|
||||
return surfaceDamage.scale(current.scale).transform(wlTransformToHyprutils(invertTransform(current.transform)), trc.x, trc.y).add(current.bufferDamage);
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::lockPendingState() {
|
||||
stateLocks++;
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::unlockPendingState() {
|
||||
stateLocks--;
|
||||
if (stateLocks <= 0)
|
||||
commitPendingState();
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::commitPendingState() {
|
||||
auto previousBuffer = current.buffer;
|
||||
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 if it's synchronous as update() has done everything thats needed
|
||||
// so we can let the app know we're done.
|
||||
if (current.buffer->isSynchronous()) {
|
||||
current.buffer->sendReleaseWithSurface(self.lock());
|
||||
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);
|
||||
}
|
||||
|
||||
// for async buffers, we can only release the buffer once we are unrefing it from current.
|
||||
if (previousBuffer && !previousBuffer->isSynchronous() && !bufferReleased) {
|
||||
if (previousBuffer->lockedByBackend) {
|
||||
previousBuffer->hlEvents.backendRelease = previousBuffer->events.backendRelease.registerListener([this, previousBuffer](std::any data) {
|
||||
if (!self.expired()) // could be dead in the dtor
|
||||
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||
else
|
||||
previousBuffer->sendRelease();
|
||||
previousBuffer->hlEvents.backendRelease.reset();
|
||||
bufferReleased = true;
|
||||
});
|
||||
} else
|
||||
previousBuffer->sendReleaseWithSurface(self.lock());
|
||||
|
||||
bufferReleased = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CWLSurfaceResource::presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync) {
|
||||
frame(when);
|
||||
auto FEEDBACK = makeShared<CQueuedPresentationData>(self.lock());
|
||||
FEEDBACK->attachMonitor(pMonitor);
|
||||
FEEDBACK->discarded();
|
||||
PROTO::presentation->queueData(FEEDBACK);
|
||||
|
||||
if (!pMonitor || !pMonitor->outTimeline || !syncobj || !needsExplicitSync)
|
||||
return;
|
||||
|
||||
// attach explicit sync
|
||||
g_pHyprRenderer->explicitPresented.emplace_back(self.lock());
|
||||
|
||||
if (syncobj->acquirePoint > pMonitor->lastWaitPoint) {
|
||||
Debug::log(TRACE, "presentFeedback lastWaitPoint {} -> {}", pMonitor->lastWaitPoint, syncobj->acquirePoint);
|
||||
pMonitor->lastWaitPoint = syncobj->acquirePoint;
|
||||
}
|
||||
}
|
||||
|
||||
CWLCompositorResource::CWLCompositorResource(SP<CWlCompositor> resource_) : resource(resource_) {
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ class CWLSurface;
|
|||
class CWLSurfaceResource;
|
||||
class CWLSubsurfaceResource;
|
||||
class CViewportResource;
|
||||
class CDRMSyncobjSurfaceResource;
|
||||
|
||||
class CWLCallbackResource {
|
||||
public:
|
||||
|
|
@ -74,6 +75,7 @@ class CWLSurfaceResource {
|
|||
Vector2D sourceSize();
|
||||
|
||||
struct {
|
||||
CSignal precommit;
|
||||
CSignal commit;
|
||||
CSignal map;
|
||||
CSignal unmap;
|
||||
|
|
@ -81,11 +83,11 @@ class CWLSurfaceResource {
|
|||
CSignal destroy;
|
||||
} events;
|
||||
|
||||
struct {
|
||||
struct SState {
|
||||
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<IHLBuffer> buffer;
|
||||
SP<CTexture> texture;
|
||||
Vector2D offset;
|
||||
Vector2D size;
|
||||
|
|
@ -95,6 +97,7 @@ class CWLSurfaceResource {
|
|||
Vector2D destination;
|
||||
CBox source;
|
||||
} viewport;
|
||||
bool rejected = false;
|
||||
|
||||
//
|
||||
void reset() {
|
||||
|
|
@ -115,9 +118,13 @@ class CWLSurfaceResource {
|
|||
std::vector<WP<CWLSubsurfaceResource>> subsurfaces;
|
||||
WP<ISurfaceRole> role;
|
||||
WP<CViewportResource> viewportResource;
|
||||
WP<CDRMSyncobjSurfaceResource> syncobj; // may not be present
|
||||
|
||||
void breadthfirst(std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
CRegion accumulateCurrentBufferDamage();
|
||||
void presentFeedback(timespec* when, CMonitor* pMonitor, bool needsExplicitSync = false);
|
||||
void lockPendingState();
|
||||
void unlockPendingState();
|
||||
|
||||
// returns a pair: found surface (null if not found) and surface local coords.
|
||||
// localCoords param is relative to 0,0 of this surface
|
||||
|
|
@ -130,7 +137,10 @@ class CWLSurfaceResource {
|
|||
// tracks whether we should release the buffer
|
||||
bool bufferReleased = false;
|
||||
|
||||
int stateLocks = 0;
|
||||
|
||||
void destroy();
|
||||
void commitPendingState();
|
||||
void bfHelper(std::vector<SP<CWLSurfaceResource>> nodes, std::function<void(SP<CWLSurfaceResource>, const Vector2D&, void*)> fn, void* data);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ CWLOutputResource::CWLOutputResource(SP<CWlOutput> resource_, SP<CMonitor> pMoni
|
|||
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);
|
||||
resource->sendGeometry(0, 0, monitor->output->physicalSize.x, monitor->output->physicalSize.y, (wl_output_subpixel)monitor->output->subpixel, monitor->output->make.c_str(),
|
||||
monitor->output->model.c_str(), monitor->transform);
|
||||
if (resource->version() >= 4) {
|
||||
resource->sendName(monitor->szName.c_str());
|
||||
resource->sendDescription(monitor->szDescription.c_str());
|
||||
|
|
@ -115,3 +115,9 @@ void CWLOutputProtocol::remove() {
|
|||
bool CWLOutputProtocol::isDefunct() {
|
||||
return defunct;
|
||||
}
|
||||
|
||||
void CWLOutputProtocol::sendDone() {
|
||||
for (auto& r : m_vOutputs) {
|
||||
r->resource->sendDone();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ class CWLOutputResource {
|
|||
private:
|
||||
SP<CWlOutput> resource;
|
||||
wl_client* pClient = nullptr;
|
||||
|
||||
friend class CWLOutputProtocol;
|
||||
};
|
||||
|
||||
class CWLOutputProtocol : public IWaylandProtocol {
|
||||
|
|
@ -35,6 +37,7 @@ class CWLOutputProtocol : public IWaylandProtocol {
|
|||
virtual void bindManager(wl_client* client, void* data, uint32_t ver, uint32_t id);
|
||||
|
||||
SP<CWLOutputResource> outputResourceFrom(wl_client* client);
|
||||
void sendDone();
|
||||
|
||||
WP<CMonitor> monitor;
|
||||
|
||||
|
|
|
|||
|
|
@ -254,8 +254,8 @@ void CWLKeyboardResource::sendKeymap(SP<IKeyboard> keyboard) {
|
|||
int fd;
|
||||
uint32_t size;
|
||||
if (keyboard) {
|
||||
fd = keyboard->wlr()->keymap_fd;
|
||||
size = keyboard->wlr()->keymap_size;
|
||||
fd = keyboard->xkbKeymapFD;
|
||||
size = keyboard->xkbKeymapString.length() + 1;
|
||||
} else {
|
||||
fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
|
|
|
|||
|
|
@ -41,20 +41,20 @@ CWLSHMBuffer::~CWLSHMBuffer() {
|
|||
;
|
||||
}
|
||||
|
||||
eBufferCapability CWLSHMBuffer::caps() {
|
||||
return BUFFER_CAPABILITY_DATAPTR;
|
||||
Aquamarine::eBufferCapability CWLSHMBuffer::caps() {
|
||||
return Aquamarine::eBufferCapability::BUFFER_CAPABILITY_DATAPTR;
|
||||
}
|
||||
|
||||
eBufferType CWLSHMBuffer::type() {
|
||||
return BUFFER_TYPE_SHM;
|
||||
Aquamarine::eBufferType CWLSHMBuffer::type() {
|
||||
return Aquamarine::eBufferType::BUFFER_TYPE_SHM;
|
||||
}
|
||||
|
||||
bool CWLSHMBuffer::isSynchronous() {
|
||||
return true;
|
||||
}
|
||||
|
||||
SSHMAttrs CWLSHMBuffer::shm() {
|
||||
SSHMAttrs attrs;
|
||||
Aquamarine::SSHMAttrs CWLSHMBuffer::shm() {
|
||||
Aquamarine::SSHMAttrs attrs;
|
||||
attrs.success = true;
|
||||
attrs.fd = pool->fd;
|
||||
attrs.format = FormatUtils::shmToDRM(fmt);
|
||||
|
|
@ -188,11 +188,18 @@ CWLSHMProtocol::CWLSHMProtocol(const wl_interface* iface, const int& ver, const
|
|||
|
||||
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);
|
||||
shmFormats.push_back(WL_SHM_FORMAT_ARGB8888);
|
||||
shmFormats.push_back(WL_SHM_FORMAT_XRGB8888);
|
||||
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
shmFormats.push_back(FormatUtils::drmToShm(formats[i]));
|
||||
static const std::array<DRMFormat, 6> supportedShmFourccFormats = {
|
||||
DRM_FORMAT_XBGR8888, DRM_FORMAT_ABGR8888, DRM_FORMAT_XRGB2101010, DRM_FORMAT_ARGB2101010, DRM_FORMAT_XBGR2101010, DRM_FORMAT_ABGR2101010,
|
||||
};
|
||||
|
||||
for (auto& fmt : g_pHyprOpenGL->getDRMFormats()) {
|
||||
if (std::find(supportedShmFourccFormats.begin(), supportedShmFourccFormats.end(), fmt.drmFormat) == supportedShmFourccFormats.end())
|
||||
continue;
|
||||
|
||||
shmFormats.push_back(fmt.drmFormat);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@ class CSHMPool {
|
|||
void resize(size_t size);
|
||||
};
|
||||
|
||||
class CWLSHMBuffer : public IWLBuffer {
|
||||
class CWLSHMBuffer : public IHLBuffer {
|
||||
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 Aquamarine::eBufferCapability caps();
|
||||
virtual Aquamarine::eBufferType type();
|
||||
virtual void update(const CRegion& damage);
|
||||
virtual bool isSynchronous();
|
||||
virtual SSHMAttrs shm();
|
||||
virtual Aquamarine::SSHMAttrs shm();
|
||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||
virtual void endDataPtr();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +1,10 @@
|
|||
#include "Buffer.hpp"
|
||||
#include "WLBuffer.hpp"
|
||||
|
||||
SDMABUFAttrs IWLBuffer::dmabuf() {
|
||||
return SDMABUFAttrs{};
|
||||
void IHLBuffer::sendRelease() {
|
||||
resource->sendRelease();
|
||||
}
|
||||
|
||||
SSHMAttrs IWLBuffer::shm() {
|
||||
return SSHMAttrs{};
|
||||
}
|
||||
|
||||
std::tuple<uint8_t*, uint32_t, size_t> IWLBuffer::beginDataPtr(uint32_t flags) {
|
||||
return {nullptr, 0, 0};
|
||||
}
|
||||
|
||||
void IWLBuffer::endDataPtr() {
|
||||
; // empty
|
||||
}
|
||||
|
||||
void IWLBuffer::sendRelease() {
|
||||
if (!resource || !resource->resource)
|
||||
return;
|
||||
resource->resource->sendRelease();
|
||||
}
|
||||
|
||||
void IWLBuffer::lock() {
|
||||
locks++;
|
||||
}
|
||||
|
||||
void IWLBuffer::unlock() {
|
||||
locks--;
|
||||
|
||||
ASSERT(locks >= 0);
|
||||
|
||||
if (locks <= 0)
|
||||
sendRelease();
|
||||
}
|
||||
|
||||
bool IWLBuffer::locked() {
|
||||
return locks;
|
||||
void IHLBuffer::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
||||
if (resource && resource->good())
|
||||
resource->sendReleaseWithSurface(surf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "../../defines.hpp"
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
#include "../../render/Texture.hpp"
|
||||
#include "./WLBuffer.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <aquamarine/buffer/Buffer.hpp>
|
||||
|
||||
enum eBufferCapability {
|
||||
BUFFER_CAPABILITY_DATAPTR = (1 << 0),
|
||||
};
|
||||
|
||||
enum eBufferType {
|
||||
BUFFER_TYPE_DMABUF = 0,
|
||||
BUFFER_TYPE_SHM,
|
||||
BUFFER_TYPE_MISC,
|
||||
};
|
||||
|
||||
class CWLBufferResource;
|
||||
|
||||
struct SDMABUFAttrs {
|
||||
bool success = false;
|
||||
Vector2D size;
|
||||
uint32_t format = 0; // fourcc
|
||||
uint64_t modifier = 0;
|
||||
|
||||
int planes = 1;
|
||||
std::array<uint32_t, 4> offsets = {0};
|
||||
std::array<uint32_t, 4> strides = {0};
|
||||
std::array<int, 4> fds = {-1, -1, -1, -1};
|
||||
};
|
||||
|
||||
struct SSHMAttrs {
|
||||
bool success = false;
|
||||
int fd = 0;
|
||||
uint32_t format = 0;
|
||||
Vector2D size;
|
||||
int stride = 0;
|
||||
int64_t offset = 0;
|
||||
};
|
||||
|
||||
class IWLBuffer {
|
||||
class IHLBuffer : public Aquamarine::IBuffer {
|
||||
public:
|
||||
virtual ~IWLBuffer() {
|
||||
virtual ~IHLBuffer() {
|
||||
;
|
||||
};
|
||||
}
|
||||
virtual Aquamarine::eBufferCapability caps() = 0;
|
||||
virtual Aquamarine::eBufferType type() = 0;
|
||||
virtual void update(const CRegion& damage) = 0;
|
||||
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
||||
virtual bool good() = 0;
|
||||
virtual void sendRelease();
|
||||
virtual void sendReleaseWithSurface(SP<CWLSurfaceResource>);
|
||||
|
||||
virtual eBufferCapability caps() = 0;
|
||||
virtual eBufferType type() = 0;
|
||||
virtual void update(const CRegion& damage) = 0;
|
||||
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
||||
virtual SDMABUFAttrs dmabuf();
|
||||
virtual SSHMAttrs shm();
|
||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||
virtual void endDataPtr();
|
||||
virtual void sendRelease();
|
||||
virtual void lock();
|
||||
virtual void unlock();
|
||||
virtual bool locked();
|
||||
|
||||
Vector2D size;
|
||||
bool opaque = false;
|
||||
|
||||
SP<CWLBufferResource> resource;
|
||||
|
||||
SP<CTexture> texture;
|
||||
SP<CTexture> texture;
|
||||
bool opaque = false;
|
||||
SP<CWLBufferResource> resource;
|
||||
|
||||
struct {
|
||||
CSignal destroy;
|
||||
} events;
|
||||
|
||||
private:
|
||||
int locks = 0;
|
||||
CHyprSignalListener backendRelease;
|
||||
} hlEvents;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#include "../../render/Renderer.hpp"
|
||||
#include "../../helpers/Format.hpp"
|
||||
|
||||
CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs const& attrs_) : attrs(attrs_) {
|
||||
CDMABuffer::CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_) : attrs(attrs_) {
|
||||
g_pHyprRenderer->makeEGLCurrent();
|
||||
|
||||
listeners.resourceDestroy = events.destroy.registerListener([this](std::any d) {
|
||||
|
|
@ -31,12 +31,12 @@ CDMABuffer::~CDMABuffer() {
|
|||
closeFDs();
|
||||
}
|
||||
|
||||
eBufferCapability CDMABuffer::caps() {
|
||||
return BUFFER_CAPABILITY_DATAPTR;
|
||||
Aquamarine::eBufferCapability CDMABuffer::caps() {
|
||||
return Aquamarine::eBufferCapability::BUFFER_CAPABILITY_DATAPTR;
|
||||
}
|
||||
|
||||
eBufferType CDMABuffer::type() {
|
||||
return BUFFER_TYPE_DMABUF;
|
||||
Aquamarine::eBufferType CDMABuffer::type() {
|
||||
return Aquamarine::eBufferType::BUFFER_TYPE_DMABUF;
|
||||
}
|
||||
|
||||
void CDMABuffer::update(const CRegion& damage) {
|
||||
|
|
@ -47,7 +47,7 @@ bool CDMABuffer::isSynchronous() {
|
|||
return false;
|
||||
}
|
||||
|
||||
SDMABUFAttrs CDMABuffer::dmabuf() {
|
||||
Aquamarine::SDMABUFAttrs CDMABuffer::dmabuf() {
|
||||
return attrs;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@
|
|||
|
||||
#include "Buffer.hpp"
|
||||
|
||||
class CDMABuffer : public IWLBuffer {
|
||||
class CDMABuffer : public IHLBuffer {
|
||||
public:
|
||||
CDMABuffer(uint32_t id, wl_client* client, SDMABUFAttrs const& attrs_);
|
||||
CDMABuffer(uint32_t id, wl_client* client, Aquamarine::SDMABUFAttrs const& attrs_);
|
||||
virtual ~CDMABuffer();
|
||||
|
||||
virtual eBufferCapability caps();
|
||||
virtual eBufferType type();
|
||||
virtual Aquamarine::eBufferCapability caps();
|
||||
virtual Aquamarine::eBufferType type();
|
||||
virtual bool isSynchronous();
|
||||
virtual void update(const CRegion& damage);
|
||||
virtual SDMABUFAttrs dmabuf();
|
||||
virtual Aquamarine::SDMABUFAttrs dmabuf();
|
||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||
virtual void endDataPtr();
|
||||
bool good();
|
||||
|
|
@ -21,7 +21,7 @@ class CDMABuffer : public IWLBuffer {
|
|||
bool success = false;
|
||||
|
||||
private:
|
||||
SDMABUFAttrs attrs;
|
||||
Aquamarine::SDMABUFAttrs attrs;
|
||||
|
||||
struct {
|
||||
CHyprSignalListener resourceDestroy;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
#include "WLBuffer.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "../core/Compositor.hpp"
|
||||
#include "../DRMSyncobj.hpp"
|
||||
#include "../../helpers/sync/SyncTimeline.hpp"
|
||||
#include "../../Compositor.hpp"
|
||||
#include <xf86drm.h>
|
||||
|
||||
CWLBufferResource::CWLBufferResource(SP<CWlBuffer> resource_) : resource(resource_) {
|
||||
if (!good())
|
||||
|
|
@ -27,6 +32,16 @@ void CWLBufferResource::sendRelease() {
|
|||
resource->sendRelease();
|
||||
}
|
||||
|
||||
void CWLBufferResource::sendReleaseWithSurface(SP<CWLSurfaceResource> surf) {
|
||||
sendRelease();
|
||||
|
||||
if (!surf || !surf->syncobj)
|
||||
return;
|
||||
|
||||
if (drmSyncobjTimelineSignal(g_pCompositor->m_iDRMFD, &surf->syncobj->releaseTimeline->timeline->handle, &surf->syncobj->releasePoint, 1))
|
||||
Debug::log(ERR, "sendReleaseWithSurface: drmSyncobjTimelineSignal failed");
|
||||
}
|
||||
|
||||
wl_resource* CWLBufferResource::getResource() {
|
||||
return resource->resource();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@
|
|||
#include "wayland.hpp"
|
||||
#include "../../helpers/signal/Signal.hpp"
|
||||
|
||||
class IWLBuffer;
|
||||
class IHLBuffer;
|
||||
class CWLSurfaceResource;
|
||||
|
||||
class CWLBufferResource {
|
||||
public:
|
||||
|
|
@ -16,9 +17,10 @@ class CWLBufferResource {
|
|||
|
||||
bool good();
|
||||
void sendRelease();
|
||||
void sendReleaseWithSurface(SP<CWLSurfaceResource>);
|
||||
wl_resource* getResource();
|
||||
|
||||
WP<IWLBuffer> buffer;
|
||||
WP<IHLBuffer> buffer;
|
||||
|
||||
WP<CWLBufferResource> self;
|
||||
|
||||
|
|
@ -27,5 +29,5 @@ class CWLBufferResource {
|
|||
|
||||
SP<CWlBuffer> resource;
|
||||
|
||||
friend class IWLBuffer;
|
||||
friend class IHLBuffer;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue