move to hw-s

This commit is contained in:
Vaxry 2024-09-19 22:38:36 +01:00
parent e695669fd8
commit 5c5cb55abb
14 changed files with 540 additions and 742 deletions

View file

@ -61,7 +61,9 @@ pkg_check_modules(
libspa-0.2 libspa-0.2
libdrm libdrm
gbm gbm
hyprlang>=0.2.0) hyprlang>=0.2.0
hyprutils
hyprwayland-scanner>=0.4.0)
# check whether we can find sdbus-c++ through pkg-config # check whether we can find sdbus-c++ through pkg-config
pkg_check_modules(SDBUS IMPORTED_TARGET sdbus-c++) pkg_check_modules(SDBUS IMPORTED_TARGET sdbus-c++)
@ -86,43 +88,43 @@ target_link_libraries(
PkgConfig::deps) PkgConfig::deps)
# protocols # protocols
find_program(WaylandScanner NAMES wayland-scanner)
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}")
pkg_get_variable(WAYLAND_CLIENT_DIR wayland-client pkgdatadir)
message(STATUS "Found wayland-client at ${WAYLAND_CLIENT_DIR}")
function(protocol protoPath protoName external) function(protocolnew protoPath protoName external)
if(external) if(external)
set(path ${protoPath}) set(path ${CMAKE_SOURCE_DIR}/${protoPath})
else() else()
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath}) set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
endif() endif()
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
COMMAND ${WaylandScanner} client-header ${path} ${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/${protoName}.cpp
protocols/${protoName}.hpp)
endfunction()
function(protocolWayland)
add_custom_command( add_custom_command(
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
COMMAND ${WaylandScanner} private-code ${path} ${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c COMMAND hyprwayland-scanner --wayland-enums --client
${WAYLAND_CLIENT_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
target_sources(xdg-desktop-portal-hyprland target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
PRIVATE protocols/${protoName}-protocol.h)
target_sources(xdg-desktop-portal-hyprland
PRIVATE protocols/${protoName}-protocol.c)
endfunction() endfunction()
protocol("protocols/wlr-foreign-toplevel-management-unstable-v1.xml" protocolwayland()
"wlr-foreign-toplevel-management-unstable-v1" true)
protocol("protocols/wlr-screencopy-unstable-v1.xml" protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
"wlr-screencopy-unstable-v1" true) protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
protocol("${HYPRLAND_PROTOCOLS}/protocols/hyprland-global-shortcuts-v1.xml" protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-global-shortcuts-v1" true)
"hyprland-global-shortcuts-v1" true) protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1" true)
protocol("${HYPRLAND_PROTOCOLS}/protocols/hyprland-toplevel-export-v1.xml" protocolnew("unstable/linux-dmabuf" "linux-dmabuf-unstable-v1" false)
"hyprland-toplevel-export-v1" true)
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml"
"linux-dmabuf-unstable-v1" false)
# Installation # Installation
install(TARGETS hyprland-share-picker) install(TARGETS hyprland-share-picker)

View file

@ -2,12 +2,6 @@
#include "../helpers/Log.hpp" #include "../helpers/Log.hpp"
#include "../helpers/MiscFunctions.hpp" #include "../helpers/MiscFunctions.hpp"
#include <protocols/hyprland-global-shortcuts-v1-protocol.h>
#include <protocols/hyprland-toplevel-export-v1-protocol.h>
#include <protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h>
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
#include <protocols/linux-dmabuf-unstable-v1-protocol.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <poll.h> #include <poll.h>
#include <sys/mman.h> #include <sys/mman.h>
@ -16,189 +10,24 @@
#include <thread> #include <thread>
void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { SOutput::SOutput(SP<CCWlOutput> output_) : output(output_) {
g_pPortalManager->onGlobal(data, registry, name, interface, version); output->setName([this](CCWlOutput* o, const char* name_) {
} if (!name_)
void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) {
; // noop
}
inline const wl_registry_listener registryListener = {
.global = handleGlobal,
.global_remove = handleGlobalRemove,
};
static void handleOutputGeometry(void* data, struct wl_output* wl_output, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make,
const char* model, int32_t transform) {
const auto POUTPUT = (SOutput*)data;
POUTPUT->transform = (wl_output_transform)transform;
}
static void handleOutputDone(void* data, struct wl_output* wl_output) {
;
}
static void handleOutputMode(void* data, struct wl_output* wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
const auto POUTPUT = (SOutput*)data;
POUTPUT->refreshRate = std::round(refresh / 1000.0);
}
static void handleOutputScale(void* data, struct wl_output* wl_output, int32_t factor) {
;
}
static void handleOutputName(void* data, struct wl_output* wl_output, const char* name) {
if (!name)
return; return;
const auto POUTPUT = (SOutput*)data; name = name_;
POUTPUT->name = name;
Debug::log(LOG, "Found output name {}", POUTPUT->name); Debug::log(LOG, "Found output name {}", name);
});
output->setMode([this](CCWlOutput* r, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { //
refreshRate = refresh;
});
output->setGeometry([this](CCWlOutput* r, int32_t x, int32_t y, int32_t physical_width, int32_t physical_height, int32_t subpixel, const char* make, const char* model,
int32_t transform_) { //
transform = (wl_output_transform)transform_;
});
} }
static void handleOutputDescription(void* data, struct wl_output* wl_output, const char* description) {
;
}
inline const wl_output_listener outputListener = {
.geometry = handleOutputGeometry,
.mode = handleOutputMode,
.done = handleOutputDone,
.scale = handleOutputScale,
.name = handleOutputName,
.description = handleOutputDescription,
};
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_pPortalManager->m_vDMABUFMods.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_pPortalManager->m_sWaylandConnection.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_pPortalManager->m_sWaylandConnection.gbmDevice = g_pPortalManager->createGBMDevice(drmDev);
}
static void dmabufFeedbackFormatTable(void* data, zwp_linux_dmabuf_feedback_v1* feedback, int fd, uint32_t size) {
Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");
g_pPortalManager->m_vDMABUFMods.clear();
g_pPortalManager->m_sWaylandConnection.dma.formatTable = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (g_pPortalManager->m_sWaylandConnection.dma.formatTable == MAP_FAILED) {
Debug::log(ERR, "[core] format table failed to mmap");
g_pPortalManager->m_sWaylandConnection.dma.formatTable = nullptr;
g_pPortalManager->m_sWaylandConnection.dma.formatTableSize = 0;
return;
}
g_pPortalManager->m_sWaylandConnection.dma.formatTableSize = size;
}
static void dmabufFeedbackDone(void* data, zwp_linux_dmabuf_feedback_v1* feedback) {
Debug::log(TRACE, "[core] dmabufFeedbackDone");
if (g_pPortalManager->m_sWaylandConnection.dma.formatTable)
munmap(g_pPortalManager->m_sWaylandConnection.dma.formatTable, g_pPortalManager->m_sWaylandConnection.dma.formatTableSize);
g_pPortalManager->m_sWaylandConnection.dma.formatTable = nullptr;
g_pPortalManager->m_sWaylandConnection.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_pPortalManager->m_sWaylandConnection.gbmDevice) {
drmDevice* drmDevRenderer = NULL;
drmGetDevice2(gbm_device_get_fd(g_pPortalManager->m_sWaylandConnection.gbmDevice), /* flags */ 0, &drmDevRenderer);
g_pPortalManager->m_sWaylandConnection.dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
} else {
g_pPortalManager->m_sWaylandConnection.gbmDevice = g_pPortalManager->createGBMDevice(drmDev);
g_pPortalManager->m_sWaylandConnection.dma.deviceUsed = g_pPortalManager->m_sWaylandConnection.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_pPortalManager->m_sWaylandConnection.dma.deviceUsed || !g_pPortalManager->m_sWaylandConnection.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(struct fm_entry) == 16);
uint32_t n_modifiers = g_pPortalManager->m_sWaylandConnection.dma.formatTableSize / sizeof(struct fm_entry);
fm_entry* fm_entry = (struct fm_entry*)g_pPortalManager->m_sWaylandConnection.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_pPortalManager->m_vDMABUFMods.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_pPortalManager->m_sWaylandConnection.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,
};
//
CPortalManager::CPortalManager() { CPortalManager::CPortalManager() {
const auto XDG_CONFIG_HOME = getenv("XDG_CONFIG_HOME"); const auto XDG_CONFIG_HOME = getenv("XDG_CONFIG_HOME");
const auto HOME = getenv("HOME"); const auto HOME = getenv("HOME");
@ -218,25 +47,31 @@ CPortalManager::CPortalManager() {
m_sConfig.config->parse(); m_sConfig.config->parse();
} }
void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { void CPortalManager::onGlobal(uint32_t name, const char* interface, uint32_t version) {
const std::string INTERFACE = interface; const std::string INTERFACE = interface;
Debug::log(LOG, " | Got interface: {} (ver {})", INTERFACE, version); Debug::log(LOG, " | Got interface: {} (ver {})", INTERFACE, version);
if (INTERFACE == zwlr_screencopy_manager_v1_interface.name && m_sPipewire.loop) if (INTERFACE == zwlr_screencopy_manager_v1_interface.name && m_sPipewire.loop) {
m_sPortals.screencopy = std::make_unique<CScreencopyPortal>((zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, version)); m_sPortals.screencopy = std::make_unique<CScreencopyPortal>(
makeShared<CCZwlrScreencopyManagerV1>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwlr_screencopy_manager_v1_interface, version)));
}
if (INTERFACE == hyprland_global_shortcuts_manager_v1_interface.name) if (INTERFACE == hyprland_global_shortcuts_manager_v1_interface.name) {
m_sPortals.globalShortcuts = std::make_unique<CGlobalShortcutsPortal>( m_sPortals.globalShortcuts = std::make_unique<CGlobalShortcutsPortal>(makeShared<CCHyprlandGlobalShortcutsManagerV1>(
(hyprland_global_shortcuts_manager_v1*)wl_registry_bind(registry, name, &hyprland_global_shortcuts_manager_v1_interface, version)); wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_global_shortcuts_manager_v1_interface, version)));
}
else if (INTERFACE == hyprland_toplevel_export_manager_v1_interface.name) else if (INTERFACE == hyprland_toplevel_export_manager_v1_interface.name) {
m_sWaylandConnection.hyprlandToplevelMgr = wl_registry_bind(registry, name, &hyprland_toplevel_export_manager_v1_interface, version); m_sWaylandConnection.hyprlandToplevelMgr = makeShared<CCHyprlandToplevelExportManagerV1>(
wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &hyprland_toplevel_export_manager_v1_interface, version));
}
else if (INTERFACE == wl_output_interface.name) { else if (INTERFACE == wl_output_interface.name) {
const auto POUTPUT = m_vOutputs.emplace_back(std::make_unique<SOutput>()).get(); const auto POUTPUT = m_vOutputs
POUTPUT->output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, version); .emplace_back(std::make_unique<SOutput>(
wl_output_add_listener(POUTPUT->output, &outputListener, POUTPUT); makeShared<CCWlOutput>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &wl_output_interface, version))))
.get();
POUTPUT->id = name; POUTPUT->id = name;
} }
@ -246,16 +81,111 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t
return; return;
} }
m_sWaylandConnection.linuxDmabuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version); m_sWaylandConnection.linuxDmabuf =
m_sWaylandConnection.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)m_sWaylandConnection.linuxDmabuf); makeShared<CCZwpLinuxDmabufV1>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwp_linux_dmabuf_v1_interface, version));
zwp_linux_dmabuf_feedback_v1_add_listener((zwp_linux_dmabuf_feedback_v1*)m_sWaylandConnection.linuxDmabufFeedback, &dmabufFeedbackListener, nullptr); m_sWaylandConnection.linuxDmabufFeedback = makeShared<CCZwpLinuxDmabufFeedbackV1>(m_sWaylandConnection.linuxDmabuf->sendGetDefaultFeedback());
m_sWaylandConnection.linuxDmabufFeedback->setMainDevice([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* device_arr) {
Debug::log(LOG, "[core] dmabufFeedbackMainDevice");
RASSERT(!m_sWaylandConnection.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);
} }
else if (INTERFACE == wl_shm_interface.name) m_sWaylandConnection.gbmDevice = createGBMDevice(drmDev);
m_sWaylandConnection.shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, version); });
m_sWaylandConnection.linuxDmabufFeedback->setFormatTable([this](CCZwpLinuxDmabufFeedbackV1* r, int fd, uint32_t size) {
Debug::log(TRACE, "[core] dmabufFeedbackFormatTable");
m_vDMABUFMods.clear();
m_sWaylandConnection.dma.formatTable = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (m_sWaylandConnection.dma.formatTable == MAP_FAILED) {
Debug::log(ERR, "[core] format table failed to mmap");
m_sWaylandConnection.dma.formatTable = nullptr;
m_sWaylandConnection.dma.formatTableSize = 0;
return;
}
m_sWaylandConnection.dma.formatTableSize = size;
});
m_sWaylandConnection.linuxDmabufFeedback->setDone([this](CCZwpLinuxDmabufFeedbackV1* r) {
Debug::log(TRACE, "[core] dmabufFeedbackDone");
if (m_sWaylandConnection.dma.formatTable)
munmap(m_sWaylandConnection.dma.formatTable, m_sWaylandConnection.dma.formatTableSize);
m_sWaylandConnection.dma.formatTable = nullptr;
m_sWaylandConnection.dma.formatTableSize = 0;
});
m_sWaylandConnection.linuxDmabufFeedback->setTrancheTargetDevice([this](CCZwpLinuxDmabufFeedbackV1* r, 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 (m_sWaylandConnection.gbmDevice) {
drmDevice* drmDevRenderer = NULL;
drmGetDevice2(gbm_device_get_fd(m_sWaylandConnection.gbmDevice), /* flags */ 0, &drmDevRenderer);
m_sWaylandConnection.dma.deviceUsed = drmDevicesEqual(drmDevRenderer, drmDev);
} else {
m_sWaylandConnection.gbmDevice = createGBMDevice(drmDev);
m_sWaylandConnection.dma.deviceUsed = m_sWaylandConnection.gbm;
}
});
m_sWaylandConnection.linuxDmabufFeedback->setTrancheFormats([this](CCZwpLinuxDmabufFeedbackV1* r, wl_array* indices) {
Debug::log(TRACE, "[core] dmabufFeedbackTrancheFormats");
if (!m_sWaylandConnection.dma.deviceUsed || !m_sWaylandConnection.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(struct fm_entry) == 16);
uint32_t n_modifiers = m_sWaylandConnection.dma.formatTableSize / sizeof(struct fm_entry);
fm_entry* fm_entry = (struct fm_entry*)m_sWaylandConnection.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;
m_vDMABUFMods.push_back({(fm_entry + *idx)->format, (fm_entry + *idx)->modifier});
}
});
m_sWaylandConnection.linuxDmabufFeedback->setTrancheDone([this](CCZwpLinuxDmabufFeedbackV1* r) {
Debug::log(TRACE, "[core] dmabufFeedbackTrancheDone");
m_sWaylandConnection.dma.deviceUsed = false;
});
}
else if (INTERFACE == wl_shm_interface.name) {
m_sWaylandConnection.shm = makeShared<CCWlShm>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &wl_shm_interface, version));
}
else if (INTERFACE == zwlr_foreign_toplevel_manager_v1_interface.name) { else if (INTERFACE == zwlr_foreign_toplevel_manager_v1_interface.name) {
m_sHelpers.toplevel = std::make_unique<CToplevelManager>(registry, name, version); m_sHelpers.toplevel = std::make_unique<CToplevelManager>(name, version);
// remove when another fix is found for https://github.com/hyprwm/xdg-desktop-portal-hyprland/issues/147 // remove when another fix is found for https://github.com/hyprwm/xdg-desktop-portal-hyprland/issues/147
if (!std::any_cast<Hyprlang::INT>(m_sConfig.config->getConfigValue("general:toplevel_dynamic_bind"))) if (!std::any_cast<Hyprlang::INT>(m_sConfig.config->getConfigValue("general:toplevel_dynamic_bind")))
@ -263,7 +193,7 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t
} }
} }
void CPortalManager::onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name) { void CPortalManager::onGlobalRemoved(uint32_t name) {
std::erase_if(m_vOutputs, [&](const auto& other) { return other->id == name; }); std::erase_if(m_vOutputs, [&](const auto& other) { return other->id == name; });
} }
@ -299,8 +229,9 @@ void CPortalManager::init() {
Debug::log(WARN, "XDG_CURRENT_DESKTOP unset, running on an unknown desktop"); Debug::log(WARN, "XDG_CURRENT_DESKTOP unset, running on an unknown desktop");
} }
wl_registry* registry = wl_display_get_registry(m_sWaylandConnection.display); m_sWaylandConnection.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandConnection.display));
wl_registry_add_listener(registry, &registryListener, nullptr); m_sWaylandConnection.registry->setGlobal([this](CCWlRegistry* r, uint32_t name, const char* iface, uint32_t ver) { onGlobal(name, iface, ver); });
m_sWaylandConnection.registry->setGlobalRemove([this](CCWlRegistry* r, uint32_t name) { onGlobalRemoved(name); });
pw_init(nullptr, nullptr); pw_init(nullptr, nullptr);
m_sPipewire.loop = pw_loop_new(nullptr); m_sPipewire.loop = pw_loop_new(nullptr);

