diff --git a/.gitignore b/.gitignore
index 33419bc..2905419 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
.vscode/
build/
-protocols/
\ No newline at end of file
+protocols/*.h
+protocols/*.c
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6aac750..3ff8e19 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,7 +33,7 @@ message(STATUS "Checking deps...")
find_package(Threads REQUIRED)
find_package(PkgConfig REQUIRED)
find_package(OpenGL REQUIRED)
-pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols wayland-egl hyprlang>=0.4.0 egl opengl xkbcommon cairo pangocairo pam)
+pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols wayland-egl hyprlang>=0.4.0 egl opengl xkbcommon cairo pangocairo pam libdrm gbm)
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
add_executable(hyprlock ${SRCFILES})
@@ -75,6 +75,8 @@ protocol("staging/cursor-shape/cursor-shape-v1.xml" "cursor-shape-v1" false)
protocol("unstable/tablet/tablet-unstable-v2.xml" "tablet-unstable-v2" false)
protocol("staging/fractional-scale/fractional-scale-v1.xml" "fractional-scale-v1" false)
protocol("stable/viewporter/viewporter.xml" "viewporter" false)
+protocol("protocols/wlr-screencopy-unstable-v1.xml" "wlr-screencopy-unstable-v1" true)
+protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml" "linux-dmabuf-unstable-v1" false)
# Installation
install(TARGETS hyprlock)
diff --git a/protocols/wlr-screencopy-unstable-v1.xml b/protocols/wlr-screencopy-unstable-v1.xml
new file mode 100644
index 0000000..b60ae2c
--- /dev/null
+++ b/protocols/wlr-screencopy-unstable-v1.xml
@@ -0,0 +1,232 @@
+
+
+
+ Copyright © 2018 Simon Ser
+ Copyright © 2019 Andri Yngvason
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
+
+
+ This protocol allows clients to ask the compositor to copy part of the
+ screen content to a client buffer.
+
+ Warning! The protocol described in this file is experimental and
+ backward incompatible changes may be made. Backward compatible changes
+ may be added together with the corresponding interface version bump.
+ Backward incompatible changes are done by bumping the version number in
+ the protocol and interface names and resetting the interface version.
+ Once the protocol is to be declared stable, the 'z' prefix and the
+ version number in the protocol and interface names are removed and the
+ interface version number is reset.
+
+
+
+
+ This object is a manager which offers requests to start capturing from a
+ source.
+
+
+
+
+ Capture the next frame of an entire output.
+
+
+
+
+
+
+
+
+ Capture the next frame of an output's region.
+
+ The region is given in output logical coordinates, see
+ xdg_output.logical_size. The region will be clipped to the output's
+ extents.
+
+
+
+
+
+
+
+
+
+
+
+
+ All objects created by the manager will still remain valid, until their
+ appropriate destroy request has been called.
+
+
+
+
+
+
+ This object represents a single frame.
+
+ When created, a series of buffer events will be sent, each representing a
+ supported buffer type. The "buffer_done" event is sent afterwards to
+ indicate that all supported buffer types have been enumerated. The client
+ will then be able to send a "copy" request. If the capture is successful,
+ the compositor will send a "flags" followed by a "ready" event.
+
+ For objects version 2 or lower, wl_shm buffers are always supported, ie.
+ the "buffer" event is guaranteed to be sent.
+
+ If the capture failed, the "failed" event is sent. This can happen anytime
+ before the "ready" event.
+
+ Once either a "ready" or a "failed" event is received, the client should
+ destroy the frame.
+
+
+
+
+ Provides information about wl_shm buffer parameters that need to be
+ used for this frame. This event is sent once after the frame is created
+ if wl_shm buffers are supported.
+
+
+
+
+
+
+
+
+
+ Copy the frame to the supplied buffer. The buffer must have a the
+ correct size, see zwlr_screencopy_frame_v1.buffer and
+ zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a
+ supported format.
+
+ If the frame is successfully copied, a "flags" and a "ready" events are
+ sent. Otherwise, a "failed" event is sent.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Provides flags about the frame. This event is sent once before the
+ "ready" event.
+
+
+
+
+
+
+ Called as soon as the frame is copied, indicating it is available
+ for reading. This event includes the time at which presentation happened
+ at.
+
+ The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples,
+ each component being an unsigned 32-bit value. Whole seconds are in
+ tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo,
+ and the additional fractional part in tv_nsec as nanoseconds. Hence,
+ for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part
+ may have an arbitrary offset at start.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+
+
+
+ This event indicates that the attempted frame copy has failed.
+
+ After receiving this event, the client should destroy the object.
+
+
+
+
+
+ Destroys the frame. This request can be sent at any time by the client.
+
+
+
+
+
+
+ Same as copy, except it waits until there is damage to copy.
+
+
+
+
+
+
+ This event is sent right before the ready event when copy_with_damage is
+ requested. It may be generated multiple times for each copy_with_damage
+ request.
+
+ The arguments describe a box around an area that has changed since the
+ last copy request that was derived from the current screencopy manager
+ instance.
+
+ The union of all regions received between the call to copy_with_damage
+ and a ready event is the total damage since the prior ready event.
+
+
+
+
+
+
+
+
+
+
+ Provides information about linux-dmabuf buffer parameters that need to
+ be used for this frame. This event is sent once after the frame is
+ created if linux-dmabuf buffers are supported.
+
+
+
+
+
+
+
+
+ This event is sent once after all buffer events have been sent.
+
+ The client should proceed to create a buffer of one of the supported
+ types, and send a "copy" request.
+
+
+
+
diff --git a/src/core/hyprlock.cpp b/src/core/hyprlock.cpp
index fcbdfaf..35f8617 100644
--- a/src/core/hyprlock.cpp
+++ b/src/core/hyprlock.cpp
@@ -10,6 +10,9 @@
#include
#include
#include
+#include
+#include
+#include
CHyprlock::CHyprlock(const std::string& wlDisplay) {
m_sWaylandState.display = wl_display_connect(wlDisplay.empty() ? nullptr : wlDisplay.c_str());
@@ -24,8 +27,6 @@ CHyprlock::CHyprlock(const std::string& wlDisplay) {
if (!m_pXKBContext)
Debug::log(ERR, "Failed to create xkb context");
- g_pRenderer = std::make_unique();
-
const auto GRACE = (Hyprlang::INT* const*)g_pConfigManager->getValuePtr("general:grace");
m_tGraceEnds = **GRACE ? std::chrono::system_clock::now() + std::chrono::seconds(**GRACE) : std::chrono::system_clock::from_time_t(0);
}
@@ -42,6 +43,177 @@ inline const wl_seat_listener seatListener = {
// end wl_seat
+// dmabuf
+
+static void handleDMABUFFormat(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1, uint32_t format) {
+ ;
+}
+
+static void handleDMABUFModifier(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {
+ g_pHyprlock->dma.dmabufMods.push_back({format, (((uint64_t)modifier_hi) << 32) | modifier_lo});
+}
+
+inline const zwp_linux_dmabuf_v1_listener dmabufListener = {
+ .format = handleDMABUFFormat,
+ .modifier = handleDMABUFModifier,
+};
+
+static void dmabufFeedbackMainDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* device_arr) {
+ Debug::log(LOG, "[core] dmabufFeedbackMainDevice");
+
+ RASSERT(!g_pHyprlock->dma.gbm, "double dmabuf feedback");
+
+ dev_t device;
+ assert(device_arr->size == sizeof(device));
+ memcpy(&device, device_arr->data, sizeof(device));
+
+ drmDevice* drmDev;
+ if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0) {
+ Debug::log(WARN, "[dmabuf] unable to open main device?");
+ exit(1);
+ }
+
+ g_pHyprlock->dma.gbmDevice = g_pHyprlock->createGBMDevice(drmDev);
+}
+
+static void dmabufFeedbackFormatTable(void* data, zwp_linux_dmabuf_feedback_v1* feedback, int fd, uint32_t size) {
+ Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");
+
+ g_pHyprlock->dma.dmabufMods.clear();
+
+ g_pHyprlock->dma.formatTable = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (g_pHyprlock->dma.formatTable == MAP_FAILED) {
+ Debug::log(ERR, "[core] format table failed to mmap");
+ g_pHyprlock->dma.formatTable = nullptr;
+ g_pHyprlock->dma.formatTableSize = 0;
+ return;
+ }
+
+ g_pHyprlock->dma.formatTableSize = size;
+}
+
+static void dmabufFeedbackDone(void* data, zwp_linux_dmabuf_feedback_v1* feedback) {
+ Debug::log(TRACE, "[core] dmabufFeedbackDone");
+
+ if (g_pHyprlock->dma.formatTable)
+ munmap(g_pHyprlock->dma.formatTable, g_pHyprlock->dma.formatTableSize);
+
+ g_pHyprlock->dma.formatTable = nullptr;
+ g_pHyprlock->dma.formatTableSize = 0;
+}
+
+static void dmabufFeedbackTrancheTargetDevice(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* device_arr) {
+ Debug::log(TRACE, "[core] dmabufFeedbackTrancheTargetDevice");
+
+ dev_t device;
+ assert(device_arr->size == sizeof(device));
+ memcpy(&device, device_arr->data, sizeof(device));
+
+ drmDevice* drmDev;
+ if (drmGetDeviceFromDevId(device, /* flags */ 0, &drmDev) != 0)
+ return;
+
+ if (g_pHyprlock->dma.gbmDevice) {
+ drmDevice* drmDevRenderer = NULL;
+ drmGetDevice2(gbm_device_get_fd(g_pHyprlock->dma.gbmDevice), /* flags */ 0, &drmDevRenderer);
+ g_pHyprlock->dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
+ } else {
+ g_pHyprlock->dma.gbmDevice = g_pHyprlock->createGBMDevice(drmDev);
+ g_pHyprlock->dma.deviceUsed = g_pHyprlock->dma.gbm;
+ }
+}
+
+static void dmabufFeedbackTrancheFlags(void* data, zwp_linux_dmabuf_feedback_v1* feedback, uint32_t flags) {
+ ;
+}
+
+static void dmabufFeedbackTrancheFormats(void* data, zwp_linux_dmabuf_feedback_v1* feedback, wl_array* indices) {
+ Debug::log(TRACE, "[core] dmabufFeedbackTrancheFormats");
+
+ if (!g_pHyprlock->dma.deviceUsed || !g_pHyprlock->dma.formatTable)
+ return;
+
+ struct fm_entry {
+ uint32_t format;
+ uint32_t padding;
+ uint64_t modifier;
+ };
+ // An entry in the table has to be 16 bytes long
+ assert(sizeof(fm_entry) == 16);
+
+ uint32_t n_modifiers = g_pHyprlock->dma.formatTableSize / sizeof(fm_entry);
+ fm_entry* fm_entry = (struct fm_entry*)g_pHyprlock->dma.formatTable;
+ uint16_t* idx;
+
+ for (idx = (uint16_t*)indices->data; (const char*)idx < (const char*)indices->data + indices->size; idx++) {
+ if (*idx >= n_modifiers)
+ continue;
+
+ g_pHyprlock->dma.dmabufMods.push_back({(fm_entry + *idx)->format, (fm_entry + *idx)->modifier});
+ }
+}
+
+static void dmabufFeedbackTrancheDone(void* data, struct zwp_linux_dmabuf_feedback_v1* zwp_linux_dmabuf_feedback_v1) {
+ Debug::log(TRACE, "[core] dmabufFeedbackTrancheDone");
+
+ g_pHyprlock->dma.deviceUsed = false;
+}
+
+inline const zwp_linux_dmabuf_feedback_v1_listener dmabufFeedbackListener = {
+ .done = dmabufFeedbackDone,
+ .format_table = dmabufFeedbackFormatTable,
+ .main_device = dmabufFeedbackMainDevice,
+ .tranche_done = dmabufFeedbackTrancheDone,
+ .tranche_target_device = dmabufFeedbackTrancheTargetDevice,
+ .tranche_formats = dmabufFeedbackTrancheFormats,
+ .tranche_flags = dmabufFeedbackTrancheFlags,
+};
+
+static char* gbm_find_render_node(drmDevice* device) {
+ drmDevice* devices[64];
+ char* render_node = NULL;
+
+ int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0]));
+ for (int i = 0; i < n; ++i) {
+ drmDevice* dev = devices[i];
+ if (device && !drmDevicesEqual(device, dev)) {
+ continue;
+ }
+ if (!(dev->available_nodes & (1 << DRM_NODE_RENDER)))
+ continue;
+
+ render_node = strdup(dev->nodes[DRM_NODE_RENDER]);
+ break;
+ }
+
+ drmFreeDevices(devices, n);
+ return render_node;
+}
+
+gbm_device* CHyprlock::createGBMDevice(drmDevice* dev) {
+ char* renderNode = gbm_find_render_node(dev);
+
+ if (!renderNode) {
+ Debug::log(ERR, "[core] Couldn't find a render node");
+ return nullptr;
+ }
+
+ Debug::log(TRACE, "[core] createGBMDevice: render node {}", renderNode);
+
+ int fd = open(renderNode, O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ Debug::log(ERR, "[core] couldn't open render node");
+ free(renderNode);
+ return NULL;
+ }
+
+ free(renderNode);
+ return gbm_create_device(fd);
+}
+
+// end dmabuf
+
// wl_registry
static void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
@@ -75,11 +247,9 @@ void CHyprlock::onGlobal(void* data, struct wl_registry* registry, uint32_t name
Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
} else if (IFACE == wl_output_interface.name) {
m_vOutputs.emplace_back(std::make_unique((wl_output*)wl_registry_bind(registry, name, &wl_output_interface, version), name));
-
Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
} else if (IFACE == wp_cursor_shape_manager_v1_interface.name) {
m_pCursorShape = std::make_unique((wp_cursor_shape_manager_v1*)wl_registry_bind(registry, name, &wp_cursor_shape_manager_v1_interface, version));
-
Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
} else if (IFACE == wl_compositor_interface.name) {
m_sWaylandState.compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, version);
@@ -90,6 +260,19 @@ void CHyprlock::onGlobal(void* data, struct wl_registry* registry, uint32_t name
} else if (IFACE == wp_viewporter_interface.name) {
m_sWaylandState.viewporter = (wp_viewporter*)wl_registry_bind(registry, name, &wp_viewporter_interface, version);
Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
+ } else if (IFACE == zwp_linux_dmabuf_v1_interface.name) {
+ if (version < 4) {
+ Debug::log(ERR, "cannot use linux_dmabuf with ver < 4");
+ return;
+ }
+
+ dma.linuxDmabuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version);
+ dma.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)dma.linuxDmabuf);
+ zwp_linux_dmabuf_feedback_v1_add_listener((zwp_linux_dmabuf_feedback_v1*)dma.linuxDmabufFeedback, &dmabufFeedbackListener, nullptr);
+ Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
+ } else if (IFACE == zwlr_screencopy_manager_v1_interface.name) {
+ m_sWaylandState.screencopy = (zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, version);
+ Debug::log(LOG, " > Bound to {} v{}", IFACE, version);
}
}
@@ -115,6 +298,8 @@ void CHyprlock::run() {
// gather info about monitors
wl_display_roundtrip(m_sWaylandState.display);
+ g_pRenderer = std::make_unique();
+
lockSession();
pollfd pollfds[] = {
@@ -244,8 +429,10 @@ void CHyprlock::run() {
m_sLoopState.timerEvent = true;
m_sLoopState.timerCV.notify_all();
g_pRenderer->asyncResourceGatherer->notify();
+ g_pRenderer->asyncResourceGatherer->await();
m_vOutputs.clear();
+ g_pEGL.reset();
wl_display_disconnect(m_sWaylandState.display);
@@ -491,8 +678,6 @@ void CHyprlock::unlockSession() {
ext_session_lock_v1_unlock_and_destroy(m_sLockState.lock);
m_sLockState.lock = nullptr;
- m_vOutputs.clear();
- g_pEGL.reset();
Debug::log(LOG, "Unlocked, exiting!");
m_bTerminate = true;
@@ -619,3 +804,7 @@ std::string CHyprlock::spawnSync(const std::string& cmd) {
}
return result;
}
+
+zwlr_screencopy_manager_v1* CHyprlock::getScreencopy() {
+ return m_sWaylandState.screencopy;
+}
\ No newline at end of file
diff --git a/src/core/hyprlock.hpp b/src/core/hyprlock.hpp
index 0a2d590..c8d3db5 100644
--- a/src/core/hyprlock.hpp
+++ b/src/core/hyprlock.hpp
@@ -3,6 +3,8 @@
#include
#include "ext-session-lock-v1-protocol.h"
#include "fractional-scale-v1-protocol.h"
+#include "linux-dmabuf-unstable-v1-protocol.h"
+#include "wlr-screencopy-unstable-v1-protocol.h"
#include "viewporter-protocol.h"
#include "Output.hpp"
#include "CursorShape.hpp"
@@ -16,6 +18,14 @@
#include
+#include
+#include
+
+struct SDMABUFModifier {
+ uint32_t fourcc = 0;
+ uint64_t mod = 0;
+};
+
class CHyprlock {
public:
CHyprlock(const std::string& wlDisplay);
@@ -49,6 +59,7 @@ class CHyprlock {
wl_display* getDisplay();
wp_fractional_scale_manager_v1* getFractionalMgr();
wp_viewporter* getViewporter();
+ zwlr_screencopy_manager_v1* getScreencopy();
wl_pointer* m_pPointer = nullptr;
wl_keyboard* m_pKeeb = nullptr;
@@ -65,6 +76,23 @@ class CHyprlock {
std::chrono::system_clock::time_point m_tGraceEnds;
Vector2D m_vLastEnterCoords = {};
+ std::vector> m_vOutputs;
+
+ struct {
+ void* linuxDmabuf = nullptr;
+ void* linuxDmabufFeedback = nullptr;
+
+ gbm_bo* gbm = nullptr;
+ gbm_device* gbmDevice = nullptr;
+
+ void* formatTable = nullptr;
+ size_t formatTableSize = 0;
+ bool deviceUsed = false;
+
+ std::vector dmabufMods;
+ } dma;
+ gbm_device* createGBMDevice(drmDevice* dev);
+
private:
struct {
wl_display* display = nullptr;
@@ -74,6 +102,7 @@ class CHyprlock {
wl_compositor* compositor = nullptr;
wp_fractional_scale_manager_v1* fractional = nullptr;
wp_viewporter* viewporter = nullptr;
+ zwlr_screencopy_manager_v1* screencopy = nullptr;
} m_sWaylandState;
struct {
@@ -98,9 +127,7 @@ class CHyprlock {
bool timerEvent = false;
} m_sLoopState;
- std::vector> m_vOutputs;
-
- std::vector> m_vTimers;
+ std::vector> m_vTimers;
};
inline std::unique_ptr g_pHyprlock;
\ No newline at end of file
diff --git a/src/renderer/AsyncResourceGatherer.cpp b/src/renderer/AsyncResourceGatherer.cpp
index 8ccf864..f4dccdb 100644
--- a/src/renderer/AsyncResourceGatherer.cpp
+++ b/src/renderer/AsyncResourceGatherer.cpp
@@ -15,6 +15,42 @@ CAsyncResourceGatherer::CAsyncResourceGatherer() {
this->asyncLoopThread.detach();
});
initThread.detach();
+
+ // some things can't be done async :(
+ // gather background textures when needed
+
+ const auto CWIDGETS = g_pConfigManager->getWidgetConfigs();
+
+ std::vector mons;
+
+ for (auto& c : CWIDGETS) {
+ if (c.type != "background")
+ continue;
+
+ if (std::string{std::any_cast(c.values.at("path"))} != "screenshot")
+ continue;
+
+ // mamma mia
+ if (c.monitor.empty()) {
+ mons.clear();
+ for (auto& m : g_pHyprlock->m_vOutputs) {
+ mons.push_back(m->stringPort);
+ }
+ break;
+ } else
+ mons.push_back(c.monitor);
+ }
+
+ for (auto& mon : mons) {
+ const auto MON = std::find_if(g_pHyprlock->m_vOutputs.begin(), g_pHyprlock->m_vOutputs.end(), [mon](const auto& other) { return other->stringPort == mon; });
+
+ if (MON == g_pHyprlock->m_vOutputs.end())
+ continue;
+
+ const auto PMONITOR = MON->get();
+
+ dmas.emplace_back(std::make_unique(PMONITOR));
+ }
}
SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
@@ -36,6 +72,11 @@ SPreloadedAsset* CAsyncResourceGatherer::getAssetByID(const std::string& id) {
}
}
+ for (auto& dma : dmas) {
+ if (id == "dma:" + dma->name)
+ return dma->asset.ready ? &dma->asset : nullptr;
+ }
+
return nullptr;
}
@@ -228,6 +269,8 @@ void CAsyncResourceGatherer::asyncAssetSpinLock() {
asyncLoopState.busy = false;
}
+
+ dmas.clear();
}
void CAsyncResourceGatherer::requestAsyncAssetPreload(const SPreloadRequest& request) {
@@ -247,3 +290,8 @@ void CAsyncResourceGatherer::notify() {
asyncLoopState.pending = true;
asyncLoopState.loopGuard.notify_all();
}
+
+void CAsyncResourceGatherer::await() {
+ if (asyncLoopThread.joinable())
+ asyncLoopThread.join();
+}
diff --git a/src/renderer/AsyncResourceGatherer.hpp b/src/renderer/AsyncResourceGatherer.hpp
index de7cf22..64a81e4 100644
--- a/src/renderer/AsyncResourceGatherer.hpp
+++ b/src/renderer/AsyncResourceGatherer.hpp
@@ -3,6 +3,7 @@
#include "Shader.hpp"
#include "../helpers/Box.hpp"
#include "../helpers/Color.hpp"
+#include "DMAFrame.hpp"
#include "Texture.hpp"
#include
#include
@@ -10,10 +11,7 @@
#include
#include
#include
-
-struct SPreloadedAsset {
- CTexture texture;
-};
+#include "Shared.hpp"
class CAsyncResourceGatherer {
public:
@@ -44,6 +42,7 @@ class CAsyncResourceGatherer {
void requestAsyncAssetPreload(const SPreloadRequest& request);
void unloadAsset(SPreloadedAsset* asset);
void notify();
+ void await();
private:
std::thread initThread;
@@ -77,6 +76,8 @@ class CAsyncResourceGatherer {
Vector2D size;
};
+ std::vector> dmas;
+
std::vector preloadTargets;
std::unordered_map assets;
diff --git a/src/renderer/DMAFrame.cpp b/src/renderer/DMAFrame.cpp
new file mode 100644
index 0000000..6e75f3e
--- /dev/null
+++ b/src/renderer/DMAFrame.cpp
@@ -0,0 +1,203 @@
+#include "DMAFrame.hpp"
+#include "wlr-screencopy-unstable-v1-protocol.h"
+#include "../helpers/Log.hpp"
+#include "../core/hyprlock.hpp"
+#include "../core/Egl.hpp"
+
+#include
+#include
+#include
+#include
+#include
+
+static PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES = nullptr;
+
+static void wlrOnBuffer(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
+ const auto PDATA = (SScreencopyData*)data;
+
+ Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)PDATA);
+
+ PDATA->size = stride * height;
+ PDATA->stride = stride;
+}
+
+static void wlrOnFlags(void* data, zwlr_screencopy_frame_v1* frame, uint32_t flags) {
+ ;
+}
+
+static void wlrOnReady(void* data, zwlr_screencopy_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
+ const auto PDATA = (SScreencopyData*)data;
+
+ Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)PDATA);
+
+ if (!PDATA->frame->onBufferReady()) {
+ Debug::log(ERR, "onBufferReady failed");
+ return;
+ }
+
+ zwlr_screencopy_frame_v1_destroy(frame);
+}
+
+static void wlrOnFailed(void* data, zwlr_screencopy_frame_v1* frame) {
+ ;
+}
+
+static void wlrOnDamage(void* data, zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
+ ;
+}
+
+static void wlrOnDmabuf(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
+ const auto PDATA = (SScreencopyData*)data;
+
+ Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)PDATA);
+
+ PDATA->w = width;
+ PDATA->h = height;
+ PDATA->fmt = format;
+}
+
+static void wlrOnBufferDone(void* data, zwlr_screencopy_frame_v1* frame) {
+ const auto PDATA = (SScreencopyData*)data;
+
+ Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)PDATA);
+
+ if (!PDATA->frame->onBufferDone()) {
+ Debug::log(ERR, "onBufferDone failed");
+ return;
+ }
+
+ zwlr_screencopy_frame_v1_copy(frame, PDATA->frame->wlBuffer);
+
+ Debug::log(TRACE, "[sc] wlr frame copied");
+}
+
+static const zwlr_screencopy_frame_v1_listener wlrFrameListener = {
+ .buffer = wlrOnBuffer,
+ .flags = wlrOnFlags,
+ .ready = wlrOnReady,
+ .failed = wlrOnFailed,
+ .damage = wlrOnDamage,
+ .linux_dmabuf = wlrOnDmabuf,
+ .buffer_done = wlrOnBufferDone,
+};
+
+CDMAFrame::CDMAFrame(COutput* output) {
+
+ if (!glEGLImageTargetTexture2DOES) {
+ glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
+ if (!glEGLImageTargetTexture2DOES) {
+ Debug::log(ERR, "No glEGLImageTargetTexture2DOES??");
+ return;
+ }
+ }
+
+ // firstly, plant a listener for the frame
+ frameCb = zwlr_screencopy_manager_v1_capture_output(g_pHyprlock->getScreencopy(), false, output->output);
+
+ scdata.frame = this;
+
+ zwlr_screencopy_frame_v1_add_listener(frameCb, &wlrFrameListener, &scdata);
+
+ name = output->stringPort;
+}
+
+CDMAFrame::~CDMAFrame() {
+ if (g_pEGL)
+ eglDestroyImage(g_pEGL->eglDisplay, image);
+}
+
+bool CDMAFrame::onBufferDone() {
+ uint32_t flags = GBM_BO_USE_RENDERING;
+
+ bo = gbm_bo_create(g_pHyprlock->dma.gbmDevice, scdata.w, scdata.h, scdata.fmt, flags);
+
+ if (!bo) {
+ Debug::log(ERR, "Couldn't create a drm buffer");
+ return false;
+ }
+
+ planes = gbm_bo_get_plane_count(bo);
+
+ zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params((zwp_linux_dmabuf_v1*)g_pHyprlock->dma.linuxDmabuf);
+ if (!params) {
+ Debug::log(ERR, "zwp_linux_dmabuf_v1_create_params failed");
+ gbm_bo_destroy(bo);
+ return false;
+ }
+
+ for (size_t plane = 0; plane < (size_t)planes; plane++) {
+ size[plane] = 0;
+ stride[plane] = gbm_bo_get_stride_for_plane(bo, plane);
+ offset[plane] = gbm_bo_get_offset(bo, plane);
+ uint64_t mod = gbm_bo_get_modifier(bo);
+ fd[plane] = gbm_bo_get_fd_for_plane(bo, plane);
+
+ if (fd[plane] < 0) {
+ Debug::log(ERR, "gbm_bo_get_fd_for_plane failed");
+ zwp_linux_buffer_params_v1_destroy(params);
+ gbm_bo_destroy(bo);
+ for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
+ close(fd[plane_tmp]);
+ }
+ return false;
+ }
+
+ zwp_linux_buffer_params_v1_add(params, fd[plane], plane, offset[plane], stride[plane], mod >> 32, mod & 0xffffffff);
+ }
+
+ wlBuffer = zwp_linux_buffer_params_v1_create_immed(params, scdata.w, scdata.h, scdata.fmt, 0);
+ zwp_linux_buffer_params_v1_destroy(params);
+
+ if (!wlBuffer) {
+ Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
+ gbm_bo_destroy(bo);
+ for (size_t plane = 0; plane < (size_t)planes; plane++)
+ close(fd[plane]);
+
+ return false;
+ }
+
+ return true;
+}
+
+bool CDMAFrame::onBufferReady() {
+ static const int general_attribs = 3;
+ static const int plane_attribs = 5;
+ static const int entries_per_attrib = 2;
+ EGLAttrib attribs[(general_attribs + plane_attribs * 4) * entries_per_attrib + 1];
+ int attr = 0;
+ attribs[attr++] = EGL_WIDTH;
+ attribs[attr++] = scdata.w;
+ attribs[attr++] = EGL_HEIGHT;
+ attribs[attr++] = scdata.h;
+ attribs[attr++] = EGL_LINUX_DRM_FOURCC_EXT;
+ attribs[attr++] = scdata.fmt;
+ attribs[attr++] = EGL_DMA_BUF_PLANE0_FD_EXT;
+ attribs[attr++] = fd[0];
+ attribs[attr++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
+ attribs[attr++] = offset[0];
+ attribs[attr++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
+ attribs[attr++] = stride[0];
+ attribs[attr] = EGL_NONE;
+
+ image = eglCreateImage(g_pEGL->eglDisplay, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs);
+
+ if (image == EGL_NO_IMAGE) {
+ Debug::log(ERR, "failed creating an egl image");
+ return false;
+ }
+
+ asset.texture.allocate();
+ asset.texture.m_vSize = {scdata.w, scdata.h};
+ glBindTexture(GL_TEXTURE_2D, asset.texture.m_iTexID);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ asset.ready = true;
+
+ return true;
+}
\ No newline at end of file
diff --git a/src/renderer/DMAFrame.hpp b/src/renderer/DMAFrame.hpp
new file mode 100644
index 0000000..9763fbd
--- /dev/null
+++ b/src/renderer/DMAFrame.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "../core/Output.hpp"
+#include
+#include "Texture.hpp"
+#include "Shared.hpp"
+
+struct zwlr_screencopy_frame_v1;
+
+class CDMAFrame;
+
+struct SScreencopyData {
+ int w = 0, h = 0;
+ uint32_t fmt;
+ size_t size;
+ size_t stride;
+ CDMAFrame* frame = nullptr;
+};
+
+class CDMAFrame {
+ public:
+ CDMAFrame(COutput* mon);
+ ~CDMAFrame();
+
+ bool onBufferDone();
+ bool onBufferReady();
+
+ wl_buffer* wlBuffer = nullptr;
+
+ std::string name;
+
+ SPreloadedAsset asset;
+
+ private:
+ gbm_bo* bo = nullptr;
+
+ int planes = 0;
+
+ int fd[4];
+ uint32_t size[4], stride[4], offset[4];
+
+ zwlr_screencopy_frame_v1* frameCb = nullptr;
+ SScreencopyData scdata;
+
+ EGLImage image = nullptr;
+};
\ No newline at end of file
diff --git a/src/renderer/Renderer.cpp b/src/renderer/Renderer.cpp
index 11c8247..a317169 100644
--- a/src/renderer/Renderer.cpp
+++ b/src/renderer/Renderer.cpp
@@ -255,8 +255,14 @@ std::vector>* CRenderer::getOrCreateWidgetsFor(const CS
// by type
if (c.type == "background") {
const std::string PATH = std::any_cast(c.values.at("path"));
- widgets[surf].emplace_back(
- std::make_unique(surf->size, PATH.empty() ? "" : std::string{"background:"} + PATH, std::any_cast(c.values.at("color"))));
+
+ std::string resourceID = "";
+ if (PATH == "screenshot")
+ resourceID = "dma:" + surf->output->stringPort;
+ else if (!PATH.empty())
+ resourceID = "background:" + PATH;
+
+ widgets[surf].emplace_back(std::make_unique(surf->size, resourceID, std::any_cast(c.values.at("color"))));
} else if (c.type == "input-field") {
widgets[surf].emplace_back(std::make_unique(surf->size, c.values));
} else if (c.type == "label") {
diff --git a/src/renderer/Shared.hpp b/src/renderer/Shared.hpp
new file mode 100644
index 0000000..b333f3f
--- /dev/null
+++ b/src/renderer/Shared.hpp
@@ -0,0 +1,7 @@
+#pragma once
+#include "Texture.hpp"
+
+struct SPreloadedAsset {
+ CTexture texture;
+ bool ready = false;
+};
\ No newline at end of file