mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-12-25 10:59:48 +01:00
begin work
This commit is contained in:
parent
57a3a41ba6
commit
b50663fcfa
54 changed files with 2898 additions and 5322 deletions
65
.clang-format
Normal file
65
.clang-format
Normal file
|
@ -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
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -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
|
||||
|
|
72
CMakeLists.txt
Normal file
72
CMakeLists.txt
Normal file
|
@ -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)
|
||||
##
|
|
@ -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
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef FPS_LIMIT_H
|
||||
#define FPS_LIMIT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
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
|
|
@ -1,31 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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);
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef LOGGER_H
|
||||
#define LOGGER_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#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
|
|
@ -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
|
|
@ -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
|
|
@ -1,238 +0,0 @@
|
|||
#ifndef SCREENCAST_COMMON_H
|
||||
#define SCREENCAST_COMMON_H
|
||||
|
||||
#include <gbm.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
#include <stdbool.h>
|
||||
#include <wayland-client-protocol.h>
|
||||
#include <xf86drm.h>
|
||||
|
||||
#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 */
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef SCREENSHOT_H
|
||||
#define SCREENSHOT_H
|
||||
|
||||
|
||||
struct xdpw_ppm_pixel {
|
||||
int max_color_value;
|
||||
unsigned char red, green, blue;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef SCREENSHOT_COMMON_H
|
||||
#define SCREENSHOT_COMMON_H
|
||||
|
||||
#define XDP_SHOT_PROTO_VER 2
|
||||
|
||||
#endif
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef TIMESPEC_UTIL_H
|
||||
#define TIMESPEC_UTIL_H
|
||||
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
|
@ -1,6 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
|
||||
char *getFormat(const char *fmt, ...);
|
|
@ -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
|
|
@ -1,78 +0,0 @@
|
|||
#ifndef XDPW_H
|
||||
#define XDPW_H
|
||||
|
||||
#include <wayland-client.h>
|
||||
#ifdef HAVE_LIBSYSTEMD
|
||||
#include <systemd/sd-bus.h>
|
||||
#elif HAVE_LIBELOGIND
|
||||
#include <elogind/sd-bus.h>
|
||||
#elif HAVE_BASU
|
||||
#include <basu/sd-bus.h>
|
||||
#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
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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]}
|
||||
'';
|
||||
}
|
|
@ -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;};
|
||||
};
|
||||
}
|
85
protocols/hyprland-global-shortcuts-v1-protocol.c
Normal file
85
protocols/hyprland-global-shortcuts-v1-protocol.c
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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,
|
||||
};
|
||||
|
274
protocols/hyprland-global-shortcuts-v1-protocol.h
Normal file
274
protocols/hyprland-global-shortcuts-v1-protocol.h
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
* <pre>
|
||||
*
|
||||
* 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.
|
||||
* </pre>
|
||||
*/
|
||||
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
|
98
protocols/hyprland-toplevel-export-v1-protocol.c
Normal file
98
protocols/hyprland-toplevel-export-v1-protocol.c
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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,
|
||||
};
|
||||
|
475
protocols/hyprland-toplevel-export-v1-protocol.h
Normal file
475
protocols/hyprland-toplevel-export-v1-protocol.h
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
* <pre>
|
||||
*
|
||||
* 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.
|
||||
* </pre>
|
||||
*/
|
||||
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
|
106
protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.c
Normal file
106
protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.c
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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,
|
||||
};
|
||||
|
615
protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h
Normal file
615
protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
* <pre>
|
||||
*
|
||||
* 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.
|
||||
* </pre>
|
||||
*/
|
||||
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
|
97
protocols/wlr-screencopy-unstable-v1-protocol.c
Normal file
97
protocols/wlr-screencopy-unstable-v1-protocol.c
Normal file
|
@ -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 <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#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,
|
||||
};
|
||||
|
491
protocols/wlr-screencopy-unstable-v1-protocol.h
Normal file
491
protocols/wlr-screencopy-unstable-v1-protocol.h
Normal file
|
@ -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 <stdint.h>
|
||||
#include <stddef.h>
|
||||
#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
|
||||
* <pre>
|
||||
*
|
||||
* 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.
|
||||
* </pre>
|
||||
*/
|
||||
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
|
86
src/core/PortalManager.cpp
Normal file
86
src/core/PortalManager.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#include "PortalManager.hpp"
|
||||
#include "../helpers/Log.hpp"
|
||||
|
||||
#include <protocols/hyprland-global-shortcuts-v1-protocol.h>
|
||||
#include <protocols/hyprland-toplevel-export-v1-protocol.h>
|
||||
#include <protocols/wlr-foreign-toplevel-management-unstable-v1-protocol.h>
|
||||
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
|
||||
|
||||
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<CScreencopyPortal>((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();
|
||||
}
|
29
src/core/PortalManager.hpp
Normal file
29
src/core/PortalManager.hpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#include <wayland-client.h>
|
||||
|
||||
#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<sdbus::IConnection> m_pConnection;
|
||||
|
||||
struct {
|
||||
wl_display* display = nullptr;
|
||||
} m_sWaylandConnection;
|
||||
|
||||
struct {
|
||||
std::unique_ptr<CScreencopyPortal> screencopy;
|
||||
} m_sPortals;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CPortalManager> g_pPortalManager;
|
|
@ -1,194 +0,0 @@
|
|||
#include "config.h"
|
||||
#include "xdpw.h"
|
||||
#include "logger.h"
|
||||
#include "screencast_common.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ini.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -1,85 +0,0 @@
|
|||
#include "logger.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
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);
|
||||
}
|
291
src/core/main.c
291
src/core/main.c
|
@ -1,291 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <poll.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/timerfd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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=<loglevel> Select log level (default is ERROR).\n"
|
||||
" QUIET, ERROR, WARN, INFO, DEBUG, TRACE\n"
|
||||
" -c, --config=<config file> 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;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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);
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#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);
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
#include <poll.h>
|
||||
#include <wayland-util.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#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);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
#include "timespec_util.h"
|
||||
#include <time.h>
|
||||
|
||||
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;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
|
@ -1,537 +0,0 @@
|
|||
#include "include/global_shortcuts.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#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
|
||||
}
|
1
src/helpers/Log.cpp
Normal file
1
src/helpers/Log.cpp
Normal file
|
@ -0,0 +1 @@
|
|||
#include "Log.hpp"
|
32
src/helpers/Log.hpp
Normal file
32
src/helpers/Log.hpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
enum eLogLevel
|
||||
{
|
||||
INFO = 0,
|
||||
LOG,
|
||||
WARN,
|
||||
ERR,
|
||||
CRIT
|
||||
};
|
||||
|
||||
namespace Debug {
|
||||
template <typename... Args>
|
||||
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";
|
||||
}
|
||||
};
|
17
src/helpers/MiscFunctions.cpp
Normal file
17
src/helpers/MiscFunctions.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "MiscFunctions.hpp"
|
||||
#include <memory>
|
||||
#include "../helpers/Log.hpp"
|
||||
|
||||
std::string execAndGet(const char* cmd) {
|
||||
std::array<char, 128> buffer;
|
||||
std::string result;
|
||||
const std::unique_ptr<FILE, decltype(&pclose)> 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;
|
||||
}
|
4
src/helpers/MiscFunctions.hpp
Normal file
4
src/helpers/MiscFunctions.hpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
std::string execAndGet(const char* cmd);
|
13
src/main.cpp
Normal file
13
src/main.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#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<CPortalManager>();
|
||||
g_pPortalManager->init();
|
||||
|
||||
return 0;
|
||||
}
|
230
src/portals/Screencopy.cpp
Normal file
230
src/portals/Screencopy.cpp
Normal file
|
@ -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<SSession>(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<std::string, sdbus::Variant>{};
|
||||
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<std::string, sdbus::Variant> 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<uint32_t>();
|
||||
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<sdbus::Struct<std::string, uint32_t, sdbus::Variant>>();
|
||||
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<sdbus::Struct<std::string, uint32_t, std::string, bool, uint64_t>>();
|
||||
|
||||
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<uint32_t>();
|
||||
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<std::string, sdbus::Variant>{};
|
||||
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<std::string, sdbus::Variant> options;
|
||||
|
||||
if (PSESSION->persistMode != 0) {
|
||||
// give them a token :)
|
||||
sdbus::Struct<std::string, uint32_t, std::string, bool, uint64_t> structData{"todo", (uint32_t)PSESSION->selection.windowHandle, PSESSION->selection.output,
|
||||
(bool)PSESSION->cursorMode, (uint64_t)time(NULL)};
|
||||
sdbus::Variant restoreData{structData};
|
||||
sdbus::Struct<std::string, uint32_t, sdbus::Variant> 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");
|
||||
}
|
51
src/portals/Screencopy.hpp
Normal file
51
src/portals/Screencopy.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
|
||||
#include <protocols/wlr-screencopy-unstable-v1-protocol.h>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
#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<sdbus::IObject> request, session;
|
||||
SSelectionData selection;
|
||||
|
||||
void onCloseRequest(sdbus::MethodCall&);
|
||||
void onCloseSession(sdbus::MethodCall&);
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<sdbus::IObject> m_pObject;
|
||||
|
||||
std::vector<std::unique_ptr<SSession>> 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";
|
||||
};
|
|
@ -1,70 +0,0 @@
|
|||
#include "fps_limit.h"
|
||||
#include "logger.h"
|
||||
#include "timespec_util.h"
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,634 +0,0 @@
|
|||
#include "pipewire_screencast.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <pipewire/pipewire.h>
|
||||
#include <spa/buffer/meta.h>
|
||||
#include <spa/param/format-utils.h>
|
||||
#include <spa/param/props.h>
|
||||
#include <spa/param/video/format-utils.h>
|
||||
#include <spa/pod/dynamic.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<<SPA_DATA_DmaBuf;
|
||||
assert(cast->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<<SPA_DATA_MemFd;
|
||||
}
|
||||
|
||||
logprint(DEBUG, "pipewire: Format negotiated:");
|
||||
logprint(DEBUG, "pipewire: buffer_type: %u (%u)", cast->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;
|
||||
}
|
||||
}
|
|
@ -1,731 +0,0 @@
|
|||
#include "screencast.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <drm_fourcc.h>
|
||||
#include <errno.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <uuid/uuid.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -1,426 +0,0 @@
|
|||
#include "xdpw.h"
|
||||
#include "screencast_common.h"
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#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;
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,300 +0,0 @@
|
|||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#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);
|
||||
}
|
31
src/shared/ScreencopyShared.cpp
Normal file
31
src/shared/ScreencopyShared.cpp
Normal file
|
@ -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;
|
||||
}
|
22
src/shared/ScreencopyShared.hpp
Normal file
22
src/shared/ScreencopyShared.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
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();
|
1
subprojects/sdbus-cpp
Submodule
1
subprojects/sdbus-cpp
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 0eda85574546d19d9f06d6d5418bc192b3846f96
|
Loading…
Reference in a new issue