core: Move to hyprwayland-scanner (#260)

* move to hw-s
* hyprland-share-picker: add missing sources to meson, format CMake
* CMake: bump hw-s version
* CMake: fix protocolnew external protos
* CMake: get wayland.xml from wayland-scanner
* Nix: add missing deps
* Meson: use hw-s for protocols, add hyprutils

---------

Co-authored-by: Mihai Fufezan <mihai@fufexan.net>
This commit is contained in:
Vaxry 2024-09-20 18:01:12 +01:00 committed by GitHub
parent e695669fd8
commit eb3f3d9854
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 701 additions and 835 deletions

4
.gitignore vendored
View file

@ -60,5 +60,5 @@ build-*/
hyprland-share-picker/build/
protocols/*.c
protocols/*.h
protocols/*.c*
protocols/*.h*

View file

@ -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.2)
# check whether we can find sdbus-c++ through pkg-config
pkg_check_modules(SDBUS IMPORTED_TARGET sdbus-c++)
@ -76,7 +78,7 @@ pkg_check_modules(HYPRLAND_PROTOS IMPORTED_TARGET hyprland-protocols)
if(HYPRLAND_PROTOS_FOUND)
set(HYPRLAND_PROTOCOLS "${HYPRLAND_PROTOS_PREFIX}/share/hyprland-protocols")
else()
set(HYPRLAND_PROTOCOLS "subprojects/hyprland-protocols")
set(HYPRLAND_PROTOCOLS "${CMAKE_SOURCE_DIR}/subprojects/hyprland-protocols")
endif()
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
@ -86,43 +88,47 @@ 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_SCANNER_DIR wayland-scanner pkgdatadir)
message(STATUS "Found wayland-scanner at ${WAYLAND_SCANNER_DIR}")
function(protocol protoPath protoName external)
function(protocolnew protoPath protoName external)
if(external)
set(path ${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_SCANNER_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"
protocolwayland()
protocolnew("${CMAKE_SOURCE_DIR}/protocols"
"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)
protocolnew("${CMAKE_SOURCE_DIR}/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)

View file

@ -29,14 +29,16 @@
"nixpkgs": [
"nixpkgs"
],
"systems": "systems"
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1725188252,
"narHash": "sha256-yBH8c4GDaEAtBrh+BqIlrx5vp6gG/Gu8fQQK63KAQgs=",
"lastModified": 1725997860,
"narHash": "sha256-d/rZ/fHR5l1n7PeyLw0StWMNLXVU9c4HFyfskw568so=",
"owner": "hyprwm",
"repo": "hyprlang",
"rev": "c12ab785ce1982f82594aff03b3104c598186ddd",
"rev": "dfeb5811dd6485490cce18d6cc1e38a055eea876",
"type": "github"
},
"original": {
@ -70,13 +72,59 @@
"type": "github"
}
},
"hyprutils_2": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1724966483,
"narHash": "sha256-WXDgKIbzjYKczxSZOsJplCS1i1yrTUpsDPuJV/xpYLo=",
"owner": "hyprwm",
"repo": "hyprutils",
"rev": "8976e3f6a5357da953a09511d0c7f6a890fb6ec2",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprutils",
"type": "github"
}
},
"hyprwayland-scanner": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1726840673,
"narHash": "sha256-HIPEXyRRVZoqD6U+lFS1B0tsIU7p83FaB9m7KT/x6mQ=",
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"rev": "b68dab23fc922eae99306988133ee80a40b39ca5",
"type": "github"
},
"original": {
"owner": "hyprwm",
"repo": "hyprwayland-scanner",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1725103162,
"narHash": "sha256-Ym04C5+qovuQDYL/rKWSR+WESseQBbNAe5DsXNx5trY=",
"lastModified": 1726463316,
"narHash": "sha256-gI9kkaH0ZjakJOKrdjaI/VbaMEo9qBbSUl93DnU7f4c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "12228ff1752d7b7624a54e9c1af4b222b3c1073b",
"rev": "99dc8785f6a0adac95f5e2ab05cc2e1bf666d172",
"type": "github"
},
"original": {
@ -90,8 +138,10 @@
"inputs": {
"hyprland-protocols": "hyprland-protocols",
"hyprlang": "hyprlang",
"hyprutils": "hyprutils_2",
"hyprwayland-scanner": "hyprwayland-scanner",
"nixpkgs": "nixpkgs",
"systems": "systems_2"
"systems": "systems"
}
},
"systems": {
@ -108,21 +158,6 @@
"repo": "default-linux",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1689347949,
"narHash": "sha256-12tWmuL2zgBgZkdoB6qXZsgJEH9LR3oUgpaQq2RbI80=",
"owner": "nix-systems",
"repo": "default-linux",
"rev": "31732fcf5e8fea42e59c2488ad31a0e651500f68",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default-linux",
"type": "github"
}
}
},
"root": "root",

View file

@ -16,6 +16,19 @@
hyprlang = {
url = "github:hyprwm/hyprlang";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprutils = {
url = "github:hyprwm/hyprutils";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
hyprwayland-scanner = {
url = "github:hyprwm/hyprwayland-scanner";
inputs.nixpkgs.follows = "nixpkgs";
inputs.systems.follows = "systems";
};
};
@ -30,10 +43,7 @@
pkgsFor = eachSystem (system:
import nixpkgs {
localSystem = system;
overlays = [
inputs.hyprland-protocols.overlays.default
self.overlays.xdg-desktop-portal-hyprland
];
overlays = [self.overlays.default];
});
in {
overlays = import ./nix/overlays.nix {inherit self inputs lib;};

View file

@ -1,6 +1,9 @@
cmake_minimum_required(VERSION 3.5)
project(hyprland-share-picker VERSION 0.1 LANGUAGES CXX)
project(
hyprland-share-picker
VERSION 0.1
LANGUAGES CXX)
set(QT_VERSION_MAJOR 6)
@ -14,49 +17,40 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets)
find_package(Qt6 REQUIRED COMPONENTS Widgets)
set(PROJECT_SOURCES
main.cpp
mainpicker.cpp
mainpicker.h
mainpicker.ui
elidedbutton.h
elidedbutton.cpp
)
set(PROJECT_SOURCES main.cpp mainpicker.cpp mainpicker.h mainpicker.ui
elidedbutton.h elidedbutton.cpp)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(hyprland-share-picker
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET hyprland-share-picker APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
qt_add_executable(hyprland-share-picker MANUAL_FINALIZATION
${PROJECT_SOURCES})
# Define target properties for Android with Qt 6 as: set_property(TARGET
# hyprland-share-picker APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android) For more information, see
# https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
else()
if(ANDROID)
add_library(hyprland-share-picker SHARED
${PROJECT_SOURCES}
)
add_library(hyprland-share-picker SHARED ${PROJECT_SOURCES})
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(hyprland-share-picker
${PROJECT_SOURCES}
)
add_executable(hyprland-share-picker ${PROJECT_SOURCES})
endif()
endif()
target_link_libraries(hyprland-share-picker PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(hyprland-share-picker
PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
set_target_properties(hyprland-share-picker PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
set_target_properties(
hyprland-share-picker
PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER my.example.com
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE_SHORT_VERSION_STRING
${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
WIN32_EXECUTABLE TRUE)
install(TARGETS hyprland-share-picker
install(
TARGETS hyprland-share-picker
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

View file

@ -10,6 +10,8 @@ sources = files([
'main.cpp',
'mainpicker.cpp',
'mainpicker.h',
'elidedbutton.h',
'elidedbutton.cpp',
])
executable('hyprland-share-picker',

View file

@ -4,11 +4,12 @@
cmake,
makeWrapper,
pkg-config,
wayland-scanner,
wrapQtAppsHook,
hyprland,
hyprland-protocols,
hyprlang,
hyprutils,
hyprwayland-scanner,
libdrm,
mesa,
pipewire,
@ -20,6 +21,7 @@
systemd,
wayland,
wayland-protocols,
wayland-scanner,
debug ? false,
version ? "git",
}:
@ -37,13 +39,14 @@ stdenv.mkDerivation {
cmake
makeWrapper
pkg-config
wayland-scanner
wrapQtAppsHook
hyprwayland-scanner
];
buildInputs = [
hyprland-protocols
hyprlang
hyprutils
libdrm
mesa
pipewire
@ -54,6 +57,7 @@ stdenv.mkDerivation {
systemd
wayland
wayland-protocols
wayland-scanner
];
cmakeBuildType =

View file

@ -20,6 +20,8 @@ in {
xdg-desktop-portal-hyprland
inputs.hyprlang.overlays.default
inputs.hyprland-protocols.overlays.default
inputs.hyprutils.overlays.default
inputs.hyprwayland-scanner.overlays.default
]);
xdg-desktop-portal-hyprland = lib.composeManyExtensions [
(final: prev: {

View file

@ -11,15 +11,12 @@ hyprland_protos = dependency('hyprland-protocols',
wl_protocol_dir = wayland_protos.get_variable('pkgdatadir')
hl_protocol_dir = hyprland_protos.get_variable('pkgdatadir')
wayland_scanner_dep = dependency('wayland-scanner', required: false, native: true)
if wayland_scanner_dep.found()
wayland_scanner = find_program(
wayland_scanner_dep.get_variable(pkgconfig: 'wayland_scanner'),
hyprwayland_scanner_dep = dependency('hyprwayland-scanner', required: true, native: true, version: '>=0.4.2')
hyprwayland_scanner = find_program(
hyprwayland_scanner_dep.get_variable(pkgconfig: 'hyprwayland_scanner'),
native: true,
)
else
wayland_scanner = find_program('wayland-scanner', native: true)
endif
client_protocols = [
'wlr-screencopy-unstable-v1.xml',
'wlr-foreign-toplevel-management-unstable-v1.xml',
@ -31,19 +28,31 @@ client_protocols = [
wl_proto_files = []
foreach xml: client_protocols
code = custom_target(
wl_proto_files += custom_target(
xml.underscorify() + '_c',
input: xml,
output: '@BASENAME@-protocol.c',
command: [wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@'],
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--client', '@INPUT@', '@OUTDIR@'],
)
client_header = custom_target(
xml.underscorify() + '_client_h',
input: xml,
output: '@BASENAME@-protocol.h',
command: [wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@'],
)
wl_proto_files += [code, client_header]
endforeach
wayland_scanner = dependency('wayland-scanner')
wayland_scanner_dir = wayland_scanner.get_variable('pkgdatadir')
wayland_xml = wayland_scanner_dir / 'wayland.xml'
wayland_protocol = custom_target(
wayland_xml.underscorify(),
input: wayland_xml,
output: ['@BASENAME@.cpp', '@BASENAME@.hpp'],
command: [hyprwayland_scanner, '--wayland-enums', '--client', '@INPUT@', '@OUTDIR@'],
)
lib_client_protos = static_library(
'client_protos',
wl_proto_files + wayland_protocol,
)
client_protos = declare_dependency(
link_with: lib_client_protos,
sources: wl_proto_files + wayland_protocol
)

View file

@ -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);
}
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)
SOutput::SOutput(SP<CCWlOutput> output_) : output(output_) {
output->setName([this](CCWlOutput* o, const char* name_) {
if (!name_)
return;
const auto POUTPUT = (SOutput*)data;
POUTPUT->name = name;
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() {
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);
}
else if (INTERFACE == wl_shm_interface.name)
m_sWaylandConnection.shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, version);
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 = 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, &registryListener, 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);

View file

@ -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);
@ -58,10 +67,11 @@ class CPortalManager {
struct {
wl_display* display = nullptr;
void* hyprlandToplevelMgr = nullptr;
void* linuxDmabuf = nullptr;
void* linuxDmabufFeedback = nullptr;
wl_shm* shm = 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 {

View file

@ -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, "?")

6
src/includes.hpp Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <hyprutils/memory/WeakPtr.hpp>
using namespace Hyprutils::Memory;
#define SP CSharedPointer
#define WP CWeakPointer

View file

@ -2,10 +2,12 @@ globber = run_command('find', '.', '-name', '*.cpp', check: true)
src = globber.stdout().strip().split('\n')
executable('xdg-desktop-portal-hyprland',
[src, wl_proto_files],
[src],
dependencies: [
client_protos,
dependency('gbm'),
dependency('hyprlang'),
dependency('hyprutils'),
dependency('libdrm'),
dependency('libpipewire-0.3'),
dependency('sdbus-c++'),

View file

@ -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);

View file

@ -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 {
SKeybind(SP<CCHyprlandGlobalShortcutV1> shortcut);
std::string id, description, preferredTrigger;
hyprland_global_shortcut_v1* shortcut = nullptr;
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;

View file

@ -5,298 +5,11 @@
#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) {
sdbus::ObjectPath requestHandle, sessionHandle;
@ -479,15 +192,13 @@ 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;
@ -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");

View file

@ -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>
@ -43,7 +43,7 @@ struct SBuffer {
gbm_bo* bo = nullptr;
wl_buffer* wlBuffer = nullptr;
SP<CCWlBuffer> wlBuffer = nullptr;
pw_buffer* pwBuffer = nullptr;
};
@ -51,9 +51,9 @@ 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 {

View file

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

View file

@ -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;
}

View file

@ -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
@ -31,7 +33,7 @@ struct zwlr_foreign_toplevel_handle_v1;
struct SSelectionData {
eSelectionType type = TYPE_INVALID;
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
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* 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);

View file

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

View file

@ -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)
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) {
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, wl_proxy* newHandle) {
Debug::log(TRACE, "[toplevel] New toplevel at {}", (void*)newHandle);
m_vToplevels.emplace_back(std::make_unique<SToplevelHandle>(makeShared<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");

View file

@ -1,38 +1,37 @@
#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 {
SToplevelHandle(SP<CCZwlrForeignToplevelHandleV1> handle);
std::string windowClass;
std::string windowTitle;
zwlr_foreign_toplevel_handle_v1* handle = nullptr;
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;
struct {
wl_registry* registry = nullptr;
uint32_t name = 0;
uint32_t version = 0;
} m_sWaylandConnection;