mirror of
https://github.com/hyprwm/aquamarine.git
synced 2024-11-17 06:06:00 +01:00
Wayland: primitive but working backend
This implements enough for wayland to be a functioning backend.
This commit is contained in:
parent
01766c0956
commit
790ce7dfbf
15 changed files with 745 additions and 39 deletions
|
@ -16,7 +16,7 @@ set(INCLUDE ${CMAKE_INSTALL_FULL_INCLUDEDIR})
|
||||||
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
set(LIBDIR ${CMAKE_INSTALL_FULL_LIBDIR})
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols hyprutils>=0.1.2 pixman-1 wayland-client)
|
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols hyprutils>=0.1.2 pixman-1 wayland-client libdrm gbm)
|
||||||
|
|
||||||
configure_file(aquamarine.pc.in aquamarine.pc @ONLY)
|
configure_file(aquamarine.pc.in aquamarine.pc @ONLY)
|
||||||
|
|
||||||
|
@ -77,6 +77,7 @@ endfunction()
|
||||||
protocolWayland()
|
protocolWayland()
|
||||||
|
|
||||||
protocolNew("stable/xdg-shell" "xdg-shell" false)
|
protocolNew("stable/xdg-shell" "xdg-shell" false)
|
||||||
|
protocolNew("stable/linux-dmabuf" "linux-dmabuf-v1" false)
|
||||||
|
|
||||||
# tests
|
# tests
|
||||||
add_custom_target(tests)
|
add_custom_target(tests)
|
||||||
|
|
20
include/aquamarine/allocator/Allocator.hpp
Normal file
20
include/aquamarine/allocator/Allocator.hpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
|
#include "../buffer/Buffer.hpp"
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
|
||||||
|
namespace Aquamarine {
|
||||||
|
class CBackend;
|
||||||
|
|
||||||
|
struct SAllocatorBufferParams {
|
||||||
|
Hyprutils::Math::Vector2D size;
|
||||||
|
uint32_t format = DRM_FORMAT_INVALID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class IAllocator {
|
||||||
|
public:
|
||||||
|
virtual Hyprutils::Memory::CSharedPointer<IBuffer> acquire(const SAllocatorBufferParams& params) = 0;
|
||||||
|
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend() = 0;
|
||||||
|
};
|
||||||
|
};
|
61
include/aquamarine/allocator/GBM.hpp
Normal file
61
include/aquamarine/allocator/GBM.hpp
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Allocator.hpp"
|
||||||
|
|
||||||
|
struct gbm_device;
|
||||||
|
struct gbm_bo;
|
||||||
|
|
||||||
|
namespace Aquamarine {
|
||||||
|
class CGBMAllocator;
|
||||||
|
class CBackend;
|
||||||
|
|
||||||
|
class CGBMBuffer : public IBuffer {
|
||||||
|
public:
|
||||||
|
virtual ~CGBMBuffer();
|
||||||
|
|
||||||
|
virtual eBufferCapability caps();
|
||||||
|
virtual eBufferType type();
|
||||||
|
virtual void update(const Hyprutils::Math::CRegion& damage);
|
||||||
|
virtual bool isSynchronous();
|
||||||
|
virtual bool good();
|
||||||
|
virtual SDMABUFAttrs dmabuf();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator_);
|
||||||
|
|
||||||
|
Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator;
|
||||||
|
|
||||||
|
// gbm stuff
|
||||||
|
gbm_bo* bo = nullptr;
|
||||||
|
SDMABUFAttrs attrs{.success = false};
|
||||||
|
|
||||||
|
friend class CGBMAllocator;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CGBMAllocator : public IAllocator {
|
||||||
|
public:
|
||||||
|
static Hyprutils::Memory::CSharedPointer<CGBMAllocator> create(int drmfd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_);
|
||||||
|
|
||||||
|
virtual Hyprutils::Memory::CSharedPointer<IBuffer> acquire(const SAllocatorBufferParams& params);
|
||||||
|
virtual Hyprutils::Memory::CSharedPointer<CBackend> getBackend();
|
||||||
|
|
||||||
|
//
|
||||||
|
Hyprutils::Memory::CWeakPointer<CGBMAllocator> self;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CGBMAllocator(int fd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_);
|
||||||
|
|
||||||
|
// a vector purely for tracking (debugging) the buffers and nothing more
|
||||||
|
std::vector<Hyprutils::Memory::CWeakPointer<CGBMBuffer>> buffers;
|
||||||
|
|
||||||
|
int fd = -1;
|
||||||
|
Hyprutils::Memory::CWeakPointer<CBackend> backend;
|
||||||
|
|
||||||
|
// gbm stuff
|
||||||
|
gbm_device* gbmDevice = nullptr;
|
||||||
|
std::string gbmDeviceBackendName = "";
|
||||||
|
std::string drmName = "";
|
||||||
|
|
||||||
|
friend class CGBMBuffer;
|
||||||
|
};
|
||||||
|
};
|
32
include/aquamarine/allocator/Swapchain.hpp
Normal file
32
include/aquamarine/allocator/Swapchain.hpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Allocator.hpp"
|
||||||
|
|
||||||
|
namespace Aquamarine {
|
||||||
|
|
||||||
|
struct SSwapchainOptions {
|
||||||
|
size_t length = 0;
|
||||||
|
Hyprutils::Math::Vector2D size;
|
||||||
|
uint32_t format = DRM_FORMAT_INVALID;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CSwapchain {
|
||||||
|
public:
|
||||||
|
CSwapchain(Hyprutils::Memory::CSharedPointer<IAllocator> allocator_);
|
||||||
|
|
||||||
|
bool reconfigure(const SSwapchainOptions& options_);
|
||||||
|
|
||||||
|
bool contains(Hyprutils::Memory::CSharedPointer<IBuffer> buffer);
|
||||||
|
Hyprutils::Memory::CSharedPointer<IBuffer> next(int* age);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool fullReconfigure(const SSwapchainOptions& options_);
|
||||||
|
bool resize(size_t newSize);
|
||||||
|
|
||||||
|
//
|
||||||
|
SSwapchainOptions options;
|
||||||
|
Hyprutils::Memory::CSharedPointer<IAllocator> allocator;
|
||||||
|
std::vector<Hyprutils::Memory::CSharedPointer<IBuffer>> buffers;
|
||||||
|
int lastAcquired = 0;
|
||||||
|
};
|
||||||
|
};
|
|
@ -6,6 +6,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include "../allocator/Allocator.hpp"
|
||||||
|
|
||||||
namespace Aquamarine {
|
namespace Aquamarine {
|
||||||
enum eBackendType {
|
enum eBackendType {
|
||||||
|
@ -56,6 +57,7 @@ namespace Aquamarine {
|
||||||
virtual eBackendType type() = 0;
|
virtual eBackendType type() = 0;
|
||||||
virtual bool start() = 0;
|
virtual bool start() = 0;
|
||||||
virtual int pollFD() = 0;
|
virtual int pollFD() = 0;
|
||||||
|
virtual int drmFD() = 0;
|
||||||
virtual bool dispatchEvents() = 0;
|
virtual bool dispatchEvents() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,6 +84,8 @@ namespace Aquamarine {
|
||||||
Hyprutils::Signal::CSignal newTouch;
|
Hyprutils::Signal::CSignal newTouch;
|
||||||
} events;
|
} events;
|
||||||
|
|
||||||
|
Hyprutils::Memory::CSharedPointer<IAllocator> allocator;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CBackend();
|
CBackend();
|
||||||
|
|
||||||
|
@ -90,6 +94,7 @@ namespace Aquamarine {
|
||||||
std::vector<SBackendImplementationOptions> implementationOptions;
|
std::vector<SBackendImplementationOptions> implementationOptions;
|
||||||
std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations;
|
std::vector<Hyprutils::Memory::CSharedPointer<IBackendImplementation>> implementations;
|
||||||
SBackendOptions options;
|
SBackendOptions options;
|
||||||
|
Hyprutils::Memory::CWeakPointer<CBackend> self;
|
||||||
|
|
||||||
//
|
//
|
||||||
struct {
|
struct {
|
||||||
|
|
|
@ -1,21 +1,44 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "./Backend.hpp"
|
#include "./Backend.hpp"
|
||||||
|
#include "../allocator/Swapchain.hpp"
|
||||||
#include "../output/Output.hpp"
|
#include "../output/Output.hpp"
|
||||||
#include "../input/Input.hpp"
|
#include "../input/Input.hpp"
|
||||||
#include <hyprutils/memory/WeakPtr.hpp>
|
#include <hyprutils/memory/WeakPtr.hpp>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
#include <wayland.hpp>
|
#include <wayland.hpp>
|
||||||
#include <xdg-shell.hpp>
|
#include <xdg-shell.hpp>
|
||||||
|
#include <linux-dmabuf-v1.hpp>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
namespace Aquamarine {
|
namespace Aquamarine {
|
||||||
class CBackend;
|
class CBackend;
|
||||||
class CWaylandBackend;
|
class CWaylandBackend;
|
||||||
|
class CWaylandOutput;
|
||||||
|
|
||||||
|
class CWaylandBuffer {
|
||||||
|
public:
|
||||||
|
CWaylandBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
|
||||||
|
~CWaylandBuffer();
|
||||||
|
bool good();
|
||||||
|
|
||||||
|
bool pendingRelease = false;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct {
|
||||||
|
Hyprutils::Memory::CSharedPointer<CWlBuffer> buffer;
|
||||||
|
} waylandState;
|
||||||
|
|
||||||
|
Hyprutils::Memory::CWeakPointer<IBuffer> buffer;
|
||||||
|
Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;
|
||||||
|
|
||||||
|
friend class CWaylandOutput;
|
||||||
|
};
|
||||||
|
|
||||||
class CWaylandOutput : public IOutput {
|
class CWaylandOutput : public IOutput {
|
||||||
public:
|
public:
|
||||||
virtual ~CWaylandOutput();
|
virtual ~CWaylandOutput();
|
||||||
virtual void commit();
|
virtual bool commit();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
|
CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_);
|
||||||
|
@ -23,10 +46,19 @@ namespace Aquamarine {
|
||||||
std::string name;
|
std::string name;
|
||||||
Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;
|
Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend;
|
||||||
|
|
||||||
|
Hyprutils::Memory::CSharedPointer<CWaylandBuffer> wlBufferFromBuffer(Hyprutils::Memory::CSharedPointer<IBuffer> buffer);
|
||||||
|
|
||||||
|
void sendFrameAndSetCallback();
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::vector<std::pair<Hyprutils::Memory::CWeakPointer<IBuffer>, Hyprutils::Memory::CSharedPointer<CWaylandBuffer>>> buffers;
|
||||||
|
} backendState;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
Hyprutils::Memory::CSharedPointer<CWlSurface> surface;
|
Hyprutils::Memory::CSharedPointer<CWlSurface> surface;
|
||||||
Hyprutils::Memory::CSharedPointer<CXdgSurface> xdgSurface;
|
Hyprutils::Memory::CSharedPointer<CXdgSurface> xdgSurface;
|
||||||
Hyprutils::Memory::CSharedPointer<CXdgToplevel> xdgToplevel;
|
Hyprutils::Memory::CSharedPointer<CXdgToplevel> xdgToplevel;
|
||||||
|
Hyprutils::Memory::CSharedPointer<CWlCallback> frameCallback;
|
||||||
} waylandState;
|
} waylandState;
|
||||||
|
|
||||||
friend class CWaylandBackend;
|
friend class CWaylandBackend;
|
||||||
|
@ -67,6 +99,7 @@ namespace Aquamarine {
|
||||||
virtual eBackendType type();
|
virtual eBackendType type();
|
||||||
virtual bool start();
|
virtual bool start();
|
||||||
virtual int pollFD();
|
virtual int pollFD();
|
||||||
|
virtual int drmFD();
|
||||||
virtual bool dispatchEvents();
|
virtual bool dispatchEvents();
|
||||||
|
|
||||||
Hyprutils::Memory::CWeakPointer<CWaylandBackend> self;
|
Hyprutils::Memory::CWeakPointer<CWaylandBackend> self;
|
||||||
|
@ -76,6 +109,7 @@ namespace Aquamarine {
|
||||||
|
|
||||||
void initSeat();
|
void initSeat();
|
||||||
void initShell();
|
void initShell();
|
||||||
|
bool initDmabuf();
|
||||||
void createOutput(const std::string& szName);
|
void createOutput(const std::string& szName);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -96,11 +130,22 @@ namespace Aquamarine {
|
||||||
Hyprutils::Memory::CSharedPointer<CWlSeat> seat;
|
Hyprutils::Memory::CSharedPointer<CWlSeat> seat;
|
||||||
Hyprutils::Memory::CSharedPointer<CXdgWmBase> xdg;
|
Hyprutils::Memory::CSharedPointer<CXdgWmBase> xdg;
|
||||||
Hyprutils::Memory::CSharedPointer<CWlCompositor> compositor;
|
Hyprutils::Memory::CSharedPointer<CWlCompositor> compositor;
|
||||||
|
Hyprutils::Memory::CSharedPointer<CZwpLinuxDmabufV1> dmabuf;
|
||||||
|
Hyprutils::Memory::CSharedPointer<CZwpLinuxDmabufFeedbackV1> dmabufFeedback;
|
||||||
|
|
||||||
|
// control
|
||||||
|
bool dmabufFailed = false;
|
||||||
} waylandState;
|
} waylandState;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
int fd = -1;
|
||||||
|
std::string nodeName = "";
|
||||||
|
} drmState;
|
||||||
|
|
||||||
friend class CBackend;
|
friend class CBackend;
|
||||||
friend class CWaylandKeyboard;
|
friend class CWaylandKeyboard;
|
||||||
friend class CWaylandPointer;
|
friend class CWaylandPointer;
|
||||||
friend class CWaylandOutput;
|
friend class CWaylandOutput;
|
||||||
|
friend class CWaylandBuffer;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,6 +49,7 @@ namespace Aquamarine {
|
||||||
virtual eBufferType type() = 0;
|
virtual eBufferType type() = 0;
|
||||||
virtual void update(const Hyprutils::Math::CRegion& damage) = 0;
|
virtual void update(const Hyprutils::Math::CRegion& damage) = 0;
|
||||||
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
virtual bool isSynchronous() = 0; // whether the updates to this buffer are synchronous, aka happen over cpu
|
||||||
|
virtual bool good() = 0;
|
||||||
virtual SDMABUFAttrs dmabuf();
|
virtual SDMABUFAttrs dmabuf();
|
||||||
virtual SSHMAttrs shm();
|
virtual SSHMAttrs shm();
|
||||||
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
virtual std::tuple<uint8_t*, uint32_t, size_t> beginDataPtr(uint32_t flags);
|
||||||
|
|
|
@ -4,6 +4,9 @@
|
||||||
#include <hyprutils/signal/Signal.hpp>
|
#include <hyprutils/signal/Signal.hpp>
|
||||||
#include <hyprutils/memory/SharedPtr.hpp>
|
#include <hyprutils/memory/SharedPtr.hpp>
|
||||||
#include <hyprutils/math/Region.hpp>
|
#include <hyprutils/math/Region.hpp>
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include "../allocator/Swapchain.hpp"
|
||||||
|
#include "../buffer/Buffer.hpp"
|
||||||
|
|
||||||
namespace Aquamarine {
|
namespace Aquamarine {
|
||||||
struct SOutputMode {
|
struct SOutputMode {
|
||||||
|
@ -21,8 +24,6 @@ namespace Aquamarine {
|
||||||
|
|
||||||
class COutputState {
|
class COutputState {
|
||||||
public:
|
public:
|
||||||
COutputState(Hyprutils::Memory::CSharedPointer<IOutput> parent);
|
|
||||||
|
|
||||||
Hyprutils::Math::CRegion damage;
|
Hyprutils::Math::CRegion damage;
|
||||||
bool enabled = false;
|
bool enabled = false;
|
||||||
bool adaptiveSync = false;
|
bool adaptiveSync = false;
|
||||||
|
@ -30,6 +31,9 @@ namespace Aquamarine {
|
||||||
std::vector<uint16_t> gammaLut;
|
std::vector<uint16_t> gammaLut;
|
||||||
Hyprutils::Math::Vector2D lastModeSize;
|
Hyprutils::Math::Vector2D lastModeSize;
|
||||||
Hyprutils::Memory::CWeakPointer<SOutputMode> mode;
|
Hyprutils::Memory::CWeakPointer<SOutputMode> mode;
|
||||||
|
std::optional<SOutputMode> customMode;
|
||||||
|
uint32_t drmFormat = DRM_FORMAT_INVALID;
|
||||||
|
Hyprutils::Memory::CSharedPointer<IBuffer> buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
class IOutput {
|
class IOutput {
|
||||||
|
@ -38,7 +42,7 @@ namespace Aquamarine {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void commit() = 0;
|
virtual bool commit() = 0;
|
||||||
|
|
||||||
std::string name, description, make, model, serial;
|
std::string name, description, make, model, serial;
|
||||||
Hyprutils::Math::Vector2D physicalSize;
|
Hyprutils::Math::Vector2D physicalSize;
|
||||||
|
@ -48,7 +52,8 @@ namespace Aquamarine {
|
||||||
|
|
||||||
//
|
//
|
||||||
std::vector<Hyprutils::Memory::CSharedPointer<SOutputMode>> modes;
|
std::vector<Hyprutils::Memory::CSharedPointer<SOutputMode>> modes;
|
||||||
Hyprutils::Memory::CSharedPointer<COutputState> state;
|
Hyprutils::Memory::CSharedPointer<COutputState> state = Hyprutils::Memory::makeShared<COutputState>();
|
||||||
|
Hyprutils::Memory::CSharedPointer<CSwapchain> swapchain;
|
||||||
|
|
||||||
//
|
//
|
||||||
struct SStateEvent {
|
struct SStateEvent {
|
||||||
|
|
147
src/allocator/GBM.cpp
Normal file
147
src/allocator/GBM.cpp
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
#include <aquamarine/allocator/GBM.hpp>
|
||||||
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
|
#include "FormatUtils.hpp"
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
|
||||||
|
using namespace Aquamarine;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
Aquamarine::CGBMBuffer::CGBMBuffer(const SAllocatorBufferParams& params, Hyprutils::Memory::CWeakPointer<CGBMAllocator> allocator_) : allocator(allocator_) {
|
||||||
|
if (!allocator)
|
||||||
|
return;
|
||||||
|
|
||||||
|
attrs.size = params.size;
|
||||||
|
attrs.format = params.format;
|
||||||
|
|
||||||
|
// FIXME: proper modifier support? This might implode on some GPUs on the Wayland backend
|
||||||
|
// for sure.
|
||||||
|
|
||||||
|
bo = gbm_bo_create(allocator->gbmDevice, params.size.x, params.size.y, params.format, GBM_BO_USE_RENDERING);
|
||||||
|
|
||||||
|
if (!bo) {
|
||||||
|
allocator->backend->log(AQ_LOG_ERROR, "GBM: Failed to allocate a GBM buffer: bo null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.planes = gbm_bo_get_plane_count(bo);
|
||||||
|
attrs.modifier = gbm_bo_get_modifier(bo);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < (size_t)attrs.planes; ++i) {
|
||||||
|
attrs.strides.at(i) = gbm_bo_get_stride_for_plane(bo, i);
|
||||||
|
attrs.offsets.at(i) = gbm_bo_get_offset(bo, i);
|
||||||
|
attrs.fds.at(i) = gbm_bo_get_fd_for_plane(bo, i);
|
||||||
|
|
||||||
|
if (attrs.fds.at(i) < 0) {
|
||||||
|
allocator->backend->log(AQ_LOG_ERROR, std::format("GBM: Failed to query fd for plane {}", i));
|
||||||
|
for (size_t j = 0; j < i; ++j) {
|
||||||
|
close(attrs.fds.at(j));
|
||||||
|
}
|
||||||
|
attrs.planes = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attrs.success = true;
|
||||||
|
|
||||||
|
auto modName = drmGetFormatModifierName(attrs.modifier);
|
||||||
|
|
||||||
|
allocator->backend->log(
|
||||||
|
AQ_LOG_ERROR,
|
||||||
|
std::format("GBM: Allocated a new buffer with size {} and format {} with modifier {}", attrs.size, fourccToName(attrs.format), modName ? modName : "Unknown"));
|
||||||
|
|
||||||
|
free(modName);
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::CGBMBuffer::~CGBMBuffer() {
|
||||||
|
if (bo)
|
||||||
|
gbm_bo_destroy(bo);
|
||||||
|
for (size_t i = 0; i < (size_t)attrs.planes; i++)
|
||||||
|
close(attrs.fds.at(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
eBufferCapability Aquamarine::CGBMBuffer::caps() {
|
||||||
|
return (Aquamarine::eBufferCapability)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
eBufferType Aquamarine::CGBMBuffer::type() {
|
||||||
|
return Aquamarine::eBufferType::BUFFER_TYPE_DMABUF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aquamarine::CGBMBuffer::update(const Hyprutils::Math::CRegion& damage) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CGBMBuffer::isSynchronous() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CGBMBuffer::good() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDMABUFAttrs Aquamarine::CGBMBuffer::dmabuf() {
|
||||||
|
return attrs;
|
||||||
|
}
|
||||||
|
|
||||||
|
SP<CGBMAllocator> Aquamarine::CGBMAllocator::create(int drmfd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_) {
|
||||||
|
uint64_t capabilities = 0;
|
||||||
|
if (drmGetCap(drmfd_, DRM_CAP_PRIME, &capabilities) || !(capabilities & DRM_PRIME_CAP_EXPORT)) {
|
||||||
|
backend_->log(AQ_LOG_ERROR, "Cannot create a GBM Allocator: PRIME export is not supported by the gpu.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto allocator = SP<CGBMAllocator>(new CGBMAllocator(drmfd_, backend_));
|
||||||
|
|
||||||
|
if (!allocator->gbmDevice) {
|
||||||
|
backend_->log(AQ_LOG_ERROR, "Cannot create a GBM Allocator: gbm failed to create a device.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend_->log(AQ_LOG_DEBUG, std::format("Created a GBM allocator with drm fd {}", drmfd_));
|
||||||
|
|
||||||
|
allocator->self = allocator;
|
||||||
|
|
||||||
|
return allocator;
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::CGBMAllocator::CGBMAllocator(int fd_, Hyprutils::Memory::CWeakPointer<CBackend> backend_) : fd(fd_), backend(backend_) {
|
||||||
|
gbmDevice = gbm_create_device(fd_);
|
||||||
|
if (!gbmDevice) {
|
||||||
|
backend->log(AQ_LOG_ERROR, std::format("Couldn't open a GBM device at fd {}", fd_));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gbmDeviceBackendName = gbm_device_get_backend_name(gbmDevice);
|
||||||
|
auto drmName_ = drmGetDeviceNameFromFd2(fd_);
|
||||||
|
drmName = drmName_;
|
||||||
|
free(drmName_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SP<IBuffer> Aquamarine::CGBMAllocator::acquire(const SAllocatorBufferParams& params) {
|
||||||
|
if (params.size.x < 1 || params.size.y < 1) {
|
||||||
|
backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with invalid size {}", params.size));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.format == DRM_FORMAT_INVALID) {
|
||||||
|
backend->log(AQ_LOG_ERROR, "Couldn't allocate a gbm buffer with invalid format");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto newBuffer = SP<CGBMBuffer>(new CGBMBuffer(params, self));
|
||||||
|
|
||||||
|
if (!newBuffer->good()) {
|
||||||
|
backend->log(AQ_LOG_ERROR, std::format("Couldn't allocate a gbm buffer with size {} and format {}", params.size, fourccToName(params.format)));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffers.emplace_back(newBuffer);
|
||||||
|
std::erase_if(buffers, [](const auto& b) { return b.expired(); });
|
||||||
|
return newBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CGBMAllocator::getBackend() {
|
||||||
|
return backend.lock();
|
||||||
|
}
|
93
src/allocator/Swapchain.cpp
Normal file
93
src/allocator/Swapchain.cpp
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
#include <aquamarine/allocator/Swapchain.hpp>
|
||||||
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
|
#include "FormatUtils.hpp"
|
||||||
|
|
||||||
|
using namespace Aquamarine;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
#define SP CSharedPointer
|
||||||
|
|
||||||
|
Aquamarine::CSwapchain::CSwapchain(SP<IAllocator> allocator_) : allocator(allocator_) {
|
||||||
|
if (!allocator)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CSwapchain::reconfigure(const SSwapchainOptions& options_) {
|
||||||
|
if (!allocator)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (options_.format == options.format && options_.size == options.size && options_.length == options.length)
|
||||||
|
return true; // no need to reconfigure
|
||||||
|
|
||||||
|
if (options_.format == options.format && options_.size == options.size) {
|
||||||
|
bool ok = resize(options_.length);
|
||||||
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
options = options_;
|
||||||
|
|
||||||
|
allocator->getBackend()->log(AQ_LOG_DEBUG, std::format("Swapchain: Resized a {} {} swapchain to length {}", options.size, fourccToName(options.format), options.length));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = fullReconfigure(options_);
|
||||||
|
if (!ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
options = options_;
|
||||||
|
|
||||||
|
allocator->getBackend()->log(AQ_LOG_DEBUG,
|
||||||
|
std::format("Swapchain: Reconfigured a swapchain to {} {} of length {}", options.size, fourccToName(options.format), options.length));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SP<IBuffer> Aquamarine::CSwapchain::next(int* age) {
|
||||||
|
if (!allocator || options.length <= 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
lastAcquired = (lastAcquired + 1) % options.length;
|
||||||
|
|
||||||
|
if (age)
|
||||||
|
*age = 1;
|
||||||
|
|
||||||
|
return buffers.at(lastAcquired);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CSwapchain::fullReconfigure(const SSwapchainOptions& options_) {
|
||||||
|
buffers.clear();
|
||||||
|
for (size_t i = 0; i < options_.length; ++i) {
|
||||||
|
auto buf = allocator->acquire(SAllocatorBufferParams{.size = options_.size, .format = options_.format});
|
||||||
|
if (!buf) {
|
||||||
|
allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffers.emplace_back(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CSwapchain::resize(size_t newSize) {
|
||||||
|
if (newSize == buffers.size())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (newSize < buffers.size()) {
|
||||||
|
while (buffers.size() > newSize) {
|
||||||
|
buffers.pop_back();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (buffers.size() < newSize) {
|
||||||
|
auto buf = allocator->acquire(SAllocatorBufferParams{.size = options.size, .format = options.format});
|
||||||
|
if (!buf) {
|
||||||
|
allocator->getBackend()->log(AQ_LOG_ERROR, "Swapchain: Failed acquiring a buffer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffers.emplace_back(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CSwapchain::contains(Hyprutils::Memory::CSharedPointer<IBuffer> buffer) {
|
||||||
|
return std::find(buffers.begin(), buffers.end(), buffer) != buffers.end();
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
#include <aquamarine/backend/Backend.hpp>
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
#include <aquamarine/backend/Wayland.hpp>
|
#include <aquamarine/backend/Wayland.hpp>
|
||||||
|
#include <aquamarine/allocator/GBM.hpp>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
@ -36,6 +37,10 @@ Hyprutils::Memory::CSharedPointer<CBackend> Aquamarine::CBackend::create(const s
|
||||||
|
|
||||||
backend->options = options;
|
backend->options = options;
|
||||||
backend->implementationOptions = backends;
|
backend->implementationOptions = backends;
|
||||||
|
backend->self = backend;
|
||||||
|
|
||||||
|
if (backends.size() <= 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!");
|
backend->log(AQ_LOG_DEBUG, "Creating an Aquamarine backend!");
|
||||||
|
|
||||||
|
@ -87,6 +92,17 @@ bool Aquamarine::CBackend::start() {
|
||||||
// erase failed impls
|
// erase failed impls
|
||||||
std::erase_if(implementations, [](const auto& i) { return i->pollFD() < 0; });
|
std::erase_if(implementations, [](const auto& i) { return i->pollFD() < 0; });
|
||||||
|
|
||||||
|
// TODO: obviously change this when (if) we add different allocators.
|
||||||
|
for (auto& b : implementations) {
|
||||||
|
if (b->drmFD() >= 0) {
|
||||||
|
allocator = CGBMAllocator::create(b->drmFD(), self);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allocator)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#include <aquamarine/backend/Wayland.hpp>
|
#include <aquamarine/backend/Wayland.hpp>
|
||||||
#include <wayland.hpp>
|
#include <wayland.hpp>
|
||||||
#include <xdg-shell.hpp>
|
#include <xdg-shell.hpp>
|
||||||
|
#include "Shared.hpp"
|
||||||
|
#include <string.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
#include <gbm.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
using namespace Aquamarine;
|
using namespace Aquamarine;
|
||||||
using namespace Hyprutils::Memory;
|
using namespace Hyprutils::Memory;
|
||||||
|
@ -8,13 +13,15 @@ using namespace Hyprutils::Math;
|
||||||
#define SP CSharedPointer
|
#define SP CSharedPointer
|
||||||
|
|
||||||
Aquamarine::CWaylandBackend::~CWaylandBackend() {
|
Aquamarine::CWaylandBackend::~CWaylandBackend() {
|
||||||
;
|
if (drmState.fd >= 0)
|
||||||
|
close(drmState.fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
eBackendType Aquamarine::CWaylandBackend::type() {
|
eBackendType Aquamarine::CWaylandBackend::type() {
|
||||||
return AQ_BACKEND_WAYLAND;
|
return AQ_BACKEND_WAYLAND;
|
||||||
}
|
}
|
||||||
|
|
||||||
Aquamarine::CWaylandBackend::CWaylandBackend(Hyprutils::Memory::CSharedPointer<CBackend> backend_) : backend(backend_) {
|
Aquamarine::CWaylandBackend::CWaylandBackend(SP<CBackend> backend_) : backend(backend_) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,13 +55,21 @@ bool Aquamarine::CWaylandBackend::start() {
|
||||||
} else if (NAME == "wl_compositor") {
|
} else if (NAME == "wl_compositor") {
|
||||||
backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 6, id));
|
backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 6, id));
|
||||||
waylandState.compositor = makeShared<CWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_compositor_interface, 6));
|
waylandState.compositor = makeShared<CWlCompositor>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &wl_compositor_interface, 6));
|
||||||
|
} else if (NAME == "zwp_linux_dmabuf_v1") {
|
||||||
|
backend->log(AQ_LOG_TRACE, std::format(" > binding to global: {} (version {}) with id {}", name, 5, id));
|
||||||
|
waylandState.dmabuf =
|
||||||
|
makeShared<CZwpLinuxDmabufV1>((wl_proxy*)wl_registry_bind((wl_registry*)waylandState.registry->resource(), id, &zwp_linux_dmabuf_v1_interface, 5));
|
||||||
|
if (!initDmabuf()) {
|
||||||
|
backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: zwp_linux_dmabuf_v1 init failed");
|
||||||
|
waylandState.dmabufFailed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
waylandState.registry->setGlobalRemove([this](CWlRegistry* r, uint32_t id) { ; });
|
waylandState.registry->setGlobalRemove([this](CWlRegistry* r, uint32_t id) { ; });
|
||||||
|
|
||||||
wl_display_roundtrip(waylandState.display);
|
wl_display_roundtrip(waylandState.display);
|
||||||
|
|
||||||
if (!waylandState.xdg || !waylandState.compositor || !waylandState.seat) {
|
if (!waylandState.xdg || !waylandState.compositor || !waylandState.seat || !waylandState.dmabuf || waylandState.dmabufFailed) {
|
||||||
backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: Missing protocols");
|
backend->log(AQ_LOG_ERROR, "Wayland backend cannot start: Missing protocols");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -66,8 +81,13 @@ bool Aquamarine::CWaylandBackend::start() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Aquamarine::CWaylandBackend::drmFD() {
|
||||||
|
return drmState.fd;
|
||||||
|
}
|
||||||
|
|
||||||
void Aquamarine::CWaylandBackend::createOutput(const std::string& szName) {
|
void Aquamarine::CWaylandBackend::createOutput(const std::string& szName) {
|
||||||
outputs.emplace_back(SP<CWaylandOutput>(new CWaylandOutput(szName, self)));
|
auto o = outputs.emplace_back(SP<CWaylandOutput>(new CWaylandOutput(szName, self)));
|
||||||
|
backend->events.newOutput.emit(SP<IOutput>(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
int Aquamarine::CWaylandBackend::pollFD() {
|
int Aquamarine::CWaylandBackend::pollFD() {
|
||||||
|
@ -95,8 +115,7 @@ bool Aquamarine::CWaylandBackend::dispatchEvents() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Aquamarine::CWaylandKeyboard::CWaylandKeyboard(Hyprutils::Memory::CSharedPointer<CWlKeyboard> keyboard_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) :
|
Aquamarine::CWaylandKeyboard::CWaylandKeyboard(SP<CWlKeyboard> keyboard_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : keyboard(keyboard_), backend(backend_) {
|
||||||
keyboard(keyboard_), backend(backend_) {
|
|
||||||
if (!keyboard->resource())
|
if (!keyboard->resource())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -111,7 +130,7 @@ Aquamarine::CWaylandKeyboard::CWaylandKeyboard(Hyprutils::Memory::CSharedPointer
|
||||||
});
|
});
|
||||||
|
|
||||||
keyboard->setModifiers([this](CWlKeyboard* r, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
|
keyboard->setModifiers([this](CWlKeyboard* r, uint32_t serial, uint32_t depressed, uint32_t latched, uint32_t locked, uint32_t group) {
|
||||||
events.key.emit(SModifiersEvent{
|
events.modifiers.emit(SModifiersEvent{
|
||||||
.depressed = depressed,
|
.depressed = depressed,
|
||||||
.latched = latched,
|
.latched = latched,
|
||||||
.locked = locked,
|
.locked = locked,
|
||||||
|
@ -128,19 +147,21 @@ const std::string& Aquamarine::CWaylandKeyboard::getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Aquamarine::CWaylandPointer::CWaylandPointer(Hyprutils::Memory::CSharedPointer<CWlPointer> pointer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) :
|
Aquamarine::CWaylandPointer::CWaylandPointer(SP<CWlPointer> pointer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : pointer(pointer_), backend(backend_) {
|
||||||
pointer(pointer_), backend(backend_) {
|
|
||||||
if (!pointer->resource())
|
if (!pointer->resource())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
backend->backend->log(AQ_LOG_DEBUG, "New wayland pointer wl_pointer");
|
backend->backend->log(AQ_LOG_DEBUG, "New wayland pointer wl_pointer");
|
||||||
|
|
||||||
pointer->setMotion([this](CWlPointer* r, uint32_t serial, wl_fixed_t x, wl_fixed_t y) {
|
pointer->setMotion([this](CWlPointer* r, uint32_t serial, wl_fixed_t x, wl_fixed_t y) {
|
||||||
if (!backend->focusedOutput || !backend->focusedOutput->state->mode)
|
if (!backend->focusedOutput || (!backend->focusedOutput->state->mode && !backend->focusedOutput->state->customMode.has_value()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const Vector2D size =
|
||||||
|
backend->focusedOutput->state->customMode.has_value() ? backend->focusedOutput->state->customMode->pixelSize : backend->focusedOutput->state->mode->pixelSize;
|
||||||
|
|
||||||
Vector2D local = {wl_fixed_to_double(x), wl_fixed_to_double(y)};
|
Vector2D local = {wl_fixed_to_double(x), wl_fixed_to_double(y)};
|
||||||
local = local / backend->focusedOutput->state->mode->pixelSize;
|
local = local / size;
|
||||||
|
|
||||||
events.warp.emit(SWarpEvent{
|
events.warp.emit(SWarpEvent{
|
||||||
.absolute = local,
|
.absolute = local,
|
||||||
|
@ -192,15 +213,17 @@ void Aquamarine::CWaylandBackend::initSeat() {
|
||||||
const bool HAS_KEYBOARD = ((uint32_t)cap) & WL_SEAT_CAPABILITY_KEYBOARD;
|
const bool HAS_KEYBOARD = ((uint32_t)cap) & WL_SEAT_CAPABILITY_KEYBOARD;
|
||||||
const bool HAS_POINTER = ((uint32_t)cap) & WL_SEAT_CAPABILITY_POINTER;
|
const bool HAS_POINTER = ((uint32_t)cap) & WL_SEAT_CAPABILITY_POINTER;
|
||||||
|
|
||||||
if (HAS_KEYBOARD && keyboards.empty())
|
if (HAS_KEYBOARD && keyboards.empty()) {
|
||||||
keyboards.emplace_back(makeShared<CWaylandKeyboard>(SP<CWlKeyboard>(waylandState.seat->sendGetKeyboard()), self));
|
auto k = keyboards.emplace_back(makeShared<CWaylandKeyboard>(makeShared<CWlKeyboard>(waylandState.seat->sendGetKeyboard()), self));
|
||||||
else if (!HAS_KEYBOARD && !keyboards.empty())
|
backend->events.newKeyboard.emit(SP<IKeyboard>(k));
|
||||||
|
} else if (!HAS_KEYBOARD && !keyboards.empty())
|
||||||
keyboards.clear();
|
keyboards.clear();
|
||||||
|
|
||||||
if (HAS_POINTER && pointers.empty())
|
if (HAS_POINTER && pointers.empty()) {
|
||||||
keyboards.emplace_back(makeShared<CWaylandKeyboard>(SP<CWlKeyboard>(waylandState.seat->sendGetKeyboard()), self));
|
auto p = pointers.emplace_back(makeShared<CWaylandPointer>(makeShared<CWlPointer>(waylandState.seat->sendGetPointer()), self));
|
||||||
else if (!HAS_POINTER && !keyboards.empty())
|
backend->events.newPointer.emit(SP<IPointer>(p));
|
||||||
keyboards.clear();
|
} else if (!HAS_POINTER && !pointers.empty())
|
||||||
|
pointers.clear();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,17 +231,82 @@ void Aquamarine::CWaylandBackend::initShell() {
|
||||||
waylandState.xdg->setPing([](CXdgWmBase* r, uint32_t serial) { r->sendPong(serial); });
|
waylandState.xdg->setPing([](CXdgWmBase* r, uint32_t serial) { r->sendPong(serial); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CWaylandBackend::initDmabuf() {
|
||||||
|
waylandState.dmabufFeedback = makeShared<CZwpLinuxDmabufFeedbackV1>(waylandState.dmabuf->sendGetDefaultFeedback());
|
||||||
|
if (!waylandState.dmabufFeedback) {
|
||||||
|
backend->log(AQ_LOG_ERROR, "initDmabuf: failed to get default feedback");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
waylandState.dmabufFeedback->setDone([this](CZwpLinuxDmabufFeedbackV1* r) {
|
||||||
|
// no-op
|
||||||
|
backend->log(AQ_LOG_DEBUG, "zwp_linux_dmabuf_v1: Got done");
|
||||||
|
});
|
||||||
|
|
||||||
|
waylandState.dmabufFeedback->setMainDevice([this](CZwpLinuxDmabufFeedbackV1* r, wl_array* deviceArr) {
|
||||||
|
backend->log(AQ_LOG_DEBUG, "zwp_linux_dmabuf_v1: Got main device");
|
||||||
|
|
||||||
|
dev_t device;
|
||||||
|
ASSERT(deviceArr->size == sizeof(device));
|
||||||
|
memcpy(&device, deviceArr->data, sizeof(device));
|
||||||
|
|
||||||
|
drmDevice* drmDev;
|
||||||
|
if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) {
|
||||||
|
backend->log(AQ_LOG_ERROR, "zwp_linux_dmabuf_v1: drmGetDeviceFromDevId failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* name = nullptr;
|
||||||
|
if (drmDev->available_nodes & (1 << DRM_NODE_RENDER))
|
||||||
|
name = drmDev->nodes[DRM_NODE_RENDER];
|
||||||
|
else {
|
||||||
|
// Likely a split display/render setup. Pick the primary node and hope
|
||||||
|
// Mesa will open the right render node under-the-hood.
|
||||||
|
ASSERT(drmDev->available_nodes & (1 << DRM_NODE_PRIMARY));
|
||||||
|
name = drmDev->nodes[DRM_NODE_PRIMARY];
|
||||||
|
backend->log(AQ_LOG_WARNING, "zwp_linux_dmabuf_v1: DRM device has no render node, using primary.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
backend->log(AQ_LOG_ERROR, "zwp_linux_dmabuf_v1: no node name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drmState.nodeName = name;
|
||||||
|
|
||||||
|
drmFreeDevice(&drmDev);
|
||||||
|
|
||||||
|
backend->log(AQ_LOG_DEBUG, std::format("zwp_linux_dmabuf_v1: Got node {}", drmState.nodeName));
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: format table and tranche
|
||||||
|
|
||||||
|
wl_display_roundtrip(waylandState.display);
|
||||||
|
|
||||||
|
if (!drmState.nodeName.empty()) {
|
||||||
|
drmState.fd = open(drmState.nodeName.c_str(), O_RDWR | O_NONBLOCK | O_CLOEXEC);
|
||||||
|
if (drmState.fd < 0) {
|
||||||
|
backend->log(AQ_LOG_ERROR, std::format("zwp_linux_dmabuf_v1: Failed to open node {}", drmState.nodeName));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
backend->log(AQ_LOG_DEBUG, std::format("zwp_linux_dmabuf_v1: opened node {} with fd {}", drmState.nodeName, drmState.fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : name(name_), backend(backend_) {
|
Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : name(name_), backend(backend_) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
|
||||||
waylandState.surface = SP<CWlSurface>(backend->waylandState.compositor->sendCreateSurface());
|
waylandState.surface = makeShared<CWlSurface>(backend->waylandState.compositor->sendCreateSurface());
|
||||||
|
|
||||||
if (!waylandState.surface->resource()) {
|
if (!waylandState.surface->resource()) {
|
||||||
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no surface given. Errno: {}", name, errno));
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no surface given. Errno: {}", name, errno));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
waylandState.xdgSurface = SP<CXdgSurface>(backend->waylandState.xdg->sendGetXdgSurface(waylandState.surface->resource()));
|
waylandState.xdgSurface = makeShared<CXdgSurface>(backend->waylandState.xdg->sendGetXdgSurface(waylandState.surface->resource()));
|
||||||
|
|
||||||
if (!waylandState.xdgSurface->resource()) {
|
if (!waylandState.xdgSurface->resource()) {
|
||||||
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgSurface given. Errno: {}", name, errno));
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgSurface given. Errno: {}", name, errno));
|
||||||
|
@ -230,7 +318,7 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
|
||||||
r->sendAckConfigure(serial);
|
r->sendAckConfigure(serial);
|
||||||
});
|
});
|
||||||
|
|
||||||
waylandState.xdgToplevel = SP<CXdgToplevel>(waylandState.xdgSurface->sendGetToplevel());
|
waylandState.xdgToplevel = makeShared<CXdgToplevel>(waylandState.xdgSurface->sendGetToplevel());
|
||||||
|
|
||||||
if (!waylandState.xdgToplevel->resource()) {
|
if (!waylandState.xdgToplevel->resource()) {
|
||||||
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgToplevel given. Errno: {}", name, errno));
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: no xdgToplevel given. Errno: {}", name, errno));
|
||||||
|
@ -241,13 +329,29 @@ Aquamarine::CWaylandOutput::CWaylandOutput(const std::string& name_, Hyprutils::
|
||||||
[this](CXdgToplevel* r, wl_array* arr) { backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: wm_capabilities received", name)); });
|
[this](CXdgToplevel* r, wl_array* arr) { backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: wm_capabilities received", name)); });
|
||||||
|
|
||||||
waylandState.xdgToplevel->setConfigure([this](CXdgToplevel* r, int32_t w, int32_t h, wl_array* arr) {
|
waylandState.xdgToplevel->setConfigure([this](CXdgToplevel* r, int32_t w, int32_t h, wl_array* arr) {
|
||||||
|
// we only create the swapchain here because in the main func we still don't have an allocator
|
||||||
|
if (!swapchain) {
|
||||||
|
swapchain = makeShared<CSwapchain>(backend->backend->allocator);
|
||||||
|
if (!swapchain) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {} failed: swapchain creation failed", name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: configure toplevel with {}x{}", name, w, h));
|
backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: configure toplevel with {}x{}", name, w, h));
|
||||||
events.state.emit(SStateEvent{.size = {w, h}});
|
events.state.emit(SStateEvent{.size = {w, h}});
|
||||||
|
sendFrameAndSetCallback();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
auto inputRegion = makeShared<CWlRegion>(backend->waylandState.compositor->sendCreateRegion());
|
||||||
|
inputRegion->sendAdd(0, 0, INT32_MAX, INT32_MAX);
|
||||||
|
|
||||||
|
waylandState.surface->sendSetInputRegion(inputRegion.get());
|
||||||
waylandState.surface->sendAttach(nullptr, 0, 0);
|
waylandState.surface->sendAttach(nullptr, 0, 0);
|
||||||
waylandState.surface->sendCommit();
|
waylandState.surface->sendCommit();
|
||||||
|
|
||||||
|
inputRegion->sendDestroy();
|
||||||
|
|
||||||
backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: initialized", name));
|
backend->backend->log(AQ_LOG_DEBUG, std::format("Output {}: initialized", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,6 +364,114 @@ Aquamarine::CWaylandOutput::~CWaylandOutput() {
|
||||||
waylandState.surface->sendDestroy();
|
waylandState.surface->sendDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Aquamarine::CWaylandOutput::commit() {
|
bool Aquamarine::CWaylandOutput::commit() {
|
||||||
;
|
Vector2D pixelSize = {};
|
||||||
|
uint32_t refreshRate = 0;
|
||||||
|
|
||||||
|
if (state->customMode)
|
||||||
|
pixelSize = state->customMode->pixelSize;
|
||||||
|
else if (state->mode)
|
||||||
|
pixelSize = state->mode->pixelSize;
|
||||||
|
else {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: invalid mode", name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t format = state->drmFormat;
|
||||||
|
|
||||||
|
if (format == DRM_FORMAT_INVALID) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: invalid format", name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!swapchain->reconfigure(SSwapchainOptions{.length = 2, .size = pixelSize, .format = format})) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: swapchain failed reconfiguring", name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state->buffer) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: no buffer", name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto wlBuffer = wlBufferFromBuffer(state->buffer);
|
||||||
|
|
||||||
|
if (!wlBuffer) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, std::format("Output {}: pending state rejected: no wlBuffer??", name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wlBuffer->pendingRelease)
|
||||||
|
backend->backend->log(AQ_LOG_WARNING, std::format("Output {}: pending state has a non-released buffer??", name));
|
||||||
|
|
||||||
|
wlBuffer->pendingRelease = true;
|
||||||
|
|
||||||
|
waylandState.surface->sendAttach(wlBuffer->waylandState.buffer.get(), 0, 0);
|
||||||
|
waylandState.surface->sendDamageBuffer(0, 0, INT32_MAX, INT32_MAX);
|
||||||
|
waylandState.surface->sendCommit();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SP<CWaylandBuffer> Aquamarine::CWaylandOutput::wlBufferFromBuffer(SP<IBuffer> buffer) {
|
||||||
|
std::erase_if(backendState.buffers, [this](const auto& el) { return el.first.expired() || !swapchain->contains(el.first.lock()); });
|
||||||
|
|
||||||
|
for (auto& [k, v] : backendState.buffers) {
|
||||||
|
if (k != buffer)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new one
|
||||||
|
auto wlBuffer = makeShared<CWaylandBuffer>(buffer, backend);
|
||||||
|
|
||||||
|
if (!wlBuffer->good())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
backendState.buffers.emplace_back(std::make_pair<>(buffer, wlBuffer));
|
||||||
|
|
||||||
|
return wlBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aquamarine::CWaylandOutput::sendFrameAndSetCallback() {
|
||||||
|
events.frame.emit();
|
||||||
|
if (waylandState.frameCallback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
waylandState.frameCallback = makeShared<CWlCallback>(waylandState.surface->sendFrame());
|
||||||
|
waylandState.frameCallback->setDone([this](CWlCallback* r, uint32_t ms) {
|
||||||
|
events.frame.emit();
|
||||||
|
waylandState.frameCallback.reset();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::CWaylandBuffer::CWaylandBuffer(SP<IBuffer> buffer_, Hyprutils::Memory::CWeakPointer<CWaylandBackend> backend_) : buffer(buffer_), backend(backend_) {
|
||||||
|
auto params = makeShared<CZwpLinuxBufferParamsV1>(backend->waylandState.dmabuf->sendCreateParams());
|
||||||
|
|
||||||
|
if (!params) {
|
||||||
|
backend->backend->log(AQ_LOG_ERROR, "WaylandBuffer: failed to query params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto attrs = buffer->dmabuf();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < attrs.planes; ++i) {
|
||||||
|
params->sendAdd(attrs.fds.at(i), i, attrs.offsets.at(i), attrs.strides.at(i), attrs.modifier >> 32, attrs.modifier & 0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
waylandState.buffer = makeShared<CWlBuffer>(params->sendCreateImmed(attrs.size.x, attrs.size.y, attrs.format, (zwpLinuxBufferParamsV1Flags)0));
|
||||||
|
|
||||||
|
waylandState.buffer->setRelease([this](CWlBuffer* r) { pendingRelease = false; });
|
||||||
|
|
||||||
|
params->sendDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
Aquamarine::CWaylandBuffer::~CWaylandBuffer() {
|
||||||
|
if (waylandState.buffer && waylandState.buffer->resource())
|
||||||
|
waylandState.buffer->sendDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Aquamarine::CWaylandBuffer::good() {
|
||||||
|
return waylandState.buffer && waylandState.buffer->resource();
|
||||||
}
|
}
|
6
src/include/FormatUtils.hpp
Normal file
6
src/include/FormatUtils.hpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
std::string fourccToName(uint32_t drmFormat);
|
8
src/utils/FormatUtils.cpp
Normal file
8
src/utils/FormatUtils.cpp
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#include "FormatUtils.hpp"
|
||||||
|
#include <drm_fourcc.h>
|
||||||
|
#include <xf86drm.h>
|
||||||
|
|
||||||
|
std::string fourccToName(uint32_t drmFormat) {
|
||||||
|
auto fmt = drmGetFormatName(drmFormat);
|
||||||
|
return fmt ? fmt : "unknown";
|
||||||
|
}
|
|
@ -1,6 +1,12 @@
|
||||||
#include <aquamarine/backend/Backend.hpp>
|
#include <aquamarine/backend/Backend.hpp>
|
||||||
|
#include <aquamarine/output/Output.hpp>
|
||||||
|
#include <aquamarine/input/Input.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace Hyprutils::Signal;
|
||||||
|
using namespace Hyprutils::Memory;
|
||||||
|
#define SP CSharedPointer
|
||||||
|
|
||||||
static const char* aqLevelToString(Aquamarine::eBackendLogLevel level) {
|
static const char* aqLevelToString(Aquamarine::eBackendLogLevel level) {
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return "TRACE";
|
case Aquamarine::eBackendLogLevel::AQ_LOG_TRACE: return "TRACE";
|
||||||
|
@ -18,6 +24,29 @@ void aqLog(Aquamarine::eBackendLogLevel level, std::string msg) {
|
||||||
std::cout << "[AQ] [" << aqLevelToString(level) << "] " << msg << "\n";
|
std::cout << "[AQ] [" << aqLevelToString(level) << "] " << msg << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CHyprSignalListener newOutputListener, outputFrameListener, outputStateListener, mouseMotionListener, keyboardKeyListener, newMouseListener, newKeyboardListener;
|
||||||
|
SP<Aquamarine::IOutput> output;
|
||||||
|
|
||||||
|
//
|
||||||
|
void onFrame() {
|
||||||
|
std::cout << "[Client] onFrame\n";
|
||||||
|
|
||||||
|
auto buf = output->swapchain->next(nullptr);
|
||||||
|
|
||||||
|
output->state->buffer = buf;
|
||||||
|
output->commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onState(const Aquamarine::IOutput::SStateEvent& event) {
|
||||||
|
std::cout << "[Client] onState with size " << std::format("{}", event.size) << "\n";
|
||||||
|
|
||||||
|
output->state->enabled = true;
|
||||||
|
output->state->customMode = {.pixelSize = event.size};
|
||||||
|
output->state->drmFormat = DRM_FORMAT_XRGB8888;
|
||||||
|
|
||||||
|
output->commit();
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv, char** envp) {
|
int main(int argc, char** argv, char** envp) {
|
||||||
Aquamarine::SBackendOptions options;
|
Aquamarine::SBackendOptions options;
|
||||||
options.logFunction = aqLog;
|
options.logFunction = aqLog;
|
||||||
|
@ -30,7 +59,32 @@ int main(int argc, char** argv, char** envp) {
|
||||||
|
|
||||||
auto aqBackend = Aquamarine::CBackend::create(implementations, options);
|
auto aqBackend = Aquamarine::CBackend::create(implementations, options);
|
||||||
|
|
||||||
if (!aqBackend->start()) {
|
newOutputListener = aqBackend->events.newOutput.registerListener([](std::any data) {
|
||||||
|
output = std::any_cast<SP<Aquamarine::IOutput>>(data);
|
||||||
|
|
||||||
|
std::cout << "[Client] Got a new output named " << output->name << "\n";
|
||||||
|
|
||||||
|
outputFrameListener = output->events.frame.registerListener([](std::any data) { onFrame(); });
|
||||||
|
outputStateListener = output->events.state.registerListener([](std::any data) { onState(std::any_cast<Aquamarine::IOutput::SStateEvent>(data)); });
|
||||||
|
});
|
||||||
|
|
||||||
|
newMouseListener = aqBackend->events.newPointer.registerListener([] (std::any pointer) {
|
||||||
|
auto p = std::any_cast<SP<Aquamarine::IPointer>>(pointer);
|
||||||
|
mouseMotionListener = p->events.warp.registerListener([] (std::any data) {
|
||||||
|
auto e = std::any_cast<Aquamarine::IPointer::SWarpEvent>(data);
|
||||||
|
std::cout << "[Client] Mouse warped to " << std::format("{}", e.absolute) << "\n";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
newKeyboardListener = aqBackend->events.newKeyboard.registerListener([] (std::any keeb) {
|
||||||
|
auto k = std::any_cast<SP<Aquamarine::IKeyboard>>(keeb);
|
||||||
|
keyboardKeyListener = k->events.key.registerListener([] (std::any data) {
|
||||||
|
auto e = std::any_cast<Aquamarine::IKeyboard::SKeyEvent>(data);
|
||||||
|
std::cout << "[Client] Key " << std::format("{}", e.key) << " state: " << e.pressed << " \n";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!aqBackend || !aqBackend->start()) {
|
||||||
std::cout << "Failed to start the aq backend\n";
|
std::cout << "Failed to start the aq backend\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue