begin work

This commit is contained in:
vaxerski 2023-08-26 23:50:30 +02:00
parent 57a3a41ba6
commit b50663fcfa
54 changed files with 2898 additions and 5322 deletions

65
.clang-format Normal file
View 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
View file

@ -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
View 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)
##

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 */

View file

@ -1,10 +0,0 @@
#ifndef SCREENSHOT_H
#define SCREENSHOT_H
struct xdpw_ppm_pixel {
int max_color_value;
unsigned char red, green, blue;
};
#endif

View file

@ -1,6 +0,0 @@
#ifndef SCREENSHOT_COMMON_H
#define SCREENSHOT_COMMON_H
#define XDP_SHOT_PROTO_VER 2
#endif

View file

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

View file

@ -1,6 +0,0 @@
#pragma once
#include <stdarg.h>
#include <stddef.h>
char *getFormat(const char *fmt, ...);

View file

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

View file

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

View file

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

View file

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

View file

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

View 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,
};

View 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

View 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,
};

View 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

View 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,
};

View 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

View 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,
};

View 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

View 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, &registryListener, 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();
}

View 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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View file

@ -0,0 +1 @@
#include "Log.hpp"

32
src/helpers/Log.hpp Normal file
View 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";
}
};

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

View file

@ -0,0 +1,4 @@
#pragma once
#include <string>
std::string execAndGet(const char* cmd);

13
src/main.cpp Normal file
View 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
View 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");
}

View 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";
};

View file

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

View file

@ -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, &params[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, &params[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;
}
}

View file

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

View file

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

View file

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

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

View 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

@ -0,0 +1 @@
Subproject commit 0eda85574546d19d9f06d6d5418bc192b3846f96