View file

@ -2,9 +2,9 @@
#include <memory> #include <memory>
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include <wayland-client.h>
#include <hyprlang.hpp> #include <hyprlang.hpp>
#include "wayland.hpp"
#include "../portals/Screencopy.hpp" #include "../portals/Screencopy.hpp"
#include "../portals/Screenshot.hpp" #include "../portals/Screenshot.hpp"
#include "../portals/GlobalShortcuts.hpp" #include "../portals/GlobalShortcuts.hpp"
@ -13,13 +13,22 @@
#include <gbm.h> #include <gbm.h>
#include <xf86drm.h> #include <xf86drm.h>
#include "hyprland-toplevel-export-v1.hpp"
#include "hyprland-global-shortcuts-v1.hpp"
#include "linux-dmabuf-unstable-v1.hpp"
#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
#include "wlr-screencopy-unstable-v1.hpp"
#include "../includes.hpp"
#include <mutex> #include <mutex>
struct pw_loop; struct pw_loop;
struct SOutput { struct SOutput {
SOutput(SP<CCWlOutput>);
std::string name; std::string name;
wl_output* output = nullptr; SP<CCWlOutput> output = nullptr;
uint32_t id = 0; uint32_t id = 0;
float refreshRate = 60.0; float refreshRate = 60.0;
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
@ -36,8 +45,8 @@ class CPortalManager {
void init(); void init();
void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version); void onGlobal(uint32_t name, const char* interface, uint32_t version);
void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name); void onGlobalRemoved(uint32_t name);
sdbus::IConnection* getConnection(); sdbus::IConnection* getConnection();
SOutput* getOutputFromName(const std::string& name); SOutput* getOutputFromName(const std::string& name);
@ -58,10 +67,11 @@ class CPortalManager {
struct { struct {
wl_display* display = nullptr; wl_display* display = nullptr;
void* hyprlandToplevelMgr = nullptr; SP<CCWlRegistry> registry;
void* linuxDmabuf = nullptr; SP<CCHyprlandToplevelExportManagerV1> hyprlandToplevelMgr;
void* linuxDmabufFeedback = nullptr; SP<CCZwpLinuxDmabufV1> linuxDmabuf;
wl_shm* shm = nullptr; SP<CCZwpLinuxDmabufFeedbackV1> linuxDmabufFeedback;
SP<CCWlShm> shm;
gbm_bo* gbm = nullptr; gbm_bo* gbm = nullptr;
gbm_device* gbmDevice = nullptr; gbm_device* gbmDevice = nullptr;
struct { struct {

View file

@ -18,7 +18,7 @@ enum eLogLevel {
std::format(reason, ##__VA_ARGS__), __LINE__, \ std::format(reason, ##__VA_ARGS__), __LINE__, \
([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \ ([]() constexpr -> std::string { return std::string(__FILE__).substr(std::string(__FILE__).find_last_of('/') + 1); })().c_str()); \
printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \ printf("Assertion failed! See the log in /tmp/hypr/hyprland.log for more info."); \
*((int*)nullptr) = 1; /* so that we crash and get a coredump */ \ abort(); /* so that we crash and get a coredump */ \
} }
#define ASSERT(expr) RASSERT(expr, "?") #define ASSERT(expr) RASSERT(expr, "?")

View file

@ -2,25 +2,15 @@
#include "../core/PortalManager.hpp" #include "../core/PortalManager.hpp"
#include "../helpers/Log.hpp" #include "../helpers/Log.hpp"
// wayland SKeybind::SKeybind(SP<CCHyprlandGlobalShortcutV1> shortcut_) : shortcut(shortcut_) {
shortcut->setPressed([this](CCHyprlandGlobalShortcutV1* r, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
static void handleActivated(void* data, hyprland_global_shortcut_v1* hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { g_pPortalManager->m_sPortals.globalShortcuts->onActivated(this, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo));
const auto PKEYBIND = (SKeybind*)data; });
shortcut->setReleased([this](CCHyprlandGlobalShortcutV1* r, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
g_pPortalManager->m_sPortals.globalShortcuts->onActivated(PKEYBIND, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo)); g_pPortalManager->m_sPortals.globalShortcuts->onDeactivated(this, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo));
});
} }
static void handleDeactivated(void* data, hyprland_global_shortcut_v1* hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
const auto PKEYBIND = (SKeybind*)data;
g_pPortalManager->m_sPortals.globalShortcuts->onDeactivated(PKEYBIND, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo));
}
static const hyprland_global_shortcut_v1_listener shortcutListener = {
.pressed = handleActivated,
.released = handleDeactivated,
};
// //
CGlobalShortcutsPortal::SSession* CGlobalShortcutsPortal::getSession(sdbus::ObjectPath& path) { CGlobalShortcutsPortal::SSession* CGlobalShortcutsPortal::getSession(sdbus::ObjectPath& path) {
@ -62,9 +52,10 @@ SKeybind* CGlobalShortcutsPortal::registerShortcut(SSession* session, const DBus
if (PSHORTCUT) if (PSHORTCUT)
Debug::log(WARN, "[globalshortcuts] shortcut {} already registered for appid {}", id, session->appid); Debug::log(WARN, "[globalshortcuts] shortcut {} already registered for appid {}", id, session->appid);
else { else {
PSHORTCUT = session->keybinds.emplace_back(std::make_unique<SKeybind>()).get(); PSHORTCUT = session->keybinds
PSHORTCUT->shortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(m_sState.manager, id.c_str(), session->appid.c_str(), description.c_str(), ""); .emplace_back(std::make_unique<SKeybind>(
hyprland_global_shortcut_v1_add_listener(PSHORTCUT->shortcut, &shortcutListener, PSHORTCUT); makeShared<CCHyprlandGlobalShortcutV1>(m_sState.manager->sendRegisterShortcut(id.c_str(), session->appid.c_str(), description.c_str(), ""))))
.get();
} }
PSHORTCUT->id = std::move(id); PSHORTCUT->id = std::move(id);
@ -195,7 +186,7 @@ void CGlobalShortcutsPortal::onListShortcuts(sdbus::MethodCall& call) {
reply.send(); reply.send();
} }
CGlobalShortcutsPortal::CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr) { CGlobalShortcutsPortal::CGlobalShortcutsPortal(SP<CCHyprlandGlobalShortcutsManagerV1> mgr) {
m_sState.manager = mgr; m_sState.manager = mgr;
m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH); m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH);

View file

@ -1,18 +1,19 @@
#pragma once #pragma once
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include <protocols/hyprland-global-shortcuts-v1-protocol.h> #include "hyprland-global-shortcuts-v1.hpp"
#include "../shared/Session.hpp" #include "../shared/Session.hpp"
struct SKeybind { struct SKeybind {
SKeybind(SP<CCHyprlandGlobalShortcutV1> shortcut);
std::string id, description, preferredTrigger; std::string id, description, preferredTrigger;
hyprland_global_shortcut_v1* shortcut = nullptr; SP<CCHyprlandGlobalShortcutV1> shortcut = nullptr;
void* session = nullptr; void* session = nullptr;
}; };
class CGlobalShortcutsPortal { class CGlobalShortcutsPortal {
public: public:
CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr); CGlobalShortcutsPortal(SP<CCHyprlandGlobalShortcutsManagerV1> mgr);
void onCreateSession(sdbus::MethodCall& call); void onCreateSession(sdbus::MethodCall& call);
void onBindShortcuts(sdbus::MethodCall& call); void onBindShortcuts(sdbus::MethodCall& call);
@ -36,7 +37,7 @@ class CGlobalShortcutsPortal {
private: private:
struct { struct {
hyprland_global_shortcuts_manager_v1* manager; SP<CCHyprlandGlobalShortcutsManagerV1> manager;
} m_sState; } m_sState;
std::unique_ptr<sdbus::IObject> m_pObject; std::unique_ptr<sdbus::IObject> m_pObject;

View file

@ -5,298 +5,11 @@
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
#include <pipewire/pipewire.h> #include <pipewire/pipewire.h>
#include <protocols/linux-dmabuf-unstable-v1-protocol.h> #include "linux-dmabuf-unstable-v1.hpp"
#include <unistd.h> #include <unistd.h>
constexpr static int MAX_RETRIES = 10; constexpr static int MAX_RETRIES = 10;
// --------------- Wayland Protocol Handlers --------------- //
static void wlrOnBuffer(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)PSESSION);
PSESSION->sharingData.frameInfoSHM.w = width;
PSESSION->sharingData.frameInfoSHM.h = height;
PSESSION->sharingData.frameInfoSHM.fmt = drmFourccFromSHM((wl_shm_format)format);
PSESSION->sharingData.frameInfoSHM.size = stride * height;
PSESSION->sharingData.frameInfoSHM.stride = stride;
// todo: done if ver < 3
}
static void wlrOnFlags(void* data, zwlr_screencopy_frame_v1* frame, uint32_t flags) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnFlags for {}", (void*)PSESSION);
// todo: maybe check for y invert?
}
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 PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)PSESSION);
PSESSION->sharingData.status = FRAME_READY;
PSESSION->sharingData.tvSec = ((((uint64_t)tv_sec_hi) << 32) + (uint64_t)tv_sec_lo);
PSESSION->sharingData.tvNsec = tv_nsec;
PSESSION->sharingData.tvTimestampNs = PSESSION->sharingData.tvSec * SPA_NSEC_PER_SEC + PSESSION->sharingData.tvNsec;
Debug::log(TRACE, "[sc] frame timestamp sec: {} nsec: {} combined: {}ns", PSESSION->sharingData.tvSec, PSESSION->sharingData.tvNsec, PSESSION->sharingData.tvTimestampNs);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(PSESSION);
if (g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(PSESSION))
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
zwlr_screencopy_frame_v1_destroy(frame);
PSESSION->sharingData.frameCallback = nullptr;
}
static void wlrOnFailed(void* data, zwlr_screencopy_frame_v1* frame) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnFailed for {}", (void*)PSESSION);
PSESSION->sharingData.status = FRAME_FAILED;
}
static void wlrOnDamage(void* data, zwlr_screencopy_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnDamage for {}", (void*)PSESSION);
if (PSESSION->sharingData.damageCount > 3) {
PSESSION->sharingData.damage[0] = {0, 0, PSESSION->sharingData.frameInfoDMA.w, PSESSION->sharingData.frameInfoDMA.h};
return;
}
PSESSION->sharingData.damage[PSESSION->sharingData.damageCount++] = {x, y, width, height};
Debug::log(TRACE, "[sc] wlr damage: {} {} {} {}", x, y, width, height);
}
static void wlrOnDmabuf(void* data, zwlr_screencopy_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)PSESSION);
PSESSION->sharingData.frameInfoDMA.w = width;
PSESSION->sharingData.frameInfoDMA.h = height;
PSESSION->sharingData.frameInfoDMA.fmt = format;
}
static void wlrOnBufferDone(void* data, zwlr_screencopy_frame_v1* frame) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)PSESSION);
const auto PSTREAM = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(PSESSION);
if (!PSTREAM) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: no stream");
zwlr_screencopy_frame_v1_destroy(frame);
PSESSION->sharingData.frameCallback = nullptr;
PSESSION->sharingData.status = FRAME_NONE;
return;
}
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[sc] wlr format {} size {}x{}", (int)PSESSION->sharingData.frameInfoSHM.fmt, PSESSION->sharingData.frameInfoSHM.w, PSESSION->sharingData.frameInfoSHM.h);
Debug::log(TRACE, "[sc] wlr format dma {} size {}x{}", (int)PSESSION->sharingData.frameInfoDMA.fmt, PSESSION->sharingData.frameInfoDMA.w, PSESSION->sharingData.frameInfoDMA.h);
const auto FMT = PSTREAM->isDMA ? PSESSION->sharingData.frameInfoDMA.fmt : PSESSION->sharingData.frameInfoSHM.fmt;
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
(PSTREAM->pwVideoInfo.size.width != PSESSION->sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != PSESSION->sharingData.frameInfoDMA.h)) {
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
PSESSION->sharingData.status = FRAME_RENEG;
zwlr_screencopy_frame_v1_destroy(frame);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
PSESSION->sharingData.status = FRAME_NONE;
return;
}
if (!PSTREAM->currentPWBuffer) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(PSESSION);
}
if (!PSTREAM->currentPWBuffer) {
zwlr_screencopy_frame_v1_destroy(frame);
PSESSION->sharingData.frameCallback = nullptr;
Debug::log(LOG, "[screencopy/pipewire] Out of buffers");
PSESSION->sharingData.status = FRAME_NONE;
if (PSESSION->sharingData.copyRetries++ < MAX_RETRIES) {
Debug::log(LOG, "[sc] Retrying screencopy ({}/{})", PSESSION->sharingData.copyRetries, MAX_RETRIES);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
}
return;
}
zwlr_screencopy_frame_v1_copy_with_damage(frame, PSTREAM->currentPWBuffer->wlBuffer);
PSESSION->sharingData.copyRetries = 0;
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,
};
static void hlOnBuffer(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnBuffer for {}", (void*)PSESSION);
PSESSION->sharingData.frameInfoSHM.w = width;
PSESSION->sharingData.frameInfoSHM.h = height;
PSESSION->sharingData.frameInfoSHM.fmt = drmFourccFromSHM((wl_shm_format)format);
PSESSION->sharingData.frameInfoSHM.size = stride * height;
PSESSION->sharingData.frameInfoSHM.stride = stride;
// todo: done if ver < 3
}
static void hlOnFlags(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t flags) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnFlags for {}", (void*)PSESSION);
// todo: maybe check for y invert?
}
static void hlOnReady(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnReady for {}", (void*)PSESSION);
PSESSION->sharingData.status = FRAME_READY;
PSESSION->sharingData.tvSec = ((((uint64_t)tv_sec_hi) << 32) + (uint64_t)tv_sec_lo);
PSESSION->sharingData.tvNsec = tv_nsec;
PSESSION->sharingData.tvTimestampNs = PSESSION->sharingData.tvSec * SPA_NSEC_PER_SEC + PSESSION->sharingData.tvNsec;
Debug::log(TRACE, "[sc] frame timestamp sec: {} nsec: {} combined: {}ns", PSESSION->sharingData.tvSec, PSESSION->sharingData.tvNsec, PSESSION->sharingData.tvTimestampNs);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(PSESSION);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
hyprland_toplevel_export_frame_v1_destroy(frame);
PSESSION->sharingData.windowFrameCallback = nullptr;
}
static void hlOnFailed(void* data, hyprland_toplevel_export_frame_v1* frame) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnFailed for {}", (void*)PSESSION);
PSESSION->sharingData.status = FRAME_FAILED;
}
static void hlOnDamage(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnDamage for {}", (void*)PSESSION);
if (PSESSION->sharingData.damageCount > 3) {
PSESSION->sharingData.damage[0] = {0, 0, PSESSION->sharingData.frameInfoDMA.w, PSESSION->sharingData.frameInfoDMA.h};
return;
}
PSESSION->sharingData.damage[PSESSION->sharingData.damageCount++] = {x, y, width, height};
Debug::log(TRACE, "[sc] hl damage: {} {} {} {}", x, y, width, height);
}
static void hlOnDmabuf(void* data, hyprland_toplevel_export_frame_v1* frame, uint32_t format, uint32_t width, uint32_t height) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnDmabuf for {}", (void*)PSESSION);
PSESSION->sharingData.frameInfoDMA.w = width;
PSESSION->sharingData.frameInfoDMA.h = height;
PSESSION->sharingData.frameInfoDMA.fmt = format;
}
static void hlOnBufferDone(void* data, hyprland_toplevel_export_frame_v1* frame) {
const auto PSESSION = (CScreencopyPortal::SSession*)data;
Debug::log(TRACE, "[sc] hlOnBufferDone for {}", (void*)PSESSION);
const auto PSTREAM = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(PSESSION);
if (!PSTREAM) {
Debug::log(TRACE, "[sc] hlOnBufferDone: no stream");
hyprland_toplevel_export_frame_v1_destroy(frame);
PSESSION->sharingData.windowFrameCallback = nullptr;
PSESSION->sharingData.status = FRAME_NONE;
return;
}
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[sc] hl format {} size {}x{}", (int)PSESSION->sharingData.frameInfoSHM.fmt, PSESSION->sharingData.frameInfoSHM.w, PSESSION->sharingData.frameInfoSHM.h);
const auto FMT = PSTREAM->isDMA ? PSESSION->sharingData.frameInfoDMA.fmt : PSESSION->sharingData.frameInfoSHM.fmt;
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
(PSTREAM->pwVideoInfo.size.width != PSESSION->sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != PSESSION->sharingData.frameInfoDMA.h)) {
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
PSESSION->sharingData.status = FRAME_RENEG;
hyprland_toplevel_export_frame_v1_destroy(frame);
PSESSION->sharingData.windowFrameCallback = nullptr;
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
PSESSION->sharingData.status = FRAME_NONE;
return;
}
if (!PSTREAM->currentPWBuffer) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(PSESSION);
}
if (!PSTREAM->currentPWBuffer) {
hyprland_toplevel_export_frame_v1_destroy(frame);
PSESSION->sharingData.windowFrameCallback = nullptr;
Debug::log(LOG, "[screencopy/pipewire] Out of buffers");
PSESSION->sharingData.status = FRAME_NONE;
if (PSESSION->sharingData.copyRetries++ < MAX_RETRIES) {
Debug::log(LOG, "[sc] Retrying screencopy ({}/{})", PSESSION->sharingData.copyRetries, MAX_RETRIES);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(PSESSION);
}
return;
}
hyprland_toplevel_export_frame_v1_copy(frame, PSTREAM->currentPWBuffer->wlBuffer, false);
PSESSION->sharingData.copyRetries = 0;
Debug::log(TRACE, "[sc] wlr frame copied");
}
static const hyprland_toplevel_export_frame_v1_listener hyprlandFrameListener = {
.buffer = hlOnBuffer,
.damage = hlOnDamage,
.flags = hlOnFlags,
.ready = hlOnReady,
.failed = hlOnFailed,
.linux_dmabuf = hlOnDmabuf,
.buffer_done = hlOnBufferDone,
};
// --------------------------------------------------------- //
void CScreencopyPortal::onCreateSession(sdbus::MethodCall& call) { void CScreencopyPortal::onCreateSession(sdbus::MethodCall& call) {
sdbus::ObjectPath requestHandle, sessionHandle; sdbus::ObjectPath requestHandle, sessionHandle;
@ -479,15 +192,13 @@ void CScreencopyPortal::onSelectSources(sdbus::MethodCall& call) {
} }
} }
const bool RESTOREDATAVALID = restoreData.exists && const bool RESTOREDATAVALID = restoreData.exists && g_pPortalManager->getOutputFromName(restoreData.output);
(g_pPortalManager->m_sHelpers.toplevel->exists((zwlr_foreign_toplevel_handle_v1*)restoreData.windowHandle) || g_pPortalManager->getOutputFromName(restoreData.output));
SSelectionData SHAREDATA; SSelectionData SHAREDATA;
if (RESTOREDATAVALID) { if (RESTOREDATAVALID) {
Debug::log(LOG, "[screencopy] restore data valid, not prompting"); Debug::log(LOG, "[screencopy] restore data valid, not prompting");
SHAREDATA.output = restoreData.output; SHAREDATA.output = restoreData.output;
SHAREDATA.windowHandle = (zwlr_foreign_toplevel_handle_v1*)restoreData.windowHandle;
SHAREDATA.type = restoreData.windowHandle ? TYPE_WINDOW : TYPE_OUTPUT; SHAREDATA.type = restoreData.windowHandle ? TYPE_WINDOW : TYPE_OUTPUT;
SHAREDATA.allowToken = true; // user allowed token before SHAREDATA.allowToken = true; // user allowed token before
PSESSION->cursorMode = restoreData.withCursor; PSESSION->cursorMode = restoreData.withCursor;
@ -641,52 +352,263 @@ void CScreencopyPortal::startSharing(CScreencopyPortal::SSession* pSession) {
} }
void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) { void CScreencopyPortal::startFrameCopy(CScreencopyPortal::SSession* pSession) {
const auto POUTPUT = g_pPortalManager->getOutputFromName(pSession->selection.output); pSession->startCopy();
if (!pSession->sharingData.active) { Debug::log(TRACE, "[screencopy] frame callbacks initialized");
}
void CScreencopyPortal::SSession::startCopy() {
const auto POUTPUT = g_pPortalManager->getOutputFromName(selection.output);
if (!sharingData.active) {
Debug::log(TRACE, "[sc] startFrameCopy: not copying, inactive session"); Debug::log(TRACE, "[sc] startFrameCopy: not copying, inactive session");
return; return;
} }
if (!POUTPUT && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) { if (!POUTPUT && (selection.type == TYPE_GEOMETRY || selection.type == TYPE_OUTPUT)) {
Debug::log(ERR, "[screencopy] Output {} not found??", pSession->selection.output); Debug::log(ERR, "[screencopy] Output {} not found??", selection.output);
return; return;
} }
if ((pSession->sharingData.frameCallback && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) || if ((sharingData.frameCallback && (selection.type == TYPE_GEOMETRY || selection.type == TYPE_OUTPUT)) || (sharingData.windowFrameCallback && selection.type == TYPE_WINDOW)) {
(pSession->sharingData.windowFrameCallback && pSession->selection.type == TYPE_WINDOW)) { Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb (type {})", (int)selection.type);
Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb (type {})", (int)pSession->selection.type);
return; return;
} }
if (pSession->selection.type == TYPE_GEOMETRY) { if (selection.type == TYPE_GEOMETRY) {
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output_region(m_sState.screencopy, pSession->cursorMode, POUTPUT->output, pSession->selection.x, sharingData.frameCallback = makeShared<CCZwlrScreencopyFrameV1>(g_pPortalManager->m_sPortals.screencopy->m_sState.screencopy->sendCaptureOutputRegion(
pSession->selection.y, pSession->selection.w, pSession->selection.h); cursorMode, POUTPUT->output->resource(), selection.x, selection.y, selection.w, selection.h));
pSession->sharingData.transform = POUTPUT->transform; sharingData.transform = POUTPUT->transform;
} else if (pSession->selection.type == TYPE_OUTPUT) { } else if (selection.type == TYPE_OUTPUT) {
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output(m_sState.screencopy, pSession->cursorMode, POUTPUT->output); sharingData.frameCallback =
pSession->sharingData.transform = POUTPUT->transform; makeShared<CCZwlrScreencopyFrameV1>(g_pPortalManager->m_sPortals.screencopy->m_sState.screencopy->sendCaptureOutput(cursorMode, POUTPUT->output->resource()));
} else if (pSession->selection.type == TYPE_WINDOW) { sharingData.transform = POUTPUT->transform;
if (!pSession->selection.windowHandle) { } else if (selection.type == TYPE_WINDOW) {
if (!selection.windowHandle) {
Debug::log(ERR, "[screencopy] selected invalid window?"); Debug::log(ERR, "[screencopy] selected invalid window?");
return; return;
} }
pSession->sharingData.windowFrameCallback = sharingData.windowFrameCallback = makeShared<CCHyprlandToplevelExportFrameV1>(
hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(m_sState.toplevel, pSession->cursorMode, pSession->selection.windowHandle); g_pPortalManager->m_sPortals.screencopy->m_sState.toplevel->sendCaptureToplevelWithWlrToplevelHandle(cursorMode, selection.windowHandle->resource()));
pSession->sharingData.transform = WL_OUTPUT_TRANSFORM_NORMAL; sharingData.transform = WL_OUTPUT_TRANSFORM_NORMAL;
} else { } else {
Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)pSession->selection.type); Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)selection.type);
return; return;
} }
pSession->sharingData.status = FRAME_QUEUED; sharingData.status = FRAME_QUEUED;
if (pSession->sharingData.frameCallback) initCallbacks();
zwlr_screencopy_frame_v1_add_listener(pSession->sharingData.frameCallback, &wlrFrameListener, pSession); }
else if (pSession->sharingData.windowFrameCallback)
hyprland_toplevel_export_frame_v1_add_listener(pSession->sharingData.windowFrameCallback, &hyprlandFrameListener, pSession);
Debug::log(TRACE, "[screencopy] frame callbacks initialized"); void CScreencopyPortal::SSession::initCallbacks() {
if (sharingData.frameCallback) {
sharingData.frameCallback->setBuffer([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
Debug::log(TRACE, "[sc] wlrOnBuffer for {}", (void*)this);
sharingData.frameInfoSHM.w = width;
sharingData.frameInfoSHM.h = height;
sharingData.frameInfoSHM.fmt = drmFourccFromSHM((wl_shm_format)format);
sharingData.frameInfoSHM.size = stride * height;
sharingData.frameInfoSHM.stride = stride;
// todo: done if ver < 3
});
sharingData.frameCallback->setReady([this](CCZwlrScreencopyFrameV1* r, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
Debug::log(TRACE, "[sc] wlrOnReady for {}", (void*)this);
sharingData.status = FRAME_READY;
sharingData.tvSec = ((((uint64_t)tv_sec_hi) << 32) + (uint64_t)tv_sec_lo);
sharingData.tvNsec = tv_nsec;
sharingData.tvTimestampNs = sharingData.tvSec * SPA_NSEC_PER_SEC + sharingData.tvNsec;
Debug::log(TRACE, "[sc] frame timestamp sec: {} nsec: {} combined: {}ns", sharingData.tvSec, sharingData.tvNsec, sharingData.tvTimestampNs);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(this);
if (g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(this))
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
sharingData.frameCallback.reset();
});
sharingData.frameCallback->setFailed([this](CCZwlrScreencopyFrameV1* r) {
Debug::log(TRACE, "[sc] wlrOnFailed for {}", (void*)this);
sharingData.status = FRAME_FAILED;
});
sharingData.frameCallback->setDamage([this](CCZwlrScreencopyFrameV1* r, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
Debug::log(TRACE, "[sc] wlrOnDamage for {}", (void*)this);
if (sharingData.damageCount > 3) {
sharingData.damage[0] = {0, 0, sharingData.frameInfoDMA.w, sharingData.frameInfoDMA.h};
return;
}
sharingData.damage[sharingData.damageCount++] = {x, y, width, height};
Debug::log(TRACE, "[sc] wlr damage: {} {} {} {}", x, y, width, height);
});
sharingData.frameCallback->setLinuxDmabuf([this](CCZwlrScreencopyFrameV1* r, uint32_t format, uint32_t width, uint32_t height) {
Debug::log(TRACE, "[sc] wlrOnDmabuf for {}", (void*)this);
sharingData.frameInfoDMA.w = width;
sharingData.frameInfoDMA.h = height;
sharingData.frameInfoDMA.fmt = format;
});
sharingData.frameCallback->setBufferDone([this](CCZwlrScreencopyFrameV1* r) {
Debug::log(TRACE, "[sc] wlrOnBufferDone for {}", (void*)this);
const auto PSTREAM = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(this);
if (!PSTREAM) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: no stream");
sharingData.frameCallback.reset();
sharingData.status = FRAME_NONE;
return;
}
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[sc] wlr format {} size {}x{}", (int)sharingData.frameInfoSHM.fmt, sharingData.frameInfoSHM.w, sharingData.frameInfoSHM.h);
Debug::log(TRACE, "[sc] wlr format dma {} size {}x{}", (int)sharingData.frameInfoDMA.fmt, sharingData.frameInfoDMA.w, sharingData.frameInfoDMA.h);
const auto FMT = PSTREAM->isDMA ? sharingData.frameInfoDMA.fmt : sharingData.frameInfoSHM.fmt;
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
(PSTREAM->pwVideoInfo.size.width != sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != sharingData.frameInfoDMA.h)) {
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
sharingData.status = FRAME_RENEG;
sharingData.frameCallback.reset();
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
sharingData.status = FRAME_NONE;
return;
}
if (!PSTREAM->currentPWBuffer) {
Debug::log(TRACE, "[sc] wlrOnBufferDone: dequeue, no current buffer");
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(this);
}
if (!PSTREAM->currentPWBuffer) {
sharingData.frameCallback.reset();
Debug::log(LOG, "[screencopy/pipewire] Out of buffers");
sharingData.status = FRAME_NONE;
if (sharingData.copyRetries++ < MAX_RETRIES) {
Debug::log(LOG, "[sc] Retrying screencopy ({}/{})", sharingData.copyRetries, MAX_RETRIES);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
}
return;
}
sharingData.frameCallback->sendCopyWithDamage(PSTREAM->currentPWBuffer->wlBuffer->resource());
sharingData.copyRetries = 0;
Debug::log(TRACE, "[sc] wlr frame copied");
});
} else if (sharingData.windowFrameCallback) {
sharingData.windowFrameCallback->setBuffer([this](CCHyprlandToplevelExportFrameV1* r, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) {
Debug::log(TRACE, "[sc] hlOnBuffer for {}", (void*)this);
sharingData.frameInfoSHM.w = width;
sharingData.frameInfoSHM.h = height;
sharingData.frameInfoSHM.fmt = drmFourccFromSHM((wl_shm_format)format);
sharingData.frameInfoSHM.size = stride * height;
sharingData.frameInfoSHM.stride = stride;
// todo: done if ver < 3
});
sharingData.windowFrameCallback->setReady([this](CCHyprlandToplevelExportFrameV1* r, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
Debug::log(TRACE, "[sc] hlOnReady for {}", (void*)this);
sharingData.status = FRAME_READY;
sharingData.tvSec = ((((uint64_t)tv_sec_hi) << 32) + (uint64_t)tv_sec_lo);
sharingData.tvNsec = tv_nsec;
sharingData.tvTimestampNs = sharingData.tvSec * SPA_NSEC_PER_SEC + sharingData.tvNsec;
Debug::log(TRACE, "[sc] frame timestamp sec: {} nsec: {} combined: {}ns", sharingData.tvSec, sharingData.tvNsec, sharingData.tvTimestampNs);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->enqueue(this);
if (g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(this))
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
sharingData.windowFrameCallback.reset();
});
sharingData.windowFrameCallback->setFailed([this](CCHyprlandToplevelExportFrameV1* r) {
Debug::log(TRACE, "[sc] hlOnFailed for {}", (void*)this);
sharingData.status = FRAME_FAILED;
});
sharingData.windowFrameCallback->setDamage([this](CCHyprlandToplevelExportFrameV1* r, uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
Debug::log(TRACE, "[sc] hlOnDamage for {}", (void*)this);
if (sharingData.damageCount > 3) {
sharingData.damage[0] = {0, 0, sharingData.frameInfoDMA.w, sharingData.frameInfoDMA.h};
return;
}
sharingData.damage[sharingData.damageCount++] = {x, y, width, height};
Debug::log(TRACE, "[sc] hl damage: {} {} {} {}", x, y, width, height);
});
sharingData.windowFrameCallback->setLinuxDmabuf([this](CCHyprlandToplevelExportFrameV1* r, uint32_t format, uint32_t width, uint32_t height) {
Debug::log(TRACE, "[sc] hlOnDmabuf for {}", (void*)this);
sharingData.frameInfoDMA.w = width;
sharingData.frameInfoDMA.h = height;
sharingData.frameInfoDMA.fmt = format;
});
sharingData.windowFrameCallback->setBufferDone([this](CCHyprlandToplevelExportFrameV1* r) {
Debug::log(TRACE, "[sc] hlOnBufferDone for {}", (void*)this);
const auto PSTREAM = g_pPortalManager->m_sPortals.screencopy->m_pPipewire->streamFromSession(this);
if (!PSTREAM) {
Debug::log(TRACE, "[sc] hlOnBufferDone: no stream");
sharingData.windowFrameCallback.reset();
sharingData.status = FRAME_NONE;
return;
}
Debug::log(TRACE, "[sc] pw format {} size {}x{}", (int)PSTREAM->pwVideoInfo.format, PSTREAM->pwVideoInfo.size.width, PSTREAM->pwVideoInfo.size.height);
Debug::log(TRACE, "[sc] hl format {} size {}x{}", (int)sharingData.frameInfoSHM.fmt, sharingData.frameInfoSHM.w, sharingData.frameInfoSHM.h);
Debug::log(TRACE, "[sc] hl format dma {} size {}x{}", (int)sharingData.frameInfoDMA.fmt, sharingData.frameInfoDMA.w, sharingData.frameInfoDMA.h);
const auto FMT = PSTREAM->isDMA ? sharingData.frameInfoDMA.fmt : sharingData.frameInfoSHM.fmt;
if ((PSTREAM->pwVideoInfo.format != pwFromDrmFourcc(FMT) && PSTREAM->pwVideoInfo.format != pwStripAlpha(pwFromDrmFourcc(FMT))) ||
(PSTREAM->pwVideoInfo.size.width != sharingData.frameInfoDMA.w || PSTREAM->pwVideoInfo.size.height != sharingData.frameInfoDMA.h)) {
Debug::log(LOG, "[sc] Incompatible formats, renegotiate stream");
sharingData.status = FRAME_RENEG;
sharingData.windowFrameCallback.reset();
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
sharingData.status = FRAME_NONE;
return;
}
if (!PSTREAM->currentPWBuffer) {
Debug::log(TRACE, "[sc] hlOnBufferDone: dequeue, no current buffer");
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->dequeue(this);
}
if (!PSTREAM->currentPWBuffer) {
sharingData.windowFrameCallback.reset();
Debug::log(LOG, "[screencopy/pipewire] Out of buffers");
sharingData.status = FRAME_NONE;
if (sharingData.copyRetries++ < MAX_RETRIES) {
Debug::log(LOG, "[sc] Retrying screencopy ({}/{})", sharingData.copyRetries, MAX_RETRIES);
g_pPortalManager->m_sPortals.screencopy->m_pPipewire->updateStreamParam(PSTREAM);
g_pPortalManager->m_sPortals.screencopy->queueNextShareFrame(this);
}
return;
}
sharingData.windowFrameCallback->sendCopy(PSTREAM->currentPWBuffer->wlBuffer->resource(), false);
sharingData.copyRetries = 0;
Debug::log(TRACE, "[sc] hl frame copied");
});
}
} }
void CScreencopyPortal::queueNextShareFrame(CScreencopyPortal::SSession* pSession) { void CScreencopyPortal::queueNextShareFrame(CScreencopyPortal::SSession* pSession) {
@ -719,7 +641,7 @@ CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& pa
return nullptr; return nullptr;
} }
CScreencopyPortal::CScreencopyPortal(zwlr_screencopy_manager_v1* mgr) { CScreencopyPortal::CScreencopyPortal(SP<CCZwlrScreencopyManagerV1> mgr) {
m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH); m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH);
m_pObject->registerMethod(INTERFACE_NAME, "CreateSession", "oosa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onCreateSession(c); }); m_pObject->registerMethod(INTERFACE_NAME, "CreateSession", "oosa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onCreateSession(c); });
@ -737,8 +659,8 @@ CScreencopyPortal::CScreencopyPortal(zwlr_screencopy_manager_v1* mgr) {
Debug::log(LOG, "[screencopy] init successful"); Debug::log(LOG, "[screencopy] init successful");
} }
void CScreencopyPortal::appendToplevelExport(void* proto) { void CScreencopyPortal::appendToplevelExport(SP<CCHyprlandToplevelExportManagerV1> proto) {
m_sState.toplevel = (hyprland_toplevel_export_manager_v1*)proto; m_sState.toplevel = proto;
Debug::log(LOG, "[screencopy] Registered for toplevel export"); Debug::log(LOG, "[screencopy] Registered for toplevel export");
} }
@ -768,10 +690,8 @@ CPipewireConnection::CPipewireConnection() {
void CPipewireConnection::removeSessionFrameCallbacks(CScreencopyPortal::SSession* pSession) { void CPipewireConnection::removeSessionFrameCallbacks(CScreencopyPortal::SSession* pSession) {
Debug::log(TRACE, "[pipewire] removeSessionFrameCallbacks called"); Debug::log(TRACE, "[pipewire] removeSessionFrameCallbacks called");
if (pSession->sharingData.frameCallback) pSession->sharingData.frameCallback.reset();
zwlr_screencopy_frame_v1_destroy(pSession->sharingData.frameCallback); pSession->sharingData.windowFrameCallback.reset();
if (pSession->sharingData.windowFrameCallback)
hyprland_toplevel_export_frame_v1_destroy(pSession->sharingData.windowFrameCallback);
pSession->sharingData.windowFrameCallback = nullptr; pSession->sharingData.windowFrameCallback = nullptr;
pSession->sharingData.frameCallback = nullptr; pSession->sharingData.frameCallback = nullptr;
@ -1002,7 +922,7 @@ static void pwStreamRemoveBuffer(void* data, pw_buffer* buffer) {
if (PBUFFER->isDMABUF) if (PBUFFER->isDMABUF)
gbm_bo_destroy(PBUFFER->bo); gbm_bo_destroy(PBUFFER->bo);
wl_buffer_destroy(PBUFFER->wlBuffer); PBUFFER->wlBuffer.reset();
for (int plane = 0; plane < PBUFFER->planeCount; plane++) { for (int plane = 0; plane < PBUFFER->planeCount; plane++) {
close(PBUFFER->fd[plane]); close(PBUFFER->fd[plane]);
} }
@ -1322,7 +1242,7 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
pBuffer->planeCount = gbm_bo_get_plane_count(pBuffer->bo); pBuffer->planeCount = gbm_bo_get_plane_count(pBuffer->bo);
zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params((zwp_linux_dmabuf_v1*)g_pPortalManager->m_sWaylandConnection.linuxDmabuf); auto params = makeShared<CCZwpLinuxBufferParamsV1>(g_pPortalManager->m_sWaylandConnection.linuxDmabuf->sendCreateParams());
if (!params) { if (!params) {
Debug::log(ERR, "[pw] zwp_linux_dmabuf_v1_create_params failed"); Debug::log(ERR, "[pw] zwp_linux_dmabuf_v1_create_params failed");
gbm_bo_destroy(pBuffer->bo); gbm_bo_destroy(pBuffer->bo);
@ -1338,7 +1258,7 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
if (pBuffer->fd[plane] < 0) { if (pBuffer->fd[plane] < 0) {
Debug::log(ERR, "[pw] gbm_bo_get_fd_for_plane failed"); Debug::log(ERR, "[pw] gbm_bo_get_fd_for_plane failed");
zwp_linux_buffer_params_v1_destroy(params); params.reset();
gbm_bo_destroy(pBuffer->bo); gbm_bo_destroy(pBuffer->bo);
for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) { for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
close(pBuffer->fd[plane_tmp]); close(pBuffer->fd[plane_tmp]);
@ -1346,11 +1266,11 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
return NULL; return NULL;
} }
zwp_linux_buffer_params_v1_add(params, pBuffer->fd[plane], plane, pBuffer->offset[plane], pBuffer->stride[plane], mod >> 32, mod & 0xffffffff); params->sendAdd(pBuffer->fd[plane], plane, pBuffer->offset[plane], pBuffer->stride[plane], mod >> 32, mod & 0xffffffff);
} }
pBuffer->wlBuffer = zwp_linux_buffer_params_v1_create_immed(params, pBuffer->w, pBuffer->h, pBuffer->fmt, /* flags */ 0); pBuffer->wlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(pBuffer->w, pBuffer->h, pBuffer->fmt, /* flags */ (zwpLinuxBufferParamsV1Flags)0));
zwp_linux_buffer_params_v1_destroy(params); params.reset();
if (!pBuffer->wlBuffer) { if (!pBuffer->wlBuffer) {
Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed"); Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");

View file

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <protocols/wlr-screencopy-unstable-v1-protocol.h> #include "wlr-screencopy-unstable-v1.hpp"
#include <protocols/hyprland-toplevel-export-v1-protocol.h> #include "hyprland-toplevel-export-v1.hpp"
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include "../shared/ScreencopyShared.hpp" #include "../shared/ScreencopyShared.hpp"
#include <gbm.h> #include <gbm.h>
@ -43,7 +43,7 @@ struct SBuffer {
gbm_bo* bo = nullptr; gbm_bo* bo = nullptr;
wl_buffer* wlBuffer = nullptr; SP<CCWlBuffer> wlBuffer = nullptr;
pw_buffer* pwBuffer = nullptr; pw_buffer* pwBuffer = nullptr;
}; };
@ -51,9 +51,9 @@ class CPipewireConnection;
class CScreencopyPortal { class CScreencopyPortal {
public: public:
CScreencopyPortal(zwlr_screencopy_manager_v1*); CScreencopyPortal(SP<CCZwlrScreencopyManagerV1>);
void appendToplevelExport(void*); void appendToplevelExport(SP<CCHyprlandToplevelExportManagerV1>);
void onCreateSession(sdbus::MethodCall& call); void onCreateSession(sdbus::MethodCall& call);
void onSelectSources(sdbus::MethodCall& call); void onSelectSources(sdbus::MethodCall& call);
@ -69,10 +69,13 @@ class CScreencopyPortal {
std::unique_ptr<SDBusSession> session; std::unique_ptr<SDBusSession> session;
SSelectionData selection; SSelectionData selection;
void startCopy();
void initCallbacks();
struct { struct {
bool active = false; bool active = false;
zwlr_screencopy_frame_v1* frameCallback = nullptr; SP<CCZwlrScreencopyFrameV1> frameCallback = nullptr;
hyprland_toplevel_export_frame_v1* windowFrameCallback = nullptr; SP<CCHyprlandToplevelExportFrameV1> windowFrameCallback = nullptr;
frameStatus status = FRAME_NONE; frameStatus status = FRAME_NONE;
uint64_t tvSec = 0; uint64_t tvSec = 0;
uint32_t tvNsec = 0; uint32_t tvNsec = 0;
@ -116,12 +119,14 @@ class CScreencopyPortal {
void startSharing(SSession* pSession); void startSharing(SSession* pSession);
struct { struct {
zwlr_screencopy_manager_v1* screencopy = nullptr; SP<CCZwlrScreencopyManagerV1> screencopy = nullptr;
hyprland_toplevel_export_manager_v1* toplevel = nullptr; SP<CCHyprlandToplevelExportManagerV1> toplevel = nullptr;
} m_sState; } m_sState;
const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.ScreenCast"; const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.ScreenCast";
const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop"; const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop";
friend struct SSession;
}; };
class CPipewireConnection { class CPipewireConnection {

View file

@ -1,7 +1,6 @@
#pragma once #pragma once
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
class CScreenshotPortal { class CScreenshotPortal {
public: public:

View file

@ -320,15 +320,14 @@ int anonymous_shm_open() {
return -1; return -1;
} }
wl_buffer* import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride) { SP<CCWlBuffer> import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride) {
int size = stride * height; int size = stride * height;
if (fd < 0) if (fd < 0)
return NULL; return nullptr;
wl_shm_pool* pool = wl_shm_create_pool(g_pPortalManager->m_sWaylandConnection.shm, fd, size); auto pool = makeShared<CCWlShmPool>(g_pPortalManager->m_sWaylandConnection.shm->sendCreatePool(fd, size));
wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt); auto buf = makeShared<CCWlBuffer>(pool->sendCreateBuffer(0, width, height, stride, fmt));
wl_shm_pool_destroy(pool);
return buffer; return buf;
} }

View file

@ -12,7 +12,9 @@ extern "C" {
#include <spa/param/video/format-utils.h> #include <spa/param/video/format-utils.h>
#include <spa/pod/dynamic.h> #include <spa/pod/dynamic.h>
} }
#include <wayland-client.h> #include "wayland.hpp"
#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
#include "../includes.hpp"
#define XDPH_PWR_BUFFERS 4 #define XDPH_PWR_BUFFERS 4
#define XDPH_PWR_BUFFERS_MIN 2 #define XDPH_PWR_BUFFERS_MIN 2
@ -31,7 +33,7 @@ struct zwlr_foreign_toplevel_handle_v1;
struct SSelectionData { struct SSelectionData {
eSelectionType type = TYPE_INVALID; eSelectionType type = TYPE_INVALID;
std::string output; std::string output;
zwlr_foreign_toplevel_handle_v1* windowHandle = nullptr; SP<CCZwlrForeignToplevelHandleV1> windowHandle = nullptr;
uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY
bool allowToken = false; bool allowToken = false;
}; };
@ -48,4 +50,4 @@ spa_pod* build_format(spa_pod_builder* b, spa_video_format format, uint3
spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier); spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier);
spa_pod* build_buffer(spa_pod_builder* b, uint32_t blocks, uint32_t size, uint32_t stride, uint32_t datatype); spa_pod* build_buffer(spa_pod_builder* b, uint32_t blocks, uint32_t size, uint32_t stride, uint32_t datatype);
int anonymous_shm_open(); int anonymous_shm_open();
wl_buffer* import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride); SP<CCWlBuffer> import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride);

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include "../includes.hpp"
#include <sdbus-c++/sdbus-c++.h> #include <sdbus-c++/sdbus-c++.h>
struct SDBusSession { struct SDBusSession {

View file

@ -2,97 +2,28 @@
#include "../helpers/Log.hpp" #include "../helpers/Log.hpp"
#include "../core/PortalManager.hpp" #include "../core/PortalManager.hpp"
static void toplevelTitle(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, const char* title) { SToplevelHandle::SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle_) : handle(handle_) {
const auto PTL = (SToplevelHandle*)data; handle->setTitle([this](CCZwlrForeignToplevelHandleV1* r, const char* title) {
if (title) if (title)
PTL->windowTitle = title; windowTitle = title;
Debug::log(TRACE, "[toplevel] toplevel at {} set title to {}", data, PTL->windowTitle); Debug::log(TRACE, "[toplevel] toplevel at {} set title to {}", (void*)this, windowTitle);
});
handle->setAppId([this](CCZwlrForeignToplevelHandleV1* r, const char* class_) {
if (class_)
windowClass = class_;
Debug::log(TRACE, "[toplevel] toplevel at {} set class to {}", (void*)this, windowClass);
});
handle->setClosed([this](CCZwlrForeignToplevelHandleV1* r) {
Debug::log(TRACE, "[toplevel] toplevel at {} closed", (void*)this);
std::erase_if(g_pPortalManager->m_sHelpers.toplevel->m_vToplevels, [&](const auto& e) { return e.get() == this; });
});
} }
static void toplevelAppid(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, const char* app_id) { CToplevelManager::CToplevelManager(uint32_t name, uint32_t version) {
const auto PTL = (SToplevelHandle*)data; m_sWaylandConnection = {name, version};
if (app_id)
PTL->windowClass = app_id;
Debug::log(TRACE, "[toplevel] toplevel at {} set class to {}", data, PTL->windowClass);
}
static void toplevelEnterOutput(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_output* output) {
;
}
static void toplevelLeaveOutput(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_output* output) {
;
}
static void toplevelState(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, wl_array* state) {
;
}
static void toplevelDone(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1) {
;
}
static void toplevelClosed(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1) {
const auto PTL = (SToplevelHandle*)data;
std::erase_if(PTL->mgr->m_vToplevels, [&](const auto& e) { return e.get() == PTL; });
Debug::log(TRACE, "[toplevel] toplevel at {} closed", data);
}
static void toplevelParent(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, struct zwlr_foreign_toplevel_handle_v1* parent) {
;
}
inline const zwlr_foreign_toplevel_handle_v1_listener toplevelListener = {
.title = toplevelTitle,
.app_id = toplevelAppid,
.output_enter = toplevelEnterOutput,
.output_leave = toplevelLeaveOutput,
.state = toplevelState,
.done = toplevelDone,
.closed = toplevelClosed,
.parent = toplevelParent,
};
static void managerToplevel(void* data, zwlr_foreign_toplevel_manager_v1* mgr, zwlr_foreign_toplevel_handle_v1* toplevel) {
const auto PMGR = (CToplevelManager*)data;
Debug::log(TRACE, "[toplevel] New toplevel at {}", (void*)toplevel);
const auto PTL = PMGR->m_vToplevels.emplace_back(std::make_unique<SToplevelHandle>("?", "?", toplevel, PMGR)).get();
zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &toplevelListener, PTL);
}
static void managerFinished(void* data, zwlr_foreign_toplevel_manager_v1* mgr) {
const auto PMGR = (CToplevelManager*)data;
Debug::log(ERR, "[toplevel] Compositor sent .finished???");
PMGR->m_vToplevels.clear();
}
inline const zwlr_foreign_toplevel_manager_v1_listener managerListener = {
.toplevel = managerToplevel,
.finished = managerFinished,
};
bool CToplevelManager::exists(zwlr_foreign_toplevel_handle_v1* handle) {
for (auto& h : m_vToplevels) {
if (h->handle == handle)
return true;
}
return false;
}
CToplevelManager::CToplevelManager(wl_registry* registry, uint32_t name, uint32_t version) {
m_sWaylandConnection = {registry, name, version};
} }
void CToplevelManager::activate() { void CToplevelManager::activate() {
@ -103,9 +34,16 @@ void CToplevelManager::activate() {
if (m_pManager || m_iActivateLocks < 1) if (m_pManager || m_iActivateLocks < 1)
return; return;
m_pManager = (zwlr_foreign_toplevel_manager_v1*)wl_registry_bind(m_sWaylandConnection.registry, m_sWaylandConnection.name, &zwlr_foreign_toplevel_manager_v1_interface, m_pManager = makeShared<CCZwlrForeignToplevelManagerV1>(wl_registry_bind((wl_registry*)g_pPortalManager->m_sWaylandConnection.registry->resource(), m_sWaylandConnection.name,
m_sWaylandConnection.version); &zwlr_foreign_toplevel_manager_v1_interface, m_sWaylandConnection.version));
zwlr_foreign_toplevel_manager_v1_add_listener(m_pManager, &managerListener, this);
m_pManager->setToplevel([this](CCZwlrForeignToplevelManagerV1* r, CCZwlrForeignToplevelHandleV1* newHandle) {
Debug::log(TRACE, "[toplevel] New toplevel at {}", (void*)newHandle);
m_vToplevels.emplace_back(std::make_unique<SToplevelHandle>(SP<CCZwlrForeignToplevelHandleV1>(newHandle)));
});
m_pManager->setFinished([this](CCZwlrForeignToplevelManagerV1* r) { m_vToplevels.clear(); });
wl_display_roundtrip(g_pPortalManager->m_sWaylandConnection.display); wl_display_roundtrip(g_pPortalManager->m_sWaylandConnection.display);
Debug::log(LOG, "[toplevel] Activated, bound to {:x}, toplevels: {}", (uintptr_t)m_pManager, m_vToplevels.size()); Debug::log(LOG, "[toplevel] Activated, bound to {:x}, toplevels: {}", (uintptr_t)m_pManager, m_vToplevels.size());
@ -119,8 +57,7 @@ void CToplevelManager::deactivate() {
if (!m_pManager || m_iActivateLocks > 0) if (!m_pManager || m_iActivateLocks > 0)
return; return;
zwlr_foreign_toplevel_manager_v1_destroy(m_pManager); m_pManager.reset();
m_pManager = nullptr;
m_vToplevels.clear(); m_vToplevels.clear();
Debug::log(LOG, "[toplevel] unbound manager"); Debug::log(LOG, "[toplevel] unbound manager");

View file

@ -1,38 +1,37 @@
#pragma once #pragma once
#include <wayland-client.h> #include "wayland.hpp"
#include <protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h> #include "wlr-foreign-toplevel-management-unstable-v1.hpp"
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "../includes.hpp"
class CToplevelManager; class CToplevelManager;
struct SToplevelHandle { struct SToplevelHandle {
SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle);
std::string windowClass; std::string windowClass;
std::string windowTitle; std::string windowTitle;
zwlr_foreign_toplevel_handle_v1* handle = nullptr; SP<CCZwlrForeignToplevelHandleV1> handle = nullptr;
CToplevelManager* mgr = nullptr; CToplevelManager* mgr = nullptr;
}; };
class CToplevelManager { class CToplevelManager {
public: public:
CToplevelManager(wl_registry* registry, uint32_t name, uint32_t version); CToplevelManager(uint32_t name, uint32_t version);
void activate(); void activate();
void deactivate(); void deactivate();
bool exists(zwlr_foreign_toplevel_handle_v1* handle);
std::vector<std::unique_ptr<SToplevelHandle>> m_vToplevels; std::vector<std::unique_ptr<SToplevelHandle>> m_vToplevels;
private: private:
zwlr_foreign_toplevel_manager_v1* m_pManager = nullptr; SP<CCZwlrForeignToplevelManagerV1> m_pManager = nullptr;
int64_t m_iActivateLocks = 0; int64_t m_iActivateLocks = 0;
struct { struct {
wl_registry* registry = nullptr;
uint32_t name = 0; uint32_t name = 0;
uint32_t version = 0; uint32_t version = 0;
} m_sWaylandConnection; } m_sWaylandConnection;