mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-12-25 10:59:48 +01:00
move to hw-s
This commit is contained in:
parent
e695669fd8
commit
5c5cb55abb
14 changed files with 540 additions and 742 deletions
|
@ -61,7 +61,9 @@ pkg_check_modules(
|
|||
libspa-0.2
|
||||
libdrm
|
||||
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
|
||||
pkg_check_modules(SDBUS IMPORTED_TARGET sdbus-c++)
|
||||
|
@ -86,43 +88,43 @@ target_link_libraries(
|
|||
PkgConfig::deps)
|
||||
|
||||
# protocols
|
||||
find_program(WaylandScanner NAMES wayland-scanner)
|
||||
message(STATUS "Found WaylandScanner at ${WaylandScanner}")
|
||||
pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir)
|
||||
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)
|
||||
set(path ${protoPath})
|
||||
set(path ${CMAKE_SOURCE_DIR}/${protoPath})
|
||||
else()
|
||||
set(path ${WAYLAND_PROTOCOLS_DIR}/${protoPath})
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
|
||||
COMMAND ${WaylandScanner} client-header ${path}
|
||||
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.h
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}.cpp
|
||||
${CMAKE_SOURCE_DIR}/protocols/${protoName}.hpp
|
||||
COMMAND hyprwayland-scanner --client ${path}/${protoName}.xml
|
||||
${CMAKE_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/${protoName}.cpp
|
||||
protocols/${protoName}.hpp)
|
||||
endfunction()
|
||||
function(protocolWayland)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
|
||||
COMMAND ${WaylandScanner} private-code ${path}
|
||||
${CMAKE_SOURCE_DIR}/protocols/${protoName}-protocol.c
|
||||
OUTPUT ${CMAKE_SOURCE_DIR}/protocols/wayland.cpp
|
||||
${CMAKE_SOURCE_DIR}/protocols/wayland.hpp
|
||||
COMMAND hyprwayland-scanner --wayland-enums --client
|
||||
${WAYLAND_CLIENT_DIR}/wayland.xml ${CMAKE_SOURCE_DIR}/protocols/
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
|
||||
target_sources(xdg-desktop-portal-hyprland
|
||||
PRIVATE protocols/${protoName}-protocol.h)
|
||||
target_sources(xdg-desktop-portal-hyprland
|
||||
PRIVATE protocols/${protoName}-protocol.c)
|
||||
target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/wayland.cpp protocols/wayland.hpp)
|
||||
endfunction()
|
||||
|
||||
protocol("protocols/wlr-foreign-toplevel-management-unstable-v1.xml"
|
||||
"wlr-foreign-toplevel-management-unstable-v1" true)
|
||||
protocol("protocols/wlr-screencopy-unstable-v1.xml"
|
||||
"wlr-screencopy-unstable-v1" true)
|
||||
protocol("${HYPRLAND_PROTOCOLS}/protocols/hyprland-global-shortcuts-v1.xml"
|
||||
"hyprland-global-shortcuts-v1" true)
|
||||
protocol("${HYPRLAND_PROTOCOLS}/protocols/hyprland-toplevel-export-v1.xml"
|
||||
"hyprland-toplevel-export-v1" true)
|
||||
protocol("unstable/linux-dmabuf/linux-dmabuf-unstable-v1.xml"
|
||||
"linux-dmabuf-unstable-v1" false)
|
||||
protocolwayland()
|
||||
|
||||
protocolnew("protocols" "wlr-foreign-toplevel-management-unstable-v1" true)
|
||||
protocolnew("protocols" "wlr-screencopy-unstable-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-global-shortcuts-v1" true)
|
||||
protocolnew("${HYPRLAND_PROTOCOLS}/protocols" "hyprland-toplevel-export-v1" true)
|
||||
protocolnew("unstable/linux-dmabuf" "linux-dmabuf-unstable-v1" false)
|
||||
|
||||
# Installation
|
||||
install(TARGETS hyprland-share-picker)
|
||||
|
|
|
@ -2,12 +2,6 @@
|
|||
#include "../helpers/Log.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 <poll.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -16,189 +10,24 @@
|
|||
|
||||
#include <thread>
|
||||
|
||||
void handleGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
|
||||
g_pPortalManager->onGlobal(data, registry, name, interface, version);
|
||||
SOutput::SOutput(SP<CCWlOutput> output_) : output(output_) {
|
||||
output->setName([this](CCWlOutput* o, const char* name_) {
|
||||
if (!name_)
|
||||
return;
|
||||
|
||||
name = 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_;
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
const auto POUTPUT = (SOutput*)data;
|
||||
POUTPUT->name = name;
|
||||
|
||||
Debug::log(LOG, "Found output name {}", POUTPUT->name);
|
||||
}
|
||||
|
||||
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() {
|
||||
const auto XDG_CONFIG_HOME = getenv("XDG_CONFIG_HOME");
|
||||
const auto HOME = getenv("HOME");
|
||||
|
@ -218,25 +47,31 @@ CPortalManager::CPortalManager() {
|
|||
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;
|
||||
|
||||
Debug::log(LOG, " | Got interface: {} (ver {})", INTERFACE, version);
|
||||
|
||||
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));
|
||||
if (INTERFACE == zwlr_screencopy_manager_v1_interface.name && m_sPipewire.loop) {
|
||||
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)
|
||||
m_sPortals.globalShortcuts = std::make_unique<CGlobalShortcutsPortal>(
|
||||
(hyprland_global_shortcuts_manager_v1*)wl_registry_bind(registry, name, &hyprland_global_shortcuts_manager_v1_interface, version));
|
||||
if (INTERFACE == hyprland_global_shortcuts_manager_v1_interface.name) {
|
||||
m_sPortals.globalShortcuts = std::make_unique<CGlobalShortcutsPortal>(makeShared<CCHyprlandGlobalShortcutsManagerV1>(
|
||||
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)
|
||||
m_sWaylandConnection.hyprlandToplevelMgr = wl_registry_bind(registry, name, &hyprland_toplevel_export_manager_v1_interface, version);
|
||||
else if (INTERFACE == hyprland_toplevel_export_manager_v1_interface.name) {
|
||||
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) {
|
||||
const auto POUTPUT = m_vOutputs.emplace_back(std::make_unique<SOutput>()).get();
|
||||
POUTPUT->output = (wl_output*)wl_registry_bind(registry, name, &wl_output_interface, version);
|
||||
wl_output_add_listener(POUTPUT->output, &outputListener, POUTPUT);
|
||||
const auto POUTPUT = m_vOutputs
|
||||
.emplace_back(std::make_unique<SOutput>(
|
||||
makeShared<CCWlOutput>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &wl_output_interface, version))))
|
||||
.get();
|
||||
POUTPUT->id = name;
|
||||
}
|
||||
|
||||
|
@ -246,16 +81,111 @@ void CPortalManager::onGlobal(void* data, struct wl_registry* registry, uint32_t
|
|||
return;
|
||||
}
|
||||
|
||||
m_sWaylandConnection.linuxDmabuf = wl_registry_bind(registry, name, &zwp_linux_dmabuf_v1_interface, version);
|
||||
m_sWaylandConnection.linuxDmabufFeedback = zwp_linux_dmabuf_v1_get_default_feedback((zwp_linux_dmabuf_v1*)m_sWaylandConnection.linuxDmabuf);
|
||||
zwp_linux_dmabuf_feedback_v1_add_listener((zwp_linux_dmabuf_feedback_v1*)m_sWaylandConnection.linuxDmabufFeedback, &dmabufFeedbackListener, nullptr);
|
||||
m_sWaylandConnection.linuxDmabuf =
|
||||
makeShared<CCZwpLinuxDmabufV1>(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwp_linux_dmabuf_v1_interface, version));
|
||||
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);
|
||||
}
|
||||
|
||||
m_sWaylandConnection.gbmDevice = createGBMDevice(drmDev);
|
||||
});
|
||||
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 = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, version);
|
||||
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) {
|
||||
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
|
||||
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; });
|
||||
}
|
||||
|
||||
|
@ -299,8 +229,9 @@ void CPortalManager::init() {
|
|||
Debug::log(WARN, "XDG_CURRENT_DESKTOP unset, running on an unknown desktop");
|
||||
}
|
||||
|
||||
wl_registry* registry = wl_display_get_registry(m_sWaylandConnection.display);
|
||||
wl_registry_add_listener(registry, ®istryListener, nullptr);
|
||||
m_sWaylandConnection.registry = makeShared<CCWlRegistry>((wl_proxy*)wl_display_get_registry(m_sWaylandConnection.display));
|
||||
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);
|
||||
m_sPipewire.loop = pw_loop_new(nullptr);
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
#include <memory>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <wayland-client.h>
|
||||
#include <hyprlang.hpp>
|
||||
|
||||
#include "wayland.hpp"
|
||||
#include "../portals/Screencopy.hpp"
|
||||
#include "../portals/Screenshot.hpp"
|
||||
#include "../portals/GlobalShortcuts.hpp"
|
||||
|
@ -13,13 +13,22 @@
|
|||
#include <gbm.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>
|
||||
|
||||
struct pw_loop;
|
||||
|
||||
struct SOutput {
|
||||
SOutput(SP<CCWlOutput>);
|
||||
std::string name;
|
||||
wl_output* output = nullptr;
|
||||
SP<CCWlOutput> output = nullptr;
|
||||
uint32_t id = 0;
|
||||
float refreshRate = 60.0;
|
||||
wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
|
@ -36,8 +45,8 @@ class CPortalManager {
|
|||
|
||||
void init();
|
||||
|
||||
void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
|
||||
void onGlobalRemoved(void* data, struct wl_registry* registry, uint32_t name);
|
||||
void onGlobal(uint32_t name, const char* interface, uint32_t version);
|
||||
void onGlobalRemoved(uint32_t name);
|
||||
|
||||
sdbus::IConnection* getConnection();
|
||||
SOutput* getOutputFromName(const std::string& name);
|
||||
|
@ -57,13 +66,14 @@ class CPortalManager {
|
|||
} m_sHelpers;
|
||||
|
||||
struct {
|
||||
wl_display* display = nullptr;
|
||||
void* hyprlandToplevelMgr = nullptr;
|
||||
void* linuxDmabuf = nullptr;
|
||||
void* linuxDmabufFeedback = nullptr;
|
||||
wl_shm* shm = nullptr;
|
||||
gbm_bo* gbm = nullptr;
|
||||
gbm_device* gbmDevice = nullptr;
|
||||
wl_display* display = nullptr;
|
||||
SP<CCWlRegistry> registry;
|
||||
SP<CCHyprlandToplevelExportManagerV1> hyprlandToplevelMgr;
|
||||
SP<CCZwpLinuxDmabufV1> linuxDmabuf;
|
||||
SP<CCZwpLinuxDmabufFeedbackV1> linuxDmabufFeedback;
|
||||
SP<CCWlShm> shm;
|
||||
gbm_bo* gbm = nullptr;
|
||||
gbm_device* gbmDevice = nullptr;
|
||||
struct {
|
||||
void* formatTable = nullptr;
|
||||
size_t formatTableSize = 0;
|
||||
|
|
|
@ -18,7 +18,7 @@ enum eLogLevel {
|
|||
std::format(reason, ##__VA_ARGS__), __LINE__, \
|
||||
([]() 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."); \
|
||||
*((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, "?")
|
||||
|
|
|
@ -2,25 +2,15 @@
|
|||
#include "../core/PortalManager.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
|
||||
// wayland
|
||||
|
||||
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) {
|
||||
const auto PKEYBIND = (SKeybind*)data;
|
||||
|
||||
g_pPortalManager->m_sPortals.globalShortcuts->onActivated(PKEYBIND, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo));
|
||||
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) {
|
||||
g_pPortalManager->m_sPortals.globalShortcuts->onActivated(this, ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo));
|
||||
});
|
||||
shortcut->setReleased([this](CCHyprlandGlobalShortcutV1* r, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) {
|
||||
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) {
|
||||
|
@ -62,9 +52,10 @@ SKeybind* CGlobalShortcutsPortal::registerShortcut(SSession* session, const DBus
|
|||
if (PSHORTCUT)
|
||||
Debug::log(WARN, "[globalshortcuts] shortcut {} already registered for appid {}", id, session->appid);
|
||||
else {
|
||||
PSHORTCUT = session->keybinds.emplace_back(std::make_unique<SKeybind>()).get();
|
||||
PSHORTCUT->shortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(m_sState.manager, id.c_str(), session->appid.c_str(), description.c_str(), "");
|
||||
hyprland_global_shortcut_v1_add_listener(PSHORTCUT->shortcut, &shortcutListener, PSHORTCUT);
|
||||
PSHORTCUT = session->keybinds
|
||||
.emplace_back(std::make_unique<SKeybind>(
|
||||
makeShared<CCHyprlandGlobalShortcutV1>(m_sState.manager->sendRegisterShortcut(id.c_str(), session->appid.c_str(), description.c_str(), ""))))
|
||||
.get();
|
||||
}
|
||||
|
||||
PSHORTCUT->id = std::move(id);
|
||||
|
@ -195,7 +186,7 @@ void CGlobalShortcutsPortal::onListShortcuts(sdbus::MethodCall& call) {
|
|||
reply.send();
|
||||
}
|
||||
|
||||
CGlobalShortcutsPortal::CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr) {
|
||||
CGlobalShortcutsPortal::CGlobalShortcutsPortal(SP<CCHyprlandGlobalShortcutsManagerV1> mgr) {
|
||||
m_sState.manager = mgr;
|
||||
|
||||
m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH);
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <protocols/hyprland-global-shortcuts-v1-protocol.h>
|
||||
#include "hyprland-global-shortcuts-v1.hpp"
|
||||
#include "../shared/Session.hpp"
|
||||
|
||||
struct SKeybind {
|
||||
std::string id, description, preferredTrigger;
|
||||
hyprland_global_shortcut_v1* shortcut = nullptr;
|
||||
void* session = nullptr;
|
||||
SKeybind(SP<CCHyprlandGlobalShortcutV1> shortcut);
|
||||
std::string id, description, preferredTrigger;
|
||||
SP<CCHyprlandGlobalShortcutV1> shortcut = nullptr;
|
||||
void* session = nullptr;
|
||||
};
|
||||
|
||||
class CGlobalShortcutsPortal {
|
||||
public:
|
||||
CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr);
|
||||
CGlobalShortcutsPortal(SP<CCHyprlandGlobalShortcutsManagerV1> mgr);
|
||||
|
||||
void onCreateSession(sdbus::MethodCall& call);
|
||||
void onBindShortcuts(sdbus::MethodCall& call);
|
||||
|
@ -36,7 +37,7 @@ class CGlobalShortcutsPortal {
|
|||
|
||||
private:
|
||||
struct {
|
||||
hyprland_global_shortcuts_manager_v1* manager;
|
||||
SP<CCHyprlandGlobalShortcutsManagerV1> manager;
|
||||
} m_sState;
|
||||
|
||||
std::unique_ptr<sdbus::IObject> m_pObject;
|
||||
|
|
|
@ -5,299 +5,12 @@
|
|||
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <protocols/linux-dmabuf-unstable-v1-protocol.h>
|
||||
#include "linux-dmabuf-unstable-v1.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
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;
|
||||
|
||||
g_pPortalManager->m_sHelpers.toplevel->activate();
|
||||
|
@ -479,18 +192,16 @@ void CScreencopyPortal::onSelectSources(sdbus::MethodCall& call) {
|
|||
}
|
||||
}
|
||||
|
||||
const bool RESTOREDATAVALID = restoreData.exists &&
|
||||
(g_pPortalManager->m_sHelpers.toplevel->exists((zwlr_foreign_toplevel_handle_v1*)restoreData.windowHandle) || g_pPortalManager->getOutputFromName(restoreData.output));
|
||||
const bool RESTOREDATAVALID = restoreData.exists && g_pPortalManager->getOutputFromName(restoreData.output);
|
||||
|
||||
SSelectionData SHAREDATA;
|
||||
if (RESTOREDATAVALID) {
|
||||
Debug::log(LOG, "[screencopy] restore data valid, not prompting");
|
||||
|
||||
SHAREDATA.output = restoreData.output;
|
||||
SHAREDATA.windowHandle = (zwlr_foreign_toplevel_handle_v1*)restoreData.windowHandle;
|
||||
SHAREDATA.type = restoreData.windowHandle ? TYPE_WINDOW : TYPE_OUTPUT;
|
||||
SHAREDATA.allowToken = true; // user allowed token before
|
||||
PSESSION->cursorMode = restoreData.withCursor;
|
||||
SHAREDATA.output = restoreData.output;
|
||||
SHAREDATA.type = restoreData.windowHandle ? TYPE_WINDOW : TYPE_OUTPUT;
|
||||
SHAREDATA.allowToken = true; // user allowed token before
|
||||
PSESSION->cursorMode = restoreData.withCursor;
|
||||
} else {
|
||||
Debug::log(LOG, "[screencopy] restore data invalid / missing, prompting");
|
||||
|
||||
|
@ -641,52 +352,263 @@ void CScreencopyPortal::startSharing(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");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!POUTPUT && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) {
|
||||
Debug::log(ERR, "[screencopy] Output {} not found??", pSession->selection.output);
|
||||
if (!POUTPUT && (selection.type == TYPE_GEOMETRY || selection.type == TYPE_OUTPUT)) {
|
||||
Debug::log(ERR, "[screencopy] Output {} not found??", selection.output);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((pSession->sharingData.frameCallback && (pSession->selection.type == TYPE_GEOMETRY || pSession->selection.type == TYPE_OUTPUT)) ||
|
||||
(pSession->sharingData.windowFrameCallback && pSession->selection.type == TYPE_WINDOW)) {
|
||||
Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb (type {})", (int)pSession->selection.type);
|
||||
if ((sharingData.frameCallback && (selection.type == TYPE_GEOMETRY || selection.type == TYPE_OUTPUT)) || (sharingData.windowFrameCallback && selection.type == TYPE_WINDOW)) {
|
||||
Debug::log(ERR, "[screencopy] tried scheduling on already scheduled cb (type {})", (int)selection.type);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pSession->selection.type == TYPE_GEOMETRY) {
|
||||
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output_region(m_sState.screencopy, pSession->cursorMode, POUTPUT->output, pSession->selection.x,
|
||||
pSession->selection.y, pSession->selection.w, pSession->selection.h);
|
||||
pSession->sharingData.transform = POUTPUT->transform;
|
||||
} else if (pSession->selection.type == TYPE_OUTPUT) {
|
||||
pSession->sharingData.frameCallback = zwlr_screencopy_manager_v1_capture_output(m_sState.screencopy, pSession->cursorMode, POUTPUT->output);
|
||||
pSession->sharingData.transform = POUTPUT->transform;
|
||||
} else if (pSession->selection.type == TYPE_WINDOW) {
|
||||
if (!pSession->selection.windowHandle) {
|
||||
if (selection.type == TYPE_GEOMETRY) {
|
||||
sharingData.frameCallback = makeShared<CCZwlrScreencopyFrameV1>(g_pPortalManager->m_sPortals.screencopy->m_sState.screencopy->sendCaptureOutputRegion(
|
||||
cursorMode, POUTPUT->output->resource(), selection.x, selection.y, selection.w, selection.h));
|
||||
sharingData.transform = POUTPUT->transform;
|
||||
} else if (selection.type == TYPE_OUTPUT) {
|
||||
sharingData.frameCallback =
|
||||
makeShared<CCZwlrScreencopyFrameV1>(g_pPortalManager->m_sPortals.screencopy->m_sState.screencopy->sendCaptureOutput(cursorMode, POUTPUT->output->resource()));
|
||||
sharingData.transform = POUTPUT->transform;
|
||||
} else if (selection.type == TYPE_WINDOW) {
|
||||
if (!selection.windowHandle) {
|
||||
Debug::log(ERR, "[screencopy] selected invalid window?");
|
||||
return;
|
||||
}
|
||||
pSession->sharingData.windowFrameCallback =
|
||||
hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(m_sState.toplevel, pSession->cursorMode, pSession->selection.windowHandle);
|
||||
pSession->sharingData.transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
sharingData.windowFrameCallback = makeShared<CCHyprlandToplevelExportFrameV1>(
|
||||
g_pPortalManager->m_sPortals.screencopy->m_sState.toplevel->sendCaptureToplevelWithWlrToplevelHandle(cursorMode, selection.windowHandle->resource()));
|
||||
sharingData.transform = WL_OUTPUT_TRANSFORM_NORMAL;
|
||||
} else {
|
||||
Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)pSession->selection.type);
|
||||
Debug::log(ERR, "[screencopy] Unsupported selection {}", (int)selection.type);
|
||||
return;
|
||||
}
|
||||
|
||||
pSession->sharingData.status = FRAME_QUEUED;
|
||||
sharingData.status = FRAME_QUEUED;
|
||||
|
||||
if (pSession->sharingData.frameCallback)
|
||||
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);
|
||||
initCallbacks();
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -719,7 +641,7 @@ CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& pa
|
|||
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->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");
|
||||
}
|
||||
|
||||
void CScreencopyPortal::appendToplevelExport(void* proto) {
|
||||
m_sState.toplevel = (hyprland_toplevel_export_manager_v1*)proto;
|
||||
void CScreencopyPortal::appendToplevelExport(SP<CCHyprlandToplevelExportManagerV1> proto) {
|
||||
m_sState.toplevel = proto;
|
||||
|
||||
Debug::log(LOG, "[screencopy] Registered for toplevel export");
|
||||
}
|
||||
|
@ -768,10 +690,8 @@ CPipewireConnection::CPipewireConnection() {
|
|||
void CPipewireConnection::removeSessionFrameCallbacks(CScreencopyPortal::SSession* pSession) {
|
||||
Debug::log(TRACE, "[pipewire] removeSessionFrameCallbacks called");
|
||||
|
||||
if (pSession->sharingData.frameCallback)
|
||||
zwlr_screencopy_frame_v1_destroy(pSession->sharingData.frameCallback);
|
||||
if (pSession->sharingData.windowFrameCallback)
|
||||
hyprland_toplevel_export_frame_v1_destroy(pSession->sharingData.windowFrameCallback);
|
||||
pSession->sharingData.frameCallback.reset();
|
||||
pSession->sharingData.windowFrameCallback.reset();
|
||||
|
||||
pSession->sharingData.windowFrameCallback = nullptr;
|
||||
pSession->sharingData.frameCallback = nullptr;
|
||||
|
@ -1002,7 +922,7 @@ static void pwStreamRemoveBuffer(void* data, pw_buffer* buffer) {
|
|||
if (PBUFFER->isDMABUF)
|
||||
gbm_bo_destroy(PBUFFER->bo);
|
||||
|
||||
wl_buffer_destroy(PBUFFER->wlBuffer);
|
||||
PBUFFER->wlBuffer.reset();
|
||||
for (int plane = 0; plane < PBUFFER->planeCount; 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);
|
||||
|
||||
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) {
|
||||
Debug::log(ERR, "[pw] zwp_linux_dmabuf_v1_create_params failed");
|
||||
gbm_bo_destroy(pBuffer->bo);
|
||||
|
@ -1338,7 +1258,7 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
|
|||
|
||||
if (pBuffer->fd[plane] < 0) {
|
||||
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);
|
||||
for (size_t plane_tmp = 0; plane_tmp < plane; plane_tmp++) {
|
||||
close(pBuffer->fd[plane_tmp]);
|
||||
|
@ -1346,11 +1266,11 @@ std::unique_ptr<SBuffer> CPipewireConnection::createBuffer(CPipewireConnection::
|
|||
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);
|
||||
zwp_linux_buffer_params_v1_destroy(params);
|
||||
pBuffer->wlBuffer = makeShared<CCWlBuffer>(params->sendCreateImmed(pBuffer->w, pBuffer->h, pBuffer->fmt, /* flags */ (zwpLinuxBufferParamsV1Flags)0));
|
||||
params.reset();
|
||||
|
||||
if (!pBuffer->wlBuffer) {
|
||||
Debug::log(ERR, "[pw] zwp_linux_buffer_params_v1_create_immed failed");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
|
||||
#include <protocols/hyprland-toplevel-export-v1-protocol.h>
|
||||
#include "wlr-screencopy-unstable-v1.hpp"
|
||||
#include "hyprland-toplevel-export-v1.hpp"
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include "../shared/ScreencopyShared.hpp"
|
||||
#include <gbm.h>
|
||||
|
@ -34,26 +34,26 @@ struct pw_stream;
|
|||
struct pw_buffer;
|
||||
|
||||
struct SBuffer {
|
||||
bool isDMABUF = false;
|
||||
uint32_t w = 0, h = 0, fmt = 0;
|
||||
int planeCount = 0;
|
||||
bool isDMABUF = false;
|
||||
uint32_t w = 0, h = 0, fmt = 0;
|
||||
int planeCount = 0;
|
||||
|
||||
int fd[4];
|
||||
uint32_t size[4], stride[4], offset[4];
|
||||
int fd[4];
|
||||
uint32_t size[4], stride[4], offset[4];
|
||||
|
||||
gbm_bo* bo = nullptr;
|
||||
gbm_bo* bo = nullptr;
|
||||
|
||||
wl_buffer* wlBuffer = nullptr;
|
||||
pw_buffer* pwBuffer = nullptr;
|
||||
SP<CCWlBuffer> wlBuffer = nullptr;
|
||||
pw_buffer* pwBuffer = nullptr;
|
||||
};
|
||||
|
||||
class CPipewireConnection;
|
||||
|
||||
class CScreencopyPortal {
|
||||
public:
|
||||
CScreencopyPortal(zwlr_screencopy_manager_v1*);
|
||||
CScreencopyPortal(SP<CCZwlrScreencopyManagerV1>);
|
||||
|
||||
void appendToplevelExport(void*);
|
||||
void appendToplevelExport(SP<CCHyprlandToplevelExportManagerV1>);
|
||||
|
||||
void onCreateSession(sdbus::MethodCall& call);
|
||||
void onSelectSources(sdbus::MethodCall& call);
|
||||
|
@ -69,10 +69,13 @@ class CScreencopyPortal {
|
|||
std::unique_ptr<SDBusSession> session;
|
||||
SSelectionData selection;
|
||||
|
||||
void startCopy();
|
||||
void initCallbacks();
|
||||
|
||||
struct {
|
||||
bool active = false;
|
||||
zwlr_screencopy_frame_v1* frameCallback = nullptr;
|
||||
hyprland_toplevel_export_frame_v1* windowFrameCallback = nullptr;
|
||||
SP<CCZwlrScreencopyFrameV1> frameCallback = nullptr;
|
||||
SP<CCHyprlandToplevelExportFrameV1> windowFrameCallback = nullptr;
|
||||
frameStatus status = FRAME_NONE;
|
||||
uint64_t tvSec = 0;
|
||||
uint32_t tvNsec = 0;
|
||||
|
@ -116,12 +119,14 @@ class CScreencopyPortal {
|
|||
void startSharing(SSession* pSession);
|
||||
|
||||
struct {
|
||||
zwlr_screencopy_manager_v1* screencopy = nullptr;
|
||||
hyprland_toplevel_export_manager_v1* toplevel = nullptr;
|
||||
SP<CCZwlrScreencopyManagerV1> screencopy = nullptr;
|
||||
SP<CCHyprlandToplevelExportManagerV1> toplevel = nullptr;
|
||||
} m_sState;
|
||||
|
||||
const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.ScreenCast";
|
||||
const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop";
|
||||
|
||||
friend struct SSession;
|
||||
};
|
||||
|
||||
class CPipewireConnection {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
|
||||
|
||||
class CScreenshotPortal {
|
||||
public:
|
||||
|
|
|
@ -320,15 +320,14 @@ int anonymous_shm_open() {
|
|||
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;
|
||||
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
return nullptr;
|
||||
|
||||
wl_shm_pool* pool = wl_shm_create_pool(g_pPortalManager->m_sWaylandConnection.shm, fd, size);
|
||||
wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt);
|
||||
wl_shm_pool_destroy(pool);
|
||||
auto pool = makeShared<CCWlShmPool>(g_pPortalManager->m_sWaylandConnection.shm->sendCreatePool(fd, size));
|
||||
auto buf = makeShared<CCWlBuffer>(pool->sendCreateBuffer(0, width, height, stride, fmt));
|
||||
|
||||
return buffer;
|
||||
return buf;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,9 @@ extern "C" {
|
|||
#include <spa/param/video/format-utils.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_MIN 2
|
||||
|
@ -29,11 +31,11 @@ enum eSelectionType {
|
|||
struct zwlr_foreign_toplevel_handle_v1;
|
||||
|
||||
struct SSelectionData {
|
||||
eSelectionType type = TYPE_INVALID;
|
||||
std::string output;
|
||||
zwlr_foreign_toplevel_handle_v1* windowHandle = nullptr;
|
||||
uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY
|
||||
bool allowToken = false;
|
||||
eSelectionType type = TYPE_INVALID;
|
||||
std::string output;
|
||||
SP<CCZwlrForeignToplevelHandleV1> windowHandle = nullptr;
|
||||
uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY
|
||||
bool allowToken = false;
|
||||
};
|
||||
|
||||
struct wl_buffer;
|
||||
|
@ -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* build_buffer(spa_pod_builder* b, uint32_t blocks, uint32_t size, uint32_t stride, uint32_t datatype);
|
||||
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);
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../includes.hpp"
|
||||
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
struct SDBusSession {
|
||||
|
|
|
@ -2,97 +2,28 @@
|
|||
#include "../helpers/Log.hpp"
|
||||
#include "../core/PortalManager.hpp"
|
||||
|
||||
static void toplevelTitle(void* data, zwlr_foreign_toplevel_handle_v1* zwlr_foreign_toplevel_handle_v1, const char* title) {
|
||||
const auto PTL = (SToplevelHandle*)data;
|
||||
SToplevelHandle::SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle_) : handle(handle_) {
|
||||
handle->setTitle([this](CCZwlrForeignToplevelHandleV1* r, const char* title) {
|
||||
if (title)
|
||||
windowTitle = title;
|
||||
|
||||
if (title)
|
||||
PTL->windowTitle = title;
|
||||
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 title to {}", data, PTL->windowTitle);
|
||||
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) {
|
||||
const auto PTL = (SToplevelHandle*)data;
|
||||
|
||||
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};
|
||||
CToplevelManager::CToplevelManager(uint32_t name, uint32_t version) {
|
||||
m_sWaylandConnection = {name, version};
|
||||
}
|
||||
|
||||
void CToplevelManager::activate() {
|
||||
|
@ -103,9 +34,16 @@ void CToplevelManager::activate() {
|
|||
if (m_pManager || m_iActivateLocks < 1)
|
||||
return;
|
||||
|
||||
m_pManager = (zwlr_foreign_toplevel_manager_v1*)wl_registry_bind(m_sWaylandConnection.registry, m_sWaylandConnection.name, &zwlr_foreign_toplevel_manager_v1_interface,
|
||||
m_sWaylandConnection.version);
|
||||
zwlr_foreign_toplevel_manager_v1_add_listener(m_pManager, &managerListener, this);
|
||||
m_pManager = makeShared<CCZwlrForeignToplevelManagerV1>(wl_registry_bind((wl_registry*)g_pPortalManager->m_sWaylandConnection.registry->resource(), m_sWaylandConnection.name,
|
||||
&zwlr_foreign_toplevel_manager_v1_interface, m_sWaylandConnection.version));
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
zwlr_foreign_toplevel_manager_v1_destroy(m_pManager);
|
||||
m_pManager = nullptr;
|
||||
m_pManager.reset();
|
||||
m_vToplevels.clear();
|
||||
|
||||
Debug::log(LOG, "[toplevel] unbound manager");
|
||||
|
|
|
@ -1,39 +1,38 @@
|
|||
#pragma once
|
||||
|
||||
#include <wayland-client.h>
|
||||
#include <protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h>
|
||||
#include "wayland.hpp"
|
||||
#include "wlr-foreign-toplevel-management-unstable-v1.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "../includes.hpp"
|
||||
|
||||
class CToplevelManager;
|
||||
|
||||
struct SToplevelHandle {
|
||||
std::string windowClass;
|
||||
std::string windowTitle;
|
||||
zwlr_foreign_toplevel_handle_v1* handle = nullptr;
|
||||
CToplevelManager* mgr = nullptr;
|
||||
SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle);
|
||||
std::string windowClass;
|
||||
std::string windowTitle;
|
||||
SP<CCZwlrForeignToplevelHandleV1> handle = nullptr;
|
||||
CToplevelManager* mgr = nullptr;
|
||||
};
|
||||
|
||||
class CToplevelManager {
|
||||
public:
|
||||
CToplevelManager(wl_registry* registry, uint32_t name, uint32_t version);
|
||||
CToplevelManager(uint32_t name, uint32_t version);
|
||||
|
||||
void activate();
|
||||
void deactivate();
|
||||
|
||||
bool exists(zwlr_foreign_toplevel_handle_v1* handle);
|
||||
|
||||
std::vector<std::unique_ptr<SToplevelHandle>> m_vToplevels;
|
||||
|
||||
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 {
|
||||
wl_registry* registry = nullptr;
|
||||
uint32_t name = 0;
|
||||
uint32_t version = 0;
|
||||
uint32_t name = 0;
|
||||
uint32_t version = 0;
|
||||
} m_sWaylandConnection;
|
||||
};
|
Loading…
Reference in a new issue