diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..90314ef --- /dev/null +++ b/.clang-format @@ -0,0 +1,65 @@ +--- +Language: Cpp +BasedOnStyle: LLVM + +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllConstructorInitializersOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: true +AllowShortCaseLabelsOnASingleLine: true +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Attach +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +ColumnLimit: 180 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false +IncludeBlocks: Preserve +IndentCaseLabels: true +IndentWidth: 4 +PointerAlignment: Left +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +TabWidth: 4 +UseTab: Never + +AllowShortEnumsOnASingleLine: false + +BraceWrapping: + AfterEnum: false + +AlignConsecutiveDeclarations: AcrossEmptyLines + +NamespaceIndentation: All diff --git a/.gitmodules b/.gitmodules index 33bc81f..e863dde 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "hyprland-protocols"] path = subprojects/hyprland-protocols url = https://github.com/hyprwm/hyprland-protocols +[submodule "subprojects/sdbus-cpp"] + path = subprojects/sdbus-cpp + url = https://github.com/Kistler-Group/sdbus-cpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a7ba24d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,72 @@ +cmake_minimum_required(VERSION 3.19) + +project(xdg-desktop-portal-hyprland + DESCRIPTION "An XDG-Destop-Portal backend for Hyprland (and wlroots)" + VERSION "2.0" +) + +set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") + +if(CMAKE_BUILD_TYPE MATCHES Debug OR CMAKE_BUILD_TYPE MATCHES DEBUG) + message(STATUS "Configuring XDPH in Debug with CMake") + add_compile_definitions(HYPRLAND_DEBUG) +else() + add_compile_options(-O3) + message(STATUS "Configuring XDPH in Release with CMake") +endif() + +include_directories( + . + "protocols/" + "subprojects/sdbus-cpp/include/" +) + +set(CMAKE_CXX_STANDARD 23) +add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-unused-value -Wno-missing-field-initializers -Wno-narrowing -Wno-pointer-arith) + +message(STATUS "Checking deps...") +add_subdirectory(subprojects/sdbus-cpp) +find_package(Threads REQUIRED) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols cairo pango pangocairo libjpeg) + +file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp") +add_executable(xdg-desktop-portal-hyprland ${SRCFILES}) +target_link_libraries(xdg-desktop-portal-hyprland PRIVATE rt sdbus-c++ PkgConfig::deps) + +# protocols +find_program(WaylandScanner NAMES wayland-scanner) +message(STATUS "Found WaylandScanner at ${WaylandScanner}") +execute_process( + COMMAND pkg-config --variable=pkgdatadir wayland-protocols + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) +message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") + +function(protocol protoPath protoName external) + if (external) + execute_process( + COMMAND ${WaylandScanner} client-header ${protoPath} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${protoPath} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/${protoName}-protocol.c) + else() + execute_process( + COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/${protoPath} protocols/${protoName}-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + target_sources(xdg-desktop-portal-hyprland PRIVATE protocols/${protoName}-protocol.c) + endif() +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("subprojects/hyprland-protocols/protocols/hyprland-global-shortcuts-v1.xml" "hyprland-global-shortcuts-v1" true) +protocol("subprojects/hyprland-protocols/protocols/hyprland-toplevel-export-v1.xml" "hyprland-toplevel-export-v1" true) +## diff --git a/include/config.h b/include/config.h deleted file mode 100644 index f856dc1..0000000 --- a/include/config.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CONFIG_H -#define CONFIG_H - -#include "logger.h" -#include "screencast_common.h" - -struct config_screencast { - char *output_name; - double max_fps; - char *exec_before; - char *exec_after; - char *chooser_cmd; - enum xdpw_chooser_types chooser_type; - bool force_mod_linear; -}; - -struct xdpw_config { - struct config_screencast screencast_conf; -}; - -void print_config(enum LOGLEVEL loglevel, struct xdpw_config *config); -void finish_config(struct xdpw_config *config); -void init_config(char ** const configfile, struct xdpw_config *config); - -#endif diff --git a/include/fps_limit.h b/include/fps_limit.h deleted file mode 100644 index 9312745..0000000 --- a/include/fps_limit.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FPS_LIMIT_H -#define FPS_LIMIT_H - -#include -#include - -struct fps_limit_state { - struct timespec frame_last_time; - - struct timespec fps_last_time; - uint64_t fps_frame_count; -}; - -void fps_limit_measure_start(struct fps_limit_state *state, double max_fps); - -uint64_t fps_limit_measure_end(struct fps_limit_state *state, double max_fps); - -#endif diff --git a/include/global_shortcuts.h b/include/global_shortcuts.h deleted file mode 100644 index 4b8f1f7..0000000 --- a/include/global_shortcuts.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include - -#include "protocols/hyprland-global-shortcuts-v1-client-protocol.h" - -struct xdpw_state; -struct xdpw_session; - -struct globalShortcut { - char* name; - char* description; - struct wl_list link; - struct hyprland_global_shortcut_v1* hlShortcut; - bool bound; -}; - -struct globalShortcutsClient { - struct xdpw_session* session; - struct wl_list shortcuts; // struct globalShortcut* - struct wl_list link; - char* parent_window; - bool sentShortcuts; -}; - -struct globalShortcutsInstance { - struct hyprland_global_shortcuts_manager_v1* manager; - struct wl_list shortcutClients; // struct globalShortcutsClient* -}; - -void initShortcutsInstance(struct xdpw_state* state, struct globalShortcutsInstance* instance); \ No newline at end of file diff --git a/include/logger.h b/include/logger.h deleted file mode 100644 index 89a2cb3..0000000 --- a/include/logger.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LOGGER_H -#define LOGGER_H - -#include - -#define DEFAULT_LOGLEVEL ERROR - -enum LOGLEVEL { QUIET, ERROR, WARN, INFO, DEBUG, TRACE }; - -struct logger_properties { - enum LOGLEVEL level; - FILE *dst; -}; - -void init_logger(FILE *dst, enum LOGLEVEL level); -enum LOGLEVEL get_loglevel(const char *level); -void logprint(enum LOGLEVEL level, char *msg, ...); - -#endif diff --git a/include/pipewire_screencast.h b/include/pipewire_screencast.h deleted file mode 100644 index 64ae141..0000000 --- a/include/pipewire_screencast.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PIPEWIRE_SCREENCAST_H -#define PIPEWIRE_SCREENCAST_H - -#include "screencast_common.h" - -#define XDPW_PWR_BUFFERS 2 -#define XDPW_PWR_BUFFERS_MIN 2 -#define XDPW_PWR_ALIGN 16 - -void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast); -void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast); -void pwr_update_stream_param(struct xdpw_screencast_instance *cast); -void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast); -void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast); -int xdpw_pwr_context_create(struct xdpw_state *state); -void xdpw_pwr_context_destroy(struct xdpw_state *state); - -#endif diff --git a/include/screencast.h b/include/screencast.h deleted file mode 100644 index 99b7411..0000000 --- a/include/screencast.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef SCREENCAST_H -#define SCREENCAST_H - -#include "screencast_common.h" - -void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast); -void xdpw_screencast_instance_teardown(struct xdpw_screencast_instance *cast); - -#endif diff --git a/include/screencast_common.h b/include/screencast_common.h deleted file mode 100644 index 9f32e42..0000000 --- a/include/screencast_common.h +++ /dev/null @@ -1,238 +0,0 @@ -#ifndef SCREENCAST_COMMON_H -#define SCREENCAST_COMMON_H - -#include -#include -#include -#include -#include -#include - -#include "fps_limit.h" -#include "hyprland-toplevel-export-v1-client-protocol.h" -#include "utils.h" - -// this seems to be right based on -// https://github.com/flatpak/xdg-desktop-portal/blob/309a1fc0cf2fb32cceb91dbc666d20cf0a3202c2/src/screen-cast.c#L955 -#define XDP_CAST_PROTO_VER 3 - -enum cursor_modes { - HIDDEN = 1, - EMBEDDED = 2, - METADATA = 4, -}; - -enum source_types { - MONITOR = 1, - WINDOW = 2, -}; - -enum buffer_type { - WL_SHM = 0, - DMABUF = 1, -}; - -enum xdpw_chooser_types { - XDPW_CHOOSER_DEFAULT, - XDPW_CHOOSER_NONE, - XDPW_CHOOSER_SIMPLE, - XDPW_CHOOSER_DMENU, -}; - -enum xdpw_frame_state { - XDPW_FRAME_STATE_NONE, - XDPW_FRAME_STATE_STARTED, - XDPW_FRAME_STATE_RENEG, - XDPW_FRAME_STATE_FAILED, - XDPW_FRAME_STATE_SUCCESS, -}; - -struct xdpw_output_chooser { - enum xdpw_chooser_types type; - char *cmd; -}; - -struct xdpw_frame_damage { - uint32_t x; - uint32_t y; - uint32_t width; - uint32_t height; -}; - -struct xdpw_frame { - bool y_invert; - uint64_t tv_sec; - uint32_t tv_nsec; - struct xdpw_frame_damage damage[4]; - uint32_t damage_count; - struct xdpw_buffer *xdpw_buffer; - struct pw_buffer *pw_buffer; -}; - -struct xdpw_screencopy_frame_info { - uint32_t width; - uint32_t height; - uint32_t size; - uint32_t stride; - uint32_t format; -}; - -struct xdpw_buffer { - struct wl_list link; - enum buffer_type buffer_type; - - uint32_t width; - uint32_t height; - uint32_t format; - int plane_count; - - int fd[4]; - uint32_t size[4]; - uint32_t stride[4]; - uint32_t offset[4]; - - struct gbm_bo *bo; - - struct wl_buffer *buffer; -}; - -struct xdpw_format_modifier_pair { - uint32_t fourcc; - uint64_t modifier; -}; - -struct xdpw_dmabuf_feedback_data { - void *format_table_data; - uint32_t format_table_size; - bool device_used; -}; - -struct xdpw_screencast_context { - // xdpw - struct xdpw_state *state; - - // pipewire - struct pw_context *pwr_context; - struct pw_core *core; - - // wlroots - struct wl_list output_list; - struct wl_registry *registry; - struct zwlr_screencopy_manager_v1 *screencopy_manager; - struct zxdg_output_manager_v1 *xdg_output_manager; - struct wl_shm *shm; - struct zwp_linux_dmabuf_v1 *linux_dmabuf; - struct zwp_linux_dmabuf_feedback_v1 *linux_dmabuf_feedback; - struct xdpw_dmabuf_feedback_data feedback_data; - struct wl_array format_modifier_pairs; - - // hyprland - struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_manager; - struct zwlr_foreign_toplevel_manager_v1 *wlroots_toplevel_manager; - struct wl_list toplevel_resource_list; - - // gbm - struct gbm_device *gbm; - - // sessions - struct wl_list screencast_instances; -}; - -struct xdpw_wlr_output { - struct wl_list link; - uint32_t id; - struct wl_output *output; - struct zxdg_output_v1 *xdg_output; - char *make; - char *model; - char *name; - int width; - int height; - float framerate; - enum wl_output_transform transform; -}; - -struct xdpw_share { - struct xdpw_wlr_output *output; - int x; - int y; - int w; - int h; - int window_handle; -}; - -struct xdph_restore_token { - char *token; - char *outputPort; // NULL if not set - char *windowClass; // NULL if not set - uint64_t windowHandle; // 0 if not set - struct wl_list link; - bool withCursor; -}; - -struct xdpw_screencast_instance { - // list - struct wl_list link; - - // xdpw - uint32_t refcount; - struct xdpw_screencast_context *ctx; - bool initialized; - struct xdpw_frame current_frame; - enum xdpw_frame_state frame_state; - struct wl_list buffer_list; - bool avoid_dmabufs; - - // pipewire - struct pw_stream *stream; - struct spa_hook stream_listener; - struct spa_video_info_raw pwr_format; - uint32_t seq; - uint32_t node_id; - bool pwr_stream_state; - uint32_t framerate; - - // wlroots - struct zwlr_screencopy_frame_v1 *frame_callback; - struct xdpw_share target; - uint32_t max_framerate; - struct zwlr_screencopy_frame_v1 *wlr_frame; - struct xdpw_screencopy_frame_info screencopy_frame_info[2]; - bool with_cursor; - int err; - bool quit; - bool teardown; - enum buffer_type buffer_type; - - // hyprland - struct hyprland_toplevel_export_frame_v1 *frame_callback_hyprland; - struct hyprland_toplevel_export_frame_v1 *hyprland_frame; - - // fps limit - struct fps_limit_state fps_limit; -}; - -struct SToplevelEntry { - struct zwlr_foreign_toplevel_handle_v1 *handle; - char name[256]; - char clazz[256]; - struct wl_list link; -}; - -void randname(char *buf); -struct gbm_device *xdpw_gbm_device_create(drmDevice *device); -struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, enum buffer_type buffer_type, - struct xdpw_screencopy_frame_info *frame_info); -void xdpw_buffer_destroy(struct xdpw_buffer *buffer); -bool wlr_query_dmabuf_modifiers(struct xdpw_screencast_context *ctx, uint32_t drm_format, uint32_t num_modifiers, uint64_t *modifiers, - uint32_t *max_modifiers); -enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format); -uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format); -enum spa_video_format xdpw_format_pw_from_drm_fourcc(uint32_t format); -enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format); - -struct xdpw_frame_damage merge_damage(struct xdpw_frame_damage *damage1, struct xdpw_frame_damage *damage2); - -enum xdpw_chooser_types get_chooser_type(const char *chooser_type); -const char *chooser_type_str(enum xdpw_chooser_types chooser_type); -#endif /* SCREENCAST_COMMON_H */ diff --git a/include/screenshot.h b/include/screenshot.h deleted file mode 100644 index c3d8e33..0000000 --- a/include/screenshot.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef SCREENSHOT_H -#define SCREENSHOT_H - - -struct xdpw_ppm_pixel { - int max_color_value; - unsigned char red, green, blue; -}; - -#endif \ No newline at end of file diff --git a/include/screenshot_common.h b/include/screenshot_common.h deleted file mode 100644 index e02863a..0000000 --- a/include/screenshot_common.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef SCREENSHOT_COMMON_H -#define SCREENSHOT_COMMON_H - -#define XDP_SHOT_PROTO_VER 2 - -#endif diff --git a/include/timespec_util.h b/include/timespec_util.h deleted file mode 100644 index 2ab27b8..0000000 --- a/include/timespec_util.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef TIMESPEC_UTIL_H -#define TIMESPEC_UTIL_H - -#include -#include -#include - -#define TIMESPEC_NSEC_PER_SEC 1000000000L - -void timespec_add(struct timespec *t, int64_t delta_ns); - -bool timespec_less(struct timespec *t1, struct timespec *t2); - -bool timespec_is_zero(struct timespec *t); - -int64_t timespec_diff_ns(struct timespec *t1, struct timespec *t2); - -#endif diff --git a/include/utils.h b/include/utils.h deleted file mode 100644 index a8110c6..0000000 --- a/include/utils.h +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include -#include - -char *getFormat(const char *fmt, ...); \ No newline at end of file diff --git a/include/wlr_screencast.h b/include/wlr_screencast.h deleted file mode 100644 index efd7269..0000000 --- a/include/wlr_screencast.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef WLR_SCREENCAST_H -#define WLR_SCREENCAST_H - -#include "screencast_common.h" - -#define WL_OUTPUT_VERSION 1 - -#define SC_MANAGER_VERSION 3 -#define SC_MANAGER_VERSION_MIN 2 - -#define WL_SHM_VERSION 1 - -#define XDG_OUTPUT_MANAGER_VERSION 3 - -#define LINUX_DMABUF_VERSION 4 -#define LINUX_DMABUF_VERSION_MIN 3 - -struct xdpw_state; - -int xdpw_wlr_screencopy_init(struct xdpw_state *state); -void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx); - -struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, - const char *name); -struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list); -struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, - struct wl_output *out, uint32_t id); -struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx); - -void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast); -void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast); -void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast); - -#endif diff --git a/include/xdpw.h b/include/xdpw.h deleted file mode 100644 index b82fb85..0000000 --- a/include/xdpw.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef XDPW_H -#define XDPW_H - -#include -#ifdef HAVE_LIBSYSTEMD -#include -#elif HAVE_LIBELOGIND -#include -#elif HAVE_BASU -#include -#endif - -#include "config.h" -#include "global_shortcuts.h" -#include "screencast_common.h" -#include "screenshot_common.h" - -struct xdpw_state { - struct wl_list xdpw_sessions; - sd_bus *bus; - struct wl_display *wl_display; - struct pw_loop *pw_loop; - struct xdpw_screencast_context screencast; - uint32_t screencast_source_types; // bitfield of enum source_types - uint32_t screencast_cursor_modes; // bitfield of enum cursor_modes - uint32_t screencast_version; - uint32_t screenshot_version; - struct xdpw_config *config; - int timer_poll_fd; - struct wl_list timers; - struct xdpw_timer *next_timer; - struct globalShortcutsInstance shortcutsInstance; - - // saved instances of screencast - // TODO: persist in storage - uint64_t lastRestoreToken; - struct wl_list restore_tokens; // xdph_restore_token -}; - -struct xdpw_request { - sd_bus_slot *slot; -}; - -struct xdpw_session { - struct wl_list link; - sd_bus_slot *slot; - char *session_handle; - char *app_id; - struct xdpw_screencast_instance *screencast_instance; - bool persist; -}; - -typedef void (*xdpw_event_loop_timer_func_t)(void *data); - -struct xdpw_timer { - struct xdpw_state *state; - xdpw_event_loop_timer_func_t func; - void *user_data; - struct timespec at; - struct wl_list link; // xdpw_state::timers -}; - -enum { PORTAL_RESPONSE_SUCCESS = 0, PORTAL_RESPONSE_CANCELLED = 1, PORTAL_RESPONSE_ENDED = 2 }; - -int xdpw_screenshot_init(struct xdpw_state *state); -int xdpw_screencast_init(struct xdpw_state *state); - -struct xdpw_request *xdpw_request_create(sd_bus *bus, const char *object_path); -void xdpw_request_destroy(struct xdpw_request *req); - -struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path); -void xdpw_session_destroy(struct xdpw_session *req); - -struct xdpw_timer *xdpw_add_timer(struct xdpw_state *state, uint64_t delay_ns, xdpw_event_loop_timer_func_t func, void *data); - -void xdpw_destroy_timer(struct xdpw_timer *timer); - -#endif diff --git a/nix/default.nix b/nix/default.nix deleted file mode 100644 index 838d46f..0000000 --- a/nix/default.nix +++ /dev/null @@ -1,65 +0,0 @@ -{ - lib, - stdenv, - makeWrapper, - meson, - ninja, - pkg-config, - wayland-protocols, - wayland-scanner, - hyprland-share-picker, - grim, - slurp, - hyprland-protocols, - inih, - libdrm, - libuuid, - mesa, - pipewire, - systemd, - wayland, - version ? "git", -}: -stdenv.mkDerivation { - pname = "xdg-desktop-portal-hyprland"; - inherit version; - - src = ../.; - - strictDeps = true; - depsBuildBuild = [pkg-config]; - nativeBuildInputs = [ - meson - ninja - pkg-config - wayland-scanner - makeWrapper - ]; - buildInputs = [ - hyprland-protocols - inih - libdrm - libuuid - mesa - pipewire - systemd - wayland - wayland-protocols - ]; - - mesonFlags = [ - "-Dsd-bus-provider=libsystemd" - ]; - - postInstall = '' - wrapProgram $out/libexec/xdg-desktop-portal-hyprland --prefix PATH ":" ${lib.makeBinPath [hyprland-share-picker grim slurp]} - ''; - - meta = with lib; { - homepage = "https://github.com/hyprwm/xdg-desktop-portal-hyprland"; - description = "xdg-desktop-portal backend for Hyprland"; - maintainers = with maintainers; [fufexan]; - platforms = platforms.linux; - license = licenses.mit; - }; -} diff --git a/nix/hyprland-share-picker.nix b/nix/hyprland-share-picker.nix deleted file mode 100644 index 6632beb..0000000 --- a/nix/hyprland-share-picker.nix +++ /dev/null @@ -1,34 +0,0 @@ -{ - stdenv, - lib, - cmake, - qtbase, - makeShellWrapper, - wrapQtAppsHook, - hyprland, - slurp, - version ? "git", - ... -}: -stdenv.mkDerivation { - pname = "hyprland-share-picker"; - inherit version; - src = ../hyprland-share-picker; - - nativeBuildInputs = [ - cmake - wrapQtAppsHook - makeShellWrapper - ]; - buildInputs = [ - qtbase - ]; - - dontWrapQtApps = true; - - postInstall = '' - wrapProgramShell $out/bin/hyprland-share-picker \ - "''${qtWrapperArgs[@]}" \ - --prefix PATH ":" ${lib.makeBinPath [slurp hyprland]} - ''; -} diff --git a/nix/overlays.nix b/nix/overlays.nix deleted file mode 100644 index 1688e5d..0000000 --- a/nix/overlays.nix +++ /dev/null @@ -1,28 +0,0 @@ -{ - self, - inputs, - lib, -}: let - mkJoinedOverlays = overlays: final: prev: - lib.foldl' (attrs: overlay: attrs // (overlay final prev)) {} overlays; - mkDate = longDate: (lib.concatStringsSep "-" [ - (builtins.substring 0 4 longDate) - (builtins.substring 4 2 longDate) - (builtins.substring 6 2 longDate) - ]); - version = "0.pre" + "+date=" + (mkDate (self.lastModifiedDate or "19700101")) + "_" + (self.shortRev or "dirty"); -in { - default = mkJoinedOverlays (with self.overlays; [ - xdg-desktop-portal-hyprland - hyprland-share-picker - ]); - xdg-desktop-portal-hyprland = final: prev: { - xdg-desktop-portal-hyprland = final.callPackage ./default.nix { - inherit (final) hyprland-protocols hyprland-share-picker; - inherit version; - }; - }; - hyprland-share-picker = final: prev: { - hyprland-share-picker = final.libsForQt5.callPackage ./hyprland-share-picker.nix {inherit version;}; - }; -} diff --git a/protocols/hyprland-global-shortcuts-v1-protocol.c b/protocols/hyprland-global-shortcuts-v1-protocol.c new file mode 100644 index 0000000..b44bb08 --- /dev/null +++ b/protocols/hyprland-global-shortcuts-v1-protocol.c @@ -0,0 +1,85 @@ +/* Generated by wayland-scanner 1.22.90 */ + +/* + * Copyright © 2022 Vaxry + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface hyprland_global_shortcut_v1_interface; + +static const struct wl_interface *hyprland_global_shortcuts_v1_types[] = { + NULL, + NULL, + NULL, + &hyprland_global_shortcut_v1_interface, + NULL, + NULL, + NULL, + NULL, +}; + +static const struct wl_message hyprland_global_shortcuts_manager_v1_requests[] = { + { "register_shortcut", "nssss", hyprland_global_shortcuts_v1_types + 3 }, + { "destroy", "", hyprland_global_shortcuts_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface hyprland_global_shortcuts_manager_v1_interface = { + "hyprland_global_shortcuts_manager_v1", 1, + 2, hyprland_global_shortcuts_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message hyprland_global_shortcut_v1_requests[] = { + { "destroy", "", hyprland_global_shortcuts_v1_types + 0 }, +}; + +static const struct wl_message hyprland_global_shortcut_v1_events[] = { + { "pressed", "uuu", hyprland_global_shortcuts_v1_types + 0 }, + { "released", "uuu", hyprland_global_shortcuts_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface hyprland_global_shortcut_v1_interface = { + "hyprland_global_shortcut_v1", 1, + 1, hyprland_global_shortcut_v1_requests, + 2, hyprland_global_shortcut_v1_events, +}; + diff --git a/protocols/hyprland-global-shortcuts-v1-protocol.h b/protocols/hyprland-global-shortcuts-v1-protocol.h new file mode 100644 index 0000000..0dcd907 --- /dev/null +++ b/protocols/hyprland-global-shortcuts-v1-protocol.h @@ -0,0 +1,274 @@ +/* Generated by wayland-scanner 1.22.90 */ + +#ifndef HYPRLAND_GLOBAL_SHORTCUTS_V1_CLIENT_PROTOCOL_H +#define HYPRLAND_GLOBAL_SHORTCUTS_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_hyprland_global_shortcuts_v1 The hyprland_global_shortcuts_v1 protocol + * registering global shortcuts + * + * @section page_desc_hyprland_global_shortcuts_v1 Description + * + * This protocol allows a client to register triggerable actions, + * meant to be global shortcuts. + * + * @section page_ifaces_hyprland_global_shortcuts_v1 Interfaces + * - @subpage page_iface_hyprland_global_shortcuts_manager_v1 - manager to register global shortcuts + * - @subpage page_iface_hyprland_global_shortcut_v1 - a shortcut + * @section page_copyright_hyprland_global_shortcuts_v1 Copyright + *
+ *
+ * Copyright © 2022 Vaxry
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */ +struct hyprland_global_shortcut_v1; +struct hyprland_global_shortcuts_manager_v1; + +#ifndef HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_INTERFACE +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_INTERFACE +/** + * @page page_iface_hyprland_global_shortcuts_manager_v1 hyprland_global_shortcuts_manager_v1 + * @section page_iface_hyprland_global_shortcuts_manager_v1_desc Description + * + * This object is a manager which offers requests to create global shortcuts. + * @section page_iface_hyprland_global_shortcuts_manager_v1_api API + * See @ref iface_hyprland_global_shortcuts_manager_v1. + */ +/** + * @defgroup iface_hyprland_global_shortcuts_manager_v1 The hyprland_global_shortcuts_manager_v1 interface + * + * This object is a manager which offers requests to create global shortcuts. + */ +extern const struct wl_interface hyprland_global_shortcuts_manager_v1_interface; +#endif +#ifndef HYPRLAND_GLOBAL_SHORTCUT_V1_INTERFACE +#define HYPRLAND_GLOBAL_SHORTCUT_V1_INTERFACE +/** + * @page page_iface_hyprland_global_shortcut_v1 hyprland_global_shortcut_v1 + * @section page_iface_hyprland_global_shortcut_v1_desc Description + * + * This object represents a single shortcut. + * @section page_iface_hyprland_global_shortcut_v1_api API + * See @ref iface_hyprland_global_shortcut_v1. + */ +/** + * @defgroup iface_hyprland_global_shortcut_v1 The hyprland_global_shortcut_v1 interface + * + * This object represents a single shortcut. + */ +extern const struct wl_interface hyprland_global_shortcut_v1_interface; +#endif + +#ifndef HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ENUM +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ENUM +enum hyprland_global_shortcuts_manager_v1_error { + /** + * the app_id + id combination has already been registered. + */ + HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ALREADY_TAKEN = 0, +}; +#endif /* HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_ERROR_ENUM */ + +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_REGISTER_SHORTCUT 0 +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_DESTROY 1 + + +/** + * @ingroup iface_hyprland_global_shortcuts_manager_v1 + */ +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_REGISTER_SHORTCUT_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_global_shortcuts_manager_v1 + */ +#define HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_hyprland_global_shortcuts_manager_v1 */ +static inline void +hyprland_global_shortcuts_manager_v1_set_user_data(struct hyprland_global_shortcuts_manager_v1 *hyprland_global_shortcuts_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) hyprland_global_shortcuts_manager_v1, user_data); +} + +/** @ingroup iface_hyprland_global_shortcuts_manager_v1 */ +static inline void * +hyprland_global_shortcuts_manager_v1_get_user_data(struct hyprland_global_shortcuts_manager_v1 *hyprland_global_shortcuts_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) hyprland_global_shortcuts_manager_v1); +} + +static inline uint32_t +hyprland_global_shortcuts_manager_v1_get_version(struct hyprland_global_shortcuts_manager_v1 *hyprland_global_shortcuts_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) hyprland_global_shortcuts_manager_v1); +} + +/** + * @ingroup iface_hyprland_global_shortcuts_manager_v1 + * + * Register a new global shortcut. + * + * A global shortcut is anonymous, meaning the app does not know what key(s) trigger it. + * + * The shortcut's keybinding shall be dealt with by the compositor. + * + * In the case of a duplicate app_id + id combination, the already_taken protocol error is raised. + */ +static inline struct hyprland_global_shortcut_v1 * +hyprland_global_shortcuts_manager_v1_register_shortcut(struct hyprland_global_shortcuts_manager_v1 *hyprland_global_shortcuts_manager_v1, const char *id, const char *app_id, const char *description, const char *trigger_description) +{ + struct wl_proxy *shortcut; + + shortcut = wl_proxy_marshal_flags((struct wl_proxy *) hyprland_global_shortcuts_manager_v1, + HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_REGISTER_SHORTCUT, &hyprland_global_shortcut_v1_interface, wl_proxy_get_version((struct wl_proxy *) hyprland_global_shortcuts_manager_v1), 0, NULL, id, app_id, description, trigger_description); + + return (struct hyprland_global_shortcut_v1 *) shortcut; +} + +/** + * @ingroup iface_hyprland_global_shortcuts_manager_v1 + * + * All objects created by the manager will still remain valid, until their + * appropriate destroy request has been called. + */ +static inline void +hyprland_global_shortcuts_manager_v1_destroy(struct hyprland_global_shortcuts_manager_v1 *hyprland_global_shortcuts_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) hyprland_global_shortcuts_manager_v1, + HYPRLAND_GLOBAL_SHORTCUTS_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) hyprland_global_shortcuts_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_hyprland_global_shortcut_v1 + * @struct hyprland_global_shortcut_v1_listener + */ +struct hyprland_global_shortcut_v1_listener { + /** + * keystroke pressed + * + * The keystroke was pressed. + * + * tv_ values hold the timestamp of the occurrence. + * @param tv_sec_hi high 32 bits of the seconds part of the timestamp + * @param tv_sec_lo low 32 bits of the seconds part of the timestamp + * @param tv_nsec nanoseconds part of the timestamp + */ + void (*pressed)(void *data, + struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec); + /** + * keystroke released + * + * The keystroke was released. + * + * tv_ values hold the timestamp of the occurrence. + * @param tv_sec_hi high 32 bits of the seconds part of the timestamp + * @param tv_sec_lo low 32 bits of the seconds part of the timestamp + * @param tv_nsec nanoseconds part of the timestamp + */ + void (*released)(void *data, + struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec); +}; + +/** + * @ingroup iface_hyprland_global_shortcut_v1 + */ +static inline int +hyprland_global_shortcut_v1_add_listener(struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, + const struct hyprland_global_shortcut_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) hyprland_global_shortcut_v1, + (void (**)(void)) listener, data); +} + +#define HYPRLAND_GLOBAL_SHORTCUT_V1_DESTROY 0 + +/** + * @ingroup iface_hyprland_global_shortcut_v1 + */ +#define HYPRLAND_GLOBAL_SHORTCUT_V1_PRESSED_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_global_shortcut_v1 + */ +#define HYPRLAND_GLOBAL_SHORTCUT_V1_RELEASED_SINCE_VERSION 1 + +/** + * @ingroup iface_hyprland_global_shortcut_v1 + */ +#define HYPRLAND_GLOBAL_SHORTCUT_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_hyprland_global_shortcut_v1 */ +static inline void +hyprland_global_shortcut_v1_set_user_data(struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) hyprland_global_shortcut_v1, user_data); +} + +/** @ingroup iface_hyprland_global_shortcut_v1 */ +static inline void * +hyprland_global_shortcut_v1_get_user_data(struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) hyprland_global_shortcut_v1); +} + +static inline uint32_t +hyprland_global_shortcut_v1_get_version(struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) hyprland_global_shortcut_v1); +} + +/** + * @ingroup iface_hyprland_global_shortcut_v1 + * + * Destroys the shortcut. Can be sent at any time by the client. + */ +static inline void +hyprland_global_shortcut_v1_destroy(struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) hyprland_global_shortcut_v1, + HYPRLAND_GLOBAL_SHORTCUT_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) hyprland_global_shortcut_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/hyprland-toplevel-export-v1-protocol.c b/protocols/hyprland-toplevel-export-v1-protocol.c new file mode 100644 index 0000000..7a597ab --- /dev/null +++ b/protocols/hyprland-toplevel-export-v1-protocol.c @@ -0,0 +1,98 @@ +/* Generated by wayland-scanner 1.22.90 */ + +/* + * Copyright © 2022 Vaxry + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface hyprland_toplevel_export_frame_v1_interface; +extern const struct wl_interface wl_buffer_interface; +extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface; + +static const struct wl_interface *hyprland_toplevel_export_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + &hyprland_toplevel_export_frame_v1_interface, + NULL, + NULL, + &hyprland_toplevel_export_frame_v1_interface, + NULL, + &zwlr_foreign_toplevel_handle_v1_interface, + &wl_buffer_interface, + NULL, +}; + +static const struct wl_message hyprland_toplevel_export_manager_v1_requests[] = { + { "capture_toplevel", "niu", hyprland_toplevel_export_v1_types + 4 }, + { "destroy", "", hyprland_toplevel_export_v1_types + 0 }, + { "capture_toplevel_with_wlr_toplevel_handle", "2nio", hyprland_toplevel_export_v1_types + 7 }, +}; + +WL_PRIVATE const struct wl_interface hyprland_toplevel_export_manager_v1_interface = { + "hyprland_toplevel_export_manager_v1", 2, + 3, hyprland_toplevel_export_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message hyprland_toplevel_export_frame_v1_requests[] = { + { "copy", "oi", hyprland_toplevel_export_v1_types + 10 }, + { "destroy", "", hyprland_toplevel_export_v1_types + 0 }, +}; + +static const struct wl_message hyprland_toplevel_export_frame_v1_events[] = { + { "buffer", "uuuu", hyprland_toplevel_export_v1_types + 0 }, + { "damage", "uuuu", hyprland_toplevel_export_v1_types + 0 }, + { "flags", "u", hyprland_toplevel_export_v1_types + 0 }, + { "ready", "uuu", hyprland_toplevel_export_v1_types + 0 }, + { "failed", "", hyprland_toplevel_export_v1_types + 0 }, + { "linux_dmabuf", "uuu", hyprland_toplevel_export_v1_types + 0 }, + { "buffer_done", "", hyprland_toplevel_export_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface hyprland_toplevel_export_frame_v1_interface = { + "hyprland_toplevel_export_frame_v1", 2, + 2, hyprland_toplevel_export_frame_v1_requests, + 7, hyprland_toplevel_export_frame_v1_events, +}; + diff --git a/protocols/hyprland-toplevel-export-v1-protocol.h b/protocols/hyprland-toplevel-export-v1-protocol.h new file mode 100644 index 0000000..7ee642f --- /dev/null +++ b/protocols/hyprland-toplevel-export-v1-protocol.h @@ -0,0 +1,475 @@ +/* Generated by wayland-scanner 1.22.90 */ + +#ifndef HYPRLAND_TOPLEVEL_EXPORT_V1_CLIENT_PROTOCOL_H +#define HYPRLAND_TOPLEVEL_EXPORT_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_hyprland_toplevel_export_v1 The hyprland_toplevel_export_v1 protocol + * capturing the contents of toplevel windows + * + * @section page_desc_hyprland_toplevel_export_v1 Description + * + * This protocol allows clients to ask for exporting another toplevel's + * surface(s) to a buffer. + * + * Particularly useful for sharing a single window. + * + * @section page_ifaces_hyprland_toplevel_export_v1 Interfaces + * - @subpage page_iface_hyprland_toplevel_export_manager_v1 - manager to inform clients and begin capturing + * - @subpage page_iface_hyprland_toplevel_export_frame_v1 - a frame ready for copy + * @section page_copyright_hyprland_toplevel_export_v1 Copyright + *
+ *
+ * Copyright © 2022 Vaxry
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */ +struct hyprland_toplevel_export_frame_v1; +struct hyprland_toplevel_export_manager_v1; +struct wl_buffer; +struct zwlr_foreign_toplevel_handle_v1; + +#ifndef HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_INTERFACE +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_INTERFACE +/** + * @page page_iface_hyprland_toplevel_export_manager_v1 hyprland_toplevel_export_manager_v1 + * @section page_iface_hyprland_toplevel_export_manager_v1_desc Description + * + * This object is a manager which offers requests to start capturing from a + * source. + * @section page_iface_hyprland_toplevel_export_manager_v1_api API + * See @ref iface_hyprland_toplevel_export_manager_v1. + */ +/** + * @defgroup iface_hyprland_toplevel_export_manager_v1 The hyprland_toplevel_export_manager_v1 interface + * + * This object is a manager which offers requests to start capturing from a + * source. + */ +extern const struct wl_interface hyprland_toplevel_export_manager_v1_interface; +#endif +#ifndef HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_INTERFACE +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_INTERFACE +/** + * @page page_iface_hyprland_toplevel_export_frame_v1 hyprland_toplevel_export_frame_v1 + * @section page_iface_hyprland_toplevel_export_frame_v1_desc Description + * + * This object represents a single frame. + * + * When created, a series of buffer events will be sent, each representing a + * supported buffer type. The "buffer_done" event is sent afterwards to + * indicate that all supported buffer types have been enumerated. The client + * will then be able to send a "copy" request. If the capture is successful, + * the compositor will send a "flags" followed by a "ready" event. + * + * wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. + * + * If the capture failed, the "failed" event is sent. This can happen anytime + * before the "ready" event. + * + * Once either a "ready" or a "failed" event is received, the client should + * destroy the frame. + * @section page_iface_hyprland_toplevel_export_frame_v1_api API + * See @ref iface_hyprland_toplevel_export_frame_v1. + */ +/** + * @defgroup iface_hyprland_toplevel_export_frame_v1 The hyprland_toplevel_export_frame_v1 interface + * + * This object represents a single frame. + * + * When created, a series of buffer events will be sent, each representing a + * supported buffer type. The "buffer_done" event is sent afterwards to + * indicate that all supported buffer types have been enumerated. The client + * will then be able to send a "copy" request. If the capture is successful, + * the compositor will send a "flags" followed by a "ready" event. + * + * wl_shm buffers are always supported, ie. the "buffer" event is guaranteed to be sent. + * + * If the capture failed, the "failed" event is sent. This can happen anytime + * before the "ready" event. + * + * Once either a "ready" or a "failed" event is received, the client should + * destroy the frame. + */ +extern const struct wl_interface hyprland_toplevel_export_frame_v1_interface; +#endif + +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL 0 +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_DESTROY 1 +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL_WITH_WLR_TOPLEVEL_HANDLE 2 + + +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL_WITH_WLR_TOPLEVEL_HANDLE_SINCE_VERSION 2 + +/** @ingroup iface_hyprland_toplevel_export_manager_v1 */ +static inline void +hyprland_toplevel_export_manager_v1_set_user_data(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) hyprland_toplevel_export_manager_v1, user_data); +} + +/** @ingroup iface_hyprland_toplevel_export_manager_v1 */ +static inline void * +hyprland_toplevel_export_manager_v1_get_user_data(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) hyprland_toplevel_export_manager_v1); +} + +static inline uint32_t +hyprland_toplevel_export_manager_v1_get_version(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_manager_v1); +} + +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + * + * Capture the next frame of a toplevel. (window) + * + * The captured frame will not contain any server-side decorations and will + * ignore the compositor-set geometry, like e.g. rounded corners. + * + * It will contain all the subsurfaces and popups, however the latter will be clipped + * to the geometry of the base surface. + * + * The handle parameter refers to the address of the window as seen in `hyprctl clients`. + * For example, for d161e7b0 it would be 3512854448. + */ +static inline struct hyprland_toplevel_export_frame_v1 * +hyprland_toplevel_export_manager_v1_capture_toplevel(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1, int32_t overlay_cursor, uint32_t handle) +{ + struct wl_proxy *frame; + + frame = wl_proxy_marshal_flags((struct wl_proxy *) hyprland_toplevel_export_manager_v1, + HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL, &hyprland_toplevel_export_frame_v1_interface, wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_manager_v1), 0, NULL, overlay_cursor, handle); + + return (struct hyprland_toplevel_export_frame_v1 *) frame; +} + +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + * + * All objects created by the manager will still remain valid, until their + * appropriate destroy request has been called. + */ +static inline void +hyprland_toplevel_export_manager_v1_destroy(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) hyprland_toplevel_export_manager_v1, + HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_hyprland_toplevel_export_manager_v1 + * + * Same as capture_toplevel, but with a zwlr_foreign_toplevel_handle_v1 handle. + */ +static inline struct hyprland_toplevel_export_frame_v1 * +hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle(struct hyprland_toplevel_export_manager_v1 *hyprland_toplevel_export_manager_v1, int32_t overlay_cursor, struct zwlr_foreign_toplevel_handle_v1 *handle) +{ + struct wl_proxy *frame; + + frame = wl_proxy_marshal_flags((struct wl_proxy *) hyprland_toplevel_export_manager_v1, + HYPRLAND_TOPLEVEL_EXPORT_MANAGER_V1_CAPTURE_TOPLEVEL_WITH_WLR_TOPLEVEL_HANDLE, &hyprland_toplevel_export_frame_v1_interface, wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_manager_v1), 0, NULL, overlay_cursor, handle); + + return (struct hyprland_toplevel_export_frame_v1 *) frame; +} + +#ifndef HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ENUM +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ENUM +enum hyprland_toplevel_export_frame_v1_error { + /** + * the object has already been used to copy a wl_buffer + */ + HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ALREADY_USED = 0, + /** + * buffer attributes are invalid + */ + HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_INVALID_BUFFER = 1, +}; +#endif /* HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_ERROR_ENUM */ + +#ifndef HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_ENUM +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_ENUM +enum hyprland_toplevel_export_frame_v1_flags { + /** + * contents are y-inverted + */ + HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_Y_INVERT = 1, +}; +#endif /* HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_ENUM */ + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + * @struct hyprland_toplevel_export_frame_v1_listener + */ +struct hyprland_toplevel_export_frame_v1_listener { + /** + * wl_shm buffer information + * + * Provides information about wl_shm buffer parameters that need + * to be used for this frame. This event is sent once after the + * frame is created if wl_shm buffers are supported. + * @param format buffer format + * @param width buffer width + * @param height buffer height + * @param stride buffer stride + */ + void (*buffer)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + uint32_t format, + uint32_t width, + uint32_t height, + uint32_t stride); + /** + * carries the coordinates of the damaged region + * + * This event is sent right before the ready event when + * ignore_damage was not set. It may be generated multiple times + * for each copy request. + * + * The arguments describe a box around an area that has changed + * since the last copy request that was derived from the current + * screencopy manager instance. + * + * The union of all regions received between the call to copy and a + * ready event is the total damage since the prior ready event. + * @param x damaged x coordinates + * @param y damaged y coordinates + * @param width current width + * @param height current height + */ + void (*damage)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height); + /** + * frame flags + * + * Provides flags about the frame. This event is sent once before + * the "ready" event. + * @param flags frame flags + */ + void (*flags)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + uint32_t flags); + /** + * indicates frame is available for reading + * + * Called as soon as the frame is copied, indicating it is + * available for reading. This event includes the time at which + * presentation happened at. + * + * The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec + * triples, each component being an unsigned 32-bit value. Whole + * seconds are in tv_sec which is a 64-bit value combined from + * tv_sec_hi and tv_sec_lo, and the additional fractional part in + * tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must + * be in [0, 999999999]. The seconds part may have an arbitrary + * offset at start. + * + * After receiving this event, the client should destroy the + * object. + * @param tv_sec_hi high 32 bits of the seconds part of the timestamp + * @param tv_sec_lo low 32 bits of the seconds part of the timestamp + * @param tv_nsec nanoseconds part of the timestamp + */ + void (*ready)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec); + /** + * frame copy failed + * + * This event indicates that the attempted frame copy has failed. + * + * After receiving this event, the client should destroy the + * object. + */ + void (*failed)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1); + /** + * linux-dmabuf buffer information + * + * Provides information about linux-dmabuf buffer parameters that + * need to be used for this frame. This event is sent once after + * the frame is created if linux-dmabuf buffers are supported. + * @param format fourcc pixel format + * @param width buffer width + * @param height buffer height + */ + void (*linux_dmabuf)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + uint32_t format, + uint32_t width, + uint32_t height); + /** + * all buffer types reported + * + * This event is sent once after all buffer events have been + * sent. + * + * The client should proceed to create a buffer of one of the + * supported types, and send a "copy" request. + */ + void (*buffer_done)(void *data, + struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1); +}; + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +static inline int +hyprland_toplevel_export_frame_v1_add_listener(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, + const struct hyprland_toplevel_export_frame_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) hyprland_toplevel_export_frame_v1, + (void (**)(void)) listener, data); +} + +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_COPY 0 +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_DESTROY 1 + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_BUFFER_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_DAMAGE_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FLAGS_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_READY_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_FAILED_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_LINUX_DMABUF_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_BUFFER_DONE_SINCE_VERSION 1 + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_COPY_SINCE_VERSION 1 +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + */ +#define HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_hyprland_toplevel_export_frame_v1 */ +static inline void +hyprland_toplevel_export_frame_v1_set_user_data(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) hyprland_toplevel_export_frame_v1, user_data); +} + +/** @ingroup iface_hyprland_toplevel_export_frame_v1 */ +static inline void * +hyprland_toplevel_export_frame_v1_get_user_data(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) hyprland_toplevel_export_frame_v1); +} + +static inline uint32_t +hyprland_toplevel_export_frame_v1_get_version(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_frame_v1); +} + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + * + * Copy the frame to the supplied buffer. The buffer must have the + * correct size, see hyprland_toplevel_export_frame_v1.buffer and + * hyprland_toplevel_export_frame_v1.linux_dmabuf. The buffer needs to have a + * supported format. + * + * If the frame is successfully copied, a "flags" and a "ready" event is + * sent. Otherwise, a "failed" event is sent. + * + * This event will wait for appropriate damage to be copied, unless the ignore_damage + * arg is set to a non-zero value. + */ +static inline void +hyprland_toplevel_export_frame_v1_copy(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1, struct wl_buffer *buffer, int32_t ignore_damage) +{ + wl_proxy_marshal_flags((struct wl_proxy *) hyprland_toplevel_export_frame_v1, + HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_COPY, NULL, wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_frame_v1), 0, buffer, ignore_damage); +} + +/** + * @ingroup iface_hyprland_toplevel_export_frame_v1 + * + * Destroys the frame. This request can be sent at any time by the client. + */ +static inline void +hyprland_toplevel_export_frame_v1_destroy(struct hyprland_toplevel_export_frame_v1 *hyprland_toplevel_export_frame_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) hyprland_toplevel_export_frame_v1, + HYPRLAND_TOPLEVEL_EXPORT_FRAME_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) hyprland_toplevel_export_frame_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.c b/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.c new file mode 100644 index 0000000..a9cab82 --- /dev/null +++ b/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.c @@ -0,0 +1,106 @@ +/* Generated by wayland-scanner 1.22.90 */ + +/* + * Copyright © 2018 Ilia Bozhinov + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this permission + * notice appear in supporting documentation, and that the name of + * the copyright holders not be used in advertising or publicity + * pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no + * representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + * THIS SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface wl_seat_interface; +extern const struct wl_interface wl_surface_interface; +extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface; + +static const struct wl_interface *wlr_foreign_toplevel_management_unstable_v1_types[] = { + NULL, + &zwlr_foreign_toplevel_handle_v1_interface, + &wl_seat_interface, + &wl_surface_interface, + NULL, + NULL, + NULL, + NULL, + &wl_output_interface, + &wl_output_interface, + &wl_output_interface, + &zwlr_foreign_toplevel_handle_v1_interface, +}; + +static const struct wl_message zwlr_foreign_toplevel_manager_v1_requests[] = { + { "stop", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_foreign_toplevel_manager_v1_events[] = { + { "toplevel", "n", wlr_foreign_toplevel_management_unstable_v1_types + 1 }, + { "finished", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface = { + "zwlr_foreign_toplevel_manager_v1", 3, + 1, zwlr_foreign_toplevel_manager_v1_requests, + 2, zwlr_foreign_toplevel_manager_v1_events, +}; + +static const struct wl_message zwlr_foreign_toplevel_handle_v1_requests[] = { + { "set_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "unset_maximized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "unset_minimized", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "activate", "o", wlr_foreign_toplevel_management_unstable_v1_types + 2 }, + { "close", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_rectangle", "oiiii", wlr_foreign_toplevel_management_unstable_v1_types + 3 }, + { "destroy", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "set_fullscreen", "2?o", wlr_foreign_toplevel_management_unstable_v1_types + 8 }, + { "unset_fullscreen", "2", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, +}; + +static const struct wl_message zwlr_foreign_toplevel_handle_v1_events[] = { + { "title", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "app_id", "s", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "output_enter", "o", wlr_foreign_toplevel_management_unstable_v1_types + 9 }, + { "output_leave", "o", wlr_foreign_toplevel_management_unstable_v1_types + 10 }, + { "state", "a", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "done", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "closed", "", wlr_foreign_toplevel_management_unstable_v1_types + 0 }, + { "parent", "3?o", wlr_foreign_toplevel_management_unstable_v1_types + 11 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface = { + "zwlr_foreign_toplevel_handle_v1", 3, + 10, zwlr_foreign_toplevel_handle_v1_requests, + 8, zwlr_foreign_toplevel_handle_v1_events, +}; + diff --git a/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h b/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h new file mode 100644 index 0000000..e4cf4b8 --- /dev/null +++ b/protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h @@ -0,0 +1,615 @@ +/* Generated by wayland-scanner 1.22.90 */ + +#ifndef WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_FOREIGN_TOPLEVEL_MANAGEMENT_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_foreign_toplevel_management_unstable_v1 The wlr_foreign_toplevel_management_unstable_v1 protocol + * @section page_ifaces_wlr_foreign_toplevel_management_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_foreign_toplevel_manager_v1 - list and control opened apps + * - @subpage page_iface_zwlr_foreign_toplevel_handle_v1 - an opened toplevel + * @section page_copyright_wlr_foreign_toplevel_management_unstable_v1 Copyright + *
+ *
+ * Copyright © 2018 Ilia Bozhinov
+ *
+ * Permission to use, copy, modify, distribute, and sell this
+ * software and its documentation for any purpose is hereby granted
+ * without fee, provided that the above copyright notice appear in
+ * all copies and that both that copyright notice and this permission
+ * notice appear in supporting documentation, and that the name of
+ * the copyright holders not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no
+ * representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ * THIS SOFTWARE.
+ * 
+ */ +struct wl_output; +struct wl_seat; +struct wl_surface; +struct zwlr_foreign_toplevel_handle_v1; +struct zwlr_foreign_toplevel_manager_v1; + +#ifndef ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_INTERFACE +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwlr_foreign_toplevel_manager_v1 zwlr_foreign_toplevel_manager_v1 + * @section page_iface_zwlr_foreign_toplevel_manager_v1_desc Description + * + * The purpose of this protocol is to enable the creation of taskbars + * and docks by providing them with a list of opened applications and + * letting them request certain actions on them, like maximizing, etc. + * + * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + * toplevel window will be sent via the toplevel event + * @section page_iface_zwlr_foreign_toplevel_manager_v1_api API + * See @ref iface_zwlr_foreign_toplevel_manager_v1. + */ +/** + * @defgroup iface_zwlr_foreign_toplevel_manager_v1 The zwlr_foreign_toplevel_manager_v1 interface + * + * The purpose of this protocol is to enable the creation of taskbars + * and docks by providing them with a list of opened applications and + * letting them request certain actions on them, like maximizing, etc. + * + * After a client binds the zwlr_foreign_toplevel_manager_v1, each opened + * toplevel window will be sent via the toplevel event + */ +extern const struct wl_interface zwlr_foreign_toplevel_manager_v1_interface; +#endif +#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_INTERFACE +/** + * @page page_iface_zwlr_foreign_toplevel_handle_v1 zwlr_foreign_toplevel_handle_v1 + * @section page_iface_zwlr_foreign_toplevel_handle_v1_desc Description + * + * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + * window. Each app may have multiple opened toplevels. + * + * Each toplevel has a list of outputs it is visible on, conveyed to the + * client with the output_enter and output_leave events. + * @section page_iface_zwlr_foreign_toplevel_handle_v1_api API + * See @ref iface_zwlr_foreign_toplevel_handle_v1. + */ +/** + * @defgroup iface_zwlr_foreign_toplevel_handle_v1 The zwlr_foreign_toplevel_handle_v1 interface + * + * A zwlr_foreign_toplevel_handle_v1 object represents an opened toplevel + * window. Each app may have multiple opened toplevels. + * + * Each toplevel has a list of outputs it is visible on, conveyed to the + * client with the output_enter and output_leave events. + */ +extern const struct wl_interface zwlr_foreign_toplevel_handle_v1_interface; +#endif + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + * @struct zwlr_foreign_toplevel_manager_v1_listener + */ +struct zwlr_foreign_toplevel_manager_v1_listener { + /** + * a toplevel has been created + * + * This event is emitted whenever a new toplevel window is + * created. It is emitted for all toplevels, regardless of the app + * that has created them. + * + * All initial details of the toplevel(title, app_id, states, etc.) + * will be sent immediately after this event via the corresponding + * events in zwlr_foreign_toplevel_handle_v1. + */ + void (*toplevel)(void *data, + struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, + struct zwlr_foreign_toplevel_handle_v1 *toplevel); + /** + * the compositor has finished with the toplevel manager + * + * This event indicates that the compositor is done sending + * events to the zwlr_foreign_toplevel_manager_v1. The server will + * destroy the object immediately after sending this request, so it + * will become invalid and the client should free any resources + * associated with it. + */ + void (*finished)(void *data, + struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1); +}; + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +static inline int +zwlr_foreign_toplevel_manager_v1_add_listener(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, + const struct zwlr_foreign_toplevel_manager_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP 0 + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_TOPLEVEL_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_FINISHED_SINCE_VERSION 1 + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void +zwlr_foreign_toplevel_manager_v1_set_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, user_data); +} + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void * +zwlr_foreign_toplevel_manager_v1_get_user_data(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +static inline uint32_t +zwlr_foreign_toplevel_manager_v1_get_version(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +/** @ingroup iface_zwlr_foreign_toplevel_manager_v1 */ +static inline void +zwlr_foreign_toplevel_manager_v1_destroy(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + wl_proxy_destroy((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_manager_v1 + * + * Indicates the client no longer wishes to receive events for new toplevels. + * However the compositor may emit further toplevel_created events, until + * the finished event is emitted. + * + * The client must not send any more requests after this one. + */ +static inline void +zwlr_foreign_toplevel_manager_v1_stop(struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1, + ZWLR_FOREIGN_TOPLEVEL_MANAGER_V1_STOP, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_manager_v1), 0); +} + +#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * types of states on the toplevel + * + * The different states that a toplevel can have. These have the same meaning + * as the states with the same names defined in xdg-toplevel + */ +enum zwlr_foreign_toplevel_handle_v1_state { + /** + * the toplevel is maximized + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED = 0, + /** + * the toplevel is minimized + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED = 1, + /** + * the toplevel is active + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED = 2, + /** + * the toplevel is fullscreen + * @since 2 + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN = 3, +}; +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN_SINCE_VERSION 2 +#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ENUM */ + +#ifndef ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM +enum zwlr_foreign_toplevel_handle_v1_error { + /** + * the provided rectangle is invalid + */ + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_INVALID_RECTANGLE = 0, +}; +#endif /* ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ERROR_ENUM */ + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * @struct zwlr_foreign_toplevel_handle_v1_listener + */ +struct zwlr_foreign_toplevel_handle_v1_listener { + /** + * title change + * + * This event is emitted whenever the title of the toplevel + * changes. + */ + void (*title)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const char *title); + /** + * app-id change + * + * This event is emitted whenever the app-id of the toplevel + * changes. + */ + void (*app_id)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const char *app_id); + /** + * toplevel entered an output + * + * This event is emitted whenever the toplevel becomes visible on + * the given output. A toplevel may be visible on multiple outputs. + */ + void (*output_enter)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_output *output); + /** + * toplevel left an output + * + * This event is emitted whenever the toplevel stops being + * visible on the given output. It is guaranteed that an + * entered-output event with the same output has been emitted + * before this event. + */ + void (*output_leave)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_output *output); + /** + * the toplevel state changed + * + * This event is emitted immediately after the + * zlw_foreign_toplevel_handle_v1 is created and each time the + * toplevel state changes, either because of a compositor action or + * because of a request in this protocol. + */ + void (*state)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct wl_array *state); + /** + * all information about the toplevel has been sent + * + * This event is sent after all changes in the toplevel state + * have been sent. + * + * This allows changes to the zwlr_foreign_toplevel_handle_v1 + * properties to be seen as atomic, even if they happen via + * multiple events. + */ + void (*done)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1); + /** + * this toplevel has been destroyed + * + * This event means the toplevel has been destroyed. It is + * guaranteed there won't be any more events for this + * zwlr_foreign_toplevel_handle_v1. The toplevel itself becomes + * inert so any requests will be ignored except the destroy + * request. + */ + void (*closed)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1); + /** + * parent change + * + * This event is emitted whenever the parent of the toplevel + * changes. + * + * No event is emitted when the parent handle is destroyed by the + * client. + * @since 3 + */ + void (*parent)(void *data, + struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + struct zwlr_foreign_toplevel_handle_v1 *parent); +}; + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +static inline int +zwlr_foreign_toplevel_handle_v1_add_listener(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, + const struct zwlr_foreign_toplevel_handle_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED 0 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED 1 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED 2 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED 3 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE 4 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE 5 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE 6 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY 7 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN 8 +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN 9 + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_TITLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_APP_ID_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_ENTER_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_OUTPUT_LEAVE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DONE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_PARENT_SINCE_VERSION 3 + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + */ +#define ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN_SINCE_VERSION 2 + +/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, user_data); +} + +/** @ingroup iface_zwlr_foreign_toplevel_handle_v1 */ +static inline void * +zwlr_foreign_toplevel_handle_v1_get_user_data(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1); +} + +static inline uint32_t +zwlr_foreign_toplevel_handle_v1_get_version(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be maximized. If the maximized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unmaximized. If the maximized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_maximized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be minimized. If the minimized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unminimized. If the minimized state actually + * changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_minimized(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Request that this toplevel be activated on the given seat. + * There is no guarantee the toplevel will be actually activated. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_activate(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_seat *seat) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_ACTIVATE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, seat); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Send a request to the toplevel to close itself. The compositor would + * typically use a shell-specific method to carry out this request, for + * example by sending the xdg_toplevel.close event. However, this gives + * no guarantees the toplevel will actually be destroyed. If and when + * this happens, the zwlr_foreign_toplevel_handle_v1.closed event will + * be emitted. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_close(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_CLOSE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * The rectangle of the surface specified in this request corresponds to + * the place where the app using this protocol represents the given toplevel. + * It can be used by the compositor as a hint for some operations, e.g + * minimizing. The client is however not required to set this, in which + * case the compositor is free to decide some default value. + * + * If the client specifies more than one rectangle, only the last one is + * considered. + * + * The dimensions are given in surface-local coordinates. + * Setting width=height=0 removes the already-set rectangle. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_rectangle(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_surface *surface, int32_t x, int32_t y, int32_t width, int32_t height) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_RECTANGLE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, surface, x, y, width, height); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Destroys the zwlr_foreign_toplevel_handle_v1 object. + * + * This request should be called either when the client does not want to + * use the toplevel anymore or after the closed event to finalize the + * destruction of the object. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_destroy(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be fullscreened on the given output. If the + * fullscreen state and/or the outputs the toplevel is visible on actually + * change, this will be indicated by the state and output_enter/leave + * events. + * + * The output parameter is only a hint to the compositor. Also, if output + * is NULL, the compositor should decide which output the toplevel will be + * fullscreened on, if at all. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_set_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1, struct wl_output *output) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0, output); +} + +/** + * @ingroup iface_zwlr_foreign_toplevel_handle_v1 + * + * Requests that the toplevel be unfullscreened. If the fullscreen state + * actually changes, this will be indicated by the state event. + */ +static inline void +zwlr_foreign_toplevel_handle_v1_unset_fullscreen(struct zwlr_foreign_toplevel_handle_v1 *zwlr_foreign_toplevel_handle_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1, + ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_foreign_toplevel_handle_v1), 0); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/protocols/wlr-screencopy-unstable-v1-protocol.c b/protocols/wlr-screencopy-unstable-v1-protocol.c new file mode 100644 index 0000000..0f53b6d --- /dev/null +++ b/protocols/wlr-screencopy-unstable-v1-protocol.c @@ -0,0 +1,97 @@ +/* Generated by wayland-scanner 1.22.90 */ + +/* + * Copyright © 2018 Simon Ser + * Copyright © 2019 Andri Yngvason + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include "wayland-util.h" + +#ifndef __has_attribute +# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ +#endif + +#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) +#define WL_PRIVATE __attribute__ ((visibility("hidden"))) +#else +#define WL_PRIVATE +#endif + +extern const struct wl_interface wl_buffer_interface; +extern const struct wl_interface wl_output_interface; +extern const struct wl_interface zwlr_screencopy_frame_v1_interface; + +static const struct wl_interface *wlr_screencopy_unstable_v1_types[] = { + NULL, + NULL, + NULL, + NULL, + &zwlr_screencopy_frame_v1_interface, + NULL, + &wl_output_interface, + &zwlr_screencopy_frame_v1_interface, + NULL, + &wl_output_interface, + NULL, + NULL, + NULL, + NULL, + &wl_buffer_interface, + &wl_buffer_interface, +}; + +static const struct wl_message zwlr_screencopy_manager_v1_requests[] = { + { "capture_output", "nio", wlr_screencopy_unstable_v1_types + 4 }, + { "capture_output_region", "nioiiii", wlr_screencopy_unstable_v1_types + 7 }, + { "destroy", "", wlr_screencopy_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_screencopy_manager_v1_interface = { + "zwlr_screencopy_manager_v1", 3, + 3, zwlr_screencopy_manager_v1_requests, + 0, NULL, +}; + +static const struct wl_message zwlr_screencopy_frame_v1_requests[] = { + { "copy", "o", wlr_screencopy_unstable_v1_types + 14 }, + { "destroy", "", wlr_screencopy_unstable_v1_types + 0 }, + { "copy_with_damage", "2o", wlr_screencopy_unstable_v1_types + 15 }, +}; + +static const struct wl_message zwlr_screencopy_frame_v1_events[] = { + { "buffer", "uuuu", wlr_screencopy_unstable_v1_types + 0 }, + { "flags", "u", wlr_screencopy_unstable_v1_types + 0 }, + { "ready", "uuu", wlr_screencopy_unstable_v1_types + 0 }, + { "failed", "", wlr_screencopy_unstable_v1_types + 0 }, + { "damage", "2uuuu", wlr_screencopy_unstable_v1_types + 0 }, + { "linux_dmabuf", "3uuu", wlr_screencopy_unstable_v1_types + 0 }, + { "buffer_done", "3", wlr_screencopy_unstable_v1_types + 0 }, +}; + +WL_PRIVATE const struct wl_interface zwlr_screencopy_frame_v1_interface = { + "zwlr_screencopy_frame_v1", 3, + 3, zwlr_screencopy_frame_v1_requests, + 7, zwlr_screencopy_frame_v1_events, +}; + diff --git a/protocols/wlr-screencopy-unstable-v1-protocol.h b/protocols/wlr-screencopy-unstable-v1-protocol.h new file mode 100644 index 0000000..7f5b786 --- /dev/null +++ b/protocols/wlr-screencopy-unstable-v1-protocol.h @@ -0,0 +1,491 @@ +/* Generated by wayland-scanner 1.22.90 */ + +#ifndef WLR_SCREENCOPY_UNSTABLE_V1_CLIENT_PROTOCOL_H +#define WLR_SCREENCOPY_UNSTABLE_V1_CLIENT_PROTOCOL_H + +#include +#include +#include "wayland-client.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page page_wlr_screencopy_unstable_v1 The wlr_screencopy_unstable_v1 protocol + * screen content capturing on client buffers + * + * @section page_desc_wlr_screencopy_unstable_v1 Description + * + * This protocol allows clients to ask the compositor to copy part of the + * screen content to a client buffer. + * + * Warning! The protocol described in this file is experimental and + * backward incompatible changes may be made. Backward compatible changes + * may be added together with the corresponding interface version bump. + * Backward incompatible changes are done by bumping the version number in + * the protocol and interface names and resetting the interface version. + * Once the protocol is to be declared stable, the 'z' prefix and the + * version number in the protocol and interface names are removed and the + * interface version number is reset. + * + * @section page_ifaces_wlr_screencopy_unstable_v1 Interfaces + * - @subpage page_iface_zwlr_screencopy_manager_v1 - manager to inform clients and begin capturing + * - @subpage page_iface_zwlr_screencopy_frame_v1 - a frame ready for copy + * @section page_copyright_wlr_screencopy_unstable_v1 Copyright + *
+ *
+ * Copyright © 2018 Simon Ser
+ * Copyright © 2019 Andri Yngvason
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ * 
+ */ +struct wl_buffer; +struct wl_output; +struct zwlr_screencopy_frame_v1; +struct zwlr_screencopy_manager_v1; + +#ifndef ZWLR_SCREENCOPY_MANAGER_V1_INTERFACE +#define ZWLR_SCREENCOPY_MANAGER_V1_INTERFACE +/** + * @page page_iface_zwlr_screencopy_manager_v1 zwlr_screencopy_manager_v1 + * @section page_iface_zwlr_screencopy_manager_v1_desc Description + * + * This object is a manager which offers requests to start capturing from a + * source. + * @section page_iface_zwlr_screencopy_manager_v1_api API + * See @ref iface_zwlr_screencopy_manager_v1. + */ +/** + * @defgroup iface_zwlr_screencopy_manager_v1 The zwlr_screencopy_manager_v1 interface + * + * This object is a manager which offers requests to start capturing from a + * source. + */ +extern const struct wl_interface zwlr_screencopy_manager_v1_interface; +#endif +#ifndef ZWLR_SCREENCOPY_FRAME_V1_INTERFACE +#define ZWLR_SCREENCOPY_FRAME_V1_INTERFACE +/** + * @page page_iface_zwlr_screencopy_frame_v1 zwlr_screencopy_frame_v1 + * @section page_iface_zwlr_screencopy_frame_v1_desc Description + * + * This object represents a single frame. + * + * When created, a series of buffer events will be sent, each representing a + * supported buffer type. The "buffer_done" event is sent afterwards to + * indicate that all supported buffer types have been enumerated. The client + * will then be able to send a "copy" request. If the capture is successful, + * the compositor will send a "flags" followed by a "ready" event. + * + * For objects version 2 or lower, wl_shm buffers are always supported, ie. + * the "buffer" event is guaranteed to be sent. + * + * If the capture failed, the "failed" event is sent. This can happen anytime + * before the "ready" event. + * + * Once either a "ready" or a "failed" event is received, the client should + * destroy the frame. + * @section page_iface_zwlr_screencopy_frame_v1_api API + * See @ref iface_zwlr_screencopy_frame_v1. + */ +/** + * @defgroup iface_zwlr_screencopy_frame_v1 The zwlr_screencopy_frame_v1 interface + * + * This object represents a single frame. + * + * When created, a series of buffer events will be sent, each representing a + * supported buffer type. The "buffer_done" event is sent afterwards to + * indicate that all supported buffer types have been enumerated. The client + * will then be able to send a "copy" request. If the capture is successful, + * the compositor will send a "flags" followed by a "ready" event. + * + * For objects version 2 or lower, wl_shm buffers are always supported, ie. + * the "buffer" event is guaranteed to be sent. + * + * If the capture failed, the "failed" event is sent. This can happen anytime + * before the "ready" event. + * + * Once either a "ready" or a "failed" event is received, the client should + * destroy the frame. + */ +extern const struct wl_interface zwlr_screencopy_frame_v1_interface; +#endif + +#define ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT 0 +#define ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT_REGION 1 +#define ZWLR_SCREENCOPY_MANAGER_V1_DESTROY 2 + + +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + */ +#define ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + */ +#define ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT_REGION_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + */ +#define ZWLR_SCREENCOPY_MANAGER_V1_DESTROY_SINCE_VERSION 1 + +/** @ingroup iface_zwlr_screencopy_manager_v1 */ +static inline void +zwlr_screencopy_manager_v1_set_user_data(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_screencopy_manager_v1, user_data); +} + +/** @ingroup iface_zwlr_screencopy_manager_v1 */ +static inline void * +zwlr_screencopy_manager_v1_get_user_data(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_screencopy_manager_v1); +} + +static inline uint32_t +zwlr_screencopy_manager_v1_get_version(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_manager_v1); +} + +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + * + * Capture the next frame of an entire output. + */ +static inline struct zwlr_screencopy_frame_v1 * +zwlr_screencopy_manager_v1_capture_output(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1, int32_t overlay_cursor, struct wl_output *output) +{ + struct wl_proxy *frame; + + frame = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_manager_v1, + ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT, &zwlr_screencopy_frame_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_manager_v1), 0, NULL, overlay_cursor, output); + + return (struct zwlr_screencopy_frame_v1 *) frame; +} + +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + * + * Capture the next frame of an output's region. + * + * The region is given in output logical coordinates, see + * xdg_output.logical_size. The region will be clipped to the output's + * extents. + */ +static inline struct zwlr_screencopy_frame_v1 * +zwlr_screencopy_manager_v1_capture_output_region(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1, int32_t overlay_cursor, struct wl_output *output, int32_t x, int32_t y, int32_t width, int32_t height) +{ + struct wl_proxy *frame; + + frame = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_manager_v1, + ZWLR_SCREENCOPY_MANAGER_V1_CAPTURE_OUTPUT_REGION, &zwlr_screencopy_frame_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_manager_v1), 0, NULL, overlay_cursor, output, x, y, width, height); + + return (struct zwlr_screencopy_frame_v1 *) frame; +} + +/** + * @ingroup iface_zwlr_screencopy_manager_v1 + * + * All objects created by the manager will still remain valid, until their + * appropriate destroy request has been called. + */ +static inline void +zwlr_screencopy_manager_v1_destroy(struct zwlr_screencopy_manager_v1 *zwlr_screencopy_manager_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_manager_v1, + ZWLR_SCREENCOPY_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_manager_v1), WL_MARSHAL_FLAG_DESTROY); +} + +#ifndef ZWLR_SCREENCOPY_FRAME_V1_ERROR_ENUM +#define ZWLR_SCREENCOPY_FRAME_V1_ERROR_ENUM +enum zwlr_screencopy_frame_v1_error { + /** + * the object has already been used to copy a wl_buffer + */ + ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED = 0, + /** + * buffer attributes are invalid + */ + ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER = 1, +}; +#endif /* ZWLR_SCREENCOPY_FRAME_V1_ERROR_ENUM */ + +#ifndef ZWLR_SCREENCOPY_FRAME_V1_FLAGS_ENUM +#define ZWLR_SCREENCOPY_FRAME_V1_FLAGS_ENUM +enum zwlr_screencopy_frame_v1_flags { + /** + * contents are y-inverted + */ + ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT = 1, +}; +#endif /* ZWLR_SCREENCOPY_FRAME_V1_FLAGS_ENUM */ + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + * @struct zwlr_screencopy_frame_v1_listener + */ +struct zwlr_screencopy_frame_v1_listener { + /** + * wl_shm buffer information + * + * Provides information about wl_shm buffer parameters that need + * to be used for this frame. This event is sent once after the + * frame is created if wl_shm buffers are supported. + * @param format buffer format + * @param width buffer width + * @param height buffer height + * @param stride buffer stride + */ + void (*buffer)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + uint32_t format, + uint32_t width, + uint32_t height, + uint32_t stride); + /** + * frame flags + * + * Provides flags about the frame. This event is sent once before + * the "ready" event. + * @param flags frame flags + */ + void (*flags)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + uint32_t flags); + /** + * indicates frame is available for reading + * + * Called as soon as the frame is copied, indicating it is + * available for reading. This event includes the time at which + * presentation happened at. + * + * The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec + * triples, each component being an unsigned 32-bit value. Whole + * seconds are in tv_sec which is a 64-bit value combined from + * tv_sec_hi and tv_sec_lo, and the additional fractional part in + * tv_nsec as nanoseconds. Hence, for valid timestamps tv_nsec must + * be in [0, 999999999]. The seconds part may have an arbitrary + * offset at start. + * + * After receiving this event, the client should destroy the + * object. + * @param tv_sec_hi high 32 bits of the seconds part of the timestamp + * @param tv_sec_lo low 32 bits of the seconds part of the timestamp + * @param tv_nsec nanoseconds part of the timestamp + */ + void (*ready)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec); + /** + * frame copy failed + * + * This event indicates that the attempted frame copy has failed. + * + * After receiving this event, the client should destroy the + * object. + */ + void (*failed)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1); + /** + * carries the coordinates of the damaged region + * + * This event is sent right before the ready event when + * copy_with_damage is requested. It may be generated multiple + * times for each copy_with_damage request. + * + * The arguments describe a box around an area that has changed + * since the last copy request that was derived from the current + * screencopy manager instance. + * + * The union of all regions received between the call to + * copy_with_damage and a ready event is the total damage since the + * prior ready event. + * @param x damaged x coordinates + * @param y damaged y coordinates + * @param width current width + * @param height current height + * @since 2 + */ + void (*damage)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height); + /** + * linux-dmabuf buffer information + * + * Provides information about linux-dmabuf buffer parameters that + * need to be used for this frame. This event is sent once after + * the frame is created if linux-dmabuf buffers are supported. + * @param format fourcc pixel format + * @param width buffer width + * @param height buffer height + * @since 3 + */ + void (*linux_dmabuf)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + uint32_t format, + uint32_t width, + uint32_t height); + /** + * all buffer types reported + * + * This event is sent once after all buffer events have been + * sent. + * + * The client should proceed to create a buffer of one of the + * supported types, and send a "copy" request. + * @since 3 + */ + void (*buffer_done)(void *data, + struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1); +}; + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +static inline int +zwlr_screencopy_frame_v1_add_listener(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, + const struct zwlr_screencopy_frame_v1_listener *listener, void *data) +{ + return wl_proxy_add_listener((struct wl_proxy *) zwlr_screencopy_frame_v1, + (void (**)(void)) listener, data); +} + +#define ZWLR_SCREENCOPY_FRAME_V1_COPY 0 +#define ZWLR_SCREENCOPY_FRAME_V1_DESTROY 1 +#define ZWLR_SCREENCOPY_FRAME_V1_COPY_WITH_DAMAGE 2 + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_BUFFER_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_FLAGS_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_READY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_FAILED_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_DAMAGE_SINCE_VERSION 2 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_LINUX_DMABUF_SINCE_VERSION 3 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_BUFFER_DONE_SINCE_VERSION 3 + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_COPY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_DESTROY_SINCE_VERSION 1 +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + */ +#define ZWLR_SCREENCOPY_FRAME_V1_COPY_WITH_DAMAGE_SINCE_VERSION 2 + +/** @ingroup iface_zwlr_screencopy_frame_v1 */ +static inline void +zwlr_screencopy_frame_v1_set_user_data(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, void *user_data) +{ + wl_proxy_set_user_data((struct wl_proxy *) zwlr_screencopy_frame_v1, user_data); +} + +/** @ingroup iface_zwlr_screencopy_frame_v1 */ +static inline void * +zwlr_screencopy_frame_v1_get_user_data(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1) +{ + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_screencopy_frame_v1); +} + +static inline uint32_t +zwlr_screencopy_frame_v1_get_version(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1) +{ + return wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1); +} + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + * + * Copy the frame to the supplied buffer. The buffer must have a the + * correct size, see zwlr_screencopy_frame_v1.buffer and + * zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a + * supported format. + * + * If the frame is successfully copied, a "flags" and a "ready" events are + * sent. Otherwise, a "failed" event is sent. + */ +static inline void +zwlr_screencopy_frame_v1_copy(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, struct wl_buffer *buffer) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, + ZWLR_SCREENCOPY_FRAME_V1_COPY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), 0, buffer); +} + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + * + * Destroys the frame. This request can be sent at any time by the client. + */ +static inline void +zwlr_screencopy_frame_v1_destroy(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, + ZWLR_SCREENCOPY_FRAME_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), WL_MARSHAL_FLAG_DESTROY); +} + +/** + * @ingroup iface_zwlr_screencopy_frame_v1 + * + * Same as copy, except it waits until there is damage to copy. + */ +static inline void +zwlr_screencopy_frame_v1_copy_with_damage(struct zwlr_screencopy_frame_v1 *zwlr_screencopy_frame_v1, struct wl_buffer *buffer) +{ + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_screencopy_frame_v1, + ZWLR_SCREENCOPY_FRAME_V1_COPY_WITH_DAMAGE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_screencopy_frame_v1), 0, buffer); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/core/PortalManager.cpp b/src/core/PortalManager.cpp new file mode 100644 index 0000000..ab34f58 --- /dev/null +++ b/src/core/PortalManager.cpp @@ -0,0 +1,86 @@ +#include "PortalManager.hpp" +#include "../helpers/Log.hpp" + +#include +#include +#include +#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); +} + +void handleGlobalRemove(void* data, struct wl_registry* registry, uint32_t name) { + ; // noop +} + +inline const struct wl_registry_listener registryListener = { + .global = handleGlobal, + .global_remove = handleGlobalRemove, +}; + +// + +void CPortalManager::onGlobal(void* data, struct wl_registry* registry, 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_sPortals.screencopy = std::make_unique((zwlr_screencopy_manager_v1*)wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, version)); +} + +void CPortalManager::init() { + m_pConnection = sdbus::createDefaultBusConnection("org.freedesktop.impl.portal.desktop.hyprland"); + + if (!m_pConnection) { + Debug::log(CRIT, "Couldn't connect to dbus"); + exit(1); + } + + // init wayland connection + m_sWaylandConnection.display = wl_display_connect(nullptr); + + if (!m_sWaylandConnection.display) { + Debug::log(CRIT, "Couldn't connect to a wayland compositor"); + exit(1); + } + + if (const auto ENV = getenv("XDG_CURRENT_DESKTOP"); ENV) { + Debug::log(LOG, "XDG_CURRENT_DESKTOP set to {}", ENV); + + if (std::string(ENV) != "Hyprland") + Debug::log(WARN, "Not running on hyprland, some features might be unavailable"); + } else { + 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); + + Debug::log(LOG, "Gathering exported interfaces"); + + wl_display_roundtrip(m_sWaylandConnection.display); + + while (1) { + // dbus events + while (m_pConnection->processPendingRequest()) { + ; + } + + // wayland events + while (1) { + auto r = wl_display_dispatch_pending(m_sWaylandConnection.display); + wl_display_flush(m_sWaylandConnection.display); + + if (r <= 0) + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +sdbus::IConnection* CPortalManager::getConnection() { + return m_pConnection.get(); +} \ No newline at end of file diff --git a/src/core/PortalManager.hpp b/src/core/PortalManager.hpp new file mode 100644 index 0000000..efec6ef --- /dev/null +++ b/src/core/PortalManager.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include "../portals/Screencopy.hpp" + +class CPortalManager { + public: + void init(); + + void onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version); + + sdbus::IConnection* getConnection(); + + private: + std::unique_ptr m_pConnection; + + struct { + wl_display* display = nullptr; + } m_sWaylandConnection; + + struct { + std::unique_ptr screencopy; + } m_sPortals; +}; + +inline std::unique_ptr g_pPortalManager; \ No newline at end of file diff --git a/src/core/config.c b/src/core/config.c deleted file mode 100644 index 081a0ea..0000000 --- a/src/core/config.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "config.h" -#include "xdpw.h" -#include "logger.h" -#include "screencast_common.h" - -#include -#include -#include -#include -#include - -void print_config(enum LOGLEVEL loglevel, struct xdpw_config *config) { - logprint(loglevel, "config: outputname: %s", config->screencast_conf.output_name); - logprint(loglevel, "config: max_fps: %f", config->screencast_conf.max_fps); - logprint(loglevel, "config: exec_before: %s", config->screencast_conf.exec_before); - logprint(loglevel, "config: exec_after: %s", config->screencast_conf.exec_after); - logprint(loglevel, "config: chooser_cmd: %s", config->screencast_conf.chooser_cmd); - logprint(loglevel, "config: chooser_type: %s", chooser_type_str(config->screencast_conf.chooser_type)); - logprint(loglevel, "config: force_mod_linear: %d", config->screencast_conf.force_mod_linear); -} - -// NOTE: calling finish_config won't prepare the config to be read again from config file -// with init_config since to pointers and other values won't be reset to NULL, or 0 -void finish_config(struct xdpw_config *config) { - logprint(DEBUG, "config: destroying config"); - - // screencast - free(config->screencast_conf.output_name); - free(config->screencast_conf.exec_before); - free(config->screencast_conf.exec_after); - free(config->screencast_conf.chooser_cmd); -} - -static void parse_string(char **dest, const char* value) { - if (value == NULL || *value == '\0') { - logprint(TRACE, "config: skipping empty value in config file"); - return; - } - free(*dest); - *dest = strdup(value); -} - -static void parse_double(double *dest, const char* value) { - if (value == NULL || *value == '\0') { - logprint(TRACE, "config: skipping empty value in config file"); - return; - } - *dest = strtod(value, (char**)NULL); -} - -static void parse_bool(bool *dest, const char* value) { - if (value == NULL || *value == '\0') { - logprint(TRACE, "config: skipping empty value in config file"); - return; - } - if (strcmp(value, "1") == 0) { - *dest = true; - } else { - *dest = false; - } -} - -static int handle_ini_screencast(struct config_screencast *screencast_conf, const char *key, const char *value) { - if (strcmp(key, "output_name") == 0) { - parse_string(&screencast_conf->output_name, value); - } else if (strcmp(key, "max_fps") == 0) { - parse_double(&screencast_conf->max_fps, value); - } else if (strcmp(key, "exec_before") == 0) { - parse_string(&screencast_conf->exec_before, value); - } else if (strcmp(key, "exec_after") == 0) { - parse_string(&screencast_conf->exec_after, value); - } else if (strcmp(key, "chooser_cmd") == 0) { - parse_string(&screencast_conf->chooser_cmd, value); - } else if (strcmp(key, "chooser_type") == 0) { - char *chooser_type = NULL; - parse_string(&chooser_type, value); - screencast_conf->chooser_type = get_chooser_type(chooser_type); - free(chooser_type); - } else if (strcmp(key, "force_mod_linear") == 0) { - parse_bool(&screencast_conf->force_mod_linear, value); - } else { - logprint(TRACE, "config: skipping invalid key in config file"); - return 0; - } - return 1; -} - -static int handle_ini_config(void *data, const char* section, const char *key, const char *value) { - struct xdpw_config *config = (struct xdpw_config*)data; - logprint(TRACE, "config: parsing setction %s, key %s, value %s", section, key, value); - - if (strcmp(section, "screencast") == 0) { - return handle_ini_screencast(&config->screencast_conf, key, value); - } - - logprint(TRACE, "config: skipping invalid key in config file"); - return 0; -} - -static void default_config(struct xdpw_config *config) { - config->screencast_conf.max_fps = 0; - config->screencast_conf.chooser_type = XDPW_CHOOSER_DEFAULT; -} - -static bool file_exists(const char *path) { - return path && access(path, R_OK) != -1; -} - -static char *config_path(const char *prefix, const char *filename) { - if (!prefix || !prefix[0] || !filename || !filename[0]) { - return NULL; - } - - char *config_folder = "xdg-desktop-portal-hyprland"; - - size_t size = 3 + strlen(prefix) + strlen(config_folder) + strlen(filename); - char *path = calloc(size, sizeof(char)); - snprintf(path, size, "%s/%s/%s", prefix, config_folder, filename); - return path; -} - -static char *get_config_path(void) { - const char *home = getenv("HOME"); - char *config_home_fallback = NULL; - if (home != NULL && home[0] != '\0') { - size_t size_fallback = 1 + strlen(home) + strlen("/.config"); - config_home_fallback = calloc(size_fallback, sizeof(char)); - snprintf(config_home_fallback, size_fallback, "%s/.config", home); - } - - const char *config_home = getenv("XDG_CONFIG_HOME"); - if (config_home == NULL || config_home[0] == '\0') { - config_home = config_home_fallback; - } - - const char *prefix[2]; - prefix[0] = config_home; - prefix[1] = SYSCONFDIR "/xdg"; - - const char *xdg_current_desktop = getenv("XDG_CURRENT_DESKTOP"); - const char *config_fallback = "config"; - - char *config_list = NULL; - for (size_t i = 0; i < 2; i++) { - if (xdg_current_desktop) { - config_list = strdup(xdg_current_desktop); - char *config = strtok(config_list, ":"); - while (config) { - char *path = config_path(prefix[i], config); - if (!path) { - config = strtok(NULL, ":"); - continue; - } - logprint(TRACE, "config: trying config file %s", path); - if (file_exists(path)) { - free(config_list); - free(config_home_fallback); - return path; - } - free(path); - config = strtok(NULL, ":"); - } - free(config_list); - } - char *path = config_path(prefix[i], config_fallback); - if (!path) { - continue; - } - logprint(TRACE, "config: trying config file %s", path); - if (file_exists(path)) { - free(config_home_fallback); - return path; - } - free(path); - } - - free(config_home_fallback); - return NULL; -} - -void init_config(char ** const configfile, struct xdpw_config *config) { - if (*configfile == NULL) { - *configfile = get_config_path(); - } - - default_config(config); - if (*configfile == NULL) { - logprint(INFO, "config: no config file found, using the default config"); - return; - } - if (ini_parse(*configfile, handle_ini_config, config) < 0) { - logprint(ERROR, "config: unable to load config file %s", *configfile); - } -} diff --git a/src/core/logger.c b/src/core/logger.c deleted file mode 100644 index a966992..0000000 --- a/src/core/logger.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "logger.h" - -#include -#include -#include -#include - -static struct logger_properties logprops; - -void init_logger(FILE *dst, enum LOGLEVEL level) { - logprops.dst = dst; - logprops.level = level; -} - -enum LOGLEVEL get_loglevel(const char *level) { - if (strcmp(level, "QUIET") == 0) { - return QUIET; - } else if (strcmp(level, "ERROR") == 0) { - return ERROR; - } else if (strcmp(level, "WARN") == 0) { - return WARN; - } else if (strcmp(level, "INFO") == 0) { - return INFO; - } else if (strcmp(level, "DEBUG") == 0) { - return DEBUG; - } else if (strcmp(level, "TRACE") == 0) { - return TRACE; - } - - fprintf(stderr, "Could not understand log level %s\n", level); - exit(1); -} - -static const char *print_loglevel(enum LOGLEVEL loglevel) { - switch (loglevel) { - case QUIET: - return "QUIET"; - case ERROR: - return "ERROR"; - case WARN: - return "WARN"; - case INFO: - return "INFO"; - case DEBUG: - return "DEBUG"; - case TRACE: - return "TRACE"; - } - fprintf(stderr, "Could not find log level %d\n", loglevel); - abort(); -} - -void logprint(enum LOGLEVEL level, char *msg, ...) { - if (!logprops.dst) { - fprintf(stderr, "Logger has been called, but was not initialized\n"); - abort(); - } - - if (level > logprops.level || level == QUIET) { - return; - } - va_list args; - - char timestr[200]; - time_t t = time(NULL); - struct tm *tmp = localtime(&t); - - if (strftime(timestr, sizeof(timestr), "%Y/%m/%d %H:%M:%S", tmp) == 0) { - fprintf(stderr, "strftime returned 0"); - abort(); - } - - fprintf(logprops.dst, "%s", timestr); - fprintf(logprops.dst, " "); - fprintf(logprops.dst, "[%s]", print_loglevel(level)); - fprintf(logprops.dst, " - "); - - va_start(args, msg); - vfprintf(logprops.dst, msg, args); - va_end(args); - - - fprintf(logprops.dst, "\n"); - fflush(logprops.dst); -} diff --git a/src/core/main.c b/src/core/main.c deleted file mode 100644 index 541f0c9..0000000 --- a/src/core/main.c +++ /dev/null @@ -1,291 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logger.h" -#include "xdpw.h" - -enum event_loop_fd { - EVENT_LOOP_DBUS, - EVENT_LOOP_WAYLAND, - EVENT_LOOP_PIPEWIRE, - EVENT_LOOP_TIMER, -}; - -static const char service_name[] = "org.freedesktop.impl.portal.desktop.hyprland"; - -static int xdpw_usage(FILE *stream, int rc) { - static const char *usage = - "Usage: xdg-desktop-portal-hyprland [options]\n" - "\n" - " -l, --loglevel= Select log level (default is ERROR).\n" - " QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n" - " -c, --config= Select config file.\n" - " (default is $XDG_CONFIG_HOME/xdg-desktop-portal-hyprland/config)\n" - " -r, --replace Replace a running instance.\n" - " -h, --help Get help (this text).\n" - "\n"; - - fprintf(stream, "%s", usage); - return rc; -} - -static int handle_name_lost(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { - logprint(INFO, "dbus: lost name, closing connection"); - sd_bus_close(sd_bus_message_get_bus(m)); - return 1; -} - -int main(int argc, char *argv[]) { - struct xdpw_config config = {0}; - char *configfile = NULL; - enum LOGLEVEL loglevel = DEFAULT_LOGLEVEL; - bool replace = false; - - static const char *shortopts = "l:o:c:f:rh"; - static const struct option longopts[] = {{"loglevel", required_argument, NULL, 'l'}, - {"config", required_argument, NULL, 'c'}, - {"replace", no_argument, NULL, 'r'}, - {"help", no_argument, NULL, 'h'}, - {NULL, 0, NULL, 0}}; - - while (1) { - int c = getopt_long(argc, argv, shortopts, longopts, NULL); - if (c < 0) { - break; - } - - switch (c) { - case 'l': - loglevel = get_loglevel(optarg); - break; - case 'c': - configfile = strdup(optarg); - break; - case 'r': - replace = true; - break; - case 'h': - return xdpw_usage(stdout, EXIT_SUCCESS); - default: - return xdpw_usage(stderr, EXIT_FAILURE); - } - } - - init_logger(stderr, loglevel); - init_config(&configfile, &config); - print_config(DEBUG, &config); - - int ret = 0; - - sd_bus *bus = NULL; - ret = sd_bus_open_user(&bus); - if (ret < 0) { - logprint(ERROR, "dbus: failed to connect to user bus: %s", strerror(-ret)); - return EXIT_FAILURE; - } - logprint(DEBUG, "dbus: connected"); - - struct wl_display *wl_display = wl_display_connect(NULL); - if (!wl_display) { - logprint(ERROR, "wayland: failed to connect to display"); - sd_bus_unref(bus); - return EXIT_FAILURE; - } - logprint(DEBUG, "wlroots: wl_display connected"); - - pw_init(NULL, NULL); - struct pw_loop *pw_loop = pw_loop_new(NULL); - if (!pw_loop) { - logprint(ERROR, "pipewire: failed to create loop"); - wl_display_disconnect(wl_display); - sd_bus_unref(bus); - return EXIT_FAILURE; - } - logprint(DEBUG, "pipewire: pw_loop created"); - - struct xdpw_state state = { - .bus = bus, - .wl_display = wl_display, - .pw_loop = pw_loop, - .screencast_source_types = MONITOR, - .screencast_cursor_modes = HIDDEN | EMBEDDED, - .screencast_version = XDP_CAST_PROTO_VER, - .screenshot_version = XDP_SHOT_PROTO_VER, - .config = &config, - }; - - wl_list_init(&state.xdpw_sessions); - wl_list_init(&state.restore_tokens); - - initShortcutsInstance(&state, &state.shortcutsInstance); - - ret = xdpw_screenshot_init(&state); - if (ret < 0) { - logprint(ERROR, "xdpw: failed to initialize screenshot"); - goto error; - } - - ret = xdpw_screencast_init(&state); - if (ret < 0) { - logprint(ERROR, "xdpw: failed to initialize screencast"); - goto error; - } - - uint64_t flags = SD_BUS_NAME_ALLOW_REPLACEMENT; - if (replace) { - flags |= SD_BUS_NAME_REPLACE_EXISTING; - } - - ret = sd_bus_request_name(bus, service_name, flags); - if (ret < 0) { - logprint(ERROR, "dbus: failed to acquire service name: %s", strerror(-ret)); - goto error; - } - - const char *unique_name; - ret = sd_bus_get_unique_name(bus, &unique_name); - if (ret < 0) { - logprint(ERROR, "dbus: failed to get unique bus name: %s", strerror(-ret)); - goto error; - } - - static char match[1024]; - snprintf(match, sizeof(match), - "sender='org.freedesktop.DBus'," - "type='signal'," - "interface='org.freedesktop.DBus'," - "member='NameOwnerChanged'," - "path='/org/freedesktop/DBus'," - "arg0='%s'," - "arg1='%s'", - service_name, unique_name); - - sd_bus_slot *slot; - ret = sd_bus_add_match(bus, &slot, match, handle_name_lost, NULL); - if (ret < 0) { - logprint(ERROR, "dbus: failed to add NameOwnerChanged signal match: %s", strerror(-ret)); - goto error; - } - - wl_list_init(&state.timers); - - struct pollfd pollfds[] = {[EVENT_LOOP_DBUS] = - { - .fd = sd_bus_get_fd(state.bus), - .events = POLLIN, - }, - [EVENT_LOOP_WAYLAND] = - { - .fd = wl_display_get_fd(state.wl_display), - .events = POLLIN, - }, - [EVENT_LOOP_PIPEWIRE] = - { - .fd = pw_loop_get_fd(state.pw_loop), - .events = POLLIN, - }, - [EVENT_LOOP_TIMER] = { - .fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC), - .events = POLLIN, - }}; - - state.timer_poll_fd = pollfds[EVENT_LOOP_TIMER].fd; - - while (1) { - ret = poll(pollfds, sizeof(pollfds) / sizeof(pollfds[0]), -1); - if (ret < 0) { - logprint(ERROR, "poll failed: %s", strerror(errno)); - goto error; - } - - if (pollfds[EVENT_LOOP_DBUS].revents & POLLHUP) { - logprint(INFO, "event-loop: disconnected from dbus"); - break; - } - if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLHUP) { - logprint(INFO, "event-loop: disconnected from wayland"); - break; - } - if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLHUP) { - logprint(INFO, "event-loop: disconnected from pipewire"); - break; - } - - if (pollfds[EVENT_LOOP_DBUS].revents & POLLIN) { - logprint(TRACE, "event-loop: got dbus event"); - do { - ret = sd_bus_process(state.bus, NULL); - } while (ret > 0); - if (ret < 0) { - logprint(ERROR, "sd_bus_process failed: %s", strerror(-ret)); - goto error; - } - } - - if (pollfds[EVENT_LOOP_WAYLAND].revents & POLLIN) { - logprint(TRACE, "event-loop: got wayland event"); - ret = wl_display_dispatch(state.wl_display); - if (ret < 0) { - logprint(ERROR, "wl_display_dispatch failed: %s", strerror(errno)); - goto error; - } - } - - if (pollfds[EVENT_LOOP_PIPEWIRE].revents & POLLIN) { - logprint(TRACE, "event-loop: got pipewire event"); - ret = pw_loop_iterate(state.pw_loop, 0); - if (ret < 0) { - logprint(ERROR, "pw_loop_iterate failed: %s", spa_strerror(ret)); - goto error; - } - } - - if (pollfds[EVENT_LOOP_TIMER].revents & POLLIN) { - logprint(TRACE, "event-loop: got a timer event"); - - int timer_fd = pollfds[EVENT_LOOP_TIMER].fd; - uint64_t expirations; - ssize_t n = read(timer_fd, &expirations, sizeof(expirations)); - if (n < 0) { - logprint(ERROR, "failed to read from timer FD\n"); - goto error; - } - - struct xdpw_timer *timer = state.next_timer; - if (timer != NULL) { - xdpw_event_loop_timer_func_t func = timer->func; - void *user_data = timer->user_data; - xdpw_destroy_timer(timer); - - func(user_data); - } - } - - do { - ret = wl_display_dispatch_pending(state.wl_display); - wl_display_flush(state.wl_display); - } while (ret > 0); - - sd_bus_flush(state.bus); - } - - // TODO: cleanup - finish_config(&config); - free(configfile); - - return EXIT_SUCCESS; - -error: - sd_bus_unref(bus); - pw_loop_leave(state.pw_loop); - pw_loop_destroy(state.pw_loop); - wl_display_disconnect(state.wl_display); - return EXIT_FAILURE; -} diff --git a/src/core/request.c b/src/core/request.c deleted file mode 100644 index 064e9d9..0000000 --- a/src/core/request.c +++ /dev/null @@ -1,60 +0,0 @@ -#include -#include -#include -#include -#include "xdpw.h" -#include "logger.h" - -static const char interface_name[] = "org.freedesktop.impl.portal.Request"; - -static int method_close(sd_bus_message *msg, void *data, - sd_bus_error *ret_error) { - struct xdpw_request *req = data; - int ret = 0; - logprint(INFO, "dbus: request closed"); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - - xdpw_request_destroy(req); - - return 0; -} - -static const sd_bus_vtable request_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", method_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -struct xdpw_request *xdpw_request_create(sd_bus *bus, const char *object_path) { - struct xdpw_request *req = calloc(1, sizeof(struct xdpw_request)); - - if (sd_bus_add_object_vtable(bus, &req->slot, object_path, interface_name, - request_vtable, NULL) < 0) { - free(req); - logprint(ERROR, "dbus: sd_bus_add_object_vtable failed: %s", - strerror(-errno)); - return NULL; - } - - return req; -} - -void xdpw_request_destroy(struct xdpw_request *req) { - if (req == NULL) { - return; - } - sd_bus_slot_unref(req->slot); - free(req); -} diff --git a/src/core/session.c b/src/core/session.c deleted file mode 100644 index 7fd3a80..0000000 --- a/src/core/session.c +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#include -#include -#include "xdpw.h" -#include "screencast.h" -#include "logger.h" - -static const char interface_name[] = "org.freedesktop.impl.portal.Session"; - -static int method_close(sd_bus_message *msg, void *data, - sd_bus_error *ret_error) { - int ret = 0; - struct xdpw_session *sess = data; - logprint(INFO, "dbus: session closed"); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - - xdpw_session_destroy(sess); - - return 0; -} - -static const sd_bus_vtable session_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Close", "", "", method_close, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_VTABLE_END -}; - -struct xdpw_session *xdpw_session_create(struct xdpw_state *state, sd_bus *bus, char *object_path) { - struct xdpw_session *sess = calloc(1, sizeof(struct xdpw_session)); - - sess->session_handle = object_path; - - if (sd_bus_add_object_vtable(bus, &sess->slot, object_path, interface_name, - session_vtable, sess) < 0) { - free(sess); - logprint(ERROR, "dbus: sd_bus_add_object_vtable failed: %s", - strerror(-errno)); - return NULL; - } - - wl_list_insert(&state->xdpw_sessions, &sess->link); - return sess; -} - -void xdpw_session_destroy(struct xdpw_session *sess) { - logprint(DEBUG, "dbus: destroying session %p", sess); - if (!sess) { - return; - } - struct xdpw_screencast_instance *cast = sess->screencast_instance; - if (cast) { - assert(cast->refcount > 0); - --cast->refcount; - logprint(DEBUG, "xdpw: screencast instance %p now has %d references", - cast, cast->refcount); - if (cast->refcount < 1) { - if (cast->frame_state == XDPW_FRAME_STATE_NONE) { - logprint(TRACE, "xdpw: screencast instance not streaming, destroy it"); - xdpw_screencast_instance_destroy(cast); - } else if (cast->teardown) { - logprint(TRACE, "xdpw: screencast instance marked for teardown, destroy it"); - xdpw_screencast_instance_destroy(cast); - } else { - logprint(TRACE, "xdpw: screencast instance still streaming, set quit flag"); - cast->quit = true; - } - } - } - - sd_bus_slot_unref(sess->slot); - wl_list_remove(&sess->link); - free(sess->session_handle); - free(sess); -} diff --git a/src/core/timer.c b/src/core/timer.c deleted file mode 100644 index 4d21d29..0000000 --- a/src/core/timer.c +++ /dev/null @@ -1,69 +0,0 @@ -#include -#include -#include - -#include "xdpw.h" -#include "logger.h" -#include "timespec_util.h" - -static void update_timer(struct xdpw_state *state) { - int timer_fd = state->timer_poll_fd; - if (timer_fd < 0) { - return; - } - - bool updated = false; - struct xdpw_timer *timer; - wl_list_for_each(timer, &state->timers, link) { - if (state->next_timer == NULL || - timespec_less(&timer->at, &state->next_timer->at)) { - state->next_timer = timer; - updated = true; - } - } - - if (updated) { - struct itimerspec delay = { .it_value = state->next_timer->at }; - errno = 0; - int ret = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &delay, NULL); - if (ret < 0) { - fprintf(stderr, "failed to timerfd_settime(): %s\n", - strerror(errno)); - } - } -} - -struct xdpw_timer *xdpw_add_timer(struct xdpw_state *state, - uint64_t delay_ns, xdpw_event_loop_timer_func_t func, void *data) { - struct xdpw_timer *timer = calloc(1, sizeof(struct xdpw_timer)); - if (timer == NULL) { - logprint(ERROR, "Timer allocation failed"); - return NULL; - } - timer->state = state; - timer->func = func; - timer->user_data = data; - wl_list_insert(&state->timers, &timer->link); - - clock_gettime(CLOCK_MONOTONIC, &timer->at); - timespec_add(&timer->at, delay_ns); - - update_timer(state); - return timer; -} - -void xdpw_destroy_timer(struct xdpw_timer *timer) { - if (timer == NULL) { - return; - } - struct xdpw_state *state = timer->state; - - if (state->next_timer == timer) { - state->next_timer = NULL; - } - - wl_list_remove(&timer->link); - free(timer); - - update_timer(state); -} diff --git a/src/core/timespec_util.c b/src/core/timespec_util.c deleted file mode 100644 index b00c428..0000000 --- a/src/core/timespec_util.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "timespec_util.h" -#include - -void timespec_add(struct timespec *t, int64_t delta_ns) { - int delta_ns_low = delta_ns % TIMESPEC_NSEC_PER_SEC; - int delta_s_high = delta_ns / TIMESPEC_NSEC_PER_SEC; - - t->tv_sec += delta_s_high; - - t->tv_nsec += (long)delta_ns_low; - if (t->tv_nsec >= TIMESPEC_NSEC_PER_SEC) { - t->tv_nsec -= TIMESPEC_NSEC_PER_SEC; - ++t->tv_sec; - } -} - -bool timespec_less(struct timespec *t1, struct timespec *t2) { - if (t1->tv_sec != t2->tv_sec) { - return t1->tv_sec < t2->tv_sec; - } - return t1->tv_nsec < t2->tv_nsec; -} - -bool timespec_is_zero(struct timespec *t) { - return t->tv_sec == 0 && t->tv_nsec == 0; -} - -int64_t timespec_diff_ns(struct timespec *t1, struct timespec *t2) { - int64_t s = t1->tv_sec - t2->tv_sec; - int64_t ns = t1->tv_nsec - t2->tv_nsec; - - return s * TIMESPEC_NSEC_PER_SEC + ns; -} diff --git a/src/core/utils.c b/src/core/utils.c deleted file mode 100644 index 1add9ad..0000000 --- a/src/core/utils.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include "utils.h" - -char *getFormat(const char *fmt, ...) { - char *outputStr = NULL; - - va_list args; - va_start(args, fmt); - vasprintf(&outputStr, fmt, args); - va_end(args); - - return outputStr; -} diff --git a/src/globalshortcuts/global_shortcuts.c b/src/globalshortcuts/global_shortcuts.c deleted file mode 100644 index 7a55f68..0000000 --- a/src/globalshortcuts/global_shortcuts.c +++ /dev/null @@ -1,537 +0,0 @@ -#include "include/global_shortcuts.h" - -#include - -#include "include/xdpw.h" - -static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { - struct globalShortcutsInstance *instance = data; - - if (!strcmp(interface, hyprland_global_shortcuts_manager_v1_interface.name) && instance->manager == NULL) { - uint32_t version = ver; - - logprint(DEBUG, "hyprland: |-- registered to interface %s (Version %u)", interface, version); - - instance->manager = wl_registry_bind(reg, id, &hyprland_global_shortcuts_manager_v1_interface, version); - } -} - -static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) { - ; // ignored -} - -static const struct wl_registry_listener wlr_registry_listener = { - .global = wlr_registry_handle_add, - .global_remove = wlr_registry_handle_remove, -}; - -static const char object_path[] = "/org/freedesktop/portal/desktop"; -static const char interface_name[] = "org.freedesktop.impl.portal.GlobalShortcuts"; - -static int method_gs_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error); -static int method_gs_bind_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error); -static int method_gs_list_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error); - -static const sd_bus_vtable gs_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", method_gs_create_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("BindShortcuts", "ooa(sa{sv})sa{sv}", "ua{sv}", method_gs_bind_shortcuts, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("ListShortcuts", "oo", "ua{sv}", method_gs_list_shortcuts, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_SIGNAL("Activated", "osta{sv}", 0), - SD_BUS_SIGNAL("Deactivated", "osta{sv}", 0), - SD_BUS_SIGNAL("ShortcutsChanged", "oa(sa{sv})", 0), - SD_BUS_VTABLE_END}; - -static void handleActivated(void *data, struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, - uint32_t tv_nsec) { - struct xdpw_state *state = data; - - bool found = false; - - struct globalShortcut *curr; - struct globalShortcutsClient *currc; - wl_list_for_each(currc, &state->shortcutsInstance.shortcutClients, link) { - wl_list_for_each(curr, &currc->shortcuts, link) { - if (curr->hlShortcut == hyprland_global_shortcut_v1) { - found = true; - goto found; - } - } - } -found: - - if (!found) return; - - sd_bus_emit_signal(state->bus, object_path, interface_name, "Activated", "osta{sv}", currc->session->session_handle, curr->name, - ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo), 0); -} - -static void handleDeactivated(void *data, struct hyprland_global_shortcut_v1 *hyprland_global_shortcut_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, - uint32_t tv_nsec) { - struct xdpw_state *state = data; - - bool found = false; - - struct globalShortcut *curr; - struct globalShortcutsClient *currc; - wl_list_for_each(currc, &state->shortcutsInstance.shortcutClients, link) { - wl_list_for_each(curr, &currc->shortcuts, link) { - if (curr->hlShortcut == hyprland_global_shortcut_v1) { - found = true; - goto found; - } - } - } -found: - - if (!found) return; - - sd_bus_emit_signal(state->bus, object_path, interface_name, "Deactivated", "osta{sv}", currc->session->session_handle, curr->name, - ((uint64_t)tv_sec_hi << 32) | (uint64_t)(tv_sec_lo), 0); -} - -static const struct hyprland_global_shortcut_v1_listener shortcutListener = { - .pressed = handleActivated, - .released = handleDeactivated, -}; - -static int method_gs_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - struct globalShortcutsClient *client = calloc(1, sizeof(struct globalShortcutsClient)); - wl_list_insert(&state->shortcutsInstance.shortcutClients, &client->link); - wl_list_init(&client->shortcuts); - - int ret = 0; - - logprint(INFO, "dbus: create session method invoked"); - - char *request_handle, *session_handle, *app_id; - ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - logprint(INFO, "dbus: app_id: %s", app_id); - - char *key; - char *option_token; - int innerRet = 0; - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - - if (strcmp(key, "session_handle_token") == 0) { - sd_bus_message_read(msg, "v", "s", &option_token); - logprint(INFO, "dbus: option token: %s", option_token); - } else if (strcmp(key, "shortcuts") == 0) { - // init shortcuts - client->sentShortcuts = true; - - innerRet = sd_bus_message_enter_container(msg, 'v', "a(sa{sv})"); - if (innerRet < 0) { - return innerRet; - } - - innerRet = sd_bus_message_enter_container(msg, 'a', "(sa{sv})"); - - while (innerRet > 0) { - char type; - char *container; - sd_bus_message_peek_type(msg, &type, &container); - - if (type != 'r') break; - - innerRet = sd_bus_message_enter_container(msg, 'r', "sa{sv}"); - if (innerRet == -ENXIO) break; - - sd_bus_message_peek_type(msg, &type, &container); - - innerRet = sd_bus_message_read(msg, "s", &key); - - if (innerRet == -ENXIO) break; - - if (innerRet < 0) { - return innerRet; - } - - logprint(DEBUG, "shortcut name %s", key); - - struct globalShortcut *shortcut = calloc(1, sizeof(struct globalShortcut)); - shortcut->name = malloc(strlen(key) + 1); - strcpy(shortcut->name, key); - - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - - if (strcmp(key, "description") == 0) { - sd_bus_message_read(msg, "v", "s", &key); - shortcut->description = malloc(strlen(key) + 1); - strcpy(shortcut->description, key); - } else { - sd_bus_message_skip(msg, "v"); - } - - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - - sd_bus_message_exit_container(msg); - - if (shortcut->description == NULL) { - shortcut->description = calloc(1, 1); - } - - wl_list_insert(&client->shortcuts, &shortcut->link); - - sd_bus_message_exit_container(msg); - } - - innerRet = sd_bus_message_exit_container(msg); - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - - } else { - logprint(WARN, "dbus: unknown option: %s", key); - sd_bus_message_skip(msg, "v"); - } - - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - struct xdpw_request *req = xdpw_request_create(sd_bus_message_get_bus(msg), request_handle); - if (req == NULL) { - return -ENOMEM; - } - - struct xdpw_session *sess = xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle)); - if (sess == NULL) { - return -ENOMEM; - } - - client->session = sess; - - sess->app_id = malloc(strlen(app_id) + 1); - strcpy(sess->app_id, app_id); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - sd_bus_message_open_container(reply, 'e', "sv"); - sd_bus_message_append(reply, "s", "shortcuts"); - sd_bus_message_open_container(reply, 'v', "a(sa{sv})"); - sd_bus_message_open_container(reply, 'a', "(sa{sv})"); - struct globalShortcut *curr; - wl_list_for_each(curr, &client->shortcuts, link) { - sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description); - curr->hlShortcut = hyprland_global_shortcuts_manager_v1_register_shortcut( - state->shortcutsInstance.manager, curr->name, (strlen(app_id) == 0 ? option_token : app_id), curr->description, ""); - hyprland_global_shortcut_v1_add_listener(curr->hlShortcut, &shortcutListener, state); - curr->bound = true; - } - - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - return 0; -} - -static int method_gs_bind_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - - int ret = 0; - - logprint(INFO, "dbus: bind shortcuts invoked"); - - char *request_handle, *session_handle; - ret = sd_bus_message_read(msg, "oo", &request_handle, &session_handle); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - - struct xdpw_session *session, *tmp_session; - wl_list_for_each_reverse_safe(session, tmp_session, &state->xdpw_sessions, link) { - if (strcmp(session->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: bind shortcuts: found matching session %s", session->session_handle); - break; - } - } - - struct globalShortcutsClient *client, *tmp_client; - wl_list_for_each_reverse_safe(client, tmp_client, &state->shortcutsInstance.shortcutClients, link) { - if (strcmp(client->session, session_handle) == 0) { - logprint(DEBUG, "dbus: bind shortcuts: found matching client %s", client->session); - break; - } - } - - if (!client->sentShortcuts) { - char *key; - int innerRet = 0; - client->sentShortcuts = true; - - innerRet = sd_bus_message_enter_container(msg, 'a', "(sa{sv})"); - - while (innerRet > 0) { - char type; - char *container; - sd_bus_message_peek_type(msg, &type, &container); - - if (type != 'r') break; - - innerRet = sd_bus_message_enter_container(msg, 'r', "sa{sv}"); - if (innerRet == -ENXIO) break; - - sd_bus_message_peek_type(msg, &type, &container); - - innerRet = sd_bus_message_read(msg, "s", &key); - - if (innerRet == -ENXIO) break; - - if (innerRet < 0) { - return innerRet; - } - - logprint(DEBUG, "shortcut name %s", key); - - struct globalShortcut *shortcut = calloc(1, sizeof(struct globalShortcut)); - shortcut->name = malloc(strlen(key) + 1); - strcpy(shortcut->name, key); - - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - - if (strcmp(key, "description") == 0) { - sd_bus_message_read(msg, "v", "s", &key); - shortcut->description = malloc(strlen(key) + 1); - strcpy(shortcut->description, key); - } else { - sd_bus_message_skip(msg, "v"); - } - - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - - sd_bus_message_exit_container(msg); - - if (shortcut->description == NULL) { - shortcut->description = calloc(1, 1); - } - - wl_list_insert(&client->shortcuts, &shortcut->link); - - sd_bus_message_exit_container(msg); - } - - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - char *parent_window; - ret = sd_bus_message_read(msg, "s", &parent_window); - - logprint(DEBUG, "dbus: parent_window %s", parent_window); - - client->parent_window = malloc(strlen(parent_window) + 1); - strcpy(client->parent_window, parent_window); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - sd_bus_message_open_container(reply, 'e', "sv"); - sd_bus_message_append(reply, "s", "shortcuts"); - sd_bus_message_open_container(reply, 'v', "a(sa{sv})"); - sd_bus_message_open_container(reply, 'a', "(sa{sv})"); - struct globalShortcut *curr; - wl_list_for_each(curr, &client->shortcuts, link) { - sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description); - if (!curr->bound) { - curr->hlShortcut = hyprland_global_shortcuts_manager_v1_register_shortcut(state->shortcutsInstance.manager, curr->name, parent_window, - curr->description, ""); - hyprland_global_shortcut_v1_add_listener(curr->hlShortcut, &shortcutListener, state); - } - } - - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - return 0; -} - -static int method_gs_list_shortcuts(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - - int ret = 0; - - logprint(INFO, "dbus: list shortcuts invoked"); - - char *request_handle, *session_handle; - ret = sd_bus_message_read(msg, "oo", &request_handle, &session_handle); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - - bool foundClient = false; - struct globalShortcutsClient *client, *tmp_client; - wl_list_for_each(client, &state->shortcutsInstance.shortcutClients, link) { - if (strcmp(client->session, session_handle) == 0) { - logprint(DEBUG, "dbus: bind shortcuts: found matching client %s", client->session); - foundClient = true; - break; - } - } - - if (!foundClient || !client->sentShortcuts) return 0; - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_append(reply, "u", PORTAL_RESPONSE_SUCCESS, 0); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_open_container(reply, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - sd_bus_message_open_container(reply, 'e', "sv"); - sd_bus_message_append(reply, "s", "shortcuts"); - sd_bus_message_open_container(reply, 'v', "a(sa{sv})"); - sd_bus_message_open_container(reply, 'a', "(sa{sv})"); - struct globalShortcut *curr; - wl_list_for_each(curr, &client->shortcuts, link) { - sd_bus_message_append(reply, "(sa{sv})", curr->name, 1, "description", "s", curr->description); - } - - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - ret = sd_bus_message_close_container(reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - return 0; -} - -void initShortcutsInstance(struct xdpw_state *state, struct globalShortcutsInstance *instance) { - instance->manager = NULL; - wl_list_init(&instance->shortcutClients); - - struct wl_registry *registry = wl_display_get_registry(state->wl_display); - wl_registry_add_listener(registry, &wlr_registry_listener, instance); - - wl_display_roundtrip(state->wl_display); - - if (instance->manager == NULL) { - logprint(ERROR, "hyprland shortcut protocol unavailable!"); - return; - } - - sd_bus_slot *slot = NULL; - int ret = sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, gs_vtable, state); - - logprint(DEBUG, "dbus: gs: ret bind %d", ret); - - // register to hl -} \ No newline at end of file diff --git a/src/helpers/Log.cpp b/src/helpers/Log.cpp new file mode 100644 index 0000000..013db6c --- /dev/null +++ b/src/helpers/Log.cpp @@ -0,0 +1 @@ +#include "Log.hpp" diff --git a/src/helpers/Log.hpp b/src/helpers/Log.hpp new file mode 100644 index 0000000..b5694a9 --- /dev/null +++ b/src/helpers/Log.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +enum eLogLevel +{ + INFO = 0, + LOG, + WARN, + ERR, + CRIT +}; + +namespace Debug { + template + void log(eLogLevel level, const std::string& fmt, Args&&... args) { + std::cout << '['; + + switch (level) { + case INFO: std::cout << "INFO"; break; + case LOG: std::cout << "LOG"; break; + case WARN: std::cout << "WARN"; break; + case ERR: std::cout << "ERR"; break; + case CRIT: std::cout << "CRITICAL"; break; + } + + std::cout << "] "; + + std::cout << std::vformat(fmt, std::make_format_args(args...)) << "\n"; + } +}; \ No newline at end of file diff --git a/src/helpers/MiscFunctions.cpp b/src/helpers/MiscFunctions.cpp new file mode 100644 index 0000000..956a663 --- /dev/null +++ b/src/helpers/MiscFunctions.cpp @@ -0,0 +1,17 @@ +#include "MiscFunctions.hpp" +#include +#include "../helpers/Log.hpp" + +std::string execAndGet(const char* cmd) { + std::array buffer; + std::string result; + const std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + Debug::log(ERR, "execAndGet: failed in pipe"); + return ""; + } + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} \ No newline at end of file diff --git a/src/helpers/MiscFunctions.hpp b/src/helpers/MiscFunctions.hpp new file mode 100644 index 0000000..ccefb68 --- /dev/null +++ b/src/helpers/MiscFunctions.hpp @@ -0,0 +1,4 @@ +#pragma once +#include + +std::string execAndGet(const char* cmd); \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..14321ac --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,13 @@ +#include + +#include "helpers/Log.hpp" +#include "core/PortalManager.hpp" + +int main(int argc, char** argv, char** envp) { + Debug::log(LOG, "Initializing xdph..."); + + g_pPortalManager = std::make_unique(); + g_pPortalManager->init(); + + return 0; +} \ No newline at end of file diff --git a/src/portals/Screencopy.cpp b/src/portals/Screencopy.cpp new file mode 100644 index 0000000..c4ddde2 --- /dev/null +++ b/src/portals/Screencopy.cpp @@ -0,0 +1,230 @@ +#include "Screencopy.hpp" +#include "../core/PortalManager.hpp" +#include "../helpers/Log.hpp" +#include "../helpers/MiscFunctions.hpp" + +void onCloseRequest(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { + if (!sess || !sess->request) + return; + + auto r = call.createReply(); + r.send(); + + sess->request.release(); +} + +void onCloseSession(sdbus::MethodCall& call, CScreencopyPortal::SSession* sess) { + if (!sess || !sess->session) + return; + + auto r = call.createReply(); + r.send(); + + sess->session.release(); +} + +void CScreencopyPortal::onCreateSession(sdbus::MethodCall& call) { + sdbus::ObjectPath requestHandle, sessionHandle; + + call >> requestHandle; + call >> sessionHandle; + + std::string appID; + call >> appID; + + Debug::log(LOG, "[screencopy] New session:"); + Debug::log(LOG, "[screencopy] | {}", requestHandle.c_str()); + Debug::log(LOG, "[screencopy] | {}", sessionHandle.c_str()); + Debug::log(LOG, "[screencopy] | appid: {}", appID); + + const auto PSESSION = m_vSessions.emplace_back(std::make_unique(appID, requestHandle, sessionHandle)).get(); + + // create objects + PSESSION->request = sdbus::createObject(*g_pPortalManager->getConnection(), requestHandle); + PSESSION->session = sdbus::createObject(*g_pPortalManager->getConnection(), sessionHandle); + + PSESSION->request->registerMethod("org.freedesktop.impl.portal.Request", "Close", "", "", [&](sdbus::MethodCall c) { onCloseRequest(c, PSESSION); }); + PSESSION->session->registerMethod("org.freedesktop.impl.portal.Session", "Close", "", "", [&](sdbus::MethodCall c) { onCloseSession(c, PSESSION); }); + + PSESSION->request->finishRegistration(); + PSESSION->session->finishRegistration(); + + auto reply = call.createReply(); + reply << (uint32_t)0; + reply << std::unordered_map{}; + reply.send(); +} + +void CScreencopyPortal::onSelectSources(sdbus::MethodCall& call) { + sdbus::ObjectPath requestHandle, sessionHandle; + + call >> requestHandle; + call >> sessionHandle; + + std::string appID; + call >> appID; + + Debug::log(LOG, "[screencopy] SelectSources:"); + Debug::log(LOG, "[screencopy] | {}", requestHandle.c_str()); + Debug::log(LOG, "[screencopy] | {}", sessionHandle.c_str()); + Debug::log(LOG, "[screencopy] | appid: {}", appID); + + const auto PSESSION = getSession(sessionHandle); + + if (!PSESSION) { + Debug::log(ERR, "[screencopy] SelectSources: no session found??"); + auto reply = call.createErrorReply(sdbus::Error{"NOSESSION", "No session found"}); + reply << (uint32_t)1; + reply.send(); + return; + } + + std::unordered_map options; + + call >> options; + + struct { + bool exists = false; + std::string token, output; + uint32_t windowHandle; + bool withCursor; + uint64_t timeIssued; + } restoreData; + + for (auto& [key, val] : options) { + + if (key == "cursor_mode") { + PSESSION->cursorMode = val.get(); + Debug::log(LOG, "[screencopy] option cursor_mode to {}", PSESSION->cursorMode); + } else if (key == "restore_data") { + // suv + // v -> r(susbt) + std::string issuer; + uint32_t version; + auto suv = val.get>(); + issuer = suv.get<0>(); + version = suv.get<1>(); + + sdbus::Variant data = suv.get<2>(); + + if (issuer != "hyprland") { + Debug::log(LOG, "[screencopy] Restore token from {}, ignoring", issuer); + continue; + } + + Debug::log(LOG, "[screencopy] Restore token from {} ver {}", issuer, version); + + if (version != 2) { + Debug::log(LOG, "[screencopy] Restore token ver unsupported, skipping", issuer); + continue; + } + + auto susbt = data.get>(); + + restoreData.exists = true; + + restoreData.token = susbt.get<0>(); + restoreData.windowHandle = susbt.get<1>(); + restoreData.output = susbt.get<2>(); + restoreData.withCursor = susbt.get<3>(); + restoreData.timeIssued = susbt.get<4>(); + + Debug::log(LOG, "[screencopy] Restore token {} with data: {} {} {} {}", restoreData.token, restoreData.windowHandle, restoreData.output, restoreData.withCursor, + restoreData.timeIssued); + } else if (key == "persist_mode") { + PSESSION->persistMode = val.get(); + Debug::log(LOG, "[screencopy] option persist_mode to {}", PSESSION->persistMode); + } else { + Debug::log(LOG, "[screencopy] unused option {}", key); + } + } + + const auto SHAREDATA = promptForScreencopySelection(); + + Debug::log(LOG, "[screencopy] SHAREDATA returned selection {}", (int)SHAREDATA.type); + + auto reply = call.createReply(); + reply << (uint32_t)(SHAREDATA.type == TYPE_INVALID ? 1 : 0); + reply << std::unordered_map{}; + reply.send(); +} + +void CScreencopyPortal::onStart(sdbus::MethodCall& call) { + sdbus::ObjectPath requestHandle, sessionHandle; + + call >> requestHandle; + call >> sessionHandle; + + std::string appID, parentWindow; + call >> appID; + call >> parentWindow; + + Debug::log(LOG, "[screencopy] Start:"); + Debug::log(LOG, "[screencopy] | {}", requestHandle.c_str()); + Debug::log(LOG, "[screencopy] | {}", sessionHandle.c_str()); + Debug::log(LOG, "[screencopy] | appid: {}", appID); + Debug::log(LOG, "[screencopy] | parent_window: {}", parentWindow); + + const auto PSESSION = getSession(sessionHandle); + + if (!PSESSION) { + Debug::log(ERR, "[screencopy] Start: no session found??"); + auto reply = call.createErrorReply(sdbus::Error{"NOSESSION", "No session found"}); + reply << (uint32_t)1; + reply.send(); + return; + } + + // TODO: start cast + + auto reply = call.createReply(); + reply << (uint32_t)0; + + std::unordered_map options; + + if (PSESSION->persistMode != 0) { + // give them a token :) + sdbus::Struct structData{"todo", (uint32_t)PSESSION->selection.windowHandle, PSESSION->selection.output, + (bool)PSESSION->cursorMode, (uint64_t)time(NULL)}; + sdbus::Variant restoreData{structData}; + sdbus::Struct fullRestoreStruct{"hyprland", 2, restoreData}; + options["restore_data"] = sdbus::Variant{fullRestoreStruct}; + } + + uint32_t type = 0; + switch (PSESSION->selection.type) { + case TYPE_OUTPUT: type = 1 << MONITOR; break; + case TYPE_WINDOW: type = 1 << WINDOW; break; + case TYPE_GEOMETRY: + case TYPE_WORKSPACE: type = 1 << VIRTUAL; break; + } + options["source_type"] = type; + + reply << options; + + reply.send(); +} + +CScreencopyPortal::SSession* CScreencopyPortal::getSession(sdbus::ObjectPath& path) { + for (auto& s : m_vSessions) { + if (s->sessionHandle == path) + return s.get(); + } + + return nullptr; +} + +CScreencopyPortal::CScreencopyPortal(zwlr_screencopy_manager_v1* 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); }); + m_pObject->registerMethod(INTERFACE_NAME, "SelectSources", "oosa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onSelectSources(c); }); + m_pObject->registerMethod(INTERFACE_NAME, "Start", "oossa{sv}", "ua{sv}", [&](sdbus::MethodCall c) { onStart(c); }); + m_pObject->registerProperty(INTERFACE_NAME, "AvailableSourceTypes", "u", [](sdbus::PropertyGetReply& reply) -> void { reply << (uint32_t)(VIRTUAL | MONITOR | WINDOW); }); + m_pObject->registerProperty(INTERFACE_NAME, "AvailableCursorModes", "u", [](sdbus::PropertyGetReply& reply) -> void { reply << (uint32_t)(HIDDEN | EMBEDDED); }); + m_pObject->registerProperty(INTERFACE_NAME, "version", "u", [](sdbus::PropertyGetReply& reply) -> void { reply << (uint32_t)(3); }); + + m_pObject->finishRegistration(); + + Debug::log(LOG, "[screencopy] init successful"); +} \ No newline at end of file diff --git a/src/portals/Screencopy.hpp b/src/portals/Screencopy.hpp new file mode 100644 index 0000000..c01dcce --- /dev/null +++ b/src/portals/Screencopy.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include "../shared/ScreencopyShared.hpp" + +enum cursorModes +{ + HIDDEN = 1, + EMBEDDED = 2, + METADATA = 4, +}; + +enum sourceTypes +{ + MONITOR = 1, + WINDOW = 2, + VIRTUAL = 4, +}; + +class CScreencopyPortal { + public: + CScreencopyPortal(zwlr_screencopy_manager_v1*); + + void onCreateSession(sdbus::MethodCall& call); + void onSelectSources(sdbus::MethodCall& call); + void onStart(sdbus::MethodCall& call); + + struct SSession { + std::string appid; + sdbus::ObjectPath requestHandle, sessionHandle; + uint32_t cursorMode = HIDDEN; + uint32_t persistMode = 0; + + std::unique_ptr request, session; + SSelectionData selection; + + void onCloseRequest(sdbus::MethodCall&); + void onCloseSession(sdbus::MethodCall&); + }; + + private: + std::unique_ptr m_pObject; + + std::vector> m_vSessions; + + SSession* getSession(sdbus::ObjectPath& path); + + const std::string INTERFACE_NAME = "org.freedesktop.impl.portal.ScreenCast"; + const std::string OBJECT_PATH = "/org/freedesktop/portal/desktop"; +}; \ No newline at end of file diff --git a/src/screencast/fps_limit.c b/src/screencast/fps_limit.c deleted file mode 100644 index f34c874..0000000 --- a/src/screencast/fps_limit.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "fps_limit.h" -#include "logger.h" -#include "timespec_util.h" -#include -#include -#include -#include - -#define FPS_MEASURE_PERIOD_SEC 5.0 - -void measure_fps(struct fps_limit_state *state, struct timespec *now); - -void fps_limit_measure_start(struct fps_limit_state *state, double max_fps) { - if (max_fps <= 0.0) { - return; - } - - clock_gettime(CLOCK_MONOTONIC, &state->frame_last_time); -} - -uint64_t fps_limit_measure_end(struct fps_limit_state *state, double max_fps) { - if (max_fps <= 0.0) { - return 0; - } - - // `fps_limit_measure_start` was not called? - assert(!timespec_is_zero(&state->frame_last_time)); - - struct timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - int64_t elapsed_ns = timespec_diff_ns(&now, &state->frame_last_time); - - measure_fps(state, &now); - - int64_t target_ns = (1.0 / max_fps) * TIMESPEC_NSEC_PER_SEC; - int64_t delay_ns = target_ns - elapsed_ns; - if (delay_ns > 0) { - logprint(TRACE, "fps_limit: elapsed time since the last measurement: %u, " - "target %u, should delay for %u (ns)", elapsed_ns, target_ns, delay_ns); - return delay_ns; - } else { - logprint(TRACE, "fps_limit: elapsed time since the last measurement: %u, " - "target %u, target not met (ns)", elapsed_ns, target_ns); - return 0; - } -} - -void measure_fps(struct fps_limit_state *state, struct timespec *now) { - if (timespec_is_zero(&state->fps_last_time)) { - state->fps_last_time = *now; - return; - } - - state->fps_frame_count++; - - int64_t elapsed_ns = timespec_diff_ns(now, &state->fps_last_time); - - double elapsed_sec = (double) elapsed_ns / (double) TIMESPEC_NSEC_PER_SEC; - if (elapsed_sec < FPS_MEASURE_PERIOD_SEC) { - return; - } - - double avg_frames_per_sec = state->fps_frame_count / elapsed_sec; - - logprint(DEBUG, "fps_limit: average FPS in the last %0.2f seconds: %0.2f", - elapsed_sec, avg_frames_per_sec); - - state->fps_last_time = *now; - state->fps_frame_count = 0; -} diff --git a/src/screencast/pipewire_screencast.c b/src/screencast/pipewire_screencast.c deleted file mode 100644 index 11a6f01..0000000 --- a/src/screencast/pipewire_screencast.c +++ /dev/null @@ -1,634 +0,0 @@ -#include "pipewire_screencast.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logger.h" -#include "wlr_screencast.h" -#include "xdpw.h" - -static struct spa_pod *build_buffer(struct spa_pod_builder *b, uint32_t blocks, uint32_t size, - uint32_t stride, uint32_t datatype) { - assert(blocks > 0); - assert(datatype > 0); - struct spa_pod_frame f[1]; - - spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers); - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_buffers, - SPA_POD_CHOICE_RANGE_Int(XDPW_PWR_BUFFERS, XDPW_PWR_BUFFERS_MIN, 32), 0); - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), 0); - if (size > 0) { - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), 0); - } - if (stride > 0) { - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), 0); - } - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_align, SPA_POD_Int(XDPW_PWR_ALIGN), 0); - spa_pod_builder_add(b, SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(datatype), 0); - return spa_pod_builder_pop(b, &f[0]); -} - -static struct spa_pod *fixate_format(struct spa_pod_builder *b, enum spa_video_format format, - uint32_t width, uint32_t height, uint32_t framerate, uint64_t *modifier) -{ - struct spa_pod_frame f[1]; - - enum spa_video_format format_without_alpha = xdpw_format_pw_strip_alpha(format); - - spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); - spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); - /* format */ - if (modifier || format_without_alpha == SPA_VIDEO_FORMAT_UNKNOWN) { - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - } else { - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, - SPA_POD_CHOICE_ENUM_Id(3, format, format, format_without_alpha), 0); - } - /* modifiers */ - if (modifier) { - // implicit modifier - spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY); - spa_pod_builder_long(b, *modifier); - } - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_size, - SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)), - 0); - // variable framerate - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_framerate, - SPA_POD_Fraction(&SPA_FRACTION(0, 1)), 0); - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_maxFramerate, - SPA_POD_CHOICE_RANGE_Fraction( - &SPA_FRACTION(framerate, 1), - &SPA_FRACTION(1, 1), - &SPA_FRACTION(framerate, 1)), - 0); - return spa_pod_builder_pop(b, &f[0]); -} - -static struct spa_pod *build_format(struct spa_pod_builder *b, enum spa_video_format format, - uint32_t width, uint32_t height, uint32_t framerate, - uint64_t *modifiers, int modifier_count) { - struct spa_pod_frame f[2]; - int i, c; - - enum spa_video_format format_without_alpha = xdpw_format_pw_strip_alpha(format); - - spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); - spa_pod_builder_add(b, SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video), 0); - spa_pod_builder_add(b, SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0); - /* format */ - if (modifier_count > 0 || format_without_alpha == SPA_VIDEO_FORMAT_UNKNOWN) { - // modifiers are defined only in combinations with their format - // we should not announce the format without alpha - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0); - } else { - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_format, - SPA_POD_CHOICE_ENUM_Id(3, format, format, format_without_alpha), 0); - } - /* modifiers */ - if (modifier_count > 0) { - // build an enumeration of modifiers - spa_pod_builder_prop(b, SPA_FORMAT_VIDEO_modifier, SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE); - spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_Enum, 0); - // modifiers from the array - for (i = 0, c = 0; i < modifier_count; i++) { - spa_pod_builder_long(b, modifiers[i]); - if (c++ == 0) - spa_pod_builder_long(b, modifiers[i]); - } - spa_pod_builder_pop(b, &f[1]); - } - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_size, - SPA_POD_Rectangle(&SPA_RECTANGLE(width, height)), - 0); - // variable framerate - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_framerate, - SPA_POD_Fraction(&SPA_FRACTION(0, 1)), 0); - spa_pod_builder_add(b, SPA_FORMAT_VIDEO_maxFramerate, - SPA_POD_CHOICE_RANGE_Fraction( - &SPA_FRACTION(framerate, 1), - &SPA_FRACTION(1, 1), - &SPA_FRACTION(framerate, 1)), - 0); - return spa_pod_builder_pop(b, &f[0]); -} - -static bool build_modifierlist(struct xdpw_screencast_instance *cast, - uint32_t drm_format, uint64_t **modifiers, uint32_t *modifier_count) { - if (!wlr_query_dmabuf_modifiers(cast->ctx, drm_format, 0, NULL, modifier_count)) { - *modifiers = NULL; - *modifier_count = 0; - return false; - } - if (*modifier_count == 0) { - logprint(INFO, "wlroots: no modifiers available for format %u", drm_format); - *modifiers = NULL; - return true; - } - *modifiers = calloc(*modifier_count, sizeof(uint64_t)); - bool ret = wlr_query_dmabuf_modifiers(cast->ctx, drm_format, *modifier_count, *modifiers, modifier_count); - logprint(INFO, "wlroots: num_modififiers %d", *modifier_count); - return ret; -} - -static uint32_t build_formats(struct spa_pod_builder *b[static 2], struct xdpw_screencast_instance *cast, - const struct spa_pod *params[static 2]) { - uint32_t param_count; - uint32_t modifier_count; - uint64_t *modifiers = NULL; - - if (!cast->avoid_dmabufs && - build_modifierlist(cast, cast->screencopy_frame_info[DMABUF].format, &modifiers, &modifier_count) && modifier_count > 0) { - param_count = 2; - params[0] = build_format(b[0], xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[DMABUF].format), - cast->screencopy_frame_info[DMABUF].width, cast->screencopy_frame_info[DMABUF].height, cast->framerate, - modifiers, modifier_count); - assert(params[0] != NULL); - params[1] = build_format(b[1], xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[WL_SHM].format), - cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, cast->framerate, - NULL, 0); - assert(params[1] != NULL); - } else { - param_count = 1; - params[0] = build_format(b[0], xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[WL_SHM].format), - cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, cast->framerate, - NULL, 0); - assert(params[0] != NULL); - } - free(modifiers); - return param_count; -} - -static void pwr_handle_stream_state_changed(void *data, - enum pw_stream_state old, enum pw_stream_state state, const char *error) { - struct xdpw_screencast_instance *cast = data; - cast->node_id = pw_stream_get_node_id(cast->stream); - - logprint(INFO, "pipewire: stream state changed to \"%s\"", - pw_stream_state_as_string(state)); - logprint(INFO, "pipewire: node id is %d", (int)cast->node_id); - - switch (state) { - case PW_STREAM_STATE_STREAMING: - cast->pwr_stream_state = true; - if (cast->frame_state == XDPW_FRAME_STATE_NONE) { - xdpw_wlr_frame_start(cast); - } - break; - case PW_STREAM_STATE_PAUSED: - if (old == PW_STREAM_STATE_STREAMING) { - xdpw_pwr_enqueue_buffer(cast); - } - // fall through - default: - cast->pwr_stream_state = false; - break; - } -} - -static void pwr_handle_stream_param_changed(void *data, uint32_t id, - const struct spa_pod *param) { - logprint(TRACE, "pipewire: stream parameters changed"); - struct xdpw_screencast_instance *cast = data; - struct pw_stream *stream = cast->stream; - uint8_t params_buffer[3][1024]; - struct spa_pod_dynamic_builder b[3]; - const struct spa_pod *params[4]; - uint32_t blocks; - uint32_t data_type; - - if (!param || id != SPA_PARAM_Format) { - return; - } - - spa_pod_dynamic_builder_init(&b[0], params_buffer[0], sizeof(params_buffer[0]), 2048); - spa_pod_dynamic_builder_init(&b[1], params_buffer[1], sizeof(params_buffer[1]), 2048); - spa_pod_dynamic_builder_init(&b[2], params_buffer[2], sizeof(params_buffer[2]), 2048); - - spa_format_video_raw_parse(param, &cast->pwr_format); - cast->framerate = (uint32_t)(cast->pwr_format.max_framerate.num / cast->pwr_format.max_framerate.denom); - - const struct spa_pod_prop *prop_modifier; - if ((prop_modifier = spa_pod_find_prop(param, NULL, SPA_FORMAT_VIDEO_modifier)) != NULL) { - cast->buffer_type = DMABUF; - data_type = 1<pwr_format.format == xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[DMABUF].format)); - if ((prop_modifier->flags & SPA_POD_PROP_FLAG_DONT_FIXATE) > 0) { - const struct spa_pod *pod_modifier = &prop_modifier->value; - - uint32_t n_modifiers = SPA_POD_CHOICE_N_VALUES(pod_modifier) - 1; - uint64_t *modifiers = SPA_POD_CHOICE_VALUES(pod_modifier); - modifiers++; - uint32_t flags = GBM_BO_USE_RENDERING; - uint64_t modifier; - uint32_t n_params; - struct spa_pod_builder *builder[2] = {&b[0].b, &b[1].b}; - - struct gbm_bo *bo = gbm_bo_create_with_modifiers2(cast->ctx->gbm, - cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height, - cast->screencopy_frame_info[cast->buffer_type].format, modifiers, n_modifiers, flags); - if (bo) { - modifier = gbm_bo_get_modifier(bo); - gbm_bo_destroy(bo); - goto fixate_format; - } - - logprint(INFO, "pipewire: unable to allocate a dmabuf with modifiers. Falling back to the old api"); - for (uint32_t i = 0; i < n_modifiers; i++) { - switch (modifiers[i]) { - case DRM_FORMAT_MOD_INVALID: - flags = cast->ctx->state->config->screencast_conf.force_mod_linear ? - GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR : GBM_BO_USE_RENDERING; - break; - case DRM_FORMAT_MOD_LINEAR: - flags = GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR; - break; - default: - continue; - } - bo = gbm_bo_create(cast->ctx->gbm, - cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height, - cast->screencopy_frame_info[cast->buffer_type].format, flags); - if (bo) { - modifier = gbm_bo_get_modifier(bo); - gbm_bo_destroy(bo); - goto fixate_format; - } - } - - logprint(WARN, "pipewire: unable to allocate a dmabuf. Falling back to shm"); - cast->avoid_dmabufs = true; - - n_params = build_formats(builder, cast, ¶ms[0]); - pw_stream_update_params(stream, params, n_params); - spa_pod_dynamic_builder_clean(&b[0]); - spa_pod_dynamic_builder_clean(&b[1]); - spa_pod_dynamic_builder_clean(&b[2]); - return; - -fixate_format: - params[0] = fixate_format(&b[2].b, xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format), - cast->screencopy_frame_info[cast->buffer_type].width, cast->screencopy_frame_info[cast->buffer_type].height, cast->framerate, &modifier); - - n_params = build_formats(builder, cast, ¶ms[1]); - n_params++; - - pw_stream_update_params(stream, params, n_params); - spa_pod_dynamic_builder_clean(&b[0]); - spa_pod_dynamic_builder_clean(&b[1]); - spa_pod_dynamic_builder_clean(&b[2]); - return; - } - - if (cast->pwr_format.modifier == DRM_FORMAT_MOD_INVALID) { - blocks = 1; - } else { - blocks = gbm_device_get_format_modifier_plane_count(cast->ctx->gbm, - cast->screencopy_frame_info[DMABUF].format, cast->pwr_format.modifier); - } - } else { - cast->buffer_type = WL_SHM; - blocks = 1; - data_type = 1<buffer_type, data_type); - logprint(DEBUG, "pipewire: format: %u", cast->pwr_format.format); - logprint(DEBUG, "pipewire: modifier: %lu", cast->pwr_format.modifier); - logprint(DEBUG, "pipewire: size: (%u, %u)", cast->pwr_format.size.width, cast->pwr_format.size.height); - logprint(DEBUG, "pipewire: max_framerate: (%u / %u)", cast->pwr_format.max_framerate.num, cast->pwr_format.max_framerate.denom); - - params[0] = build_buffer(&b[0].b, blocks, cast->screencopy_frame_info[cast->buffer_type].size, - cast->screencopy_frame_info[cast->buffer_type].stride, data_type); - - params[1] = spa_pod_builder_add_object(&b[1].b, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_Header), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_header))); - - params[2] = spa_pod_builder_add_object(&b[1].b, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoTransform), - SPA_PARAM_META_size, SPA_POD_Int(sizeof(struct spa_meta_videotransform))); - - params[3] = spa_pod_builder_add_object(&b[2].b, - SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, - SPA_PARAM_META_type, SPA_POD_Id(SPA_META_VideoDamage), - SPA_PARAM_META_size, SPA_POD_CHOICE_RANGE_Int( - sizeof(struct spa_meta_region) * 4, - sizeof(struct spa_meta_region) * 1, - sizeof(struct spa_meta_region) * 4)); - - pw_stream_update_params(stream, params, 3); - spa_pod_dynamic_builder_clean(&b[0]); - spa_pod_dynamic_builder_clean(&b[1]); - spa_pod_dynamic_builder_clean(&b[2]); -} - -static void pwr_handle_stream_add_buffer(void *data, struct pw_buffer *buffer) { - struct xdpw_screencast_instance *cast = data; - struct spa_data *d; - enum spa_data_type t; - - logprint(DEBUG, "pipewire: add buffer event handle"); - - d = buffer->buffer->datas; - - // Select buffer type from negotiation result - if ((d[0].type & (1u << SPA_DATA_MemFd)) > 0) { - assert(cast->buffer_type == WL_SHM); - t = SPA_DATA_MemFd; - } else if ((d[0].type & (1u << SPA_DATA_DmaBuf)) > 0) { - assert(cast->buffer_type == DMABUF); - t = SPA_DATA_DmaBuf; - } else { - logprint(ERROR, "pipewire: unsupported buffer type"); - cast->err = 1; - return; - } - - logprint(TRACE, "pipewire: selected buffertype %u", t); - - struct xdpw_buffer *xdpw_buffer = xdpw_buffer_create(cast, cast->buffer_type, &cast->screencopy_frame_info[cast->buffer_type]); - if (xdpw_buffer == NULL) { - logprint(ERROR, "pipewire: failed to create xdpw buffer"); - cast->err = 1; - return; - } - wl_list_insert(&cast->buffer_list, &xdpw_buffer->link); - buffer->user_data = xdpw_buffer; - - assert(xdpw_buffer->plane_count >= 0 && buffer->buffer->n_datas == (uint32_t)xdpw_buffer->plane_count); - for (uint32_t plane = 0; plane < buffer->buffer->n_datas; plane++) { - d[plane].type = t; - d[plane].maxsize = xdpw_buffer->size[plane]; - d[plane].mapoffset = 0; - d[plane].chunk->size = xdpw_buffer->size[plane]; - d[plane].chunk->stride = xdpw_buffer->stride[plane]; - d[plane].chunk->offset = xdpw_buffer->offset[plane]; - d[plane].flags = 0; - d[plane].fd = xdpw_buffer->fd[plane]; - d[plane].data = NULL; - // clients have implemented to check chunk->size if the buffer is valid instead - // of using the flags. Until they are patched we should use some arbitrary value. - if (xdpw_buffer->buffer_type == DMABUF && d[plane].chunk->size == 0) { - d[plane].chunk->size = 9; // This was choosen by a fair d20. - } - } -} - -static void pwr_handle_stream_remove_buffer(void *data, struct pw_buffer *buffer) { - struct xdpw_screencast_instance *cast = data; - - logprint(DEBUG, "pipewire: remove buffer event handle"); - - struct xdpw_buffer *xdpw_buffer = buffer->user_data; - if (xdpw_buffer) { - xdpw_buffer_destroy(xdpw_buffer); - } - if (cast->current_frame.pw_buffer == buffer) { - cast->current_frame.pw_buffer = NULL; - } - for (uint32_t plane = 0; plane < buffer->buffer->n_datas; plane++) { - buffer->buffer->datas[plane].fd = -1; - } - buffer->user_data = NULL; -} - -static const struct pw_stream_events pwr_stream_events = { - PW_VERSION_STREAM_EVENTS, - .state_changed = pwr_handle_stream_state_changed, - .param_changed = pwr_handle_stream_param_changed, - .add_buffer = pwr_handle_stream_add_buffer, - .remove_buffer = pwr_handle_stream_remove_buffer, -}; - -void xdpw_pwr_dequeue_buffer(struct xdpw_screencast_instance *cast) { - logprint(TRACE, "pipewire: dequeueing buffer"); - - assert(!cast->current_frame.pw_buffer); - if ((cast->current_frame.pw_buffer = pw_stream_dequeue_buffer(cast->stream)) == NULL) { - logprint(WARN, "pipewire: out of buffers"); - return; - } - - cast->current_frame.xdpw_buffer = cast->current_frame.pw_buffer->user_data; -} - -void xdpw_pwr_enqueue_buffer(struct xdpw_screencast_instance *cast) { - logprint(TRACE, "pipewire: enqueueing buffer"); - - if (!cast->current_frame.pw_buffer) { - logprint(WARN, "pipewire: no buffer to queue"); - goto done; - } - struct pw_buffer *pw_buf = cast->current_frame.pw_buffer; - struct spa_buffer *spa_buf = pw_buf->buffer; - struct spa_data *d = spa_buf->datas; - - bool buffer_corrupt = cast->frame_state != XDPW_FRAME_STATE_SUCCESS; - - if (cast->current_frame.y_invert) { - //TODO: Flip buffer or set stride negative - buffer_corrupt = true; - cast->err = 1; - } - - logprint(TRACE, "********************"); - struct spa_meta_header *h; - if ((h = spa_buffer_find_meta_data(spa_buf, SPA_META_Header, sizeof(*h)))) { - h->pts = SPA_TIMESPEC_TO_NSEC(&cast->current_frame); - h->flags = buffer_corrupt ? SPA_META_HEADER_FLAG_CORRUPTED : 0; - h->seq = cast->seq++; - h->dts_offset = 0; - logprint(TRACE, "pipewire: timestamp %"PRId64, h->pts); - } - - struct spa_meta_videotransform *vt; - if ((vt = spa_buffer_find_meta_data(spa_buf, SPA_META_VideoTransform, sizeof(*vt)))) { - vt->transform = cast->target.output ? cast->target.output->transform : 0; - logprint(TRACE, "pipewire: transform %u", vt->transform); - } - - struct spa_meta *damage; - if ((damage = spa_buffer_find_meta(spa_buf, SPA_META_VideoDamage))) { - struct spa_region *d_region = spa_meta_first(damage); - uint32_t damage_counter = 0; - do { - if (damage_counter >= cast->current_frame.damage_count) { - *d_region = SPA_REGION(0, 0, 0, 0); - logprint(TRACE, "pipewire: end damage %u %u,%u (%ux%u)", damage_counter, - d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); - break; - } - *d_region = SPA_REGION(cast->current_frame.damage[damage_counter].x, - cast->current_frame.damage[damage_counter].y, - cast->current_frame.damage[damage_counter].width, - cast->current_frame.damage[damage_counter].height); - logprint(TRACE, "pipewire: damage %u %u,%u (%ux%u)", damage_counter, - d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); - damage_counter++; - } while (spa_meta_check(d_region + 1, damage) && d_region++); - - if (damage_counter < cast->current_frame.damage_count) { - struct xdpw_frame_damage damage = - {d_region->position.x, d_region->position.x, d_region->size.width, d_region->size.height}; - for (; damage_counter < cast->current_frame.damage_count; damage_counter++) { - damage = merge_damage(&damage, &cast->current_frame.damage[damage_counter]); - } - *d_region = SPA_REGION(damage.x, damage.y, damage.width, damage.height); - logprint(TRACE, "pipewire: collected damage %u %u,%u (%ux%u)", damage_counter, - d_region->position.x, d_region->position.y, d_region->size.width, d_region->size.height); - } - } - - if (buffer_corrupt) { - for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) { - d[plane].chunk->flags = SPA_CHUNK_FLAG_CORRUPTED; - } - } else { - for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) { - d[plane].chunk->flags = SPA_CHUNK_FLAG_NONE; - } - } - - for (uint32_t plane = 0; plane < spa_buf->n_datas; plane++) { - logprint(TRACE, "pipewire: plane %d", plane); - logprint(TRACE, "pipewire: fd %u", d[plane].fd); - logprint(TRACE, "pipewire: maxsize %d", d[plane].maxsize); - logprint(TRACE, "pipewire: size %d", d[plane].chunk->size); - logprint(TRACE, "pipewire: stride %d", d[plane].chunk->stride); - logprint(TRACE, "pipewire: offset %d", d[plane].chunk->offset); - logprint(TRACE, "pipewire: chunk flags %d", d[plane].chunk->flags); - } - logprint(TRACE, "pipewire: width %d", cast->current_frame.xdpw_buffer->width); - logprint(TRACE, "pipewire: height %d", cast->current_frame.xdpw_buffer->height); - logprint(TRACE, "pipewire: y_invert %d", cast->current_frame.y_invert); - logprint(TRACE, "********************"); - - pw_stream_queue_buffer(cast->stream, pw_buf); - -done: - cast->current_frame.xdpw_buffer = NULL; - cast->current_frame.pw_buffer = NULL; -} - -void pwr_update_stream_param(struct xdpw_screencast_instance *cast) { - logprint(TRACE, "pipewire: stream update parameters"); - struct pw_stream *stream = cast->stream; - uint8_t params_buffer[2][1024]; - struct spa_pod_dynamic_builder b[2]; - spa_pod_dynamic_builder_init(&b[0], params_buffer[0], sizeof(params_buffer[0]), 2048); - spa_pod_dynamic_builder_init(&b[1], params_buffer[1], sizeof(params_buffer[1]), 2048); - const struct spa_pod *params[2]; - - struct spa_pod_builder *builder[2] = {&b[0].b, &b[1].b}; - uint32_t n_params = build_formats(builder, cast, params); - - pw_stream_update_params(stream, params, n_params); - spa_pod_dynamic_builder_clean(&b[0]); - spa_pod_dynamic_builder_clean(&b[1]); -} - -void xdpw_pwr_stream_create(struct xdpw_screencast_instance *cast) { - struct xdpw_screencast_context *ctx = cast->ctx; - struct xdpw_state *state = ctx->state; - - pw_loop_enter(state->pw_loop); - - uint8_t buffer[2][1024]; - struct spa_pod_dynamic_builder b[2]; - spa_pod_dynamic_builder_init(&b[0], buffer[0], sizeof(buffer[0]), 2048); - spa_pod_dynamic_builder_init(&b[1], buffer[1], sizeof(buffer[1]), 2048); - const struct spa_pod *params[2]; - - char name[] = "xdpw-stream-XXXXXX"; - randname(name + strlen(name) - 6); - cast->stream = pw_stream_new(ctx->core, name, - pw_properties_new( - PW_KEY_MEDIA_CLASS, "Video/Source", - NULL)); - - if (!cast->stream) { - logprint(ERROR, "pipewire: failed to create stream"); - abort(); - } - cast->pwr_stream_state = false; - - struct spa_pod_builder *builder[2] = {&b[0].b, &b[1].b}; - uint32_t param_count = build_formats(builder, cast, params); - spa_pod_dynamic_builder_clean(&b[0]); - spa_pod_dynamic_builder_clean(&b[1]); - - pw_stream_add_listener(cast->stream, &cast->stream_listener, - &pwr_stream_events, cast); - - pw_stream_connect(cast->stream, - PW_DIRECTION_OUTPUT, - PW_ID_ANY, - (PW_STREAM_FLAG_DRIVER | - PW_STREAM_FLAG_ALLOC_BUFFERS), - params, param_count); -} - -void xdpw_pwr_stream_destroy(struct xdpw_screencast_instance *cast) { - if (!cast->stream) { - return; - } - - logprint(DEBUG, "pipewire: destroying stream"); - pw_stream_flush(cast->stream, false); - pw_stream_disconnect(cast->stream); - pw_stream_destroy(cast->stream); - cast->stream = NULL; -} - -int xdpw_pwr_context_create(struct xdpw_state *state) { - struct xdpw_screencast_context *ctx = &state->screencast; - - logprint(DEBUG, "pipewire: establishing connection to core"); - - if (!ctx->pwr_context) { - ctx->pwr_context = pw_context_new(state->pw_loop, NULL, 0); - if (!ctx->pwr_context) { - logprint(ERROR, "pipewire: failed to create context"); - return -1; - } - } - - if (!ctx->core) { - ctx->core = pw_context_connect(ctx->pwr_context, NULL, 0); - if (!ctx->core) { - logprint(ERROR, "pipewire: couldn't connect to context"); - return -1; - } - } - return 0; -} - -void xdpw_pwr_context_destroy(struct xdpw_state *state) { - struct xdpw_screencast_context *ctx = &state->screencast; - - logprint(DEBUG, "pipewire: disconnecting fom core"); - - if (ctx->core) { - pw_core_disconnect(ctx->core); - ctx->core = NULL; - } - - if (ctx->pwr_context) { - pw_context_destroy(ctx->pwr_context); - ctx->pwr_context = NULL; - } -} diff --git a/src/screencast/screencast.c b/src/screencast/screencast.c deleted file mode 100644 index 89115e2..0000000 --- a/src/screencast/screencast.c +++ /dev/null @@ -1,731 +0,0 @@ -#include "screencast.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "logger.h" -#include "pipewire_screencast.h" -#include "wlr_screencast.h" -#include "xdpw.h" - -static const char object_path[] = "/org/freedesktop/portal/desktop"; -static const char interface_name[] = "org.freedesktop.impl.portal.ScreenCast"; - -void exec_with_shell(char *command) { - pid_t pid1 = fork(); - if (pid1 < 0) { - perror("fork"); - return; - } else if (pid1 == 0) { - pid_t pid2 = fork(); - if (pid2 < 0) { - perror("fork"); - } else if (pid2 == 0) { - char *const argv[] = { - "sh", - "-c", - command, - NULL, - }; - execvp("sh", argv); - perror("execvp"); - _exit(127); - } - _exit(0); - } - int stat; - if (waitpid(pid1, &stat, 0) < 0) { - perror("waitpid"); - } -} - -void xdpw_screencast_instance_init(struct xdpw_screencast_context *ctx, struct xdpw_screencast_instance *cast, struct xdpw_share out, - bool with_cursor) { - // only run exec_before if there's no other instance running that already ran it - if (wl_list_empty(&ctx->screencast_instances)) { - char *exec_before = ctx->state->config->screencast_conf.exec_before; - if (exec_before) { - logprint(INFO, "xdpw: executing %s before screencast", exec_before); - exec_with_shell(exec_before); - } - } - - cast->ctx = ctx; - cast->target = out; - if (out.output == NULL) { - cast->max_framerate = 60; // dirty - } else { - if (ctx->state->config->screencast_conf.max_fps > 0) { - cast->max_framerate = ctx->state->config->screencast_conf.max_fps < (uint32_t)out.output->framerate - ? ctx->state->config->screencast_conf.max_fps - : (uint32_t)out.output->framerate; - } else { - cast->max_framerate = (uint32_t)out.output->framerate; - } - } - - cast->framerate = cast->max_framerate; - cast->with_cursor = with_cursor; - cast->refcount = 1; - cast->node_id = SPA_ID_INVALID; - cast->avoid_dmabufs = false; - cast->teardown = false; - wl_list_init(&cast->buffer_list); - logprint(INFO, "xdpw: screencast instance %p has %d references", cast, cast->refcount); - wl_list_insert(&ctx->screencast_instances, &cast->link); - logprint(INFO, "xdpw: %d active screencast instances", wl_list_length(&ctx->screencast_instances)); -} - -void xdpw_screencast_instance_destroy(struct xdpw_screencast_instance *cast) { - assert(cast->refcount == 0); // Fails assert if called by screencast_finish - logprint(DEBUG, "xdpw: destroying cast instance"); - - // make sure this is the last running instance that is being destroyed - if (wl_list_length(&cast->link) == 1) { - char *exec_after = cast->ctx->state->config->screencast_conf.exec_after; - if (exec_after) { - logprint(INFO, "xdpw: executing %s after screencast", exec_after); - exec_with_shell(exec_after); - } - } - - wl_list_remove(&cast->link); - xdpw_pwr_stream_destroy(cast); - assert(wl_list_length(&cast->buffer_list) == 0); - free(cast); -} - -void xdpw_screencast_instance_teardown(struct xdpw_screencast_instance *cast) { - struct xdpw_session *sess, *tmp; - wl_list_for_each_safe(sess, tmp, &cast->ctx->state->xdpw_sessions, link) { - if (sess->screencast_instance == cast) { - xdpw_session_destroy(sess); - } - } -} - -bool setup_outputs(struct xdpw_screencast_context *ctx, struct xdpw_session *sess, bool with_cursor, struct xdph_restore_token *token) { - struct xdpw_wlr_output *output, *tmp_o; - wl_list_for_each_reverse_safe(output, tmp_o, &ctx->output_list, link) { - logprint(INFO, "wlroots: capturable output: %s model: %s: id: %i name: %s", output->make, output->model, output->id, output->name); - } - - struct xdpw_share out; - out.window_handle = -1; - out.x = -1; - out.y = -1; - out.w = -1; - out.h = -1; - out.output = NULL; - bool tokenSuccess = false; - - if (token) { - // attempt to restore - if (token->outputPort) { - if (strncmp(token->outputPort, "class:", 6) == 0) { - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (strcmp(token->outputPort + 6, current->clazz) == 0) { - out.window_handle = token->windowHandle; - tokenSuccess = true; - break; - } - } - } else { - struct xdpw_wlr_output *output; - wl_list_for_each(output, &ctx->output_list, link) { - if (strcmp(output->name, token->outputPort) == 0) { - out.output = output; - tokenSuccess = true; - break; - } - } - } - - } else if (token->windowHandle > 0) { - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (((uint64_t)current->handle & 0xFFFFFFFF) == token->windowHandle) { - out.window_handle = token->windowHandle; - tokenSuccess = true; - break; - } - } - } - } - - if (!tokenSuccess) out = xdpw_wlr_chooser(ctx); - - if (!out.output && out.window_handle == -1) { - logprint(ERROR, "wlroots: no output / window found"); - return false; - } - - // Disable screencast sharing to avoid sharing between dmabuf and shm capable clients - /* - struct xdpw_screencast_instance *cast, *tmp_c; - wl_list_for_each_reverse_safe(cast, tmp_c, &ctx->screencast_instances, link) { - logprint(INFO, "xdpw: existing screencast instance: %d %s cursor", - cast->target_output->id, - cast->with_cursor ? "with" : "without"); - - if (cast->target_output->id == out->id && cast->with_cursor == with_cursor) { - if (cast->refcount == 0) { - logprint(DEBUG, - "xdpw: matching cast instance found, " - "but is already scheduled for destruction, skipping"); - } - else { - sess->screencast_instance = cast; - ++cast->refcount; - } - logprint(INFO, "xdpw: screencast instance %p now has %d references", - cast, cast->refcount); - } - } - */ - - if (!sess->screencast_instance) { - sess->screencast_instance = calloc(1, sizeof(struct xdpw_screencast_instance)); - xdpw_screencast_instance_init(ctx, sess->screencast_instance, out, with_cursor); - } - if (out.output) { - logprint(INFO, "wlroots: output: %s", sess->screencast_instance->target.output->name); - } else { - logprint(INFO, "hyprland: window handle %d", sess->screencast_instance->target.window_handle); - } - - return true; -} - -static int start_screencast(struct xdpw_screencast_instance *cast) { - xdpw_wlr_register_cb(cast); - - // process at least one frame so that we know - // some of the metadata required for the pipewire - // remote state connected event - wl_display_dispatch(cast->ctx->state->wl_display); - wl_display_roundtrip(cast->ctx->state->wl_display); - - if (cast->screencopy_frame_info[WL_SHM].format == DRM_FORMAT_INVALID /*|| - (cast->ctx->state->screencast_version >= 3 && cast->screencopy_frame_info[DMABUF].format == DRM_FORMAT_INVALID)*/) { - logprint(INFO, "wlroots: unable to receive a valid format from wlr_screencopy"); - return -1; - } - - xdpw_pwr_stream_create(cast); - - cast->initialized = true; - return 0; -} - -static struct xdph_restore_token *findRestoreToken(char *token, struct xdpw_state *state) { - struct xdph_restore_token *current; - wl_list_for_each(current, &state->restore_tokens, link) { - if (strcmp(current->token, token) == 0) { - return current; - } - } - - return NULL; -} - -static struct xdph_restore_token *getRestoreToken(char *sessionToken, struct xdpw_state *state, char *outputSelected, uint64_t windowSelected, - bool withCursor) { - state->lastRestoreToken++; - uuid_t uuid; - uuid_generate_random(uuid); - char *uuid_str = malloc(37); - uuid_unparse_upper(uuid, uuid_str); - while (findRestoreToken(uuid_str, state) != NULL) { - uuid_generate_random(uuid); - uuid_unparse_upper(uuid, uuid_str); - } - - struct xdph_restore_token *restoreToken = calloc(1, sizeof(struct xdph_restore_token)); - if (outputSelected) { - restoreToken->outputPort = strdup(outputSelected); - } - restoreToken->windowHandle = windowSelected; - - restoreToken->token = uuid_str; - restoreToken->withCursor = withCursor; - - if (windowSelected) { - struct SToplevelEntry *current; - wl_list_for_each(current, &state->screencast.toplevel_resource_list, link) { - if (current->handle == restoreToken->windowHandle) { - restoreToken->windowClass = getFormat("class:%x", current->clazz); - break; - } - } - } - - return restoreToken; -} - -static int method_screencast_create_session(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - - int ret = 0; - - logprint(INFO, "dbus: create session method invoked"); - - char *request_handle, *session_handle, *app_id; - ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - logprint(INFO, "dbus: app_id: %s", app_id); - - char *key; - int innerRet = 0; - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - - if (strcmp(key, "session_handle_token") == 0) { - char *token; - sd_bus_message_read(msg, "v", "s", &token); - logprint(INFO, "dbus: option token: %s", token); - } else { - logprint(WARN, "dbus: unknown option: %s", key); - sd_bus_message_skip(msg, "v"); - } - - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - struct xdpw_request *req = xdpw_request_create(sd_bus_message_get_bus(msg), request_handle); - if (req == NULL) { - return -ENOMEM; - } - - struct xdpw_session *sess = xdpw_session_create(state, sd_bus_message_get_bus(msg), strdup(session_handle)); - if (sess == NULL) { - return -ENOMEM; - } - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0); - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - return 0; -} - -static int method_screencast_select_sources(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - struct xdpw_screencast_context *ctx = &state->screencast; - - int ret = 0; - struct xdpw_session *sess, *tmp_s; - sd_bus_message *reply = NULL; - - logprint(INFO, "dbus: select sources method invoked"); - - // default to embedded cursor mode if not specified - bool cursor_embedded = true; - - char *request_handle, *session_handle, *app_id; - ret = sd_bus_message_read(msg, "oos", &request_handle, &session_handle, &app_id); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - logprint(INFO, "dbus: app_id: %s", app_id); - - char *key; - int innerRet = 0; - uint32_t persist = 0; - - struct xdph_restore_token *foundToken = NULL; - - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - - if (strcmp(key, "multiple") == 0) { - int multiple; - sd_bus_message_read(msg, "v", "b", &multiple); - logprint(INFO, "dbus: option multiple: %d", multiple); - } else if (strcmp(key, "types") == 0) { - uint32_t mask; - sd_bus_message_read(msg, "v", "u", &mask); - if (mask & (1 << WINDOW)) { - logprint(INFO, "dbus: non-monitor cast requested, not replying"); - return -1; - } - logprint(INFO, "dbus: option types:%x", mask); - } else if (strcmp(key, "cursor_mode") == 0) { - uint32_t cursor_mode; - sd_bus_message_read(msg, "v", "u", &cursor_mode); - if (cursor_mode & HIDDEN) { - cursor_embedded = false; - } - if (cursor_mode & METADATA) { - logprint(ERROR, "dbus: unsupported cursor mode requested"); - goto error; - } - logprint(INFO, "dbus: option cursor_mode:%x", cursor_mode); - } else if (strcmp(key, "restore_token") == 0) { - char *restoreToken; - sd_bus_message_read(msg, "v", "s", &restoreToken); - - logprint(INFO, "dbus: restore_token %s", restoreToken); - - foundToken = findRestoreToken(restoreToken, state); - } else if (strcmp(key, "restore_data") == 0) { - logprint(INFO, "dbus: restore_data"); - innerRet = sd_bus_message_enter_container(msg, 'v', "(suv)"); - if (innerRet < 0) { - logprint(ERROR, "dbus: restore_data malformed container"); - return innerRet; - } - innerRet = sd_bus_message_enter_container(msg, 'r', "suv"); - if (innerRet < 0) { - logprint(ERROR, "dbus: error entering struct"); - return innerRet; - } - char *issuer; - sd_bus_message_read(msg, "s", &issuer); - if (strcmp(issuer, "hyprland") != 0) { - logprint(INFO, "dbus: skipping unknown issuer (%s)", issuer); - sd_bus_message_skip(msg, "uv"); - continue; - } - uint32_t ver; - sd_bus_message_read(msg, "u", &ver); - - if (ver == 1) { - char *restoreToken; - sd_bus_message_read(msg, "v", "s", &restoreToken); - - logprint(INFO, "dbus: restore_token %s", restoreToken); - - foundToken = findRestoreToken(restoreToken, state); - - if (foundToken) - logprint(INFO, "xdph: found token %s", restoreToken); - else - logprint(INFO, "xdph: token not found"); - } else if (ver == 2) { - innerRet = sd_bus_message_enter_container(msg, 'v', "(susbt)" /* amogus */); - innerRet = sd_bus_message_enter_container(msg, 'r', "susbt" /* amogus */); - if (innerRet < 0) { - logprint(ERROR, "dbus: error entering struct"); - return innerRet; - } - - char *token; - char *output; - uint32_t windowHandle; - bool withCursor; - uint64_t timeIssued; - - sd_bus_message_read(msg, "s", &token); - sd_bus_message_read(msg, "u", &windowHandle); - sd_bus_message_read(msg, "s", &output); - sd_bus_message_read(msg, "b", &withCursor); - sd_bus_message_read(msg, "t", &timeIssued); - - foundToken = findRestoreToken(token, state); - - if (!foundToken) { - foundToken = getRestoreToken(session_handle, state, strlen(output) == 0 ? NULL : output, windowHandle, withCursor); - } - - sd_bus_message_exit_container(msg); - sd_bus_message_exit_container(msg); - - } else { - logprint(INFO, "dbus: skipping unknown ver (%d)", ver); - sd_bus_message_skip(msg, "v"); - } - - sd_bus_message_exit_container(msg); - sd_bus_message_exit_container(msg); - } else if (strcmp(key, "persist_mode") == 0) { - sd_bus_message_read(msg, "v", "u", &persist); - logprint(INFO, "dbus: persist %d", persist); - } else { - logprint(WARN, "dbus: unknown option %s", key); - sd_bus_message_skip(msg, "v"); - } - - innerRet = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - } - if (ret < 0) { - return ret; - } - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - bool output_selection_canceled = 1; - wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { - if (strcmp(sess->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: select sources: found matching session %s", sess->session_handle); - - output_selection_canceled = !setup_outputs(ctx, sess, cursor_embedded, foundToken); - - sess->persist = persist; - - // foundToken has been used, if it existed - if (foundToken) { - wl_list_remove(&foundToken->link); - free(foundToken); - foundToken = NULL; - } - } - } - - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - if (output_selection_canceled) { - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0); - } else { - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 0); - } - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - sd_bus_message_unref(reply); - return 0; - -error: - wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { - if (strcmp(sess->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: select sources error: destroying matching session %s", sess->session_handle); - xdpw_session_destroy(sess); - } - } - - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_CANCELLED, 0); - if (ret < 0) { - return ret; - } - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - sd_bus_message_unref(reply); - return -1; -} - -static int method_screencast_start(sd_bus_message *msg, void *data, sd_bus_error *ret_error) { - struct xdpw_state *state = data; - - int ret = 0; - - logprint(INFO, "dbus: start method invoked"); - - char *request_handle, *session_handle, *app_id, *parent_window; - ret = sd_bus_message_read(msg, "ooss", &request_handle, &session_handle, &app_id, &parent_window); - if (ret < 0) { - return ret; - } - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - - logprint(INFO, "dbus: request_handle: %s", request_handle); - logprint(INFO, "dbus: session_handle: %s", session_handle); - logprint(INFO, "dbus: app_id: %s", app_id); - logprint(INFO, "dbus: parent_window: %s", parent_window); - - char *key; - int innerRet = 0; - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - innerRet = sd_bus_message_read(msg, "s", &key); - if (innerRet < 0) { - return innerRet; - } - logprint(WARN, "dbus: unknown option: %s", key); - sd_bus_message_skip(msg, "v"); - innerRet = sd_bus_message_exit_container(msg); - if (innerRet < 0) { - return innerRet; - } - } - if (ret < 0) { - return ret; - } - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - struct xdpw_screencast_instance *cast = NULL; - struct xdpw_session *sess, *tmp_s; - wl_list_for_each_reverse_safe(sess, tmp_s, &state->xdpw_sessions, link) { - if (strcmp(sess->session_handle, session_handle) == 0) { - logprint(DEBUG, "dbus: start: found matching session %s", sess->session_handle); - cast = sess->screencast_instance; - } - } - if (!cast) { - return -1; - } - - if (!cast->initialized) { - ret = start_screencast(cast); - } - if (ret < 0) { - return ret; - } - - while (cast->node_id == SPA_ID_INVALID) { - int ret = pw_loop_iterate(state->pw_loop, 0); - if (ret < 0) { - logprint(ERROR, "pipewire_loop_iterate failed: %s", spa_strerror(ret)); - return ret; - } - } - - // create token - struct xdph_restore_token *restoreToken = NULL; - if (sess->persist) { - restoreToken = getRestoreToken(session_handle, state, cast->target.output ? cast->target.output->name : NULL, - cast->target.window_handle < 1 ? 0 : cast->target.window_handle, cast->with_cursor); - - wl_list_insert(&state->restore_tokens, &restoreToken->link); - - logprint(INFO, "xdph: registered restoreToken with token %s", restoreToken->token); - } - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - - logprint(DEBUG, "dbus: start: returning node %d", (int)cast->node_id); - if (restoreToken) - ret = sd_bus_message_append( - reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 3, "streams", "a(ua{sv})", 1, cast->node_id, 3, "position", "(ii)", 0, 0, "size", "(ii)", - cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, "source_type", "u", - (cast->target.output ? (1 << MONITOR) : (1 << WINDOW)), "persist_mode", "u", sess->persist, "restore_data", "(suv)", "hyprland", 2, - "(susbt)" /* amogus */, restoreToken->token, restoreToken->windowHandle, - (restoreToken->outputPort == NULL ? (restoreToken->windowHandle ? restoreToken->windowClass : "") : restoreToken->outputPort), - restoreToken->withCursor, (unsigned long)time(NULL)); - else - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, "streams", "a(ua{sv})", 1, cast->node_id, 3, "position", "(ii)", 0, - 0, "size", "(ii)", cast->screencopy_frame_info[WL_SHM].width, cast->screencopy_frame_info[WL_SHM].height, - "source_type", "u", (cast->target.output ? (1 << MONITOR) : (1 << WINDOW))); - - if (ret < 0) { - return ret; - } - - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - sd_bus_message_unref(reply); - - return 0; -} - -static const sd_bus_vtable screencast_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("CreateSession", "oosa{sv}", "ua{sv}", method_screencast_create_session, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("SelectSources", "oosa{sv}", "ua{sv}", method_screencast_select_sources, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("Start", "oossa{sv}", "ua{sv}", method_screencast_start, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("AvailableSourceTypes", "u", NULL, offsetof(struct xdpw_state, screencast_source_types), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("AvailableCursorModes", "u", NULL, offsetof(struct xdpw_state, screencast_cursor_modes), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_PROPERTY("version", "u", NULL, offsetof(struct xdpw_state, screencast_version), SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END}; - -int xdpw_screencast_init(struct xdpw_state *state) { - sd_bus_slot *slot = NULL; - - state->screencast = (struct xdpw_screencast_context){0}; - state->screencast.state = state; - state->screencast.hyprland_toplevel_manager = NULL; - - int err; - err = xdpw_pwr_context_create(state); - if (err) { - goto fail_pipewire; - } - - err = xdpw_wlr_screencopy_init(state); - if (err) { - goto fail_screencopy; - } - - return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, screencast_vtable, state); - -fail_screencopy: - xdpw_wlr_screencopy_finish(&state->screencast); - -fail_pipewire: - xdpw_pwr_context_destroy(state); - - return err; -} diff --git a/src/screencast/screencast_common.c b/src/screencast/screencast_common.c deleted file mode 100644 index 696a225..0000000 --- a/src/screencast/screencast_common.c +++ /dev/null @@ -1,426 +0,0 @@ -#include "xdpw.h" -#include "screencast_common.h" -#include -#include -#include -#include -#include -#include -#include -#include "linux-dmabuf-unstable-v1-client-protocol.h" - -#include "logger.h" - -void randname(char *buf) { - struct timespec ts; - clock_gettime(CLOCK_REALTIME, &ts); - long r = ts.tv_nsec; - for (int i = 0; i < 6; ++i) { - assert(buf[i] == 'X'); - buf[i] = 'A'+(r&15)+(r&16)*2; - r >>= 5; - } -} - -static char *gbm_find_render_node(drmDevice *device) { - drmDevice *devices[64]; - char *render_node = NULL; - - int n = drmGetDevices2(0, devices, sizeof(devices) / sizeof(devices[0])); - for (int i = 0; i < n; ++i) { - drmDevice *dev = devices[i]; - if (device && !drmDevicesEqual(device, dev)) { - continue; - } - if (!(dev->available_nodes & (1 << DRM_NODE_RENDER))) - continue; - - render_node = strdup(dev->nodes[DRM_NODE_RENDER]); - break; - } - - drmFreeDevices(devices, n); - return render_node; -} - -struct gbm_device *xdpw_gbm_device_create(drmDevice *device) { - struct gbm_device *gbm; - char *render_node = NULL; - - render_node = gbm_find_render_node(device); - if (render_node == NULL) { - logprint(ERROR, "xdpw: Could not find render node"); - return NULL; - } - logprint(INFO, "xdpw: Using render node %s", render_node); - - int fd = open(render_node, O_RDWR | O_CLOEXEC); - if (fd < 0) { - logprint(ERROR, "xdpw: Could not open render node %s", render_node); - free(render_node); - return NULL; - } - - free(render_node); - gbm = gbm_create_device(fd); - return gbm; -} - -static int anonymous_shm_open(void) { - char name[] = "/xdpw-shm-XXXXXX"; - int retries = 100; - - do { - randname(name + strlen(name) - 6); - - --retries; - // shm_open guarantees that O_CLOEXEC is set - int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); - if (fd >= 0) { - shm_unlink(name); - return fd; - } - } while (retries > 0 && errno == EEXIST); - - return -1; -} - -static struct wl_buffer *import_wl_shm_buffer(struct xdpw_screencast_instance *cast, int fd, - enum wl_shm_format fmt, int width, int height, int stride) { - struct xdpw_screencast_context *ctx = cast->ctx; - int size = stride * height; - - if (fd < 0) { - return NULL; - } - - struct wl_shm_pool *pool = wl_shm_create_pool(ctx->shm, fd, size); - struct wl_buffer *buffer = - wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt); - wl_shm_pool_destroy(pool); - - return buffer; -} - -struct xdpw_buffer *xdpw_buffer_create(struct xdpw_screencast_instance *cast, - enum buffer_type buffer_type, struct xdpw_screencopy_frame_info *frame_info) { - struct xdpw_buffer *buffer = calloc(1, sizeof(struct xdpw_buffer)); - buffer->width = frame_info->width; - buffer->height = frame_info->height; - buffer->format = frame_info->format; - buffer->buffer_type = buffer_type; - - switch (buffer_type) { - case WL_SHM: - buffer->plane_count = 1; - buffer->size[0] = frame_info->size; - buffer->stride[0] = frame_info->stride; - buffer->offset[0] = 0; - buffer->fd[0] = anonymous_shm_open(); - if (buffer->fd[0] == -1) { - logprint(ERROR, "xdpw: unable to create anonymous filedescriptor"); - free(buffer); - return NULL; - } - - if (ftruncate(buffer->fd[0], buffer->size[0]) < 0) { - logprint(ERROR, "xdpw: unable to truncate filedescriptor"); - close(buffer->fd[0]); - free(buffer); - return NULL; - } - - buffer->buffer = import_wl_shm_buffer(cast, buffer->fd[0], xdpw_format_wl_shm_from_drm_fourcc(frame_info->format), - frame_info->width, frame_info->height, frame_info->stride); - if (buffer->buffer == NULL) { - logprint(ERROR, "xdpw: unable to create wl_buffer"); - close(buffer->fd[0]); - free(buffer); - return NULL; - } - break; - case DMABUF:; - uint32_t flags = GBM_BO_USE_RENDERING; - if (cast->pwr_format.modifier != DRM_FORMAT_MOD_INVALID) { - uint64_t *modifiers = (uint64_t*)&cast->pwr_format.modifier; - buffer->bo = gbm_bo_create_with_modifiers2(cast->ctx->gbm, frame_info->width, frame_info->height, - frame_info->format, modifiers, 1, flags); - } else { - if (cast->ctx->state->config->screencast_conf.force_mod_linear) { - flags |= GBM_BO_USE_LINEAR; - } - buffer->bo = gbm_bo_create(cast->ctx->gbm, frame_info->width, frame_info->height, - frame_info->format, flags); - } - - // Fallback for linear buffers via the implicit api - if (buffer->bo == NULL && cast->pwr_format.modifier == DRM_FORMAT_MOD_LINEAR) { - buffer->bo = gbm_bo_create(cast->ctx->gbm, frame_info->width, frame_info->height, - frame_info->format, flags | GBM_BO_USE_LINEAR); - } - - if (buffer->bo == NULL) { - logprint(ERROR, "xdpw: failed to create gbm_bo"); - free(buffer); - return NULL; - } - buffer->plane_count = gbm_bo_get_plane_count(buffer->bo); - - struct zwp_linux_buffer_params_v1 *params; - params = zwp_linux_dmabuf_v1_create_params(cast->ctx->linux_dmabuf); - if (!params) { - logprint(ERROR, "xdpw: failed to create linux_buffer_params"); - gbm_bo_destroy(buffer->bo); - free(buffer); - return NULL; - } - - for (int plane = 0; plane < buffer->plane_count; plane++) { - buffer->size[plane] = 0; - buffer->stride[plane] = gbm_bo_get_stride_for_plane(buffer->bo, plane); - buffer->offset[plane] = gbm_bo_get_offset(buffer->bo, plane); - uint64_t mod = gbm_bo_get_modifier(buffer->bo); - buffer->fd[plane] = gbm_bo_get_fd_for_plane(buffer->bo, plane); - - if (buffer->fd[plane] < 0) { - logprint(ERROR, "xdpw: failed to get file descriptor"); - zwp_linux_buffer_params_v1_destroy(params); - gbm_bo_destroy(buffer->bo); - for (int plane_tmp = 0; plane_tmp < plane; plane_tmp++) { - close(buffer->fd[plane_tmp]); - } - free(buffer); - return NULL; - } - - zwp_linux_buffer_params_v1_add(params, buffer->fd[plane], plane, - buffer->offset[plane], buffer->stride[plane], mod >> 32, mod & 0xffffffff); - } - buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params, - buffer->width, buffer->height, - buffer->format, /* flags */ 0); - zwp_linux_buffer_params_v1_destroy(params); - - if (!buffer->buffer) { - logprint(ERROR, "xdpw: failed to create buffer"); - gbm_bo_destroy(buffer->bo); - for (int plane = 0; plane < buffer->plane_count; plane++) { - close(buffer->fd[plane]); - } - free(buffer); - return NULL; - } - } - - return buffer; -} - -void xdpw_buffer_destroy(struct xdpw_buffer *buffer) { - wl_buffer_destroy(buffer->buffer); - if (buffer->buffer_type == DMABUF) { - gbm_bo_destroy(buffer->bo); - } - for (int plane = 0; plane < buffer->plane_count; plane++) { - close(buffer->fd[plane]); - } - wl_list_remove(&buffer->link); - free(buffer); -} - -bool wlr_query_dmabuf_modifiers(struct xdpw_screencast_context *ctx, uint32_t drm_format, - uint32_t num_modifiers, uint64_t *modifiers, uint32_t *max_modifiers) { - if (ctx->format_modifier_pairs.size == 0) - return false; - struct xdpw_format_modifier_pair *fm_pair; - if (num_modifiers == 0) { - *max_modifiers = 0; - wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) { - if (fm_pair->fourcc == drm_format && - (fm_pair->modifier == DRM_FORMAT_MOD_INVALID || - gbm_device_get_format_modifier_plane_count(ctx->gbm, fm_pair->fourcc, fm_pair->modifier) > 0)) - *max_modifiers += 1; - } - return true; - } - - uint32_t i = 0; - wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) { - if (i == num_modifiers) - break; - if (fm_pair->fourcc == drm_format && - (fm_pair->modifier == DRM_FORMAT_MOD_INVALID || - gbm_device_get_format_modifier_plane_count(ctx->gbm, fm_pair->fourcc, fm_pair->modifier) > 0)) { - modifiers[i] = fm_pair->modifier; - i++; - } - } - *max_modifiers = num_modifiers; - return true; -} - -enum wl_shm_format xdpw_format_wl_shm_from_drm_fourcc(uint32_t format) { - switch (format) { - case DRM_FORMAT_ARGB8888: - return WL_SHM_FORMAT_ARGB8888; - case DRM_FORMAT_XRGB8888: - return WL_SHM_FORMAT_XRGB8888; - case DRM_FORMAT_RGBA8888: - case DRM_FORMAT_RGBX8888: - case DRM_FORMAT_ABGR8888: - case DRM_FORMAT_XBGR8888: - case DRM_FORMAT_BGRA8888: - case DRM_FORMAT_BGRX8888: - case DRM_FORMAT_NV12: - case DRM_FORMAT_XRGB2101010: - case DRM_FORMAT_XBGR2101010: - case DRM_FORMAT_RGBX1010102: - case DRM_FORMAT_BGRX1010102: - case DRM_FORMAT_ARGB2101010: - case DRM_FORMAT_ABGR2101010: - case DRM_FORMAT_RGBA1010102: - case DRM_FORMAT_BGRA1010102: - return (enum wl_shm_format)format; - default: - logprint(ERROR, "xdg-desktop-portal-hyprland: unsupported drm " - "format 0x%08x", format); - abort(); - } -} - -uint32_t xdpw_format_drm_fourcc_from_wl_shm(enum wl_shm_format format) { - switch (format) { - case WL_SHM_FORMAT_ARGB8888: - return DRM_FORMAT_ARGB8888; - case WL_SHM_FORMAT_XRGB8888: - return DRM_FORMAT_XRGB8888; - case WL_SHM_FORMAT_RGBA8888: - case WL_SHM_FORMAT_RGBX8888: - case WL_SHM_FORMAT_ABGR8888: - case WL_SHM_FORMAT_XBGR8888: - case WL_SHM_FORMAT_BGRA8888: - case WL_SHM_FORMAT_BGRX8888: - case WL_SHM_FORMAT_NV12: - case WL_SHM_FORMAT_XRGB2101010: - case WL_SHM_FORMAT_XBGR2101010: - case WL_SHM_FORMAT_RGBX1010102: - case WL_SHM_FORMAT_BGRX1010102: - case WL_SHM_FORMAT_ARGB2101010: - case WL_SHM_FORMAT_ABGR2101010: - case WL_SHM_FORMAT_RGBA1010102: - case WL_SHM_FORMAT_BGRA1010102: - return (uint32_t)format; - default: - logprint(ERROR, "xdg-desktop-portal-hyprland: unsupported wl_shm " - "format 0x%08x", format); - abort(); - } -} - -enum spa_video_format xdpw_format_pw_from_drm_fourcc(uint32_t format) { - switch (format) { - case DRM_FORMAT_ARGB8888: - return SPA_VIDEO_FORMAT_BGRA; - case DRM_FORMAT_XRGB8888: - return SPA_VIDEO_FORMAT_BGRx; - case DRM_FORMAT_RGBA8888: - return SPA_VIDEO_FORMAT_ABGR; - case DRM_FORMAT_RGBX8888: - return SPA_VIDEO_FORMAT_xBGR; - case DRM_FORMAT_ABGR8888: - return SPA_VIDEO_FORMAT_RGBA; - case DRM_FORMAT_XBGR8888: - return SPA_VIDEO_FORMAT_RGBx; - case DRM_FORMAT_BGRA8888: - return SPA_VIDEO_FORMAT_ARGB; - case DRM_FORMAT_BGRX8888: - return SPA_VIDEO_FORMAT_xRGB; - case DRM_FORMAT_NV12: - return SPA_VIDEO_FORMAT_NV12; - case DRM_FORMAT_XRGB2101010: - return SPA_VIDEO_FORMAT_xRGB_210LE; - case DRM_FORMAT_XBGR2101010: - return SPA_VIDEO_FORMAT_xBGR_210LE; - case DRM_FORMAT_RGBX1010102: - return SPA_VIDEO_FORMAT_RGBx_102LE; - case DRM_FORMAT_BGRX1010102: - return SPA_VIDEO_FORMAT_BGRx_102LE; - case DRM_FORMAT_ARGB2101010: - return SPA_VIDEO_FORMAT_ARGB_210LE; - case DRM_FORMAT_ABGR2101010: - return SPA_VIDEO_FORMAT_ABGR_210LE; - case DRM_FORMAT_RGBA1010102: - return SPA_VIDEO_FORMAT_RGBA_102LE; - case DRM_FORMAT_BGRA1010102: - return SPA_VIDEO_FORMAT_BGRA_102LE; - default: - logprint(ERROR, "xdg-desktop-portal-hyprland: failed to convert drm " - "format 0x%08x to spa_video_format", format); - abort(); - } -} - -enum spa_video_format xdpw_format_pw_strip_alpha(enum spa_video_format format) { - switch (format) { - case SPA_VIDEO_FORMAT_BGRA: - return SPA_VIDEO_FORMAT_BGRx; - case SPA_VIDEO_FORMAT_ABGR: - return SPA_VIDEO_FORMAT_xBGR; - case SPA_VIDEO_FORMAT_RGBA: - return SPA_VIDEO_FORMAT_RGBx; - case SPA_VIDEO_FORMAT_ARGB: - return SPA_VIDEO_FORMAT_xRGB; - case SPA_VIDEO_FORMAT_ARGB_210LE: - return SPA_VIDEO_FORMAT_xRGB_210LE; - case SPA_VIDEO_FORMAT_ABGR_210LE: - return SPA_VIDEO_FORMAT_xBGR_210LE; - case SPA_VIDEO_FORMAT_RGBA_102LE: - return SPA_VIDEO_FORMAT_RGBx_102LE; - case SPA_VIDEO_FORMAT_BGRA_102LE: - return SPA_VIDEO_FORMAT_BGRx_102LE; - default: - return SPA_VIDEO_FORMAT_UNKNOWN; - } -} - -enum xdpw_chooser_types get_chooser_type(const char *chooser_type) { - if (!chooser_type || strcmp(chooser_type, "default") == 0) { - return XDPW_CHOOSER_DEFAULT; - } else if (strcmp(chooser_type, "none") == 0) { - return XDPW_CHOOSER_NONE; - } else if (strcmp(chooser_type, "simple") == 0) { - return XDPW_CHOOSER_SIMPLE; - } else if (strcmp(chooser_type, "dmenu") == 0) { - return XDPW_CHOOSER_DMENU; - } - fprintf(stderr, "Could not understand chooser type %s\n", chooser_type); - exit(1); -} - -const char *chooser_type_str(enum xdpw_chooser_types chooser_type) { - switch (chooser_type) { - case XDPW_CHOOSER_DEFAULT: - return "default"; - case XDPW_CHOOSER_NONE: - return "none"; - case XDPW_CHOOSER_SIMPLE: - return "simple"; - case XDPW_CHOOSER_DMENU: - return "dmenu"; - } - fprintf(stderr, "Could not find chooser type %d\n", chooser_type); - abort(); -} - -struct xdpw_frame_damage merge_damage(struct xdpw_frame_damage *damage1, struct xdpw_frame_damage *damage2) { - struct xdpw_frame_damage damage; - uint32_t x0, y0; - damage.x = damage1->x < damage2->y ? damage1->x : damage2->x; - damage.y = damage1->y < damage2->y ? damage1->y : damage2->y; - - x0 = damage1->x + damage1->width < damage2->x + damage2->width ? damage2->x + damage2->width : damage1->x + damage1->width; - y0 = damage1->y + damage1->height < damage2->y + damage2->height ? damage2->y + damage2->height : damage1->y + damage1->height; - damage.width = x0 - damage.x; - damage.height = y0 - damage.y; - - return damage; -} diff --git a/src/screencast/wlr_screencast.c b/src/screencast/wlr_screencast.c deleted file mode 100644 index 2b439ac..0000000 --- a/src/screencast/wlr_screencast.c +++ /dev/null @@ -1,1154 +0,0 @@ -#include "wlr_screencast.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "fps_limit.h" -#include "hyprland-toplevel-export-v1-client-protocol.h" -#include "linux-dmabuf-unstable-v1-client-protocol.h" -#include "logger.h" -#include "pipewire_screencast.h" -#include "screencast.h" -#include "wlr-foreign-toplevel-management-unstable-v1-client-protocol.h" -#include "wlr-screencopy-unstable-v1-client-protocol.h" -#include "xdg-output-unstable-v1-client-protocol.h" -#include "xdpw.h" -// - -void handleTitle(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *title) { - struct xdpw_screencast_context *ctx = data; - - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (current->handle == handle) { - strncpy(current->name, title, 255); - for (int i = 0; i < 255; ++i) - if (current->name[i] == '\"' || current->name[i] == '>' || current->name[i] == '\'') current->name[i] = ' '; - current->name[255] = '\0'; - break; - } - } -} - -void handleAppID(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, const char *app_id) { - struct xdpw_screencast_context *ctx = data; - - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (current->handle == handle) { - strncpy(current->clazz, app_id, 255); - for (int i = 0; i < 255; ++i) - if (current->clazz[i] == '\"' || current->clazz[i] == '>' || current->clazz[i] == '\'') current->clazz[i] = ' '; - current->name[255] = '\0'; - break; - } - } - - logprint(DEBUG, "hyprland: toplevel app_id %s", app_id); -} - -void handleOutputEnter(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) { - ; // noop -} - -void handleOutputLeave(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_output *output) { - ; // noop -} - -void handleState(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct wl_array *state) { - ; // noop -} - -void handleDone(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) { - ; // noop -} - -void handleClosed(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle) { - struct xdpw_screencast_context *ctx = data; - - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (current->handle == handle) { - break; - } - } - - logprint(DEBUG, "hyprland: toplevel closed %s", current->clazz); - - wl_list_remove(¤t->link); -} - -void handleParent(void *data, struct zwlr_foreign_toplevel_handle_v1 *handle, struct zwlr_foreign_toplevel_handle_v1 *parent) { - ; // noop -} - -struct zwlr_foreign_toplevel_handle_v1_listener toplevelHandleListener = { - .title = handleTitle, - .app_id = handleAppID, - .output_enter = handleOutputEnter, - .output_leave = handleOutputLeave, - .state = handleState, - .done = handleDone, - .closed = handleClosed, - .parent = handleParent, -}; - -void handleToplevel(void *data, struct zwlr_foreign_toplevel_manager_v1 *manager, struct zwlr_foreign_toplevel_handle_v1 *toplevel) { - struct xdpw_screencast_context *ctx = data; - - struct SToplevelEntry *entry = malloc(sizeof(struct SToplevelEntry)); - - entry->handle = toplevel; - - wl_list_insert(&ctx->toplevel_resource_list, &entry->link); - - zwlr_foreign_toplevel_handle_v1_add_listener(toplevel, &toplevelHandleListener, ctx); - - logprint(DEBUG, "hyprland: toplevel handle created %lx", toplevel); -} - -void handleFinished(void *data, struct zwlr_foreign_toplevel_manager_v1 *zwlr_foreign_toplevel_manager_v1) { - logprint(ERROR, "hyprland: compositor called finished on zwlr_foreign_toplevel_manager_v1!"); -} - -struct zwlr_foreign_toplevel_manager_v1_listener toplevelListener = { - .toplevel = handleToplevel, - .finished = handleFinished, -}; - -struct SToplevelEntry *toplevelEntryFromID(struct xdpw_screencast_context *ctx, uint32_t id) { - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - if (((uint64_t)current->handle & 0xFFFFFFFF) == id) { - return current; - } - } - return NULL; -} - -/// - -void wlr_frame_free(struct xdpw_screencast_instance *cast) { - if (!cast->wlr_frame) { - return; - } - zwlr_screencopy_frame_v1_destroy(cast->wlr_frame); - cast->wlr_frame = NULL; - logprint(TRACE, "wlroots: frame destroyed"); -} - -void xdpw_wlr_frame_finish(struct xdpw_screencast_instance *cast) { - logprint(TRACE, "wlroots: finish screencopy"); - - wlr_frame_free(cast); - - if (cast->quit || cast->err) { - // TODO: revisit the exit condition (remove quit?) - // and clean up sessions that still exist if err - // is the cause of the instance_destroy call - xdpw_screencast_instance_destroy(cast); - return; - } - - if (!cast->pwr_stream_state) { - cast->frame_state = XDPW_FRAME_STATE_NONE; - return; - } - - if (cast->frame_state == XDPW_FRAME_STATE_RENEG) { - pwr_update_stream_param(cast); - } - - if (cast->frame_state == XDPW_FRAME_STATE_FAILED) { - xdpw_pwr_enqueue_buffer(cast); - } - - if (cast->frame_state == XDPW_FRAME_STATE_SUCCESS) { - xdpw_pwr_enqueue_buffer(cast); - uint64_t delay_ns = fps_limit_measure_end(&cast->fps_limit, cast->framerate); - if (delay_ns > 0) { - xdpw_add_timer(cast->ctx->state, delay_ns, (xdpw_event_loop_timer_func_t)xdpw_wlr_frame_start, cast); - return; - } - } - xdpw_wlr_frame_start(cast); -} - -void xdpw_wlr_frame_start(struct xdpw_screencast_instance *cast) { - logprint(TRACE, "wlroots: start screencopy"); - if (cast->quit || cast->err) { - xdpw_screencast_instance_destroy(cast); - return; - } - - if (cast->initialized && !cast->pwr_stream_state) { - cast->frame_state = XDPW_FRAME_STATE_NONE; - return; - } - - cast->frame_state = XDPW_FRAME_STATE_STARTED; - xdpw_wlr_register_cb(cast); -} - -static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame); - -static void wlr_frame_buffer(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, uint32_t stride) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: buffer event handler"); - cast->wlr_frame = frame; - - cast->screencopy_frame_info[WL_SHM].width = width; - cast->screencopy_frame_info[WL_SHM].height = height; - cast->screencopy_frame_info[WL_SHM].stride = stride; - cast->screencopy_frame_info[WL_SHM].size = stride * height; - cast->screencopy_frame_info[WL_SHM].format = xdpw_format_drm_fourcc_from_wl_shm(format); - - if (zwlr_screencopy_manager_v1_get_version(cast->ctx->screencopy_manager) < 3) { - wlr_frame_buffer_done(cast, frame); - } -} - -static void wlr_frame_linux_dmabuf(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: linux_dmabuf event handler"); - - cast->screencopy_frame_info[DMABUF].width = width; - cast->screencopy_frame_info[DMABUF].height = height; - cast->screencopy_frame_info[DMABUF].format = format; -} - -static void wlr_frame_buffer_done(void *data, struct zwlr_screencopy_frame_v1 *frame) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: buffer_done event handler"); - - if (!cast->initialized) { - xdpw_wlr_frame_finish(cast); - return; - } - - // Check if announced screencopy information is compatible with pipewire meta - if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) && - cast->pwr_format.format != - xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) || - cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width || - cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) { - logprint(DEBUG, "wlroots: pipewire and wlroots metadata are incompatible. Renegotiate stream"); - cast->frame_state = XDPW_FRAME_STATE_RENEG; - xdpw_wlr_frame_finish(cast); - return; - } - - if (!cast->current_frame.xdpw_buffer) { - xdpw_pwr_dequeue_buffer(cast); - } - - if (!cast->current_frame.xdpw_buffer) { - logprint(WARN, "wlroots: no current buffer"); - xdpw_wlr_frame_finish(cast); - return; - } - - assert(cast->current_frame.xdpw_buffer); - - // Check if dequeued buffer is compatible with announced buffer - if ((cast->buffer_type == WL_SHM && (cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size || - cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) || - cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width || - cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) { - logprint(DEBUG, "wlroots: pipewire buffer has wrong dimensions"); - cast->frame_state = XDPW_FRAME_STATE_FAILED; - xdpw_wlr_frame_finish(cast); - return; - } - - cast->current_frame.damage_count = 0; - zwlr_screencopy_frame_v1_copy_with_damage(frame, cast->current_frame.xdpw_buffer->buffer); - logprint(TRACE, "wlroots: frame copied"); - - fps_limit_measure_start(&cast->fps_limit, cast->framerate); -} - -static void wlr_frame_flags(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: flags event handler"); - cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; -} - -static void wlr_frame_damage(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: damage event handler"); - - logprint(TRACE, "wlroots: damage %"PRIu32": %"PRIu32",%"PRIu32"x%"PRIu32",%"PRIu32, cast->current_frame.damage_count, x, y, width, height); - struct xdpw_frame_damage damage = {x, y, width, height}; - if (cast->current_frame.damage_count < 4) { - cast->current_frame.damage[cast->current_frame.damage_count++] = damage; - } else { - cast->current_frame.damage[3] = merge_damage(&cast->current_frame.damage[3], &damage); - } -} - -static void wlr_frame_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: ready event handler"); - - cast->current_frame.tv_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo); - cast->current_frame.tv_nsec = tv_nsec; - logprint(TRACE, "wlroots: timestamp %" PRIu64 ":%" PRIu32, cast->current_frame.tv_sec, cast->current_frame.tv_nsec); - - cast->frame_state = XDPW_FRAME_STATE_SUCCESS; - - xdpw_wlr_frame_finish(cast); -} - -static void wlr_frame_failed(void *data, struct zwlr_screencopy_frame_v1 *frame) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "wlroots: failed event handler"); - - cast->frame_state = XDPW_FRAME_STATE_FAILED; - - xdpw_wlr_frame_finish(cast); -} - -static const struct zwlr_screencopy_frame_v1_listener wlr_frame_listener = { - .buffer = wlr_frame_buffer, - .buffer_done = wlr_frame_buffer_done, - .linux_dmabuf = wlr_frame_linux_dmabuf, - .flags = wlr_frame_flags, - .ready = wlr_frame_ready, - .failed = wlr_frame_failed, - .damage = wlr_frame_damage, -}; - -static void hyprland_frame_buffer_done(void *data, struct hyprland_toplevel_export_frame_v1 *frame); - -static void hyprland_frame_buffer(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t format, uint32_t width, uint32_t height, - uint32_t stride) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: buffer event handler"); - cast->hyprland_frame = frame; - - cast->screencopy_frame_info[WL_SHM].width = width; - cast->screencopy_frame_info[WL_SHM].height = height; - cast->screencopy_frame_info[WL_SHM].stride = stride; - cast->screencopy_frame_info[WL_SHM].size = stride * height; - cast->screencopy_frame_info[WL_SHM].format = xdpw_format_drm_fourcc_from_wl_shm(format); -} - -static void hyprland_frame_linux_dmabuf(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t format, uint32_t width, - uint32_t height) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: linux_dmabuf event handler"); - - cast->screencopy_frame_info[DMABUF].width = width; - cast->screencopy_frame_info[DMABUF].height = height; - cast->screencopy_frame_info[DMABUF].format = format; -} - -static void hyprland_frame_buffer_done(void *data, struct hyprland_toplevel_export_frame_v1 *frame) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: buffer_done event handler"); - - if (!cast->initialized) { - xdpw_wlr_frame_finish(cast); - return; - } - - // Check if announced screencopy information is compatible with pipewire meta - if ((cast->pwr_format.format != xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format) && - cast->pwr_format.format != - xdpw_format_pw_strip_alpha(xdpw_format_pw_from_drm_fourcc(cast->screencopy_frame_info[cast->buffer_type].format))) || - cast->pwr_format.size.width != cast->screencopy_frame_info[cast->buffer_type].width || - cast->pwr_format.size.height != cast->screencopy_frame_info[cast->buffer_type].height) { - logprint(DEBUG, "hyprland: pipewire and wlroots metadata are incompatible. Renegotiate stream"); - cast->frame_state = XDPW_FRAME_STATE_RENEG; - xdpw_wlr_frame_finish(cast); - return; - } - - if (!cast->current_frame.xdpw_buffer) { - xdpw_pwr_dequeue_buffer(cast); - } - - if (!cast->current_frame.xdpw_buffer) { - logprint(WARN, "hyprland: no current buffer"); - xdpw_wlr_frame_finish(cast); - return; - } - - assert(cast->current_frame.xdpw_buffer); - - // Check if dequeued buffer is compatible with announced buffer - if ((cast->buffer_type == WL_SHM && (cast->current_frame.xdpw_buffer->size[0] != cast->screencopy_frame_info[cast->buffer_type].size || - cast->current_frame.xdpw_buffer->stride[0] != cast->screencopy_frame_info[cast->buffer_type].stride)) || - cast->current_frame.xdpw_buffer->width != cast->screencopy_frame_info[cast->buffer_type].width || - cast->current_frame.xdpw_buffer->height != cast->screencopy_frame_info[cast->buffer_type].height) { - logprint(DEBUG, "hyprland: pipewire buffer has wrong dimensions"); - cast->frame_state = XDPW_FRAME_STATE_FAILED; - xdpw_wlr_frame_finish(cast); - return; - } - - hyprland_toplevel_export_frame_v1_copy(frame, cast->current_frame.xdpw_buffer->buffer, 0); - logprint(TRACE, "hyprland: frame copied"); - - fps_limit_measure_start(&cast->fps_limit, cast->framerate); -} - -static void hyprland_frame_flags(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t flags) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: flags event handler"); - cast->current_frame.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; -} - -static void hyprland_frame_damage(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t x, uint32_t y, uint32_t width, - uint32_t height) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: damage event handler"); - - logprint(TRACE, "hyprland: damage %"PRIu32": %"PRIu32",%"PRIu32"x%"PRIu32",%"PRIu32, cast->current_frame.damage_count, x, y, width, height); - struct xdpw_frame_damage damage = {x, y, width, height}; - if (cast->current_frame.damage_count < 4) { - cast->current_frame.damage[cast->current_frame.damage_count++] = damage; - } else { - cast->current_frame.damage[3] = merge_damage(&cast->current_frame.damage[3], &damage); - } -} - -static void hyprland_frame_ready(void *data, struct hyprland_toplevel_export_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, - uint32_t tv_nsec) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: ready event handler"); - - cast->current_frame.tv_sec = ((((uint64_t)tv_sec_hi) << 32) | tv_sec_lo); - cast->current_frame.tv_nsec = tv_nsec; - logprint(TRACE, "hyprland: timestamp %" PRIu64 ":%" PRIu32, cast->current_frame.tv_sec, cast->current_frame.tv_nsec); - - cast->frame_state = XDPW_FRAME_STATE_SUCCESS; - - xdpw_wlr_frame_finish(cast); -} - -static void hyprland_frame_failed(void *data, struct hyprland_toplevel_export_frame_v1 *frame) { - struct xdpw_screencast_instance *cast = data; - if (!frame) { - return; - } - - logprint(TRACE, "hyprland: failed event handler"); - - cast->frame_state = XDPW_FRAME_STATE_FAILED; - - xdpw_wlr_frame_finish(cast); -} - -static const struct hyprland_toplevel_export_frame_v1_listener hyprland_frame_listener = {.buffer = hyprland_frame_buffer, - .buffer_done = hyprland_frame_buffer_done, - .linux_dmabuf = hyprland_frame_linux_dmabuf, - .flags = hyprland_frame_flags, - .ready = hyprland_frame_ready, - .failed = hyprland_frame_failed, - .damage = hyprland_frame_damage}; - -void xdpw_wlr_register_cb(struct xdpw_screencast_instance *cast) { - if (cast->target.x != -1 && cast->target.y != -1 && cast->target.w != -1 && cast->target.h != -1 && cast->target.window_handle <= 0) { - // capture region - cast->frame_callback = - zwlr_screencopy_manager_v1_capture_output_region(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output, - cast->target.x, cast->target.y, cast->target.w, cast->target.h); - } else if (cast->target.window_handle == -1) { - cast->frame_callback = - zwlr_screencopy_manager_v1_capture_output(cast->ctx->screencopy_manager, cast->with_cursor, cast->target.output->output); - } else { - // share window - struct SToplevelEntry *entry = toplevelEntryFromID(cast->ctx, cast->target.window_handle); - - if (!entry) { - logprint(DEBUG, "hyprland: error in getting entry"); - return; - } - - cast->frame_callback_hyprland = hyprland_toplevel_export_manager_v1_capture_toplevel_with_wlr_toplevel_handle( - cast->ctx->hyprland_toplevel_manager, cast->with_cursor, entry->handle); - - hyprland_toplevel_export_frame_v1_add_listener(cast->frame_callback_hyprland, &hyprland_frame_listener, cast); - - logprint(TRACE, "hyprland: callbacks registered"); - return; - } - - zwlr_screencopy_frame_v1_add_listener(cast->frame_callback, &wlr_frame_listener, cast); - logprint(TRACE, "wlroots: callbacks registered"); -} - -static void wlr_output_handle_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t phys_width, int32_t phys_height, - int32_t subpixel, const char *make, const char *model, int32_t transform) { - struct xdpw_wlr_output *output = data; - output->make = strdup(make); - output->model = strdup(model); - output->transform = transform; -} - -static void wlr_output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - if (flags & WL_OUTPUT_MODE_CURRENT) { - struct xdpw_wlr_output *output = data; - output->framerate = (float)refresh / 1000; - } -} - -static void wlr_output_handle_done(void *data, struct wl_output *wl_output) { /* Nothing to do */ -} - -static void wlr_output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor) { /* Nothing to do */ -} - -static const struct wl_output_listener wlr_output_listener = { - .geometry = wlr_output_handle_geometry, - .mode = wlr_output_handle_mode, - .done = wlr_output_handle_done, - .scale = wlr_output_handle_scale, -}; - -static void wlr_xdg_output_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) { - struct xdpw_wlr_output *output = data; - - output->name = strdup(name); - - logprint(INFO, "Output %lx name: %s", data, name); -}; - -static void noop() { - // This space intentionally left blank -} - -static const struct zxdg_output_v1_listener wlr_xdg_output_listener = { - .logical_position = noop, - .logical_size = noop, - .done = NULL, /* Deprecated */ - .description = noop, - .name = wlr_xdg_output_name, -}; - -static void wlr_add_xdg_output_listener(struct xdpw_wlr_output *output, struct zxdg_output_v1 *xdg_output) { - output->xdg_output = xdg_output; - zxdg_output_v1_add_listener(output->xdg_output, &wlr_xdg_output_listener, output); -} - -static void wlr_init_xdg_output(struct xdpw_screencast_context *ctx, struct xdpw_wlr_output *output) { - struct zxdg_output_v1 *xdg_output = zxdg_output_manager_v1_get_xdg_output(ctx->xdg_output_manager, output->output); - wlr_add_xdg_output_listener(output, xdg_output); -} - -static void wlr_init_xdg_outputs(struct xdpw_screencast_context *ctx) { - struct xdpw_wlr_output *output, *tmp; - wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { - if (output->xdg_output) { - continue; - } - wlr_init_xdg_output(ctx, output); - } -} - -char *buildWindowList(struct xdpw_screencast_context *ctx) { - char *rolling = calloc(1, 1); - - if (!ctx->wlroots_toplevel_manager) return rolling; - - struct SToplevelEntry *current; - wl_list_for_each(current, &ctx->toplevel_resource_list, link) { - char *oldRolling = rolling; - - rolling = getFormat("%s%u[HC>]%s[HT>]%s[HE>]", rolling, (uint32_t)(((uint64_t)current->handle) & 0xFFFFFFFF), current->clazz, current->name); - - free(oldRolling); - } - - for (size_t i = 0; i < strlen(rolling); ++i) { - if (rolling[i] == '\"') rolling[i] = ' '; - } - - return rolling; -} - -struct xdpw_share xdpw_wlr_chooser(struct xdpw_screencast_context *ctx) { - char result[1024] = {0}; - FILE *fp; - char buf[1024] = {0}; - - const char *WAYLAND_DISPLAY = getenv("WAYLAND_DISPLAY"); - const char *XCURSOR_SIZE = getenv("XCURSOR_SIZE"); - const char *HYPRLAND_INSTANCE_SIGNATURE = getenv("HYPRLAND_INSTANCE_SIGNATURE"); - - char *windowList = buildWindowList(ctx); - - char *cmd = getFormat( - "WAYLAND_DISPLAY=%s QT_QPA_PLATFORM=\"wayland\" XCURSOR_SIZE=%s HYPRLAND_INSTANCE_SIGNATURE=%s XDPH_WINDOW_SHARING_LIST=\"%s\" " - "hyprland-share-picker", - WAYLAND_DISPLAY, XCURSOR_SIZE ? XCURSOR_SIZE : "24", HYPRLAND_INSTANCE_SIGNATURE ? HYPRLAND_INSTANCE_SIGNATURE : "0", windowList); - - free(windowList); - - logprint(DEBUG, "Screencast: Picker: Running command \"%s\"", cmd); - - fp = popen(cmd, "r"); - if (fp == NULL) { - printf("Failed to run command\n"); - exit(1); - } - - while (fgets(buf, sizeof(buf), fp) != NULL) { - strcat(result, buf); - } - - pclose(fp); - - free(cmd); - - // great, let's parse it. - - struct xdpw_share res = {NULL, -1, -1, -1, -1, -1}; - - if (strncmp(result, "screen:", 7) == 0) { - // find output - logprint(DEBUG, "Screencast: Attempting to find screen for %s", result); - - char *display_name = malloc(strlen(result) - 7); - strncpy(display_name, result + 7, strlen(result) - 8); - display_name[strlen(result) - 8] = 0; - - struct xdpw_wlr_output *out; - bool found = false; - wl_list_for_each(out, &ctx->output_list, link) { - if (strcmp(out->name, display_name) == 0) { - found = true; - break; - } - } - - free(display_name); - - if (!found) return res; - - res.output = out; - return res; - } else if (strncmp(result, "region:", 7) == 0) { - // find output - logprint(DEBUG, "Screencast: Attempting to find region for %s", result); - - int atPos = 7; - for (int i = 7; i < (int)strlen(result); ++i) { - if (result[i] == '@') { - atPos = i; - break; - } - } - - char *display_name = malloc(atPos - 6); - strncpy(display_name, result + 7, atPos - 7); - display_name[atPos - 7] = 0; - - struct xdpw_wlr_output *out; - wl_list_for_each(out, &ctx->output_list, link) { - if (strcmp(out->name, display_name) == 0) { - break; - } - } - - // then get coords - int coordno = 0; - int coords[4] = {-1, -1, -1, -1}; - int coordbegin = 7 + strlen(display_name) + 1; - for (int i = 7 + strlen(display_name) + 1; i < (int)strlen(result); ++i) { - if (result[i] == ',' || result[i] == '@' || i + 1 == (int)strlen(result)) { - char *entire = malloc(i - coordbegin + 1); - strncpy(entire, result + coordbegin, i - coordbegin); - entire[i - coordbegin] = 0; - coords[coordno] = strtol(entire, NULL, 10); - free(entire); - - coordno++; - coordbegin = i + 1; - i++; - } - } - - free(display_name); - - struct xdpw_share res2 = {out, coords[0], coords[1], coords[2], coords[3]}; - return res2; - } else if (strncmp(result, "window:", 7) == 0) { - if (ctx->hyprland_toplevel_manager == NULL) { - logprint(DEBUG, "Screencast: Window sharing attempted but the toplevel protocol is not implemented by the compositor!"); - return res; - } - - logprint(DEBUG, "Screencast: Attempting to find window for %s", result); - - char *display_name = malloc(strlen(result) - 7); - strncpy(display_name, result + 7, strlen(result) - 8); - display_name[strlen(result) - 8] = 0; - - res.window_handle = strtol(display_name, NULL, 10); - - free(display_name); - return res; - } else { - logprint(DEBUG, "Screencast: Invalid result from hyprland-share-picker: %s", result); - return res; - } - - return res; -} - -struct xdpw_wlr_output *xdpw_wlr_output_first(struct wl_list *output_list) { - struct xdpw_wlr_output *output, *tmp; - wl_list_for_each_safe(output, tmp, output_list, link) { return output; } - return NULL; -} - -struct xdpw_wlr_output *xdpw_wlr_output_find_by_name(struct wl_list *output_list, const char *name) { - struct xdpw_wlr_output *output, *tmp; - wl_list_for_each_safe(output, tmp, output_list, link) { - if (strcmp(output->name, name) == 0) { - return output; - } - } - return NULL; -} - -struct xdpw_wlr_output *xdpw_wlr_output_find(struct xdpw_screencast_context *ctx, struct wl_output *out, uint32_t id) { - struct xdpw_wlr_output *output, *tmp; - wl_list_for_each_safe(output, tmp, &ctx->output_list, link) { - if ((output->output == out) || (output->id == id)) { - return output; - } - } - return NULL; -} - -static void wlr_remove_output(struct xdpw_wlr_output *out) { - free(out->name); - free(out->make); - free(out->model); - zxdg_output_v1_destroy(out->xdg_output); - wl_output_destroy(out->output); - wl_list_remove(&out->link); - free(out); -} - -static void wlr_format_modifier_pair_add(struct xdpw_screencast_context *ctx, uint32_t format, uint64_t modifier) { - struct xdpw_format_modifier_pair *fm_pair; - wl_array_for_each(fm_pair, &ctx->format_modifier_pairs) { - if (fm_pair->fourcc == format && fm_pair->modifier == modifier) { - logprint(TRACE, "wlroots: skipping duplicated format %u (%lu)", fm_pair->fourcc, fm_pair->modifier); - return; - } - } - - fm_pair = wl_array_add(&ctx->format_modifier_pairs, sizeof(struct xdpw_format_modifier_pair)); - fm_pair->fourcc = format; - fm_pair->modifier = modifier; - logprint(TRACE, "wlroots: format %u (%lu)", fm_pair->fourcc, fm_pair->modifier); -} - -static void linux_dmabuf_handle_modifier(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf_v1, uint32_t format, uint32_t modifier_hi, - uint32_t modifier_lo) { - struct xdpw_screencast_context *ctx = data; - - logprint(TRACE, "wlroots: linux_dmabuf_handle_modifier called"); - - uint64_t modifier = (((uint64_t)modifier_hi) << 32) | modifier_lo; - wlr_format_modifier_pair_add(ctx, format, modifier); -} - -static const struct zwp_linux_dmabuf_v1_listener linux_dmabuf_listener = { - .format = noop, - .modifier = linux_dmabuf_handle_modifier, -}; - -static void linux_dmabuf_feedback_handle_main_device(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, - struct wl_array *device_arr) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_main_device called"); - - assert(ctx->gbm == NULL); - - 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) { - logprint(WARN, "wlroots: unable to open main device"); - ctx->state->config->screencast_conf.force_mod_linear = true; - return; - } - ctx->gbm = xdpw_gbm_device_create(drmDev); -} - -static void linux_dmabuf_feedback_format_table(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, int fd, uint32_t size) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_format_table called"); - - wl_array_release(&ctx->format_modifier_pairs); - wl_array_init(&ctx->format_modifier_pairs); - - ctx->feedback_data.format_table_data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (ctx->feedback_data.format_table_data == MAP_FAILED) { - ctx->feedback_data.format_table_data = NULL; - ctx->feedback_data.format_table_size = 0; - return; - } - ctx->feedback_data.format_table_size = size; -} - -static void linux_dmabuf_feedback_handle_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_handle_done called"); - - if (ctx->feedback_data.format_table_data) { - munmap(ctx->feedback_data.format_table_data, ctx->feedback_data.format_table_size); - } - ctx->feedback_data.format_table_data = NULL; - ctx->feedback_data.format_table_size = 0; -} - -static void linux_dmabuf_feedback_tranche_target_devices(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, - struct wl_array *device_arr) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_target_devices called"); - - 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 (ctx->gbm) { - drmDevice *drmDevRenderer = NULL; - drmGetDevice2(gbm_device_get_fd(ctx->gbm), /* flags */ 0, &drmDevRenderer); - ctx->feedback_data.device_used = drmDevicesEqual(drmDevRenderer, drmDev); - } else { - ctx->gbm = xdpw_gbm_device_create(drmDev); - ctx->feedback_data.device_used = ctx->gbm != NULL; - } -} - -static void linux_dmabuf_feedback_tranche_flags(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, uint32_t flags) { - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_flags called"); -} - -static void linux_dmabuf_feedback_tranche_formats(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1, - struct wl_array *indices) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_formats called"); - - if (!ctx->feedback_data.device_used || !ctx->feedback_data.format_table_data) { - 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 = ctx->feedback_data.format_table_size / sizeof(struct fm_entry); - struct fm_entry *fm_entry = ctx->feedback_data.format_table_data; - uint16_t *idx; - wl_array_for_each(idx, indices) { - if (*idx >= n_modifiers) { - continue; - } - wlr_format_modifier_pair_add(ctx, (fm_entry + *idx)->format, (fm_entry + *idx)->modifier); - } -} - -static void linux_dmabuf_feedback_tranche_done(void *data, struct zwp_linux_dmabuf_feedback_v1 *zwp_linux_dmabuf_feedback_v1) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: linux_dmabuf_feedback_tranche_done called"); - - ctx->feedback_data.device_used = false; -} - -static const struct zwp_linux_dmabuf_feedback_v1_listener linux_dmabuf_listener_feedback = { - .main_device = linux_dmabuf_feedback_handle_main_device, - .format_table = linux_dmabuf_feedback_format_table, - .done = linux_dmabuf_feedback_handle_done, - .tranche_target_device = linux_dmabuf_feedback_tranche_target_devices, - .tranche_flags = linux_dmabuf_feedback_tranche_flags, - .tranche_formats = linux_dmabuf_feedback_tranche_formats, - .tranche_done = linux_dmabuf_feedback_tranche_done, -}; - -static void wlr_registry_handle_add(void *data, struct wl_registry *reg, uint32_t id, const char *interface, uint32_t ver) { - struct xdpw_screencast_context *ctx = data; - - logprint(DEBUG, "wlroots: interface to register %s (Version: %u)", interface, ver); - if (!strcmp(interface, wl_output_interface.name)) { - struct xdpw_wlr_output *output = calloc(1, sizeof(*output)); - - output->id = id; - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, WL_OUTPUT_VERSION); - output->output = wl_registry_bind(reg, id, &wl_output_interface, WL_OUTPUT_VERSION); - - wl_output_add_listener(output->output, &wlr_output_listener, output); - wl_list_insert(&ctx->output_list, &output->link); - if (ctx->xdg_output_manager) { - wlr_init_xdg_output(ctx, output); - } - } - - if (!strcmp(interface, zwlr_screencopy_manager_v1_interface.name)) { - uint32_t version = ver; - if (SC_MANAGER_VERSION < ver) { - version = SC_MANAGER_VERSION; - } else if (ver < SC_MANAGER_VERSION_MIN) { - version = SC_MANAGER_VERSION_MIN; - } - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version); - ctx->screencopy_manager = wl_registry_bind(reg, id, &zwlr_screencopy_manager_v1_interface, version); - } - - if (!strcmp(interface, hyprland_toplevel_export_manager_v1_interface.name) && !ctx->hyprland_toplevel_manager) { - uint32_t version = ver; - - logprint(DEBUG, "hyprland: |-- registered to interface %s (Version %u)", interface, version); - - ctx->hyprland_toplevel_manager = wl_registry_bind(reg, id, &hyprland_toplevel_export_manager_v1_interface, version); - } - - if (!strcmp(interface, zwlr_foreign_toplevel_manager_v1_interface.name) && !ctx->wlroots_toplevel_manager) { - uint32_t version = ver; - - logprint(DEBUG, "hyprland: |-- registered to interface %s (Version %u)", interface, version); - - ctx->wlroots_toplevel_manager = wl_registry_bind(reg, id, &zwlr_foreign_toplevel_manager_v1_interface, version); - wl_list_init(&ctx->toplevel_resource_list); - - zwlr_foreign_toplevel_manager_v1_add_listener(ctx->wlroots_toplevel_manager, &toplevelListener, ctx); - } - - if (strcmp(interface, wl_shm_interface.name) == 0) { - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, WL_SHM_VERSION); - ctx->shm = wl_registry_bind(reg, id, &wl_shm_interface, WL_SHM_VERSION); - } - - if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) { - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, XDG_OUTPUT_MANAGER_VERSION); - ctx->xdg_output_manager = wl_registry_bind(reg, id, &zxdg_output_manager_v1_interface, XDG_OUTPUT_MANAGER_VERSION); - } - if (strcmp(interface, zwp_linux_dmabuf_v1_interface.name) == 0) { - uint32_t version = ver; - if (LINUX_DMABUF_VERSION < ver) { - version = LINUX_DMABUF_VERSION; - } else if (LINUX_DMABUF_VERSION_MIN > ver) { - logprint(INFO, "wlroots: interface %s (Version %u) is required for DMA-BUF screencast", interface, LINUX_DMABUF_VERSION_MIN); - return; - } - logprint(DEBUG, "wlroots: |-- registered to interface %s (Version %u)", interface, version); - ctx->linux_dmabuf = wl_registry_bind(reg, id, &zwp_linux_dmabuf_v1_interface, version); - - if (version >= 4) { - ctx->linux_dmabuf_feedback = zwp_linux_dmabuf_v1_get_default_feedback(ctx->linux_dmabuf); - zwp_linux_dmabuf_feedback_v1_add_listener(ctx->linux_dmabuf_feedback, &linux_dmabuf_listener_feedback, ctx); - } else { - zwp_linux_dmabuf_v1_add_listener(ctx->linux_dmabuf, &linux_dmabuf_listener, ctx); - } - } -} - -static void wlr_registry_handle_remove(void *data, struct wl_registry *reg, uint32_t id) { - struct xdpw_screencast_context *ctx = data; - struct xdpw_wlr_output *output = xdpw_wlr_output_find(ctx, NULL, id); - if (output) { - logprint(DEBUG, "wlroots: output removed (%s)", output->name); - struct xdpw_screencast_instance *cast, *tmp; - wl_list_for_each_safe(cast, tmp, &ctx->screencast_instances, link) { - if (cast->target.output == output) { - // screencopy might be in process for this instance - wlr_frame_free(cast); - // instance might be waiting for wakeup by the frame limiter - struct xdpw_timer *timer, *ttmp; - wl_list_for_each_safe(timer, ttmp, &cast->ctx->state->timers, link) { - if (timer->user_data == cast) { - xdpw_destroy_timer(timer); - } - } - cast->teardown = true; - xdpw_screencast_instance_teardown(cast); - } - } - wlr_remove_output(output); - } -} - -static const struct wl_registry_listener wlr_registry_listener = { - .global = wlr_registry_handle_add, - .global_remove = wlr_registry_handle_remove, -}; - -int xdpw_wlr_screencopy_init(struct xdpw_state *state) { - struct xdpw_screencast_context *ctx = &state->screencast; - - // initialize a list of outputs - wl_list_init(&ctx->output_list); - - // initialize a list of active screencast instances - wl_list_init(&ctx->screencast_instances); - - // initialize a list of format modifier pairs - wl_array_init(&ctx->format_modifier_pairs); - - // retrieve registry - ctx->registry = wl_display_get_registry(state->wl_display); - wl_registry_add_listener(ctx->registry, &wlr_registry_listener, ctx); - - wl_display_roundtrip(state->wl_display); - - logprint(DEBUG, "wayland: registry listeners run"); - - // make sure our wlroots supports xdg_output_manager - if (!ctx->xdg_output_manager) { - logprint(ERROR, "Compositor doesn't support %s!", zxdg_output_manager_v1_interface.name); - return -1; - } - - wlr_init_xdg_outputs(ctx); - - wl_display_roundtrip(state->wl_display); - - logprint(DEBUG, "wayland: xdg output listeners run"); - - // make sure our wlroots supports shm protocol - if (!ctx->shm) { - logprint(ERROR, "Compositor doesn't support %s!", "wl_shm"); - return -1; - } - - // make sure our wlroots supports screencopy protocol - if (!ctx->screencopy_manager) { - logprint(ERROR, "Compositor doesn't support %s!", zwlr_screencopy_manager_v1_interface.name); - return -1; - } - - // make sure we have a gbm device - if (ctx->linux_dmabuf && !ctx->gbm) { - ctx->gbm = xdpw_gbm_device_create(NULL); - if (!ctx->gbm) { - logprint(ERROR, "System doesn't support gbm!"); - } - } - - return 0; -} - -void xdpw_wlr_screencopy_finish(struct xdpw_screencast_context *ctx) { - wl_array_release(&ctx->format_modifier_pairs); - - struct xdpw_wlr_output *output, *tmp_o; - wl_list_for_each_safe(output, tmp_o, &ctx->output_list, link) { - wl_list_remove(&output->link); - zxdg_output_v1_destroy(output->xdg_output); - wl_output_destroy(output->output); - } - - struct xdpw_screencast_instance *cast, *tmp_c; - wl_list_for_each_safe(cast, tmp_c, &ctx->screencast_instances, link) { cast->quit = true; } - - if (ctx->screencopy_manager) { - zwlr_screencopy_manager_v1_destroy(ctx->screencopy_manager); - } - if (ctx->shm) { - wl_shm_destroy(ctx->shm); - } - if (ctx->xdg_output_manager) { - zxdg_output_manager_v1_destroy(ctx->xdg_output_manager); - } - if (ctx->gbm) { - int fd = gbm_device_get_fd(ctx->gbm); - gbm_device_destroy(ctx->gbm); - close(fd); - } - if (ctx->linux_dmabuf_feedback) { - zwp_linux_dmabuf_feedback_v1_destroy(ctx->linux_dmabuf_feedback); - } - if (ctx->linux_dmabuf) { - zwp_linux_dmabuf_v1_destroy(ctx->linux_dmabuf); - } - if (ctx->registry) { - wl_registry_destroy(ctx->registry); - } -} diff --git a/src/screenshot/screenshot.c b/src/screenshot/screenshot.c deleted file mode 100644 index 48c4c00..0000000 --- a/src/screenshot/screenshot.c +++ /dev/null @@ -1,300 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "xdpw.h" -#include "screenshot.h" - -static const char object_path[] = "/org/freedesktop/portal/desktop"; -static const char interface_name[] = "org.freedesktop.impl.portal.Screenshot"; - -static bool exec_screenshooter(const char *path) { - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - return false; - } else if (pid == 0) { - char *const argv[] = { - "grim", - "--", - (char *)path, - NULL, - }; - execvp("grim", argv); - - perror("execvp"); - exit(127); - } - - int stat; - if (waitpid(pid, &stat, 0) < 0) { - perror("waitpid"); - return false; - } - - return stat == 0; -} -static bool exec_screenshooter_interactive(const char *path) { - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - return false; - } else if (pid == 0) { - char cmd[strlen(path) + 25]; - snprintf(cmd, sizeof(cmd), "grim -g \"$(slurp)\" -- %s", path); - execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); - perror("execl"); - exit(127); - } - - int stat; - if (waitpid(pid, &stat, 0) < 0) { - perror("waitpid"); - return false; - } - - return stat == 0; -} - -static int method_screenshot(sd_bus_message *msg, void *data, - sd_bus_error *ret_error) { - int ret = 0; - - bool interactive = false; - - char *handle, *app_id, *parent_window; - ret = sd_bus_message_read(msg, "oss", &handle, &app_id, &parent_window); - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_enter_container(msg, 'a', "{sv}"); - if (ret < 0) { - return ret; - } - char *key; - int inner_ret = 0; - while ((ret = sd_bus_message_enter_container(msg, 'e', "sv")) > 0) { - inner_ret = sd_bus_message_read(msg, "s", &key); - if (inner_ret < 0) { - return inner_ret; - } - - if (strcmp(key, "interactive") == 0) { - int mode; - sd_bus_message_read(msg, "v", "b", &mode); - logprint(DEBUG, "dbus: option interactive: %d", mode); - interactive = mode; - } else if (strcmp(key, "modal") == 0) { - int modal; - sd_bus_message_read(msg, "v", "b", &modal); - logprint(DEBUG, "dbus: option modal: %d", modal); - } else { - logprint(WARN, "dbus: unknown option %s", key); - sd_bus_message_skip(msg, "v"); - } - - inner_ret = sd_bus_message_exit_container(msg); - if (inner_ret < 0) { - return inner_ret; - } - } - if (ret < 0) { - return ret; - } - ret = sd_bus_message_exit_container(msg); - if (ret < 0) { - return ret; - } - - // TODO: cleanup this - struct xdpw_request *req = - xdpw_request_create(sd_bus_message_get_bus(msg), handle); - if (req == NULL) { - return -ENOMEM; - } - - // TODO: choose a better path - const char path[] = "/tmp/out.png"; - if (interactive && !exec_screenshooter_interactive(path)) { - return -1; - } - if (!interactive && !exec_screenshooter(path)) { - return -1; - } - - const char uri_prefix[] = "file://"; - char uri[strlen(path) + strlen(uri_prefix) + 1]; - snprintf(uri, sizeof(uri), "%s%s", uri_prefix, path); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, "uri", "s", uri); - if (ret < 0) { - return ret; - } - - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - return 0; -} - -static bool spawn_chooser(int chooser_out[2]) { - pid_t pid = fork(); - if (pid < 0) { - perror("fork"); - return false; - } else if (pid == 0) { - close(chooser_out[0]); - - dup2(chooser_out[1], STDOUT_FILENO); - close(chooser_out[1]); - - execl("/bin/sh", "/bin/sh", "-c", "grim -g \"$(slurp -p)\" -t ppm -", NULL); - - perror("execl"); - _exit(127); - } - - int stat; - if (waitpid(pid, &stat, 0) < 0) { - perror("waitpid"); - return false; - } - - close(chooser_out[1]); - return stat == 0; -} - -static bool exec_color_picker(struct xdpw_ppm_pixel *pixel) { - int chooser_out[2]; - if (pipe(chooser_out) == -1) { - perror("pipe chooser_out"); - return false; - } - - if (!spawn_chooser(chooser_out)) { - logprint(ERROR, "Selection failed"); - close(chooser_out[0]); - return false; - } - - FILE *f = fdopen(chooser_out[0], "r"); - if (f == NULL) { - perror("fopen pipe chooser_out"); - close(chooser_out[0]); - return false; - } - - char *format = NULL; - size_t len = 1; - if (getline(&format, &len, f) < 0) { - goto error_img; - } - if (strcmp(format, "P6\n") != 0) { - goto error_img; - } - if (getline(&format, &len, f) < 0) { - goto error_img; - } - if (strcmp(format, "1 1\n") != 0) { - goto error_img; - } - - if (fscanf(f, "%d\n", &pixel->max_color_value) != 1) { - goto error_img; - } - - unsigned char pixels[3]; - if (fread(pixels, 3, 1, f) != 1) { - goto error_img; - } - - pixel->red = pixels[0]; - pixel->green = pixels[1]; - pixel->blue = pixels[2]; - - free(format); - fclose(f); - - return true; - -error_img: - logprint(WARN, "Invalid image format or size"); - free(format); - fclose(f); - return false; -} - -static int method_pick_color(sd_bus_message *msg, void *data, - sd_bus_error *ret_error) { - - int ret = 0; - - char *handle, *app_id, *parent_window; - ret = sd_bus_message_read(msg, "oss", &handle, &app_id, &parent_window); - if (ret < 0) { - return ret; - } - - struct xdpw_request *req = - xdpw_request_create(sd_bus_message_get_bus(msg), handle); - if (req == NULL) { - return -ENOMEM; - } - - struct xdpw_ppm_pixel pixel = {0}; - if (!exec_color_picker(&pixel)) { - return -1; - } - - double red = pixel.red / (pixel.max_color_value * 1.0); - double green = pixel.green / (pixel.max_color_value * 1.0); - double blue = pixel.blue / (pixel.max_color_value * 1.0); - - sd_bus_message *reply = NULL; - ret = sd_bus_message_new_method_return(msg, &reply); - if (ret < 0) { - return ret; - } - - ret = sd_bus_message_append(reply, "ua{sv}", PORTAL_RESPONSE_SUCCESS, 1, "color", "(ddd)", red, green, blue); - if (ret < 0) { - return ret; - } - - ret = sd_bus_send(NULL, reply, NULL); - if (ret < 0) { - return ret; - } - - sd_bus_message_unref(reply); - return 0; -} - -static const sd_bus_vtable screenshot_vtable[] = { - SD_BUS_VTABLE_START(0), - SD_BUS_METHOD("Screenshot", "ossa{sv}", "ua{sv}", method_screenshot, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("PickColor", "ossa{sv}", "ua{sv}", method_pick_color, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_PROPERTY("version", "u", NULL, - offsetof(struct xdpw_state, screenshot_version), - SD_BUS_VTABLE_PROPERTY_CONST), - SD_BUS_VTABLE_END -}; - -int xdpw_screenshot_init(struct xdpw_state *state) { - // TODO: cleanup - sd_bus_slot *slot = NULL; - return sd_bus_add_object_vtable(state->bus, &slot, object_path, interface_name, - screenshot_vtable, state); -} diff --git a/src/shared/ScreencopyShared.cpp b/src/shared/ScreencopyShared.cpp new file mode 100644 index 0000000..063f7c1 --- /dev/null +++ b/src/shared/ScreencopyShared.cpp @@ -0,0 +1,31 @@ +#include "ScreencopyShared.hpp" +#include "../helpers/MiscFunctions.hpp" + +SSelectionData promptForScreencopySelection() { + SSelectionData data; + + const auto RETVAL = execAndGet("hyprland-share-picker"); + + if (RETVAL.find("screen:") == 0) { + data.type = TYPE_OUTPUT; + data.output = RETVAL.substr(7); + } else if (RETVAL.find("window:") == 0) { + // todo + } else if (RETVAL.find("region:") == 0) { + std::string running = RETVAL; + running = running.substr(7); + data.type = TYPE_GEOMETRY; + data.output = running.substr(0, running.find_first_of('@')); + running = running.substr(running.find_first_of('@') + 1); + + data.x = std::stoi(running.substr(running.find_first_of(','))); + running = running.substr(running.find_first_of(',') + 1); + data.y = std::stoi(running.substr(running.find_first_of(','))); + running = running.substr(running.find_first_of(',') + 1); + data.w = std::stoi(running.substr(running.find_first_of(','))); + running = running.substr(running.find_first_of(',') + 1); + data.h = std::stoi(running); + } + + return data; +} \ No newline at end of file diff --git a/src/shared/ScreencopyShared.hpp b/src/shared/ScreencopyShared.hpp new file mode 100644 index 0000000..c91e622 --- /dev/null +++ b/src/shared/ScreencopyShared.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +enum eSelectionType +{ + TYPE_INVALID = -1, + TYPE_OUTPUT = 0, + TYPE_WINDOW, + TYPE_GEOMETRY, + TYPE_WORKSPACE, +}; + +struct SSelectionData { + eSelectionType type = TYPE_INVALID; + std::string output; + uint64_t windowHandle = 0; + uint32_t x = 0, y = 0, w = 0, h = 0; // for TYPE_GEOMETRY +}; + +SSelectionData promptForScreencopySelection(); \ No newline at end of file diff --git a/subprojects/sdbus-cpp b/subprojects/sdbus-cpp new file mode 160000 index 0000000..0eda855 --- /dev/null +++ b/subprojects/sdbus-cpp @@ -0,0 +1 @@ +Subproject commit 0eda85574546d19d9f06d6d5418bc192b3846f96