mirror of
https://github.com/hyprwm/xdg-desktop-portal-hyprland.git
synced 2024-11-24 07:05:58 +01:00
9dfb9e125b
fixes #91
328 lines
14 KiB
C++
328 lines
14 KiB
C++
#include "ScreencopyShared.hpp"
|
|
#include "../helpers/MiscFunctions.hpp"
|
|
#include <wayland-client.h>
|
|
#include "../helpers/Log.hpp"
|
|
#include <libdrm/drm_fourcc.h>
|
|
#include <assert.h>
|
|
#include "../core/PortalManager.hpp"
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
std::string sanitizeNameForWindowList(const std::string& name) {
|
|
std::string result = name;
|
|
for (size_t i = 1; i < result.size(); ++i) {
|
|
if (result[i - 1] == '>' && result[i] == ']')
|
|
result[i] = ' ';
|
|
if (result[i] == '\"')
|
|
result[i] = ' ';
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string buildWindowList() {
|
|
std::string result = "";
|
|
if (!g_pPortalManager->m_sPortals.screencopy->hasToplevelCapabilities())
|
|
return result;
|
|
|
|
for (auto& e : g_pPortalManager->m_sHelpers.toplevel->m_vToplevels) {
|
|
|
|
result += std::format("{}[HC>]{}[HT>]{}[HE>]", (uint32_t)(((uint64_t)e->handle) & 0xFFFFFFFF), sanitizeNameForWindowList(e->windowClass),
|
|
sanitizeNameForWindowList(e->windowTitle));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
SSelectionData promptForScreencopySelection() {
|
|
SSelectionData data;
|
|
|
|
const char* WAYLAND_DISPLAY = getenv("WAYLAND_DISPLAY");
|
|
const char* XCURSOR_SIZE = getenv("XCURSOR_SIZE");
|
|
const char* HYPRLAND_INSTANCE_SIGNATURE = getenv("HYPRLAND_INSTANCE_SIGNATURE");
|
|
|
|
std::string cmd =
|
|
std::format("WAYLAND_DISPLAY={} QT_QPA_PLATFORM=\"wayland\" XCURSOR_SIZE={} HYPRLAND_INSTANCE_SIGNATURE={} XDPH_WINDOW_SHARING_LIST=\"{}\" hyprland-share-picker 2>&1",
|
|
WAYLAND_DISPLAY ? WAYLAND_DISPLAY : "", XCURSOR_SIZE ? XCURSOR_SIZE : "24", HYPRLAND_INSTANCE_SIGNATURE ? HYPRLAND_INSTANCE_SIGNATURE : "0", buildWindowList());
|
|
|
|
const auto RETVAL = execAndGet(cmd.c_str());
|
|
|
|
if (!RETVAL.contains("[SELECTION]")) {
|
|
// failed
|
|
|
|
if (RETVAL.contains("qt.qpa.plugin: Could not find the Qt platform plugin")) {
|
|
// prompt the user to install qt5-wayland and qt6-wayland
|
|
addHyprlandNotification("3", 7000, "0", "[xdph] Could not open the picker: qt5-wayland or qt6-wayland doesn't seem to be installed.");
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
const auto SELECTION = RETVAL.substr(RETVAL.find("[SELECTION]") + 11);
|
|
|
|
Debug::log(LOG, "[sc] Selection: {}", SELECTION);
|
|
|
|
const auto FLAGS = SELECTION.substr(0, SELECTION.find_first_of('/'));
|
|
const auto SEL = SELECTION.substr(SELECTION.find_first_of('/') + 1);
|
|
|
|
for (auto& flag : FLAGS) {
|
|
if (flag == 'r') {
|
|
data.allowToken = true;
|
|
} else {
|
|
Debug::log(LOG, "[screencopy] unknown flag from share-picker: {}", flag);
|
|
}
|
|
}
|
|
|
|
if (SEL.find("screen:") == 0) {
|
|
data.type = TYPE_OUTPUT;
|
|
data.output = SEL.substr(7);
|
|
|
|
data.output.pop_back();
|
|
} else if (SEL.find("window:") == 0) {
|
|
data.type = TYPE_WINDOW;
|
|
uint32_t handleLo = std::stoull(SEL.substr(7));
|
|
data.windowHandle = nullptr;
|
|
|
|
for (auto& e : g_pPortalManager->m_sHelpers.toplevel->m_vToplevels) {
|
|
uint32_t handleLoE = (uint32_t)(((uint64_t)e->handle) & 0xFFFFFFFF);
|
|
|
|
if (handleLoE == handleLo) {
|
|
data.windowHandle = e->handle;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if (SEL.find("region:") == 0) {
|
|
std::string running = SEL;
|
|
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(0, running.find_first_of(',')));
|
|
running = running.substr(running.find_first_of(',') + 1);
|
|
data.y = std::stoi(running.substr(0, running.find_first_of(',')));
|
|
running = running.substr(running.find_first_of(',') + 1);
|
|
data.w = std::stoi(running.substr(0, running.find_first_of(',')));
|
|
running = running.substr(running.find_first_of(',') + 1);
|
|
data.h = std::stoi(running);
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
wl_shm_format wlSHMFromDrmFourcc(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 (wl_shm_format)format;
|
|
default: Debug::log(ERR, "[screencopy] Unknown format {}", format); exit(1);
|
|
}
|
|
}
|
|
|
|
uint32_t drmFourccFromSHM(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: Debug::log(ERR, "[screencopy] Unknown format {}", (int)format); exit(1);
|
|
}
|
|
}
|
|
|
|
spa_video_format pwFromDrmFourcc(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: Debug::log(ERR, "[screencopy] Unknown format {}", (int)format); exit(1);
|
|
}
|
|
}
|
|
|
|
std::string getRandName(std::string prefix) {
|
|
std::srand(time(NULL));
|
|
return prefix +
|
|
std::format("{}{}{}{}{}{}", (int)(std::rand() % 10), (int)(std::rand() % 10), (int)(std::rand() % 10), (int)(std::rand() % 10), (int)(std::rand() % 10),
|
|
(int)(std::rand() % 10));
|
|
}
|
|
|
|
spa_video_format pwStripAlpha(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;
|
|
}
|
|
}
|
|
|
|
spa_pod* build_buffer(spa_pod_builder* b, uint32_t blocks, uint32_t size, uint32_t stride, uint32_t datatype) {
|
|
assert(blocks > 0);
|
|
assert(datatype > 0);
|
|
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(XDPH_PWR_BUFFERS, XDPH_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(XDPH_PWR_ALIGN), 0);
|
|
spa_pod_builder_add(b, SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(datatype), 0);
|
|
return (spa_pod*)spa_pod_builder_pop(b, &f[0]);
|
|
}
|
|
|
|
spa_pod* fixate_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifier) {
|
|
spa_pod_frame f[1];
|
|
|
|
spa_video_format format_without_alpha = pwStripAlpha(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*)spa_pod_builder_pop(b, &f[0]);
|
|
}
|
|
|
|
spa_pod* build_format(spa_pod_builder* b, spa_video_format format, uint32_t width, uint32_t height, uint32_t framerate, uint64_t* modifiers, int modifier_count) {
|
|
spa_pod_frame f[2];
|
|
int i, c;
|
|
|
|
spa_video_format format_without_alpha = pwStripAlpha(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*)spa_pod_builder_pop(b, &f[0]);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
int anonymous_shm_open() {
|
|
char name[] = "/xdph-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;
|
|
}
|
|
|
|
wl_buffer* import_wl_shm_buffer(int fd, wl_shm_format fmt, int width, int height, int stride) {
|
|
int size = stride * height;
|
|
|
|
if (fd < 0)
|
|
return NULL;
|
|
|
|
wl_shm_pool* pool = wl_shm_create_pool(g_pPortalManager->m_sWaylandConnection.shm, fd, size);
|
|
wl_buffer* buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, fmt);
|
|
wl_shm_pool_destroy(pool);
|
|
|
|
return buffer;
|
|
}
|