diff --git a/.gitignore b/.gitignore index e2d84e4..289c646 100644 --- a/.gitignore +++ b/.gitignore @@ -60,5 +60,5 @@ build-*/ hyprland-share-picker/build/ -protocols/*.c -protocols/*.h \ No newline at end of file +protocols/*.c* +protocols/*.h* \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e70b494..8f194f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" - "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("${CMAKE_SOURCE_DIR}/protocols" + "wlr-foreign-toplevel-management-unstable-v1" true) +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) diff --git a/flake.lock b/flake.lock index b70d187..ef4f04f 100644 --- a/flake.lock +++ b/flake.lock @@ -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", diff --git a/flake.nix b/flake.nix index 338c84f..5d484a3 100644 --- a/flake.nix +++ b/flake.nix @@ -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;}; diff --git a/hyprland-share-picker/CMakeLists.txt b/hyprland-share-picker/CMakeLists.txt index 0546c02..d9546c3 100644 --- a/hyprland-share-picker/CMakeLists.txt +++ b/hyprland-share-picker/CMakeLists.txt @@ -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,52 +17,43 @@ 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} - ) -# 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} - ) - endif() + if(ANDROID) + 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}) + 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 - MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} - MACOSX_BUNDLE TRUE - WIN32_EXECUTABLE TRUE -) +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 TRUE + WIN32_EXECUTABLE TRUE) -install(TARGETS hyprland-share-picker - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +install( + TARGETS hyprland-share-picker + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) if(QT_VERSION_MAJOR EQUAL 6) - qt_finalize_executable(hyprland-share-picker) + qt_finalize_executable(hyprland-share-picker) endif() diff --git a/hyprland-share-picker/meson.build b/hyprland-share-picker/meson.build index c984951..ef29621 100644 --- a/hyprland-share-picker/meson.build +++ b/hyprland-share-picker/meson.build @@ -10,6 +10,8 @@ sources = files([ 'main.cpp', 'mainpicker.cpp', 'mainpicker.h', + 'elidedbutton.h', + 'elidedbutton.cpp', ]) executable('hyprland-share-picker', diff --git a/nix/default.nix b/nix/default.nix index e0d0250..cfc9e86 100644 --- a/nix/default.nix +++ b/nix/default.nix @@ -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 = diff --git a/nix/overlays.nix b/nix/overlays.nix index 93dc86b..016cf73 100644 --- a/nix/overlays.nix +++ b/nix/overlays.nix @@ -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: { diff --git a/protocols/meson.build b/protocols/meson.build index 2547daf..3910e24 100644 --- a/protocols/meson.build +++ b/protocols/meson.build @@ -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'), - native: true, - ) -else - wayland_scanner = find_program('wayland-scanner', native: true) -endif +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, +) + 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 +) diff --git a/src/core/PortalManager.cpp b/src/core/PortalManager.cpp index bedc55e..e8878ed 100644 --- a/src/core/PortalManager.cpp +++ b/src/core/PortalManager.cpp @@ -2,12 +2,6 @@ #include "../helpers/Log.hpp" #include "../helpers/MiscFunctions.hpp" -#include -#include -#include -#include -#include - #include #include #include @@ -16,189 +10,24 @@ #include -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 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((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( + makeShared(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( - (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(makeShared( + 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( + 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()).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( + makeShared(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(wl_registry_bind((wl_registry*)m_sWaylandConnection.registry->resource(), name, &zwp_linux_dmabuf_v1_interface, version)); + m_sWaylandConnection.linuxDmabufFeedback = makeShared(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(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(registry, name, version); + m_sHelpers.toplevel = std::make_unique(name, version); // remove when another fix is found for https://github.com/hyprwm/xdg-desktop-portal-hyprland/issues/147 if (!std::any_cast(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((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); diff --git a/src/core/PortalManager.hpp b/src/core/PortalManager.hpp index 6907a33..4474df0 100644 --- a/src/core/PortalManager.hpp +++ b/src/core/PortalManager.hpp @@ -2,9 +2,9 @@ #include #include -#include #include +#include "wayland.hpp" #include "../portals/Screencopy.hpp" #include "../portals/Screenshot.hpp" #include "../portals/GlobalShortcuts.hpp" @@ -13,13 +13,22 @@ #include #include +#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 struct pw_loop; struct SOutput { + SOutput(SP); std::string name; - wl_output* output = nullptr; + SP 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 registry; + SP hyprlandToplevelMgr; + SP linuxDmabuf; + SP linuxDmabufFeedback; + SP shm; + gbm_bo* gbm = nullptr; + gbm_device* gbmDevice = nullptr; struct { void* formatTable = nullptr; size_t formatTableSize = 0; diff --git a/src/helpers/Log.hpp b/src/helpers/Log.hpp index 5fcaaef..be38029 100644 --- a/src/helpers/Log.hpp +++ b/src/helpers/Log.hpp @@ -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, "?") diff --git a/src/includes.hpp b/src/includes.hpp new file mode 100644 index 0000000..5043864 --- /dev/null +++ b/src/includes.hpp @@ -0,0 +1,6 @@ +#pragma once + +#include +using namespace Hyprutils::Memory; +#define SP CSharedPointer +#define WP CWeakPointer \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index f816508..b76a85f 100644 --- a/src/meson.build +++ b/src/meson.build @@ -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++'), diff --git a/src/portals/GlobalShortcuts.cpp b/src/portals/GlobalShortcuts.cpp index bacd36b..f7cdf6c 100644 --- a/src/portals/GlobalShortcuts.cpp +++ b/src/portals/GlobalShortcuts.cpp @@ -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 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()).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( + makeShared(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 mgr) { m_sState.manager = mgr; m_pObject = sdbus::createObject(*g_pPortalManager->getConnection(), OBJECT_PATH); diff --git a/src/portals/GlobalShortcuts.hpp b/src/portals/GlobalShortcuts.hpp index bafaaa8..d57419d 100644 --- a/src/portals/GlobalShortcuts.hpp +++ b/src/portals/GlobalShortcuts.hpp @@ -1,18 +1,19 @@ #pragma once #include -#include +#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 shortcut); + std::string id, description, preferredTrigger; + SP shortcut = nullptr; + void* session = nullptr; }; class CGlobalShortcutsPortal { public: - CGlobalShortcutsPortal(hyprland_global_shortcuts_manager_v1* mgr); + CGlobalShortcutsPortal(SP 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 manager; } m_sState; std::unique_ptr m_pObject; diff --git a/src/portals/Screencopy.cpp b/src/portals/Screencopy.cpp index 9a11c99..14b4d9b 100644 --- a/src/portals/Screencopy.cpp +++ b/src/portals/Screencopy.cpp @@ -5,299 +5,12 @@ #include #include -#include +#include "linux-dmabuf-unstable-v1.hpp" #include 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(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(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( + 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 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 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 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(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 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 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(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"); diff --git a/src/portals/Screencopy.hpp b/src/portals/Screencopy.hpp index b2fc874..b9e0507 100644 --- a/src/portals/Screencopy.hpp +++ b/src/portals/Screencopy.hpp @@ -1,7 +1,7 @@ #pragma once -#include -#include +#include "wlr-screencopy-unstable-v1.hpp" +#include "hyprland-toplevel-export-v1.hpp" #include #include "../shared/ScreencopyShared.hpp" #include @@ -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 wlBuffer = nullptr; + pw_buffer* pwBuffer = nullptr; }; class CPipewireConnection; class CScreencopyPortal { public: - CScreencopyPortal(zwlr_screencopy_manager_v1*); + CScreencopyPortal(SP); - void appendToplevelExport(void*); + void appendToplevelExport(SP); void onCreateSession(sdbus::MethodCall& call); void onSelectSources(sdbus::MethodCall& call); @@ -69,10 +69,13 @@ class CScreencopyPortal { std::unique_ptr 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 frameCallback = nullptr; + SP 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 screencopy = nullptr; + SP 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 { diff --git a/src/portals/Screenshot.hpp b/src/portals/Screenshot.hpp index b833f62..b0f51de 100644 --- a/src/portals/Screenshot.hpp +++ b/src/portals/Screenshot.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include class CScreenshotPortal { public: diff --git a/src/shared/ScreencopyShared.cpp b/src/shared/ScreencopyShared.cpp index c3cff1e..a8c6888 100644 --- a/src/shared/ScreencopyShared.cpp +++ b/src/shared/ScreencopyShared.cpp @@ -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 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(g_pPortalManager->m_sWaylandConnection.shm->sendCreatePool(fd, size)); + auto buf = makeShared(pool->sendCreateBuffer(0, width, height, stride, fmt)); - return buffer; + return buf; } diff --git a/src/shared/ScreencopyShared.hpp b/src/shared/ScreencopyShared.hpp index a71fd23..a314ac5 100644 --- a/src/shared/ScreencopyShared.hpp +++ b/src/shared/ScreencopyShared.hpp @@ -12,7 +12,9 @@ extern "C" { #include #include } -#include +#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 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); \ No newline at end of file +SP import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride); \ No newline at end of file diff --git a/src/shared/Session.hpp b/src/shared/Session.hpp index a006bc9..aaf8d40 100644 --- a/src/shared/Session.hpp +++ b/src/shared/Session.hpp @@ -1,5 +1,7 @@ #pragma once +#include "../includes.hpp" + #include struct SDBusSession { diff --git a/src/shared/ToplevelManager.cpp b/src/shared/ToplevelManager.cpp index 880bdcd..b947ca5 100644 --- a/src/shared/ToplevelManager.cpp +++ b/src/shared/ToplevelManager.cpp @@ -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 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("?", "?", 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(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(makeShared(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"); diff --git a/src/shared/ToplevelManager.hpp b/src/shared/ToplevelManager.hpp index de447ee..97612ee 100644 --- a/src/shared/ToplevelManager.hpp +++ b/src/shared/ToplevelManager.hpp @@ -1,39 +1,38 @@ #pragma once -#include -#include +#include "wayland.hpp" +#include "wlr-foreign-toplevel-management-unstable-v1.hpp" #include #include #include +#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 handle); + std::string windowClass; + std::string windowTitle; + SP 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> m_vToplevels; private: - zwlr_foreign_toplevel_manager_v1* m_pManager = nullptr; + SP 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; }; \ No newline at end of file